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.
28 #include "place_overlap.h"
32 static Rect
*choose_pointer_monitor(ObClient
*c
)
34 return screen_area(c
->desktop
, screen_monitor_pointer(), NULL
);
37 /* use the following priority lists for choose_monitor()
39 When a window is being placed in the FOREGROUND, use a monitor chosen in
42 2. same monitor as parent
43 3. primary monitor if placement=PRIMARY
44 active monitor if placement=ACTIVE
45 pointer monitor if placement=MOUSE
47 5. other monitors where the window has group members on the same desktop
48 6. other monitors where the window has group members on other desktops
51 When a window is being placed in the BACKGROUND, use a monitor chosen in the
54 2. same monitor as parent
55 3. other monitors where the window has group members on the same desktop
56 3a. primary monitor in this set
57 3b. other monitors in this set
58 4. other monitors where the window has group members on other desktops
59 4a. primary monitor in this set
60 4b. other monitors in this set
62 5a. primary monitor in this set
63 5b. other monitors in this set
66 /*! One for each possible head, used to sort them in order of precedence. */
72 /*! Flags for ObPlaceHead */
74 HEAD_PARENT
= 1 << 0, /* parent's monitor */
75 HEAD_PLACED
= 1 << 1, /* chosen monitor by placement */
76 HEAD_PRIMARY
= 1 << 2, /* primary monitor */
77 HEAD_GROUP_DESK
= 1 << 3, /* has a group member on the same desktop */
78 HEAD_GROUP
= 1 << 4, /* has a group member on another desktop */
79 HEAD_PERAPP
= 1 << 5, /* chosen by per-app settings */
82 gint
cmp_foreground(const void *a
, const void *b
)
84 const ObPlaceHead
*h1
= a
;
85 const ObPlaceHead
*h2
= b
;
88 if (h1
->monitor
== h2
->monitor
) return 0;
90 if (h1
->flags
& HEAD_PERAPP
) --i
;
91 if (h2
->flags
& HEAD_PERAPP
) ++i
;
94 if (h1
->flags
& HEAD_PARENT
) --i
;
95 if (h2
->flags
& HEAD_PARENT
) ++i
;
98 if (h1
->flags
& HEAD_PLACED
) --i
;
99 if (h2
->flags
& HEAD_PLACED
) ++i
;
102 if (h1
->flags
& HEAD_PRIMARY
) --i
;
103 if (h2
->flags
& HEAD_PRIMARY
) ++i
;
106 if (h1
->flags
& HEAD_GROUP_DESK
) --i
;
107 if (h2
->flags
& HEAD_GROUP_DESK
) ++i
;
110 if (h1
->flags
& HEAD_GROUP
) --i
;
111 if (h2
->flags
& HEAD_GROUP
) ++i
;
114 return h1
->monitor
- h2
->monitor
;
117 gint
cmp_background(const void *a
, const void *b
)
119 const ObPlaceHead
*h1
= a
;
120 const ObPlaceHead
*h2
= b
;
123 if (h1
->monitor
== h2
->monitor
) return 0;
125 if (h1
->flags
& HEAD_PERAPP
) --i
;
126 if (h2
->flags
& HEAD_PERAPP
) ++i
;
129 if (h1
->flags
& HEAD_PARENT
) --i
;
130 if (h2
->flags
& HEAD_PARENT
) ++i
;
133 if (h1
->flags
& HEAD_GROUP_DESK
|| h2
->flags
& HEAD_GROUP_DESK
) {
134 if (h1
->flags
& HEAD_GROUP_DESK
) --i
;
135 if (h2
->flags
& HEAD_GROUP_DESK
) ++i
;
137 if (h1
->flags
& HEAD_PRIMARY
) --i
;
138 if (h2
->flags
& HEAD_PRIMARY
) ++i
;
142 if (h1
->flags
& HEAD_GROUP
|| h2
->flags
& HEAD_GROUP
) {
143 if (h1
->flags
& HEAD_GROUP
) --i
;
144 if (h2
->flags
& HEAD_GROUP
) ++i
;
146 if (h1
->flags
& HEAD_PRIMARY
) --i
;
147 if (h2
->flags
& HEAD_PRIMARY
) ++i
;
151 if (h1
->flags
& HEAD_PRIMARY
) --i
;
152 if (h2
->flags
& HEAD_PRIMARY
) ++i
;
155 return h1
->monitor
- h2
->monitor
;
158 /*! Pick a monitor to place a window on. */
159 static Rect
* choose_monitor(ObClient
*c
, gboolean client_to_be_foregrounded
,
160 ObAppSettings
*settings
)
168 choice
= g_new(ObPlaceHead
, screen_num_monitors
);
169 for (i
= 0; i
< screen_num_monitors
; ++i
) {
170 choice
[i
].monitor
= i
;
174 /* find monitors with group members */
176 for (it
= c
->group
->members
; it
; it
= g_slist_next(it
)) {
177 ObClient
*itc
= it
->data
;
179 guint m
= client_monitor(itc
);
181 if (m
< screen_num_monitors
) {
182 if (screen_compare_desktops(itc
->desktop
, c
->desktop
))
183 choice
[m
].flags
|= HEAD_GROUP_DESK
;
185 choice
[m
].flags
|= HEAD_GROUP
;
191 i
= screen_monitor_primary(FALSE
);
192 if (i
< screen_num_monitors
) {
193 choice
[i
].flags
|= HEAD_PRIMARY
;
194 if (config_place_monitor
== OB_PLACE_MONITOR_PRIMARY
)
195 choice
[i
].flags
|= HEAD_PLACED
;
197 settings
->monitor_type
== OB_PLACE_MONITOR_PRIMARY
)
198 choice
[i
].flags
|= HEAD_PERAPP
;
201 i
= screen_monitor_active();
202 if (i
< screen_num_monitors
) {
203 if (config_place_monitor
== OB_PLACE_MONITOR_ACTIVE
)
204 choice
[i
].flags
|= HEAD_PLACED
;
206 settings
->monitor_type
== OB_PLACE_MONITOR_ACTIVE
)
207 choice
[i
].flags
|= HEAD_PERAPP
;
210 i
= screen_monitor_pointer();
211 if (i
< screen_num_monitors
) {
212 if (config_place_monitor
== OB_PLACE_MONITOR_MOUSE
)
213 choice
[i
].flags
|= HEAD_PLACED
;
215 settings
->monitor_type
== OB_PLACE_MONITOR_MOUSE
)
216 choice
[i
].flags
|= HEAD_PERAPP
;
220 i
= settings
->monitor
- 1;
221 if (i
< screen_num_monitors
)
222 choice
[i
].flags
|= HEAD_PERAPP
;
225 /* direct parent takes highest precedence */
226 if ((p
= client_direct_parent(c
))) {
227 i
= client_monitor(p
);
228 if (i
< screen_num_monitors
)
229 choice
[i
].flags
|= HEAD_PARENT
;
232 qsort(choice
, screen_num_monitors
, sizeof(ObPlaceHead
),
233 client_to_be_foregrounded
? cmp_foreground
: cmp_background
);
235 /* save the areas of the monitors in order of their being chosen */
236 for (i
= 0; i
< screen_num_monitors
; ++i
)
238 ob_debug("placement choice %d is monitor %d", i
, choice
[i
].monitor
);
239 if (choice
[i
].flags
& HEAD_PARENT
)
240 ob_debug(" - parent on monitor");
241 if (choice
[i
].flags
& HEAD_PLACED
)
242 ob_debug(" - placement choice");
243 if (choice
[i
].flags
& HEAD_PRIMARY
)
244 ob_debug(" - primary monitor");
245 if (choice
[i
].flags
& HEAD_GROUP_DESK
)
246 ob_debug(" - group on same desktop");
247 if (choice
[i
].flags
& HEAD_GROUP
)
248 ob_debug(" - group on other desktop");
251 area
= screen_area(c
->desktop
, choice
[0].monitor
, NULL
);
255 /* return the area for the chosen monitor */
259 static gboolean
place_under_mouse(ObClient
*client
, gint
*x
, gint
*y
,
262 if (config_place_policy
!= OB_PLACE_POLICY_MOUSE
)
269 ob_debug("placing under mouse");
271 if (!screen_pointer_pos(&px
, &py
))
273 area
= choose_pointer_monitor(client
);
277 r
= area
->x
+ area
->width
- frame_size
.width
;
278 b
= area
->y
+ area
->height
- frame_size
.height
;
280 *x
= px
- frame_size
.width
/ 2;
281 *x
= MIN(MAX(*x
, l
), r
);
282 *y
= py
- frame_size
.height
/ 2;
283 *y
= MIN(MAX(*y
, t
), b
);
285 g_slice_free(Rect
, area
);
290 static gboolean
place_per_app_setting_position(ObClient
*client
, Rect
*screen
,
292 ObAppSettings
*settings
,
295 if (!settings
|| !settings
->pos_given
)
298 ob_debug("placing by per-app settings");
300 if (settings
->position
.x
.center
)
301 *x
= screen
->x
+ screen
->width
/ 2 - client
->area
.width
/ 2;
302 else if (settings
->position
.x
.opposite
)
303 *x
= screen
->x
+ screen
->width
- frame_size
.width
-
304 settings
->position
.x
.pos
;
306 *x
= screen
->x
+ settings
->position
.x
.pos
;
307 if (settings
->position
.x
.denom
)
308 *x
= (*x
* screen
->width
) / settings
->position
.x
.denom
;
310 if (settings
->position
.y
.center
)
311 *y
= screen
->y
+ screen
->height
/ 2 - client
->area
.height
/ 2;
312 else if (settings
->position
.y
.opposite
)
313 *y
= screen
->y
+ screen
->height
- frame_size
.height
-
314 settings
->position
.y
.pos
;
316 *y
= screen
->y
+ settings
->position
.y
.pos
;
317 if (settings
->position
.y
.denom
)
318 *y
= (*y
* screen
->height
) / settings
->position
.y
.denom
;
323 static void place_per_app_setting_size(ObClient
*client
, Rect
*screen
,
325 ObAppSettings
*settings
)
327 if (!settings
|| !settings
->size_given
)
330 ob_debug("sizing by per-app settings");
332 g_assert(settings
->width_num
> 0);
333 g_assert(settings
->width_denom
>= 0);
334 g_assert(settings
->height_num
> 0);
335 g_assert(settings
->height_denom
>= 0);
337 if (!settings
->width_denom
)
338 *w
= settings
->width_num
;
340 *w
= screen
->width
* settings
->width_num
/ settings
->width_denom
;
341 *w
= MIN(*w
, screen
->width
);
344 if (!settings
->height_denom
)
345 *h
= settings
->height_num
;
347 *h
= screen
->height
* settings
->height_num
/ settings
->height_denom
;
348 *h
= MIN(*h
, screen
->height
);
352 static gboolean
place_transient_splash(ObClient
*client
, Rect
*area
,
353 gint
*x
, gint
*y
, Size frame_size
)
355 if (client
->type
== OB_CLIENT_TYPE_DIALOG
) {
357 gboolean first
= TRUE
;
360 ob_debug("placing dialog");
362 for (it
= client
->parents
; it
; it
= g_slist_next(it
)) {
363 ObClient
*m
= it
->data
;
366 l
= RECT_LEFT(m
->frame
->area
);
367 t
= RECT_TOP(m
->frame
->area
);
368 r
= RECT_RIGHT(m
->frame
->area
);
369 b
= RECT_BOTTOM(m
->frame
->area
);
372 l
= MIN(l
, RECT_LEFT(m
->frame
->area
));
373 t
= MIN(t
, RECT_TOP(m
->frame
->area
));
374 r
= MAX(r
, RECT_RIGHT(m
->frame
->area
));
375 b
= MAX(b
, RECT_BOTTOM(m
->frame
->area
));
379 *x
= ((r
+ 1 - l
) - frame_size
.width
) / 2 + l
;
380 *y
= ((b
+ 1 - t
) - frame_size
.height
) / 2 + t
;
386 if (client
->type
== OB_CLIENT_TYPE_DIALOG
||
387 client
->type
== OB_CLIENT_TYPE_SPLASH
)
389 ob_debug("placing dialog or splash");
391 *x
= (area
->width
- frame_size
.width
) / 2 + area
->x
;
392 *y
= (area
->height
- frame_size
.height
) / 2 + area
->y
;
399 static gboolean
place_least_overlap(ObClient
*c
, Rect
*head
, int *x
, int *y
,
402 /* Assemble the list of windows that could overlap with @c in the user's
404 GSList
* potential_overlap_clients
= NULL
;
405 gint n_client_rects
= 0;
407 /* if we're "showing desktop", ignore all existing windows */
408 if (!screen_showing_desktop
) {
410 for (it
= client_list
; it
!= NULL
; it
= g_list_next(it
)) {
411 ObClient
* maybe_client
= (ObClient
*)it
->data
;
412 if (maybe_client
== c
)
414 if (maybe_client
->iconic
)
416 if (!client_occupies_space(maybe_client
))
418 if (c
->desktop
!= DESKTOP_ALL
) {
419 if (maybe_client
->desktop
!= c
->desktop
&&
420 maybe_client
->desktop
!= DESKTOP_ALL
)
423 if (maybe_client
->desktop
!= screen_desktop
&&
424 maybe_client
->desktop
!= DESKTOP_ALL
)
428 potential_overlap_clients
= g_slist_prepend(
429 potential_overlap_clients
, maybe_client
);
433 Rect client_rects
[n_client_rects
];
437 for (it
= potential_overlap_clients
; it
!= NULL
; it
= g_slist_next(it
)) {
438 ObClient
* potential_overlap_client
= (ObClient
*)it
->data
;
439 client_rects
[i
] = potential_overlap_client
->frame
->area
;
442 g_slist_free(potential_overlap_clients
);
445 place_overlap_find_least_placement(client_rects
, n_client_rects
, head
,
446 &frame_size
, &result
);
453 static gboolean
should_set_client_position(ObClient
*client
,
454 ObAppSettings
*settings
)
456 gboolean has_position
= settings
&& settings
->pos_given
;
457 gboolean has_forced_position
= has_position
&& settings
->pos_force
;
459 gboolean user_positioned
= client
->positioned
& USPosition
;
460 if (user_positioned
&& !has_forced_position
)
463 gboolean program_positioned
= client
->positioned
& PPosition
;
464 if (program_positioned
&& !has_position
)
470 gboolean
place_client(ObClient
*client
, gboolean client_to_be_foregrounded
,
471 Rect
* client_area
, ObAppSettings
*settings
)
478 monitor_area
= choose_monitor(client
, client_to_be_foregrounded
, settings
);
480 w
= &client_area
->width
;
481 h
= &client_area
->height
;
482 place_per_app_setting_size(client
, monitor_area
, w
, h
, settings
);
484 if (!should_set_client_position(client
, settings
))
491 *w
+ client
->frame
->size
.left
+ client
->frame
->size
.right
,
492 *h
+ client
->frame
->size
.top
+ client
->frame
->size
.bottom
);
495 place_per_app_setting_position(client
, monitor_area
, x
, y
, settings
,
497 place_transient_splash(client
, monitor_area
, x
, y
, frame_size
) ||
498 place_under_mouse(client
, x
, y
, frame_size
) ||
499 place_least_overlap(client
, monitor_area
, x
, y
, frame_size
);
502 g_slice_free(Rect
, monitor_area
);
504 /* get where the client should be */
505 frame_frame_gravity(client
->frame
, x
, y
);