]>
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 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_PLACE_MONITOR_PRIMARY
)
197 choice
[i
].flags
|= HEAD_PERAPP
;
200 i
= screen_monitor_active();
201 if (i
< screen_num_monitors
) {
202 if (config_place_monitor
== OB_PLACE_MONITOR_ACTIVE
)
203 choice
[i
].flags
|= HEAD_PLACED
;
205 settings
->monitor_type
== OB_PLACE_MONITOR_ACTIVE
)
206 choice
[i
].flags
|= HEAD_PERAPP
;
209 i
= screen_monitor_pointer();
210 if (i
< screen_num_monitors
) {
211 if (config_place_monitor
== OB_PLACE_MONITOR_MOUSE
)
212 choice
[i
].flags
|= HEAD_PLACED
;
214 settings
->monitor_type
== OB_PLACE_MONITOR_MOUSE
)
215 choice
[i
].flags
|= HEAD_PERAPP
;
219 i
= settings
->monitor
- 1;
220 if (i
< screen_num_monitors
)
221 choice
[i
].flags
|= HEAD_PERAPP
;
224 /* direct parent takes highest precedence */
225 if ((p
= client_direct_parent(c
))) {
226 i
= client_monitor(p
);
227 if (i
< screen_num_monitors
)
228 choice
[i
].flags
|= HEAD_PARENT
;
231 qsort(choice
, screen_num_monitors
, sizeof(ObPlaceHead
),
232 foreground
? cmp_foreground
: cmp_background
);
234 /* save the areas of the monitors in order of their being chosen */
235 for (i
= 0; i
< screen_num_monitors
; ++i
)
237 ob_debug("placement choice %d is monitor %d", i
, choice
[i
].monitor
);
238 if (choice
[i
].flags
& HEAD_PARENT
)
239 ob_debug(" - parent on monitor");
240 if (choice
[i
].flags
& HEAD_PLACED
)
241 ob_debug(" - placement choice");
242 if (choice
[i
].flags
& HEAD_PRIMARY
)
243 ob_debug(" - primary monitor");
244 if (choice
[i
].flags
& HEAD_GROUP_DESK
)
245 ob_debug(" - group on same desktop");
246 if (choice
[i
].flags
& HEAD_GROUP
)
247 ob_debug(" - group on other desktop");
250 area
= screen_area(c
->desktop
, choice
[0].monitor
, NULL
);
254 /* return the area for the chosen monitor */
258 static gboolean
place_random(ObClient
*client
, Rect
*area
, gint
*x
, gint
*y
)
262 ob_debug("placing randomly");
266 r
= area
->x
+ area
->width
- client
->frame
->area
.width
;
267 b
= area
->y
+ area
->height
- client
->frame
->area
.height
;
269 if (r
> l
) *x
= g_random_int_range(l
, r
+ 1);
271 if (b
> t
) *y
= g_random_int_range(t
, b
+ 1);
277 static GSList
* area_add(GSList
*list
, Rect
*a
)
279 Rect
*r
= g_slice_new(Rect
);
281 return g_slist_prepend(list
, r
);
284 static GSList
* area_remove(GSList
*list
, Rect
*a
)
287 GSList
*result
= NULL
;
289 for (sit
= list
; sit
; sit
= g_slist_next(sit
)) {
292 if (!RECT_INTERSECTS_RECT(*r
, *a
)) {
293 result
= g_slist_prepend(result
, r
);
294 /* dont free r, it's moved to the result list */
298 /* Use an intersection of a and r to determine the space
299 around r that we can use.
301 NOTE: the spaces calculated can overlap.
304 RECT_SET_INTERSECTION(isect
, *r
, *a
);
306 if (RECT_LEFT(isect
) > RECT_LEFT(*r
)) {
307 RECT_SET(extra
, r
->x
, r
->y
,
308 RECT_LEFT(isect
) - r
->x
, r
->height
);
309 result
= area_add(result
, &extra
);
312 if (RECT_TOP(isect
) > RECT_TOP(*r
)) {
313 RECT_SET(extra
, r
->x
, r
->y
,
314 r
->width
, RECT_TOP(isect
) - r
->y
+ 1);
315 result
= area_add(result
, &extra
);
318 if (RECT_RIGHT(isect
) < RECT_RIGHT(*r
)) {
319 RECT_SET(extra
, RECT_RIGHT(isect
) + 1, r
->y
,
320 RECT_RIGHT(*r
) - RECT_RIGHT(isect
), r
->height
);
321 result
= area_add(result
, &extra
);
324 if (RECT_BOTTOM(isect
) < RECT_BOTTOM(*r
)) {
325 RECT_SET(extra
, r
->x
, RECT_BOTTOM(isect
) + 1,
326 r
->width
, RECT_BOTTOM(*r
) - RECT_BOTTOM(isect
));
327 result
= area_add(result
, &extra
);
330 /* 'r' is not being added to the result list, so free it */
331 g_slice_free(Rect
, r
);
339 IGNORE_FULLSCREEN
= 1,
340 IGNORE_MAXIMIZED
= 2,
342 /*IGNORE_SHADED = 3,*/
345 /*IGNORE_NONFOCUS = 1 << 5,*/
350 static gboolean
place_nooverlap(ObClient
*c
, Rect
*area
, gint
*x
, gint
*y
)
355 GSList
*spaces
= NULL
, *sit
, *maxit
;
357 ob_debug("placing nonoverlap");
363 /* try ignoring different things to find empty space */
364 for (ignore
= 0; ignore
< IGNORE_END
&& !ret
; ignore
++) {
367 /* add the whole monitor */
368 spaces
= area_add(spaces
, area
);
370 /* go thru all the windows */
371 for (it
= client_list
; it
; it
= g_list_next(it
)) {
372 ObClient
*test
= it
->data
;
374 /* should we ignore this client? */
375 if (screen_showing_desktop
) continue;
376 if (c
== test
) continue;
377 if (test
->iconic
) continue;
378 if (c
->desktop
!= DESKTOP_ALL
) {
379 if (test
->desktop
!= c
->desktop
&&
380 test
->desktop
!= DESKTOP_ALL
) continue;
382 if (test
->desktop
!= screen_desktop
&&
383 test
->desktop
!= DESKTOP_ALL
) continue;
385 if (test
->type
== OB_CLIENT_TYPE_SPLASH
||
386 test
->type
== OB_CLIENT_TYPE_DESKTOP
) continue;
389 if ((ignore
>= IGNORE_FULLSCREEN
) &&
390 test
->fullscreen
) continue;
391 if ((ignore
>= IGNORE_MAXIMIZED
) &&
392 test
->max_horz
&& test
->max_vert
) continue;
393 if ((ignore
>= IGNORE_MENUTOOL
) &&
394 (test
->type
== OB_CLIENT_TYPE_MENU
||
395 test
->type
== OB_CLIENT_TYPE_TOOLBAR
) &&
396 client_has_parent(c
)) continue;
398 if ((ignore >= IGNORE_SHADED) &&
399 test->shaded) continue;
401 if ((ignore
>= IGNORE_NONGROUP
) &&
402 client_has_group_siblings(c
) &&
403 test
->group
!= c
->group
) continue;
404 if ((ignore
>= IGNORE_BELOW
) &&
405 test
->layer
< c
->layer
) continue;
407 if ((ignore >= IGNORE_NONFOCUS) &&
408 focus_client != test) continue;
410 /* don't ignore this window, so remove it from the available
412 spaces
= area_remove(spaces
, &test
->frame
->area
);
415 if (ignore
< IGNORE_DOCK
) {
418 spaces
= area_remove(spaces
, &a
);
421 for (sit
= spaces
; sit
; sit
= g_slist_next(sit
)) {
424 if (r
->width
>= c
->frame
->area
.width
&&
425 r
->height
>= c
->frame
->area
.height
&&
426 r
->width
* r
->height
> maxsize
)
428 maxsize
= r
->width
* r
->height
;
434 Rect
*r
= maxit
->data
;
436 /* center it in the area */
439 if (config_place_center
) {
440 *x
+= (r
->width
- c
->frame
->area
.width
) / 2;
441 *y
+= (r
->height
- c
->frame
->area
.height
) / 2;
447 g_slice_free(Rect
, spaces
->data
);
448 spaces
= g_slist_delete_link(spaces
, spaces
);
455 static gboolean
place_under_mouse(ObClient
*client
, gint
*x
, gint
*y
)
461 ob_debug("placing under mouse");
463 if (!screen_pointer_pos(&px
, &py
))
465 area
= pick_pointer_head(client
);
469 r
= area
->x
+ area
->width
- client
->frame
->area
.width
;
470 b
= area
->y
+ area
->height
- client
->frame
->area
.height
;
472 *x
= px
- client
->area
.width
/ 2 - client
->frame
->size
.left
;
473 *x
= MIN(MAX(*x
, l
), r
);
474 *y
= py
- client
->area
.height
/ 2 - client
->frame
->size
.top
;
475 *y
= MIN(MAX(*y
, t
), b
);
477 g_slice_free(Rect
, area
);
482 static gboolean
place_per_app_setting(ObClient
*client
, Rect
*screen
,
484 ObAppSettings
*settings
)
486 if (!settings
|| (settings
&& !settings
->pos_given
))
489 ob_debug("placing by per-app settings");
491 if (settings
->position
.x
.center
)
492 *x
= screen
->x
+ screen
->width
/ 2 - client
->area
.width
/ 2;
493 else if (settings
->position
.x
.opposite
)
494 *x
= screen
->x
+ screen
->width
- client
->frame
->area
.width
-
495 settings
->position
.x
.pos
;
497 *x
= screen
->x
+ settings
->position
.x
.pos
;
498 if (settings
->position
.x
.denom
)
499 *x
= (*x
* screen
->width
) / settings
->position
.x
.denom
;
501 if (settings
->position
.y
.center
)
502 *y
= screen
->y
+ screen
->height
/ 2 - client
->area
.height
/ 2;
503 else if (settings
->position
.y
.opposite
)
504 *y
= screen
->y
+ screen
->height
- client
->frame
->area
.height
-
505 settings
->position
.y
.pos
;
507 *y
= screen
->y
+ settings
->position
.y
.pos
;
508 if (settings
->position
.y
.denom
)
509 *y
= (*y
* screen
->height
) / settings
->position
.y
.denom
;
514 static gboolean
place_transient_splash(ObClient
*client
, Rect
*area
,
517 if (client
->type
== OB_CLIENT_TYPE_DIALOG
) {
519 gboolean first
= TRUE
;
522 ob_debug("placing dialog");
524 for (it
= client
->parents
; it
; it
= g_slist_next(it
)) {
525 ObClient
*m
= it
->data
;
528 l
= RECT_LEFT(m
->frame
->area
);
529 t
= RECT_TOP(m
->frame
->area
);
530 r
= RECT_RIGHT(m
->frame
->area
);
531 b
= RECT_BOTTOM(m
->frame
->area
);
534 l
= MIN(l
, RECT_LEFT(m
->frame
->area
));
535 t
= MIN(t
, RECT_TOP(m
->frame
->area
));
536 r
= MAX(r
, RECT_RIGHT(m
->frame
->area
));
537 b
= MAX(b
, RECT_BOTTOM(m
->frame
->area
));
541 *x
= ((r
+ 1 - l
) - client
->frame
->area
.width
) / 2 + l
;
542 *y
= ((b
+ 1 - t
) - client
->frame
->area
.height
) / 2 + t
;
548 if (client
->type
== OB_CLIENT_TYPE_DIALOG
||
549 client
->type
== OB_CLIENT_TYPE_SPLASH
)
551 ob_debug("placing dialog or splash");
553 *x
= (area
->width
- client
->frame
->area
.width
) / 2 + area
->x
;
554 *y
= (area
->height
- client
->frame
->area
.height
) / 2 + area
->y
;
561 /*! Return TRUE if openbox chose the position for the window, and FALSE if
562 the application chose it */
563 gboolean
place_client(ObClient
*client
, gboolean foreground
, gint
*x
, gint
*y
,
564 ObAppSettings
*settings
)
569 /* per-app settings override program specified position
570 * but not user specified, unless pos_force is enabled */
571 if (((client
->positioned
& USPosition
) &&
572 !(settings
&& settings
->pos_given
&& settings
->pos_force
)) ||
573 ((client
->positioned
& PPosition
) &&
574 !(settings
&& settings
->pos_given
)))
577 area
= pick_head(client
, foreground
, settings
);
579 /* try a number of methods */
580 ret
= place_per_app_setting(client
, area
, x
, y
, settings
) ||
581 place_transient_splash(client
, area
, x
, y
) ||
582 (config_place_policy
== OB_PLACE_POLICY_MOUSE
&&
583 place_under_mouse(client
, x
, y
)) ||
584 place_nooverlap(client
, area
, x
, y
) ||
585 place_random(client
, area
, x
, y
);
588 g_slice_free(Rect
, area
);
590 /* get where the client should be */
591 frame_frame_gravity(client
->frame
, x
, y
);
This page took 0.068907 seconds and 5 git commands to generate.