]>
Dogcows Code - chaz/openbox/blob - openbox/place.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 place.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 See the COPYING file for a copy of the GNU General Public License.
31 static void add_choice(guint
*choice
, guint mychoice
)
34 for (i
= 0; i
< screen_num_monitors
; ++i
) {
35 if (choice
[i
] == mychoice
)
37 else if (choice
[i
] == screen_num_monitors
) {
44 static Rect
*pick_pointer_head(ObClient
*c
)
49 if (screen_pointer_pos(&px
, &py
)) {
50 for (i
= 0; i
< screen_num_monitors
; ++i
) {
51 Rect
*monitor
= screen_physical_area_monitor(i
);
52 gboolean contain
= RECT_CONTAINS(*monitor
, px
, py
);
55 return screen_area(c
->desktop
, i
, NULL
);
57 g_assert_not_reached();
62 /*! Pick a monitor to place a window on. */
63 static Rect
**pick_head(ObClient
*c
)
71 area
= g_new(Rect
*, screen_num_monitors
);
72 choice
= g_new(guint
, screen_num_monitors
);
73 for (i
= 0; i
< screen_num_monitors
; ++i
)
74 choice
[i
] = screen_num_monitors
; /* make them all invalid to start */
76 /* try direct parent first */
77 if ((p
= client_direct_parent(c
))) {
78 add_choice(choice
, client_monitor(p
));
79 ob_debug("placement adding choice %d for parent",
83 /* more than one window in its group (more than just this window) */
84 if (client_has_group_siblings(c
)) {
87 /* try on the client's desktop */
88 for (it
= c
->group
->members
; it
; it
= g_slist_next(it
)) {
89 ObClient
*itc
= it
->data
;
91 (itc
->desktop
== c
->desktop
||
92 itc
->desktop
== DESKTOP_ALL
|| c
->desktop
== DESKTOP_ALL
))
94 add_choice(choice
, client_monitor(it
->data
));
95 ob_debug("placement adding choice %d for group sibling",
96 client_monitor(it
->data
));
100 /* try on all desktops */
101 for (it
= c
->group
->members
; it
; it
= g_slist_next(it
)) {
102 ObClient
*itc
= it
->data
;
104 add_choice(choice
, client_monitor(it
->data
));
105 ob_debug("placement adding choice %d for group sibling on "
106 "another desktop", client_monitor(it
->data
));
111 /* skip this if placing by the mouse position */
112 if (focus_client
&& client_normal(focus_client
) &&
113 config_place_monitor
!= OB_PLACE_MONITOR_MOUSE
)
115 add_choice(choice
, client_monitor(focus_client
));
116 ob_debug("placement adding choice %d for normal focused window",
117 client_monitor(focus_client
));
120 screen_pointer_pos(&px
, &py
);
122 for (i
= 0; i
< screen_num_monitors
; i
++) {
123 Rect
*monitor
= screen_physical_area_monitor(i
);
124 gboolean contain
= RECT_CONTAINS(*monitor
, px
, py
);
127 add_choice(choice
, i
);
128 ob_debug("placement adding choice %d for mouse pointer", i
);
133 /* add any leftover choices */
134 for (i
= 0; i
< screen_num_monitors
; ++i
)
135 add_choice(choice
, i
);
137 for (i
= 0; i
< screen_num_monitors
; ++i
)
138 area
[i
] = screen_area(c
->desktop
, choice
[i
], NULL
);
145 static gboolean
place_random(ObClient
*client
, gint
*x
, gint
*y
)
151 areas
= pick_head(client
);
152 i
= (config_place_monitor
!= OB_PLACE_MONITOR_ANY
) ?
153 0 : g_random_int_range(0, screen_num_monitors
);
157 r
= areas
[i
]->x
+ areas
[i
]->width
- client
->frame
->area
.width
;
158 b
= areas
[i
]->y
+ areas
[i
]->height
- client
->frame
->area
.height
;
160 if (r
> l
) *x
= g_random_int_range(l
, r
+ 1);
161 else *x
= areas
[i
]->x
;
162 if (b
> t
) *y
= g_random_int_range(t
, b
+ 1);
163 else *y
= areas
[i
]->y
;
165 for (i
= 0; i
< screen_num_monitors
; ++i
)
172 static GSList
* area_add(GSList
*list
, Rect
*a
)
174 Rect
*r
= g_new(Rect
, 1);
176 return g_slist_prepend(list
, r
);
179 static GSList
* area_remove(GSList
*list
, Rect
*a
)
182 GSList
*result
= NULL
;
184 for (sit
= list
; sit
; sit
= g_slist_next(sit
)) {
187 if (!RECT_INTERSECTS_RECT(*r
, *a
)) {
188 result
= g_slist_prepend(result
, r
);
189 /* dont free r, it's moved to the result list */
193 /* Use an intersection of a and r to determine the space
194 around r that we can use.
196 NOTE: the spaces calculated can overlap.
199 RECT_SET_INTERSECTION(isect
, *r
, *a
);
201 if (RECT_LEFT(isect
) > RECT_LEFT(*r
)) {
202 RECT_SET(extra
, r
->x
, r
->y
,
203 RECT_LEFT(isect
) - r
->x
, r
->height
);
204 result
= area_add(result
, &extra
);
207 if (RECT_TOP(isect
) > RECT_TOP(*r
)) {
208 RECT_SET(extra
, r
->x
, r
->y
,
209 r
->width
, RECT_TOP(isect
) - r
->y
+ 1);
210 result
= area_add(result
, &extra
);
213 if (RECT_RIGHT(isect
) < RECT_RIGHT(*r
)) {
214 RECT_SET(extra
, RECT_RIGHT(isect
) + 1, r
->y
,
215 RECT_RIGHT(*r
) - RECT_RIGHT(isect
), r
->height
);
216 result
= area_add(result
, &extra
);
219 if (RECT_BOTTOM(isect
) < RECT_BOTTOM(*r
)) {
220 RECT_SET(extra
, r
->x
, RECT_BOTTOM(isect
) + 1,
221 r
->width
, RECT_BOTTOM(*r
) - RECT_BOTTOM(isect
));
222 result
= area_add(result
, &extra
);
225 /* 'r' is not being added to the result list, so free it */
234 IGNORE_FULLSCREEN
= 1,
235 IGNORE_MAXIMIZED
= 2,
237 /*IGNORE_SHADED = 3,*/
240 /*IGNORE_NONFOCUS = 1 << 5,*/
245 static gboolean
place_nooverlap(ObClient
*c
, gint
*x
, gint
*y
)
251 GSList
*spaces
= NULL
, *sit
, *maxit
;
254 areas
= pick_head(c
);
259 /* try ignoring different things to find empty space */
260 for (ignore
= 0; ignore
< IGNORE_END
&& !ret
; ignore
++) {
261 /* try all monitors in order of preference, but only the first one
262 if config_place_monitor is MOUSE or ACTIVE */
263 for (i
= 0; (i
< (config_place_monitor
!= OB_PLACE_MONITOR_ANY
?
264 1 : screen_num_monitors
) && !ret
); ++i
)
268 /* add the whole monitor */
269 spaces
= area_add(spaces
, areas
[i
]);
271 /* go thru all the windows */
272 for (it
= client_list
; it
; it
= g_list_next(it
)) {
273 ObClient
*test
= it
->data
;
275 /* should we ignore this client? */
276 if (screen_showing_desktop
) continue;
277 if (c
== test
) continue;
278 if (test
->iconic
) continue;
279 if (c
->desktop
!= DESKTOP_ALL
) {
280 if (test
->desktop
!= c
->desktop
&&
281 test
->desktop
!= DESKTOP_ALL
) continue;
283 if (test
->desktop
!= screen_desktop
&&
284 test
->desktop
!= DESKTOP_ALL
) continue;
286 if (test
->type
== OB_CLIENT_TYPE_SPLASH
||
287 test
->type
== OB_CLIENT_TYPE_DESKTOP
) continue;
290 if ((ignore
>= IGNORE_FULLSCREEN
) &&
291 test
->fullscreen
) continue;
292 if ((ignore
>= IGNORE_MAXIMIZED
) &&
293 test
->max_horz
&& test
->max_vert
) continue;
294 if ((ignore
>= IGNORE_MENUTOOL
) &&
295 (test
->type
== OB_CLIENT_TYPE_MENU
||
296 test
->type
== OB_CLIENT_TYPE_TOOLBAR
) &&
297 client_has_parent(c
)) continue;
299 if ((ignore >= IGNORE_SHADED) &&
300 test->shaded) continue;
302 if ((ignore
>= IGNORE_NONGROUP
) &&
303 client_has_group_siblings(c
) &&
304 test
->group
!= c
->group
) continue;
305 if ((ignore
>= IGNORE_BELOW
) &&
306 test
->layer
< c
->layer
) continue;
308 if ((ignore >= IGNORE_NONFOCUS) &&
309 focus_client != test) continue;
311 /* don't ignore this window, so remove it from the available
313 spaces
= area_remove(spaces
, &test
->frame
->area
);
316 if (ignore
< IGNORE_DOCK
) {
319 spaces
= area_remove(spaces
, &a
);
322 for (sit
= spaces
; sit
; sit
= g_slist_next(sit
)) {
325 if (r
->width
>= c
->frame
->area
.width
&&
326 r
->height
>= c
->frame
->area
.height
&&
327 r
->width
* r
->height
> maxsize
)
329 maxsize
= r
->width
* r
->height
;
335 Rect
*r
= maxit
->data
;
337 /* center it in the area */
340 if (config_place_center
) {
341 *x
+= (r
->width
- c
->frame
->area
.width
) / 2;
342 *y
+= (r
->height
- c
->frame
->area
.height
) / 2;
348 g_free(spaces
->data
);
349 spaces
= g_slist_delete_link(spaces
, spaces
);
354 for (i
= 0; i
< screen_num_monitors
; ++i
)
360 static gboolean
place_under_mouse(ObClient
*client
, gint
*x
, gint
*y
)
366 if (!screen_pointer_pos(&px
, &py
))
368 area
= pick_pointer_head(client
);
372 r
= area
->x
+ area
->width
- client
->frame
->area
.width
;
373 b
= area
->y
+ area
->height
- client
->frame
->area
.height
;
375 *x
= px
- client
->area
.width
/ 2 - client
->frame
->size
.left
;
376 *x
= MIN(MAX(*x
, l
), r
);
377 *y
= py
- client
->area
.height
/ 2 - client
->frame
->size
.top
;
378 *y
= MIN(MAX(*y
, t
), b
);
383 static gboolean
place_per_app_setting(ObClient
*client
, gint
*x
, gint
*y
,
384 ObAppSettings
*settings
)
388 if (!settings
|| (settings
&& !settings
->pos_given
))
391 /* Find which head the pointer is on */
392 if (settings
->monitor
== 0)
393 /* this can return NULL */
394 screen
= pick_pointer_head(client
);
395 else if (settings
->monitor
> 0 &&
396 (guint
)settings
->monitor
<= screen_num_monitors
)
397 screen
= screen_area(client
->desktop
, (guint
)settings
->monitor
- 1,
400 /* if we have't found a screen yet.. */
405 areas
= pick_head(client
);
408 /* don't free the first one, it's being set as "screen" */
409 for (i
= 1; i
< screen_num_monitors
; ++i
)
414 if (settings
->position
.x
.center
)
415 *x
= screen
->x
+ screen
->width
/ 2 - client
->area
.width
/ 2;
416 else if (settings
->position
.x
.opposite
)
417 *x
= screen
->x
+ screen
->width
- client
->frame
->area
.width
-
418 settings
->position
.x
.pos
;
420 *x
= screen
->x
+ settings
->position
.x
.pos
;
422 if (settings
->position
.y
.center
)
423 *y
= screen
->y
+ screen
->height
/ 2 - client
->area
.height
/ 2;
424 else if (settings
->position
.y
.opposite
)
425 *y
= screen
->y
+ screen
->height
- client
->frame
->area
.height
-
426 settings
->position
.y
.pos
;
428 *y
= screen
->y
+ settings
->position
.y
.pos
;
434 static gboolean
place_transient_splash(ObClient
*client
, gint
*x
, gint
*y
)
436 if (client
->type
== OB_CLIENT_TYPE_DIALOG
) {
438 gboolean first
= TRUE
;
440 for (it
= client
->parents
; it
; it
= g_slist_next(it
)) {
441 ObClient
*m
= it
->data
;
444 l
= RECT_LEFT(m
->frame
->area
);
445 t
= RECT_TOP(m
->frame
->area
);
446 r
= RECT_RIGHT(m
->frame
->area
);
447 b
= RECT_BOTTOM(m
->frame
->area
);
450 l
= MIN(l
, RECT_LEFT(m
->frame
->area
));
451 t
= MIN(t
, RECT_TOP(m
->frame
->area
));
452 r
= MAX(r
, RECT_RIGHT(m
->frame
->area
));
453 b
= MAX(b
, RECT_BOTTOM(m
->frame
->area
));
457 *x
= ((r
+ 1 - l
) - client
->frame
->area
.width
) / 2 + l
;
458 *y
= ((b
+ 1 - t
) - client
->frame
->area
.height
) / 2 + t
;
464 if (client
->type
== OB_CLIENT_TYPE_DIALOG
||
465 client
->type
== OB_CLIENT_TYPE_SPLASH
)
470 areas
= pick_head(client
);
472 *x
= (areas
[0]->width
- client
->frame
->area
.width
) / 2 + areas
[0]->x
;
473 *y
= (areas
[0]->height
- client
->frame
->area
.height
) / 2 + areas
[0]->y
;
475 for (i
= 0; i
< screen_num_monitors
; ++i
)
484 /* Return TRUE if we want client.c to enforce on-screen-keeping */
485 gboolean
place_client(ObClient
*client
, gint
*x
, gint
*y
,
486 ObAppSettings
*settings
)
489 gboolean userplaced
= FALSE
;
491 /* per-app settings override program specified position
492 * but not user specified, unless pos_force is enabled */
493 if (((client
->positioned
& USPosition
) &&
494 !(settings
&& settings
->pos_given
&& settings
->pos_force
)) ||
495 ((client
->positioned
& PPosition
) &&
496 !(settings
&& settings
->pos_given
)))
499 /* try a number of methods */
500 ret
= place_transient_splash(client
, x
, y
) ||
501 (userplaced
= place_per_app_setting(client
, x
, y
, settings
)) ||
502 (config_place_policy
== OB_PLACE_POLICY_MOUSE
&&
503 place_under_mouse(client
, x
, y
)) ||
504 place_nooverlap(client
, x
, y
) ||
505 place_random(client
, x
, y
);
508 /* get where the client should be */
509 frame_frame_gravity(client
->frame
, x
, y
);
This page took 0.061658 seconds and 4 git commands to generate.