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 Rect
*pick_pointer_head(ObClient
*c
)
33 return screen_area(c
->desktop
, screen_monitor_pointer(), NULL
);
36 /* use the following priority lists for pick_head()
38 When a window is being placed in the FOREGROUND, use a monitor chosen in
41 2. same monitor as parent
42 3. primary monitor if placement=PRIMARY
43 active monitor if placement=ACTIVE
44 pointer monitor if placement=MOUSE
46 5. other monitors where the window has group members on the same desktop
47 6. other monitors where the window has group members on other desktops
50 When a window is being placed in the BACKGROUND, use a monitor chosen in the
53 2. same monitor as parent
54 3. other monitors where the window has group members on the same desktop
55 3a. primary monitor in this set
56 3b. other monitors in this set
57 4. other monitors where the window has group members on other desktops
58 4a. primary monitor in this set
59 4b. other monitors in this set
61 5a. primary monitor in this set
62 5b. other monitors in this set
65 /*! One for each possible head, used to sort them in order of precedence. */
71 /*! Flags for ObPlaceHead */
73 HEAD_PARENT
= 1 << 0, /* parent's monitor */
74 HEAD_PLACED
= 1 << 1, /* chosen monitor by placement */
75 HEAD_PRIMARY
= 1 << 2, /* primary monitor */
76 HEAD_GROUP_DESK
= 1 << 3, /* has a group member on the same desktop */
77 HEAD_GROUP
= 1 << 4, /* has a group member on another desktop */
78 HEAD_PERAPP
= 1 << 5, /* chosen by per-app settings */
81 gint
cmp_foreground(const void *a
, const void *b
)
83 const ObPlaceHead
*h1
= a
;
84 const ObPlaceHead
*h2
= b
;
87 if (h1
->monitor
== h2
->monitor
) return 0;
89 if (h1
->flags
& HEAD_PERAPP
) --i
;
90 if (h2
->flags
& HEAD_PERAPP
) ++i
;
93 if (h1
->flags
& HEAD_PARENT
) --i
;
94 if (h2
->flags
& HEAD_PARENT
) ++i
;
97 if (h1
->flags
& HEAD_PLACED
) --i
;
98 if (h2
->flags
& HEAD_PLACED
) ++i
;
101 if (h1
->flags
& HEAD_PRIMARY
) --i
;
102 if (h2
->flags
& HEAD_PRIMARY
) ++i
;
105 if (h1
->flags
& HEAD_GROUP_DESK
) --i
;
106 if (h2
->flags
& HEAD_GROUP_DESK
) ++i
;
109 if (h1
->flags
& HEAD_GROUP
) --i
;
110 if (h2
->flags
& HEAD_GROUP
) ++i
;
113 return h1
->monitor
- h2
->monitor
;
116 gint
cmp_background(const void *a
, const void *b
)
118 const ObPlaceHead
*h1
= a
;
119 const ObPlaceHead
*h2
= b
;
122 if (h1
->monitor
== h2
->monitor
) return 0;
124 if (h1
->flags
& HEAD_PERAPP
) --i
;
125 if (h2
->flags
& HEAD_PERAPP
) ++i
;
128 if (h1
->flags
& HEAD_PARENT
) --i
;
129 if (h2
->flags
& HEAD_PARENT
) ++i
;
132 if (h1
->flags
& HEAD_GROUP_DESK
|| h2
->flags
& HEAD_GROUP_DESK
) {
133 if (h1
->flags
& HEAD_GROUP_DESK
) --i
;
134 if (h2
->flags
& HEAD_GROUP_DESK
) ++i
;
136 if (h1
->flags
& HEAD_PRIMARY
) --i
;
137 if (h2
->flags
& HEAD_PRIMARY
) ++i
;
141 if (h1
->flags
& HEAD_GROUP
|| h2
->flags
& HEAD_GROUP
) {
142 if (h1
->flags
& HEAD_GROUP
) --i
;
143 if (h2
->flags
& HEAD_GROUP
) ++i
;
145 if (h1
->flags
& HEAD_PRIMARY
) --i
;
146 if (h2
->flags
& HEAD_PRIMARY
) ++i
;
150 if (h1
->flags
& HEAD_PRIMARY
) --i
;
151 if (h2
->flags
& HEAD_PRIMARY
) ++i
;
154 return h1
->monitor
- h2
->monitor
;
157 /*! Pick a monitor to place a window on. */
158 static Rect
*pick_head(ObClient
*c
, gboolean foreground
,
159 ObAppSettings
*settings
)
167 choice
= g_new(ObPlaceHead
, screen_num_monitors
);
168 for (i
= 0; i
< screen_num_monitors
; ++i
) {
169 choice
[i
].monitor
= i
;
173 /* find monitors with group members */
175 for (it
= c
->group
->members
; it
; it
= g_slist_next(it
)) {
176 ObClient
*itc
= it
->data
;
178 guint m
= client_monitor(itc
);
180 if (m
< screen_num_monitors
) {
181 if (screen_compare_desktops(itc
->desktop
, c
->desktop
))
182 choice
[m
].flags
|= HEAD_GROUP_DESK
;
184 choice
[m
].flags
|= HEAD_GROUP
;
190 i
= screen_monitor_primary(FALSE
);
191 if (i
< screen_num_monitors
) {
192 choice
[i
].flags
|= HEAD_PRIMARY
;
193 if (config_place_monitor
== OB_PLACE_MONITOR_PRIMARY
)
194 choice
[i
].flags
|= HEAD_PLACED
;
196 settings
->monitor_type
== OB_APP_SETTINGS_MONITOR_PRIMARY
)
197 choice
[i
].flags
|= HEAD_PERAPP
;
200 i
= screen_monitor_active();
201 if (i
< screen_num_monitors
) {
203 settings
->monitor_type
== OB_APP_SETTINGS_MONITOR_ACTIVE
)
204 choice
[i
].flags
|= HEAD_PERAPP
;
207 i
= screen_monitor_pointer();
208 if (i
< screen_num_monitors
) {
210 settings
->monitor_type
== OB_APP_SETTINGS_MONITOR_MOUSE
)
211 choice
[i
].flags
|= HEAD_PERAPP
;
215 i
= settings
->monitor
- 1;
216 if (i
< screen_num_monitors
)
217 choice
[i
].flags
|= HEAD_PERAPP
;
220 /* direct parent takes highest precedence */
221 if ((p
= client_direct_parent(c
))) {
222 i
= client_monitor(p
);
223 if (i
< screen_num_monitors
)
224 choice
[i
].flags
|= HEAD_PARENT
;
227 qsort(choice
, screen_num_monitors
, sizeof(ObPlaceHead
),
228 foreground
? cmp_foreground
: cmp_background
);
230 /* save the areas of the monitors in order of their being chosen */
231 for (i
= 0; i
< screen_num_monitors
; ++i
)
233 ob_debug("placement choice %d is monitor %d", i
, choice
[i
].monitor
);
234 if (choice
[i
].flags
& HEAD_PARENT
)
235 ob_debug(" - parent on monitor");
236 if (choice
[i
].flags
& HEAD_PLACED
)
237 ob_debug(" - placement choice");
238 if (choice
[i
].flags
& HEAD_PRIMARY
)
239 ob_debug(" - primary monitor");
240 if (choice
[i
].flags
& HEAD_GROUP_DESK
)
241 ob_debug(" - group on same desktop");
242 if (choice
[i
].flags
& HEAD_GROUP
)
243 ob_debug(" - group on other desktop");
246 area
= screen_area(c
->desktop
, choice
[0].monitor
, NULL
);
250 /* return the area for the chosen monitor */
254 static gboolean
place_random(ObClient
*client
, Rect
*area
, gint
*x
, gint
*y
)
258 ob_debug("placing randomly");
262 r
= area
->x
+ area
->width
- client
->frame
->area
.width
;
263 b
= area
->y
+ area
->height
- client
->frame
->area
.height
;
265 if (r
> l
) *x
= g_random_int_range(l
, r
+ 1);
267 if (b
> t
) *y
= g_random_int_range(t
, b
+ 1);
273 static GSList
* area_add(GSList
*list
, Rect
*a
)
275 Rect
*r
= g_slice_new(Rect
);
277 return g_slist_prepend(list
, r
);
280 static GSList
* area_remove(GSList
*list
, Rect
*a
)
283 GSList
*result
= NULL
;
285 for (sit
= list
; sit
; sit
= g_slist_next(sit
)) {
288 if (!RECT_INTERSECTS_RECT(*r
, *a
)) {
289 result
= g_slist_prepend(result
, r
);
290 /* dont free r, it's moved to the result list */
294 /* Use an intersection of a and r to determine the space
295 around r that we can use.
297 NOTE: the spaces calculated can overlap.
300 RECT_SET_INTERSECTION(isect
, *r
, *a
);
302 if (RECT_LEFT(isect
) > RECT_LEFT(*r
)) {
303 RECT_SET(extra
, r
->x
, r
->y
,
304 RECT_LEFT(isect
) - r
->x
, r
->height
);
305 result
= area_add(result
, &extra
);
308 if (RECT_TOP(isect
) > RECT_TOP(*r
)) {
309 RECT_SET(extra
, r
->x
, r
->y
,
310 r
->width
, RECT_TOP(isect
) - r
->y
+ 1);
311 result
= area_add(result
, &extra
);
314 if (RECT_RIGHT(isect
) < RECT_RIGHT(*r
)) {
315 RECT_SET(extra
, RECT_RIGHT(isect
) + 1, r
->y
,
316 RECT_RIGHT(*r
) - RECT_RIGHT(isect
), r
->height
);
317 result
= area_add(result
, &extra
);
320 if (RECT_BOTTOM(isect
) < RECT_BOTTOM(*r
)) {
321 RECT_SET(extra
, r
->x
, RECT_BOTTOM(isect
) + 1,
322 r
->width
, RECT_BOTTOM(*r
) - RECT_BOTTOM(isect
));
323 result
= area_add(result
, &extra
);
326 /* 'r' is not being added to the result list, so free it */
327 g_slice_free(Rect
, r
);
335 IGNORE_FULLSCREEN
= 1,
336 IGNORE_MAXIMIZED
= 2,
338 /*IGNORE_SHADED = 3,*/
341 /*IGNORE_NONFOCUS = 1 << 5,*/
346 static gboolean
place_nooverlap(ObClient
*c
, Rect
*area
, gint
*x
, gint
*y
)
351 GSList
*spaces
= NULL
, *sit
, *maxit
;
353 ob_debug("placing nonoverlap");
359 /* try ignoring different things to find empty space */
360 for (ignore
= 0; ignore
< IGNORE_END
&& !ret
; ignore
++) {
363 /* add the whole monitor */
364 spaces
= area_add(spaces
, area
);
366 /* go thru all the windows */
367 for (it
= client_list
; it
; it
= g_list_next(it
)) {
368 ObClient
*test
= it
->data
;
370 /* should we ignore this client? */
371 if (screen_showing_desktop
) continue;
372 if (c
== test
) continue;
373 if (test
->iconic
) continue;
374 if (c
->desktop
!= DESKTOP_ALL
) {
375 if (test
->desktop
!= c
->desktop
&&
376 test
->desktop
!= DESKTOP_ALL
) continue;
378 if (test
->desktop
!= screen_desktop
&&
379 test
->desktop
!= DESKTOP_ALL
) continue;
381 if (test
->type
== OB_CLIENT_TYPE_SPLASH
||
382 test
->type
== OB_CLIENT_TYPE_DESKTOP
) continue;
385 if ((ignore
>= IGNORE_FULLSCREEN
) &&
386 test
->fullscreen
) continue;
387 if ((ignore
>= IGNORE_MAXIMIZED
) &&
388 test
->max_horz
&& test
->max_vert
) continue;
389 if ((ignore
>= IGNORE_MENUTOOL
) &&
390 (test
->type
== OB_CLIENT_TYPE_MENU
||
391 test
->type
== OB_CLIENT_TYPE_TOOLBAR
) &&
392 client_has_parent(c
)) continue;
394 if ((ignore >= IGNORE_SHADED) &&
395 test->shaded) continue;
397 if ((ignore
>= IGNORE_NONGROUP
) &&
398 client_has_group_siblings(c
) &&
399 test
->group
!= c
->group
) continue;
400 if ((ignore
>= IGNORE_BELOW
) &&
401 test
->layer
< c
->layer
) continue;
403 if ((ignore >= IGNORE_NONFOCUS) &&
404 focus_client != test) continue;
406 /* don't ignore this window, so remove it from the available
408 spaces
= area_remove(spaces
, &test
->frame
->area
);
411 if (ignore
< IGNORE_DOCK
) {
414 spaces
= area_remove(spaces
, &a
);
417 for (sit
= spaces
; sit
; sit
= g_slist_next(sit
)) {
420 if (r
->width
>= c
->frame
->area
.width
&&
421 r
->height
>= c
->frame
->area
.height
&&
422 r
->width
* r
->height
> maxsize
)
424 maxsize
= r
->width
* r
->height
;
430 Rect
*r
= maxit
->data
;
432 /* center it in the area */
435 if (config_place_center
) {
436 *x
+= (r
->width
- c
->frame
->area
.width
) / 2;
437 *y
+= (r
->height
- c
->frame
->area
.height
) / 2;
443 g_slice_free(Rect
, spaces
->data
);
444 spaces
= g_slist_delete_link(spaces
, spaces
);
451 static gboolean
place_under_mouse(ObClient
*client
, gint
*x
, gint
*y
)
457 ob_debug("placing under mouse");
459 if (!screen_pointer_pos(&px
, &py
))
461 area
= pick_pointer_head(client
);
465 r
= area
->x
+ area
->width
- client
->frame
->area
.width
;
466 b
= area
->y
+ area
->height
- client
->frame
->area
.height
;
468 *x
= px
- client
->area
.width
/ 2 - client
->frame
->size
.left
;
469 *x
= MIN(MAX(*x
, l
), r
);
470 *y
= py
- client
->area
.height
/ 2 - client
->frame
->size
.top
;
471 *y
= MIN(MAX(*y
, t
), b
);
473 g_slice_free(Rect
, area
);
478 static gboolean
place_per_app_setting(ObClient
*client
, Rect
*screen
,
480 ObAppSettings
*settings
)
482 if (!settings
|| (settings
&& !settings
->pos_given
))
485 ob_debug("placing by per-app settings");
487 if (settings
->position
.x
.center
)
488 *x
= screen
->x
+ screen
->width
/ 2 - client
->area
.width
/ 2;
489 else if (settings
->position
.x
.opposite
)
490 *x
= screen
->x
+ screen
->width
- client
->frame
->area
.width
-
491 settings
->position
.x
.pos
;
493 *x
= screen
->x
+ settings
->position
.x
.pos
;
494 if (settings
->position
.x
.denom
)
495 *x
= (*x
* screen
->width
) / settings
->position
.x
.denom
;
497 if (settings
->position
.y
.center
)
498 *y
= screen
->y
+ screen
->height
/ 2 - client
->area
.height
/ 2;
499 else if (settings
->position
.y
.opposite
)
500 *y
= screen
->y
+ screen
->height
- client
->frame
->area
.height
-
501 settings
->position
.y
.pos
;
503 *y
= screen
->y
+ settings
->position
.y
.pos
;
504 if (settings
->position
.y
.denom
)
505 *y
= (*y
* screen
->height
) / settings
->position
.y
.denom
;
510 static gboolean
place_transient_splash(ObClient
*client
, Rect
*area
,
513 if (client
->type
== OB_CLIENT_TYPE_DIALOG
) {
515 gboolean first
= TRUE
;
518 ob_debug("placing dialog");
520 for (it
= client
->parents
; it
; it
= g_slist_next(it
)) {
521 ObClient
*m
= it
->data
;
524 l
= RECT_LEFT(m
->frame
->area
);
525 t
= RECT_TOP(m
->frame
->area
);
526 r
= RECT_RIGHT(m
->frame
->area
);
527 b
= RECT_BOTTOM(m
->frame
->area
);
530 l
= MIN(l
, RECT_LEFT(m
->frame
->area
));
531 t
= MIN(t
, RECT_TOP(m
->frame
->area
));
532 r
= MAX(r
, RECT_RIGHT(m
->frame
->area
));
533 b
= MAX(b
, RECT_BOTTOM(m
->frame
->area
));
537 *x
= ((r
+ 1 - l
) - client
->frame
->area
.width
) / 2 + l
;
538 *y
= ((b
+ 1 - t
) - client
->frame
->area
.height
) / 2 + t
;
544 if (client
->type
== OB_CLIENT_TYPE_DIALOG
||
545 client
->type
== OB_CLIENT_TYPE_SPLASH
)
547 ob_debug("placing dialog or splash");
549 *x
= (area
->width
- client
->frame
->area
.width
) / 2 + area
->x
;
550 *y
= (area
->height
- client
->frame
->area
.height
) / 2 + area
->y
;
557 /*! Return TRUE if openbox chose the position for the window, and FALSE if
558 the application chose it */
559 gboolean
place_client(ObClient
*client
, gboolean foreground
, gint
*x
, gint
*y
,
560 ObAppSettings
*settings
)
565 /* per-app settings override program specified position
566 * but not user specified, unless pos_force is enabled */
567 if (((client
->positioned
& USPosition
) &&
568 !(settings
&& settings
->pos_given
&& settings
->pos_force
)) ||
569 ((client
->positioned
& PPosition
) &&
570 !(settings
&& settings
->pos_given
)))
573 area
= pick_head(client
, foreground
, settings
);
575 /* try a number of methods */
576 ret
= place_per_app_setting(client
, area
, x
, y
, settings
) ||
577 place_transient_splash(client
, area
, x
, y
) ||
578 (config_place_policy
== OB_PLACE_POLICY_MOUSE
&&
579 place_under_mouse(client
, x
, y
)) ||
580 place_nooverlap(client
, area
, x
, y
) ||
581 place_random(client
, area
, x
, y
);
584 g_slice_free(Rect
, area
);
586 /* get where the client should be */
587 frame_frame_gravity(client
->frame
, x
, y
);