]>
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
40 1. same monitor as parent
41 2. primary monitor if placement=PRIMARY
42 active monitor if placement=ACTIVE
43 pointer monitor if placement=MOUSE
45 4. other monitors where the window has group members on the same desktop
46 5. other monitors where the window has group members on other desktops
49 When a window is being placed in the BACKGROUND, use a monitor chosen in the
51 1. same monitor as parent
52 2. other monitors where the window has group members on the same desktop
53 2a. primary monitor in this set
54 2b. other monitors in this set
55 3. other monitors where the window has group members on other desktops
56 3a. primary monitor in this set
57 3b. other monitors in this set
59 4a. primary monitor in this set
60 4b. other monitors in this set
63 /*! One for each possible head, used to sort them in order of precedence. */
69 /*! Flags for ObPlaceHead */
71 HEAD_PARENT
= 1 << 0, /* parent's monitor */
72 HEAD_PLACED
= 1 << 1, /* chosen monitor by placement */
73 HEAD_PRIMARY
= 1 << 2, /* primary monitor */
74 HEAD_GROUP_DESK
= 1 << 3, /* has a group member on the same desktop */
75 HEAD_GROUP
= 1 << 4, /* has a group member on another desktop */
78 gint
cmp_foreground(const void *a
, const void *b
)
80 const ObPlaceHead
*h1
= a
;
81 const ObPlaceHead
*h2
= b
;
84 if (h1
->monitor
== h2
->monitor
) return 0;
86 if (h1
->flags
& HEAD_PARENT
) --i
;
87 if (h2
->flags
& HEAD_PARENT
) ++i
;
90 if (h1
->flags
& HEAD_PLACED
) --i
;
91 if (h2
->flags
& HEAD_PLACED
) ++i
;
94 if (h1
->flags
& HEAD_PRIMARY
) --i
;
95 if (h2
->flags
& HEAD_PRIMARY
) ++i
;
98 if (h1
->flags
& HEAD_GROUP_DESK
) --i
;
99 if (h2
->flags
& HEAD_GROUP_DESK
) ++i
;
102 if (h1
->flags
& HEAD_GROUP
) --i
;
103 if (h2
->flags
& HEAD_GROUP
) ++i
;
106 return h1
->monitor
- h2
->monitor
;
109 gint
cmp_background(const void *a
, const void *b
)
111 const ObPlaceHead
*h1
= a
;
112 const ObPlaceHead
*h2
= b
;
115 if (h1
->monitor
== h2
->monitor
) return 0;
117 if (h1
->flags
& HEAD_PARENT
) --i
;
118 if (h2
->flags
& HEAD_PARENT
) ++i
;
121 if (h1
->flags
& HEAD_GROUP_DESK
|| h2
->flags
& HEAD_GROUP_DESK
) {
122 if (h1
->flags
& HEAD_GROUP_DESK
) --i
;
123 if (h2
->flags
& HEAD_GROUP_DESK
) ++i
;
125 if (h1
->flags
& HEAD_PRIMARY
) --i
;
126 if (h2
->flags
& HEAD_PRIMARY
) ++i
;
130 if (h1
->flags
& HEAD_GROUP
|| h2
->flags
& HEAD_GROUP
) {
131 if (h1
->flags
& HEAD_GROUP
) --i
;
132 if (h2
->flags
& HEAD_GROUP
) ++i
;
134 if (h1
->flags
& HEAD_PRIMARY
) --i
;
135 if (h2
->flags
& HEAD_PRIMARY
) ++i
;
139 if (h1
->flags
& HEAD_PRIMARY
) --i
;
140 if (h2
->flags
& HEAD_PRIMARY
) ++i
;
143 return h1
->monitor
- h2
->monitor
;
146 /*! Pick a monitor to place a window on. */
147 static Rect
*pick_head(ObClient
*c
, gboolean foreground
)
155 choice
= g_new(ObPlaceHead
, screen_num_monitors
);
156 for (i
= 0; i
< screen_num_monitors
; ++i
) {
157 choice
[i
].monitor
= i
;
161 /* find monitors with group members */
163 for (it
= c
->group
->members
; it
; it
= g_slist_next(it
)) {
164 ObClient
*itc
= it
->data
;
166 guint m
= client_monitor(itc
);
168 if (m
< screen_num_monitors
) {
169 if (screen_compare_desktops(itc
->desktop
, c
->desktop
))
170 choice
[m
].flags
|= HEAD_GROUP_DESK
;
172 choice
[m
].flags
|= HEAD_GROUP
;
178 i
= screen_monitor_primary(FALSE
);
179 if (i
< screen_num_monitors
) {
180 choice
[i
].flags
|= HEAD_PRIMARY
;
181 if (config_place_monitor
== OB_PLACE_MONITOR_PRIMARY
)
182 choice
[i
].flags
|= HEAD_PLACED
;
185 /* direct parent takes highest precedence */
186 if ((p
= client_direct_parent(c
))) {
187 i
= client_monitor(p
);
188 if (i
< screen_num_monitors
)
189 choice
[i
].flags
|= HEAD_PARENT
;
192 qsort(choice
, screen_num_monitors
, sizeof(ObPlaceHead
),
193 foreground
? cmp_foreground
: cmp_background
);
195 /* save the areas of the monitors in order of their being chosen */
196 for (i
= 0; i
< screen_num_monitors
; ++i
)
198 ob_debug("placement choice %d is monitor %d", i
, choice
[i
].monitor
);
199 if (choice
[i
].flags
& HEAD_PARENT
)
200 ob_debug(" - parent on monitor");
201 if (choice
[i
].flags
& HEAD_PLACED
)
202 ob_debug(" - placement choice");
203 if (choice
[i
].flags
& HEAD_PRIMARY
)
204 ob_debug(" - primary monitor");
205 if (choice
[i
].flags
& HEAD_GROUP_DESK
)
206 ob_debug(" - group on same desktop");
207 if (choice
[i
].flags
& HEAD_GROUP
)
208 ob_debug(" - group on other desktop");
211 area
= screen_area(c
->desktop
, choice
[0].monitor
, NULL
);
215 /* return the area for the chosen monitor */
219 static gboolean
place_random(ObClient
*client
, Rect
*area
, gint
*x
, gint
*y
)
223 ob_debug("placing randomly");
227 r
= area
->x
+ area
->width
- client
->frame
->area
.width
;
228 b
= area
->y
+ area
->height
- client
->frame
->area
.height
;
230 if (r
> l
) *x
= g_random_int_range(l
, r
+ 1);
232 if (b
> t
) *y
= g_random_int_range(t
, b
+ 1);
238 static GSList
* area_add(GSList
*list
, Rect
*a
)
240 Rect
*r
= g_slice_new(Rect
);
242 return g_slist_prepend(list
, r
);
245 static GSList
* area_remove(GSList
*list
, Rect
*a
)
248 GSList
*result
= NULL
;
250 for (sit
= list
; sit
; sit
= g_slist_next(sit
)) {
253 if (!RECT_INTERSECTS_RECT(*r
, *a
)) {
254 result
= g_slist_prepend(result
, r
);
255 /* dont free r, it's moved to the result list */
259 /* Use an intersection of a and r to determine the space
260 around r that we can use.
262 NOTE: the spaces calculated can overlap.
265 RECT_SET_INTERSECTION(isect
, *r
, *a
);
267 if (RECT_LEFT(isect
) > RECT_LEFT(*r
)) {
268 RECT_SET(extra
, r
->x
, r
->y
,
269 RECT_LEFT(isect
) - r
->x
, r
->height
);
270 result
= area_add(result
, &extra
);
273 if (RECT_TOP(isect
) > RECT_TOP(*r
)) {
274 RECT_SET(extra
, r
->x
, r
->y
,
275 r
->width
, RECT_TOP(isect
) - r
->y
+ 1);
276 result
= area_add(result
, &extra
);
279 if (RECT_RIGHT(isect
) < RECT_RIGHT(*r
)) {
280 RECT_SET(extra
, RECT_RIGHT(isect
) + 1, r
->y
,
281 RECT_RIGHT(*r
) - RECT_RIGHT(isect
), r
->height
);
282 result
= area_add(result
, &extra
);
285 if (RECT_BOTTOM(isect
) < RECT_BOTTOM(*r
)) {
286 RECT_SET(extra
, r
->x
, RECT_BOTTOM(isect
) + 1,
287 r
->width
, RECT_BOTTOM(*r
) - RECT_BOTTOM(isect
));
288 result
= area_add(result
, &extra
);
291 /* 'r' is not being added to the result list, so free it */
292 g_slice_free(Rect
, r
);
300 IGNORE_FULLSCREEN
= 1,
301 IGNORE_MAXIMIZED
= 2,
303 /*IGNORE_SHADED = 3,*/
306 /*IGNORE_NONFOCUS = 1 << 5,*/
311 static gboolean
place_nooverlap(ObClient
*c
, Rect
*area
, gint
*x
, gint
*y
)
316 GSList
*spaces
= NULL
, *sit
, *maxit
;
318 ob_debug("placing nonoverlap");
324 /* try ignoring different things to find empty space */
325 for (ignore
= 0; ignore
< IGNORE_END
&& !ret
; ignore
++) {
328 /* add the whole monitor */
329 spaces
= area_add(spaces
, area
);
331 /* go thru all the windows */
332 for (it
= client_list
; it
; it
= g_list_next(it
)) {
333 ObClient
*test
= it
->data
;
335 /* should we ignore this client? */
336 if (screen_showing_desktop
) continue;
337 if (c
== test
) continue;
338 if (test
->iconic
) continue;
339 if (c
->desktop
!= DESKTOP_ALL
) {
340 if (test
->desktop
!= c
->desktop
&&
341 test
->desktop
!= DESKTOP_ALL
) continue;
343 if (test
->desktop
!= screen_desktop
&&
344 test
->desktop
!= DESKTOP_ALL
) continue;
346 if (test
->type
== OB_CLIENT_TYPE_SPLASH
||
347 test
->type
== OB_CLIENT_TYPE_DESKTOP
) continue;
350 if ((ignore
>= IGNORE_FULLSCREEN
) &&
351 test
->fullscreen
) continue;
352 if ((ignore
>= IGNORE_MAXIMIZED
) &&
353 test
->max_horz
&& test
->max_vert
) continue;
354 if ((ignore
>= IGNORE_MENUTOOL
) &&
355 (test
->type
== OB_CLIENT_TYPE_MENU
||
356 test
->type
== OB_CLIENT_TYPE_TOOLBAR
) &&
357 client_has_parent(c
)) continue;
359 if ((ignore >= IGNORE_SHADED) &&
360 test->shaded) continue;
362 if ((ignore
>= IGNORE_NONGROUP
) &&
363 client_has_group_siblings(c
) &&
364 test
->group
!= c
->group
) continue;
365 if ((ignore
>= IGNORE_BELOW
) &&
366 test
->layer
< c
->layer
) continue;
368 if ((ignore >= IGNORE_NONFOCUS) &&
369 focus_client != test) continue;
371 /* don't ignore this window, so remove it from the available
373 spaces
= area_remove(spaces
, &test
->frame
->area
);
376 if (ignore
< IGNORE_DOCK
) {
379 spaces
= area_remove(spaces
, &a
);
382 for (sit
= spaces
; sit
; sit
= g_slist_next(sit
)) {
385 if (r
->width
>= c
->frame
->area
.width
&&
386 r
->height
>= c
->frame
->area
.height
&&
387 r
->width
* r
->height
> maxsize
)
389 maxsize
= r
->width
* r
->height
;
395 Rect
*r
= maxit
->data
;
397 /* center it in the area */
400 if (config_place_center
) {
401 *x
+= (r
->width
- c
->frame
->area
.width
) / 2;
402 *y
+= (r
->height
- c
->frame
->area
.height
) / 2;
408 g_slice_free(Rect
, spaces
->data
);
409 spaces
= g_slist_delete_link(spaces
, spaces
);
416 static gboolean
place_under_mouse(ObClient
*client
, gint
*x
, gint
*y
)
422 ob_debug("placing under mouse");
424 if (!screen_pointer_pos(&px
, &py
))
426 area
= pick_pointer_head(client
);
430 r
= area
->x
+ area
->width
- client
->frame
->area
.width
;
431 b
= area
->y
+ area
->height
- client
->frame
->area
.height
;
433 *x
= px
- client
->area
.width
/ 2 - client
->frame
->size
.left
;
434 *x
= MIN(MAX(*x
, l
), r
);
435 *y
= py
- client
->area
.height
/ 2 - client
->frame
->size
.top
;
436 *y
= MIN(MAX(*y
, t
), b
);
438 g_slice_free(Rect
, area
);
443 static gboolean
place_per_app_setting(ObClient
*client
, gint
*x
, gint
*y
,
444 ObAppSettings
*settings
)
448 if (!settings
|| (settings
&& !settings
->pos_given
))
451 ob_debug("placing by per-app settings");
453 /* Find which head the pointer is on */
454 if (settings
->monitor
== 0)
455 /* this can return NULL */
456 screen
= pick_pointer_head(client
);
458 guint m
= settings
->monitor
;
459 if (m
< 1 || m
> screen_num_monitors
)
460 m
= screen_monitor_primary(TRUE
) + 1;
461 screen
= screen_area(client
->desktop
, m
- 1, NULL
);
464 if (settings
->position
.x
.center
)
465 *x
= screen
->x
+ screen
->width
/ 2 - client
->area
.width
/ 2;
466 else if (settings
->position
.x
.opposite
)
467 *x
= screen
->x
+ screen
->width
- client
->frame
->area
.width
-
468 settings
->position
.x
.pos
;
470 *x
= screen
->x
+ settings
->position
.x
.pos
;
471 if (settings
->position
.x
.denom
)
472 *x
= (*x
* screen
->width
) / settings
->position
.x
.denom
;
474 if (settings
->position
.y
.center
)
475 *y
= screen
->y
+ screen
->height
/ 2 - client
->area
.height
/ 2;
476 else if (settings
->position
.y
.opposite
)
477 *y
= screen
->y
+ screen
->height
- client
->frame
->area
.height
-
478 settings
->position
.y
.pos
;
480 *y
= screen
->y
+ settings
->position
.y
.pos
;
481 if (settings
->position
.y
.denom
)
482 *y
= (*y
* screen
->height
) / settings
->position
.y
.denom
;
484 g_slice_free(Rect
, screen
);
488 static gboolean
place_transient_splash(ObClient
*client
, Rect
*area
,
491 if (client
->type
== OB_CLIENT_TYPE_DIALOG
) {
493 gboolean first
= TRUE
;
496 ob_debug("placing dialog");
498 for (it
= client
->parents
; it
; it
= g_slist_next(it
)) {
499 ObClient
*m
= it
->data
;
502 l
= RECT_LEFT(m
->frame
->area
);
503 t
= RECT_TOP(m
->frame
->area
);
504 r
= RECT_RIGHT(m
->frame
->area
);
505 b
= RECT_BOTTOM(m
->frame
->area
);
508 l
= MIN(l
, RECT_LEFT(m
->frame
->area
));
509 t
= MIN(t
, RECT_TOP(m
->frame
->area
));
510 r
= MAX(r
, RECT_RIGHT(m
->frame
->area
));
511 b
= MAX(b
, RECT_BOTTOM(m
->frame
->area
));
515 *x
= ((r
+ 1 - l
) - client
->frame
->area
.width
) / 2 + l
;
516 *y
= ((b
+ 1 - t
) - client
->frame
->area
.height
) / 2 + t
;
522 if (client
->type
== OB_CLIENT_TYPE_DIALOG
||
523 client
->type
== OB_CLIENT_TYPE_SPLASH
)
525 ob_debug("placing dialog or splash");
527 *x
= (area
->width
- client
->frame
->area
.width
) / 2 + area
->x
;
528 *y
= (area
->height
- client
->frame
->area
.height
) / 2 + area
->y
;
535 /*! Return TRUE if openbox chose the position for the window, and FALSE if
536 the application chose it */
537 gboolean
place_client(ObClient
*client
, gboolean foreground
, gint
*x
, gint
*y
,
538 ObAppSettings
*settings
)
543 /* per-app settings override program specified position
544 * but not user specified, unless pos_force is enabled */
545 if (((client
->positioned
& USPosition
) &&
546 !(settings
&& settings
->pos_given
&& settings
->pos_force
)) ||
547 ((client
->positioned
& PPosition
) &&
548 !(settings
&& settings
->pos_given
)))
551 area
= pick_head(client
, foreground
);
553 /* try a number of methods */
554 ret
= place_per_app_setting(client
, x
, y
, settings
) ||
555 place_transient_splash(client
, area
, x
, y
) ||
556 (config_place_policy
== OB_PLACE_POLICY_MOUSE
&&
557 place_under_mouse(client
, x
, y
)) ||
558 place_nooverlap(client
, area
, x
, y
) ||
559 place_random(client
, area
, x
, y
);
562 g_slice_free(Rect
, area
);
564 /* get where the client should be */
565 frame_frame_gravity(client
->frame
, x
, y
);
This page took 0.062792 seconds and 4 git commands to generate.