]>
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.
28 static void add_choice(guint
*choice
, guint mychoice
)
31 for (i
= 0; i
< screen_num_monitors
; ++i
) {
32 if (choice
[i
] == mychoice
)
34 else if (choice
[i
] == screen_num_monitors
) {
41 static Rect
*pick_pointer_head(ObClient
*c
)
46 screen_pointer_pos(&px
, &py
);
48 for (i
= 0; i
< screen_num_monitors
; ++i
) {
49 if (RECT_CONTAINS(*screen_physical_area_monitor(i
), px
, py
)) {
50 return screen_area_monitor(c
->desktop
, i
);
53 g_assert_not_reached();
56 /*! Pick a monitor to place a window on.
57 The returned array value should be freed with g_free. The areas within the
58 array should not be freed. */
59 static Rect
**pick_head(ObClient
*c
)
66 area
= g_new(Rect
*, screen_num_monitors
);
67 choice
= g_new(guint
, screen_num_monitors
);
68 for (i
= 0; i
< screen_num_monitors
; ++i
)
69 choice
[i
] = screen_num_monitors
; /* make them all invalid to start */
71 /* try direct parent first */
72 if (c
->transient_for
&& c
->transient_for
!= OB_TRAN_GROUP
&&
73 client_normal(c
->transient_for
))
75 add_choice(choice
, client_monitor(c
->transient_for
));
76 ob_debug("placement adding choice %d for parent\n",
77 client_monitor(c
->transient_for
));
80 /* more than one window in its group (more than just this window) */
81 if (client_has_group_siblings(c
)) {
84 /* try on the client's desktop */
85 for (it
= c
->group
->members
; it
; it
= g_slist_next(it
)) {
86 ObClient
*itc
= it
->data
;
88 (itc
->desktop
== c
->desktop
||
89 itc
->desktop
== DESKTOP_ALL
|| c
->desktop
== DESKTOP_ALL
))
91 add_choice(choice
, client_monitor(it
->data
));
92 ob_debug("placement adding choice %d for group sibling\n",
93 client_monitor(it
->data
));
97 /* try on all desktops */
98 for (it
= c
->group
->members
; it
; it
= g_slist_next(it
)) {
99 ObClient
*itc
= it
->data
;
101 add_choice(choice
, client_monitor(it
->data
));
102 ob_debug("placement adding choice %d for group sibling on "
103 "another desktop\n", client_monitor(it
->data
));
109 add_choice(choice
, client_monitor(focus_client
));
110 ob_debug("placement adding choice %d for focused window\n",
111 client_monitor(focus_client
));
114 screen_pointer_pos(&px
, &py
);
116 for (i
= 0; i
< screen_num_monitors
; i
++)
117 if (RECT_CONTAINS(*screen_physical_area_monitor(i
), px
, py
)) {
118 add_choice(choice
, i
);
119 ob_debug("placement adding choice %d for mouse pointer\n", i
);
123 /* add any leftover choices */
124 for (i
= 0; i
< screen_num_monitors
; ++i
)
125 add_choice(choice
, i
);
127 for (i
= 0; i
< screen_num_monitors
; ++i
)
128 area
[i
] = screen_area_monitor(c
->desktop
, choice
[i
]);
133 static gboolean
place_random(ObClient
*client
, gint
*x
, gint
*y
)
139 areas
= pick_head(client
);
140 i
= g_random_int_range(0, screen_num_monitors
);
144 r
= areas
[i
]->x
+ areas
[i
]->width
- client
->frame
->area
.width
;
145 b
= areas
[i
]->y
+ areas
[i
]->height
- client
->frame
->area
.height
;
147 if (r
> l
) *x
= g_random_int_range(l
, r
+ 1);
148 else *x
= areas
[i
]->x
;
149 if (b
> t
) *y
= g_random_int_range(t
, b
+ 1);
150 else *y
= areas
[i
]->y
;
157 static GSList
* area_add(GSList
*list
, Rect
*a
)
159 Rect
*r
= g_new(Rect
, 1);
161 return g_slist_prepend(list
, r
);
164 static GSList
* area_remove(GSList
*list
, Rect
*a
)
167 GSList
*result
= NULL
;
169 for (sit
= list
; sit
; sit
= g_slist_next(sit
)) {
172 if (!RECT_INTERSECTS_RECT(*r
, *a
)) {
173 result
= g_slist_prepend(result
, r
);
174 r
= NULL
; /* dont free it */
178 /* Use an intersection of a and r to determine the space
179 around r that we can use.
181 NOTE: the spaces calculated can overlap.
184 RECT_SET_INTERSECTION(isect
, *r
, *a
);
186 if (RECT_LEFT(isect
) > RECT_LEFT(*r
)) {
187 RECT_SET(extra
, r
->x
, r
->y
,
188 RECT_LEFT(isect
) - r
->x
, r
->height
);
189 result
= area_add(result
, &extra
);
192 if (RECT_TOP(isect
) > RECT_TOP(*r
)) {
193 RECT_SET(extra
, r
->x
, r
->y
,
194 r
->width
, RECT_TOP(isect
) - r
->y
+ 1);
195 result
= area_add(result
, &extra
);
198 if (RECT_RIGHT(isect
) < RECT_RIGHT(*r
)) {
199 RECT_SET(extra
, RECT_RIGHT(isect
) + 1, r
->y
,
200 RECT_RIGHT(*r
) - RECT_RIGHT(isect
), r
->height
);
201 result
= area_add(result
, &extra
);
204 if (RECT_BOTTOM(isect
) < RECT_BOTTOM(*r
)) {
205 RECT_SET(extra
, r
->x
, RECT_BOTTOM(isect
) + 1,
206 r
->width
, RECT_BOTTOM(*r
) - RECT_BOTTOM(isect
));
207 result
= area_add(result
, &extra
);
217 static gint
area_cmp(gconstpointer p1
, gconstpointer p2
, gpointer data
)
220 Rect
*carea
= &c
->frame
->area
;
221 const Rect
*a1
= p1
, *a2
= p2
;
222 gboolean diffhead
= FALSE
;
226 for (i
= 0; i
< screen_num_monitors
; ++i
) {
227 a
= screen_physical_area_monitor(i
);
228 if (RECT_CONTAINS(*a
, a1
->x
, a1
->y
) &&
229 !RECT_CONTAINS(*a
, a2
->x
, a2
->y
))
236 /* has to be more than me in the group */
237 if (diffhead
&& client_has_group_siblings(c
)) {
241 /* find how many clients in the group are on each monitor, use the
242 monitor with the most in it */
243 num
= g_new0(guint
, screen_num_monitors
);
244 for (it
= c
->group
->members
; it
; it
= g_slist_next(it
))
246 ++num
[client_monitor(it
->data
)];
248 for (i
= 1; i
< screen_num_monitors
; ++i
)
249 if (num
[i
] > num
[most
])
254 a
= screen_physical_area_monitor(most
);
255 if (RECT_CONTAINS(*a
, a1
->x
, a1
->y
))
257 if (RECT_CONTAINS(*a
, a2
->x
, a2
->y
))
261 return MIN((a1
->width
- carea
->width
), (a1
->height
- carea
->height
)) -
262 MIN((a2
->width
- carea
->width
), (a2
->height
- carea
->height
));
272 #define SMART_IGNORE(placer, c) \
273 (placer == c || c->shaded || !c->frame->visible || \
274 c->type == OB_CLIENT_TYPE_SPLASH || c->type == OB_CLIENT_TYPE_DESKTOP || \
275 ((c->type == OB_CLIENT_TYPE_MENU || c->type == OB_CLIENT_TYPE_TOOLBAR) &&\
276 client_has_parent(c)) || \
277 (c->desktop != DESKTOP_ALL && \
278 c->desktop != (placer->desktop == DESKTOP_ALL ? \
279 screen_desktop : placer->desktop)))
281 static gboolean
place_smart(ObClient
*client
, gint
*x
, gint
*y
,
282 ObSmartType type
, gboolean ignore_max
)
284 gboolean ret
= FALSE
;
285 GSList
*spaces
= NULL
, *sit
;
290 if (type
== SMART_GROUP
) {
291 /* has to be more than me in the group */
292 if (!client_has_group_siblings(client
))
296 areas
= pick_head(client
);
298 for (i
= 0; i
< screen_num_monitors
&& !ret
; ++i
) {
299 spaces
= area_add(spaces
, areas
[i
]);
301 /* stay out from under windows in higher layers */
302 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
305 if (WINDOW_IS_CLIENT(it
->data
)) {
308 (c
->fullscreen
|| (c
->max_vert
&& c
->max_horz
)))
313 if (c
->layer
> client
->layer
) {
314 if (!SMART_IGNORE(client
, c
))
315 spaces
= area_remove(spaces
, &c
->frame
->area
);
320 if (type
== SMART_FULL
|| type
== SMART_FOCUSED
) {
321 gboolean found_foc
= FALSE
, stop
= FALSE
;
324 foc
= focus_order_find_first(client
->desktop
== DESKTOP_ALL
?
325 screen_desktop
: client
->desktop
);
327 for (; it
&& !stop
; it
= g_list_next(it
)) {
330 if (WINDOW_IS_CLIENT(it
->data
)) {
333 (c
->fullscreen
|| (c
->max_vert
&& c
->max_horz
)))
338 if (!SMART_IGNORE(client
, c
)) {
339 if (type
== SMART_FOCUSED
)
343 spaces
= area_remove(spaces
, &c
->frame
->area
);
349 } else if (type
== SMART_GROUP
) {
350 for (sit
= client
->group
->members
; sit
; sit
= g_slist_next(sit
)) {
351 ObClient
*c
= sit
->data
;
352 if (!SMART_IGNORE(client
, c
))
353 spaces
= area_remove(spaces
, &c
->frame
->area
);
356 g_assert_not_reached();
358 spaces
= g_slist_sort_with_data(spaces
, area_cmp
, client
);
360 for (sit
= spaces
; sit
; sit
= g_slist_next(sit
)) {
364 if (r
->width
>= client
->frame
->area
.width
&&
365 r
->height
>= client
->frame
->area
.height
) {
367 if (client
->type
== OB_CLIENT_TYPE_DIALOG
||
370 *x
= r
->x
+ (r
->width
- client
->frame
->area
.width
)/2;
371 *y
= r
->y
+ (r
->height
- client
->frame
->area
.height
)/2;
381 g_slist_free(spaces
);
390 static gboolean
place_under_mouse(ObClient
*client
, gint
*x
, gint
*y
)
396 area
= pick_pointer_head(client
);
397 screen_pointer_pos(&px
, &py
);
401 r
= area
->x
+ area
->width
- client
->frame
->area
.width
;
402 b
= area
->y
+ area
->height
- client
->frame
->area
.height
;
404 *x
= px
- client
->area
.width
/ 2 - client
->frame
->size
.left
;
405 *x
= MIN(MAX(*x
, l
), r
);
406 *y
= py
- client
->area
.height
/ 2 - client
->frame
->size
.top
;
407 *y
= MIN(MAX(*y
, t
), b
);
412 static gboolean
place_per_app_setting(ObClient
*client
, gint
*x
, gint
*y
,
413 ObAppSettings
*settings
)
417 if (!settings
|| (settings
&& !settings
->pos_given
))
420 /* Find which head the pointer is on */
421 if (settings
->monitor
== 0)
422 screen
= pick_pointer_head(client
);
423 else if (settings
->monitor
> 0 &&
424 (guint
)settings
->monitor
<= screen_num_monitors
)
425 screen
= screen_area_monitor(client
->desktop
,
426 (guint
)settings
->monitor
- 1);
429 all
= pick_head(client
);
431 g_free(all
); /* the areas themselves don't need to be freed */
434 if (settings
->center_x
)
435 *x
= screen
->x
+ screen
->width
/ 2 - client
->area
.width
/ 2;
437 *x
= screen
->x
+ settings
->position
.x
;
439 if (settings
->center_y
)
440 *y
= screen
->y
+ screen
->height
/ 2 - client
->area
.height
/ 2;
442 *y
= screen
->y
+ settings
->position
.y
;
447 static gboolean
place_transient_splash(ObClient
*client
, gint
*x
, gint
*y
)
449 if (client
->transient_for
&& client
->type
== OB_CLIENT_TYPE_DIALOG
) {
450 if (client
->transient_for
!= OB_TRAN_GROUP
&&
451 client_normal(client
->transient_for
) &&
454 ObClient
*c
= client
;
455 ObClient
*p
= client
->transient_for
;
457 if (client_normal(p
)) {
458 *x
= (p
->frame
->area
.width
- c
->frame
->area
.width
) / 2 +
460 *y
= (p
->frame
->area
.height
- c
->frame
->area
.height
) / 2 +
466 gboolean first
= TRUE
;
468 for (it
= client
->group
->members
; it
; it
= g_slist_next(it
)) {
469 ObClient
*m
= it
->data
;
470 if (!(m
== client
|| m
->transient_for
) && client_normal(m
) &&
474 l
= RECT_LEFT(m
->frame
->area
);
475 t
= RECT_TOP(m
->frame
->area
);
476 r
= RECT_RIGHT(m
->frame
->area
);
477 b
= RECT_BOTTOM(m
->frame
->area
);
480 l
= MIN(l
, RECT_LEFT(m
->frame
->area
));
481 t
= MIN(t
, RECT_TOP(m
->frame
->area
));
482 r
= MAX(r
, RECT_RIGHT(m
->frame
->area
));
483 b
= MAX(b
, RECT_BOTTOM(m
->frame
->area
));
488 *x
= ((r
+ 1 - l
) - client
->frame
->area
.width
) / 2 + l
;
489 *y
= ((b
+ 1 - t
) - client
->frame
->area
.height
) / 2 + t
;
495 if ((client
->transient
&& client
->type
== OB_CLIENT_TYPE_DIALOG
)
496 || client
->type
== OB_CLIENT_TYPE_SPLASH
)
500 areas
= pick_head(client
);
502 *x
= (areas
[0]->width
- client
->frame
->area
.width
) / 2 + areas
[0]->x
;
503 *y
= (areas
[0]->height
- client
->frame
->area
.height
) / 2 + areas
[0]->y
;
512 /* Return TRUE if we want client.c to enforce on-screen-keeping */
513 gboolean
place_client(ObClient
*client
, gint
*x
, gint
*y
,
514 ObAppSettings
*settings
)
516 gboolean ret
= FALSE
;
517 if (client
->positioned
)
519 if (place_transient_splash(client
, x
, y
))
522 place_per_app_setting(client
, x
, y
, settings
) ||
523 ((config_place_policy
== OB_PLACE_POLICY_MOUSE
) ?
524 place_under_mouse(client
, x
, y
) :
525 place_smart(client
, x
, y
, SMART_FULL
, FALSE
) ||
526 place_smart(client
, x
, y
, SMART_FULL
, TRUE
) ||
527 place_smart(client
, x
, y
, SMART_GROUP
, FALSE
) ||
528 place_smart(client
, x
, y
, SMART_GROUP
, TRUE
) ||
529 place_smart(client
, x
, y
, SMART_FOCUSED
, TRUE
) ||
530 place_random(client
, x
, y
))))
531 g_assert_not_reached(); /* the last one better succeed */
532 /* get where the client should be */
533 frame_frame_gravity(client
->frame
, x
, y
,
534 client
->area
.width
, client
->area
.height
);
This page took 0.056713 seconds and 5 git commands to generate.