1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 stacking.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.
30 GList
*stacking_list
= NULL
;
32 void stacking_set_list(void)
34 Window
*windows
= NULL
;
38 /* on shutdown, don't update the properties, so that we can read it back
39 in on startup and re-stack the windows as they were before we shut down
41 if (ob_state() == OB_STATE_EXITING
) return;
43 /* create an array of the window ids (from bottom to top,
46 windows
= g_new(Window
, g_list_length(stacking_list
));
47 for (it
= g_list_last(stacking_list
); it
; it
= g_list_previous(it
)) {
48 if (WINDOW_IS_CLIENT(it
->data
))
49 windows
[i
++] = WINDOW_AS_CLIENT(it
->data
)->window
;
53 PROP_SETA32(RootWindow(ob_display
, ob_screen
),
54 net_client_list_stacking
, window
, (gulong
*)windows
, i
);
59 static void do_restack(GList
*wins
, GList
*before
)
67 /* pls only restack stuff in the same layer at a time */
68 for (it
= wins
; it
; it
= next
) {
69 next
= g_list_next(it
);
71 g_assert (window_layer(it
->data
) == window_layer(next
->data
));
74 g_assert(window_layer(it
->data
) >= window_layer(before
->data
));
77 win
= g_new(Window
, g_list_length(wins
) + 1);
79 if (before
== stacking_list
)
80 win
[0] = screen_support_win
;
82 win
[0] = window_top(g_list_last(stacking_list
)->data
);
84 win
[0] = window_top(g_list_previous(before
)->data
);
86 for (i
= 1, it
= wins
; it
; ++i
, it
= g_list_next(it
)) {
87 win
[i
] = window_top(it
->data
);
88 g_assert(win
[i
] != None
); /* better not call stacking shit before
89 setting your top level window value */
90 stacking_list
= g_list_insert_before(stacking_list
, before
, it
->data
);
94 /* some debug checking of the stacking list's order */
95 for (it
= stacking_list
; ; it
= next
) {
96 next
= g_list_next(it
);
98 g_assert(window_layer(it
->data
) >= window_layer(next
->data
));
102 XRestackWindows(ob_display
, win
, i
);
108 void stacking_temp_raise(ObWindow
*window
)
113 /* don't use this for internal windows..! it would lower them.. */
114 g_assert(window_layer(window
) < OB_STACKING_LAYER_INTERNAL
);
116 /* find the window to drop it underneath */
117 win
[0] = screen_support_win
;
118 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
119 ObWindow
*w
= it
->data
;
120 if (window_layer(w
) >= OB_STACKING_LAYER_INTERNAL
)
121 win
[0] = window_top(w
);
126 win
[1] = window_top(window
);
127 XRestackWindows(ob_display
, win
, 2);
130 void stacking_restore()
136 win
= g_new(Window
, g_list_length(stacking_list
) + 1);
137 win
[0] = screen_support_win
;
138 for (i
= 1, it
= stacking_list
; it
; ++i
, it
= g_list_next(it
))
139 win
[i
] = window_top(it
->data
);
140 XRestackWindows(ob_display
, win
, i
);
144 static void do_raise(GList
*wins
)
147 GList
*layer
[OB_NUM_STACKING_LAYERS
] = {NULL
};
150 for (it
= wins
; it
; it
= g_list_next(it
)) {
153 l
= window_layer(it
->data
);
154 layer
[l
] = g_list_append(layer
[l
], it
->data
);
158 for (i
= OB_NUM_STACKING_LAYERS
- 1; i
>= 0; --i
) {
160 for (; it
; it
= g_list_next(it
)) {
161 /* look for the top of the layer */
162 if (window_layer(it
->data
) <= (ObStackingLayer
) i
)
165 do_restack(layer
[i
], it
);
166 g_list_free(layer
[i
]);
171 static void do_lower(GList
*wins
)
174 GList
*layer
[OB_NUM_STACKING_LAYERS
] = {NULL
};
177 for (it
= wins
; it
; it
= g_list_next(it
)) {
180 l
= window_layer(it
->data
);
181 layer
[l
] = g_list_append(layer
[l
], it
->data
);
185 for (i
= OB_NUM_STACKING_LAYERS
- 1; i
>= 0; --i
) {
187 for (; it
; it
= g_list_next(it
)) {
188 /* look for the top of the next layer down */
189 if (window_layer(it
->data
) < (ObStackingLayer
) i
)
192 do_restack(layer
[i
], it
);
193 g_list_free(layer
[i
]);
198 static void restack_windows(ObClient
*selected
, gboolean raise
)
200 GList
*it
, *last
, *below
, *above
, *next
;
203 GList
*group_modals
= NULL
;
204 GList
*group_trans
= NULL
;
205 GList
*modals
= NULL
;
208 /* remove first so we can't run into ourself */
209 it
= g_list_find(stacking_list
, selected
);
211 stacking_list
= g_list_delete_link(stacking_list
, it
);
213 /* go from the bottom of the stacking list up. don't move any other windows
214 when lowering, we call this for each window independently */
216 for (it
= g_list_last(stacking_list
); it
; it
= next
) {
217 next
= g_list_previous(it
);
219 if (WINDOW_IS_CLIENT(it
->data
)) {
220 ObClient
*ch
= it
->data
;
222 /* only move windows in the same stacking layer */
223 if (ch
->layer
== selected
->layer
&&
224 client_search_transient(selected
, ch
))
226 if (client_is_direct_child(selected
, ch
)) {
228 modals
= g_list_prepend(modals
, ch
);
230 trans
= g_list_prepend(trans
, ch
);
234 group_modals
= g_list_prepend(group_modals
, ch
);
236 group_trans
= g_list_prepend(group_trans
, ch
);
238 stacking_list
= g_list_delete_link(stacking_list
, it
);
244 /* put transients of the selected window right above it */
245 wins
= g_list_concat(modals
, trans
);
246 wins
= g_list_append(wins
, selected
);
248 /* if selected window is transient for group then raise it above others */
249 if (selected
->transient_for_group
) {
250 /* if it's modal, raise it above those also */
251 if (selected
->modal
) {
252 wins
= g_list_concat(wins
, group_modals
);
255 wins
= g_list_concat(wins
, group_trans
);
259 /* find where to put the selected window, start from bottom of list,
260 this is the window below everything we are re-adding to the list */
262 for (it
= g_list_last(stacking_list
); it
; it
= g_list_previous(it
))
264 if (window_layer(it
->data
) < selected
->layer
) {
268 /* if lowering, stop at the beginning of the layer */
271 /* if raising, stop at the end of the layer */
272 if (window_layer(it
->data
) > selected
->layer
)
278 /* save this position in the stacking list */
281 /* find where to put the group transients, start from the top of list */
282 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
283 /* skip past higher layers */
284 if (window_layer(it
->data
) > selected
->layer
)
286 /* if we reach the end of the layer (how?) then don't go further */
287 if (window_layer(it
->data
) < selected
->layer
)
289 /* stop when we reach the first window in the group */
290 if (WINDOW_IS_CLIENT(it
->data
)) {
291 ObClient
*c
= it
->data
;
292 if (c
->group
== selected
->group
)
295 /* if we don't hit any other group members, stop here because this
296 is where we are putting the selected window (and its children) */
301 /* save this position, this is the top of the group of windows between the
302 group transient ones we're restacking and the others up above that we're
305 we actually want to save 1 position _above_ that, for for loops to work
306 nicely, so move back one position in the list while saving it
308 above
= it
? g_list_previous(it
) : g_list_last(stacking_list
);
310 /* put the windows inside the gap to the other windows we're stacking
311 into the restacking list, go from the bottom up so that we can use
313 if (below
) it
= g_list_previous(below
);
314 else it
= g_list_last(stacking_list
);
315 for (; it
!= above
; it
= next
) {
316 next
= g_list_previous(it
);
317 wins
= g_list_prepend(wins
, it
->data
);
318 stacking_list
= g_list_delete_link(stacking_list
, it
);
321 /* group transients go above the rest of the stuff acquired to now */
322 wins
= g_list_concat(group_trans
, wins
);
323 /* group modals go on the very top */
324 wins
= g_list_concat(group_modals
, wins
);
326 do_restack(wins
, below
);
329 /* lower our parents after us, so they go below us */
330 if (!raise
&& selected
->parents
) {
331 GSList
*parents_copy
, *sit
;
332 GSList
*reorder
= NULL
;
334 parents_copy
= g_slist_copy(selected
->parents
);
336 /* go thru stacking list backwards so we can use g_slist_prepend */
337 for (it
= g_list_last(stacking_list
); it
&& parents_copy
;
338 it
= g_list_previous(it
))
339 if ((sit
= g_slist_find(parents_copy
, it
->data
))) {
340 reorder
= g_slist_prepend(reorder
, sit
->data
);
341 parents_copy
= g_slist_delete_link(parents_copy
, sit
);
343 g_assert(parents_copy
== NULL
);
345 /* call restack for each of these to lower them */
346 for (sit
= reorder
; sit
; sit
= g_slist_next(sit
))
347 restack_windows(sit
->data
, raise
);
351 void stacking_raise(ObWindow
*window
)
353 if (WINDOW_IS_CLIENT(window
)) {
355 selected
= WINDOW_AS_CLIENT(window
);
356 restack_windows(selected
, TRUE
);
359 wins
= g_list_append(NULL
, window
);
360 stacking_list
= g_list_remove(stacking_list
, window
);
366 void stacking_lower(ObWindow
*window
)
368 if (WINDOW_IS_CLIENT(window
)) {
370 selected
= WINDOW_AS_CLIENT(window
);
371 restack_windows(selected
, FALSE
);
374 wins
= g_list_append(NULL
, window
);
375 stacking_list
= g_list_remove(stacking_list
, window
);
381 void stacking_below(ObWindow
*window
, ObWindow
*below
)
383 GList
*wins
, *before
;
385 if (window_layer(window
) != window_layer(below
))
388 wins
= g_list_append(NULL
, window
);
389 stacking_list
= g_list_remove(stacking_list
, window
);
390 before
= g_list_next(g_list_find(stacking_list
, below
));
391 do_restack(wins
, before
);
395 void stacking_add(ObWindow
*win
)
397 g_assert(screen_support_win
!= None
); /* make sure I dont break this in the
400 stacking_list
= g_list_append(stacking_list
, win
);
404 static GList
*find_highest_relative(ObClient
*client
)
408 if (client
->parents
) {
412 /* get all top level relatives of this client */
413 top
= client_search_all_top_parents_layer(client
);
415 /* go from the top of the stacking order down */
416 for (it
= stacking_list
; !ret
&& it
; it
= g_list_next(it
)) {
417 if (WINDOW_IS_CLIENT(it
->data
)) {
418 ObClient
*c
= it
->data
;
419 /* only look at windows in the same layer and that are
421 if (c
->layer
== client
->layer
&&
423 (c
->desktop
== client
->desktop
||
424 c
->desktop
== DESKTOP_ALL
||
425 client
->desktop
== DESKTOP_ALL
))
429 /* go through each top level parent and see it this window
430 is related to them */
431 for (sit
= top
; !ret
&& sit
; sit
= g_slist_next(sit
)) {
432 ObClient
*topc
= sit
->data
;
434 /* are they related ? */
435 if (topc
== c
|| client_search_transient(topc
, c
))
445 void stacking_add_nonintrusive(ObWindow
*win
)
448 GList
*it_below
= NULL
; /* this client will be below us */
452 if (!WINDOW_IS_CLIENT(win
)) {
453 stacking_add(win
); /* no special rules for others */
457 client
= WINDOW_AS_CLIENT(win
);
459 /* insert above its highest parent (or its highest child !) */
460 it_below
= find_highest_relative(client
);
463 /* nothing to put it directly above, so try find the focused client
464 to put it underneath it */
465 if (focus_client
&& client
!= focus_client
&&
466 focus_client
->layer
== client
->layer
)
468 it_below
= g_list_find(stacking_list
, focus_client
);
469 /* this can give NULL, but it means the focused window is on the
470 bottom of the stacking order, so go to the bottom in that case,
472 it_below
= g_list_next(it_below
);
475 /* There is no window to put this directly above, so put it at the
476 top, so you know it is there.
478 It used to do this only if the window was focused and lower
481 We also put it at the top not the bottom to fix a bug with
482 fullscreen windows. When focusLast is off and followsMouse is
483 on, when you switch desktops, the fullscreen window loses
484 focus and goes into its lower layer. If this puts it at the
485 bottom then when you come back to the desktop, the window is
486 at the bottom and won't get focus back.
488 it_below
= stacking_list
;
492 /* make sure it's not in the wrong layer though ! */
493 for (; it_below
; it_below
= g_list_next(it_below
)) {
494 /* stop when the window is not in a higher layer than the window
495 it is going above (it_below) */
496 if (client
->layer
>= window_layer(it_below
->data
))
499 for (; it_below
!= stacking_list
; it_below
= it_above
) {
500 /* stop when the window is not in a lower layer than the
501 window it is going under (it_above) */
502 it_above
= it_below
?
503 g_list_previous(it_below
) : g_list_last(stacking_list
);
504 if (client
->layer
<= window_layer(it_above
->data
))
508 wins
= g_list_append(NULL
, win
);
509 do_restack(wins
, it_below
);
513 /*! Returns TRUE if client is occluded by the sibling. If sibling is NULL it
514 tries against all other clients.
516 static gboolean
stacking_occluded(ObClient
*client
, ObClient
*sibling
)
519 gboolean occluded
= FALSE
;
520 gboolean found
= FALSE
;
522 /* no need for any looping in this case */
523 if (sibling
&& client
->layer
!= sibling
->layer
)
526 for (it
= stacking_list
; it
;
527 it
= (found
? g_list_previous(it
) :g_list_next(it
)))
528 if (WINDOW_IS_CLIENT(it
->data
)) {
529 ObClient
*c
= it
->data
;
530 if (found
&& !c
->iconic
&&
531 (c
->desktop
== DESKTOP_ALL
|| client
->desktop
== DESKTOP_ALL
||
532 c
->desktop
== client
->desktop
) &&
533 !client_search_transient(client
, c
))
535 if (RECT_INTERSECTS_RECT(c
->frame
->area
, client
->frame
->area
))
537 if (sibling
!= NULL
) {
543 else if (c
->layer
== client
->layer
) {
547 else if (c
->layer
> client
->layer
)
548 break; /* we past its layer */
551 else if (c
== client
)
557 /*! Returns TRUE if client occludes the sibling. If sibling is NULL it tries
558 against all other clients.
560 static gboolean
stacking_occludes(ObClient
*client
, ObClient
*sibling
)
563 gboolean occludes
= FALSE
;
564 gboolean found
= FALSE
;
566 /* no need for any looping in this case */
567 if (sibling
&& client
->layer
!= sibling
->layer
)
570 for (it
= stacking_list
; it
; it
= g_list_next(it
))
571 if (WINDOW_IS_CLIENT(it
->data
)) {
572 ObClient
*c
= it
->data
;
573 if (found
&& !c
->iconic
&&
574 (c
->desktop
== DESKTOP_ALL
|| client
->desktop
== DESKTOP_ALL
||
575 c
->desktop
== client
->desktop
) &&
576 !client_search_transient(c
, client
))
578 if (RECT_INTERSECTS_RECT(c
->frame
->area
, client
->frame
->area
))
580 if (sibling
!= NULL
) {
586 else if (c
->layer
== client
->layer
) {
590 else if (c
->layer
< client
->layer
)
591 break; /* we past its layer */
594 else if (c
== client
)
600 gboolean
stacking_restack_request(ObClient
*client
, ObClient
*sibling
,
603 gboolean ret
= FALSE
;
605 if (sibling
&& ((client
->desktop
!= sibling
->desktop
&&
606 client
->desktop
!= DESKTOP_ALL
&&
607 sibling
->desktop
!= DESKTOP_ALL
) ||
610 ob_debug("Setting restack sibling to NULL, they are not on the same "
611 "desktop or it is iconified\n");
617 ob_debug("Restack request Below for client %s sibling %s\n",
618 client
->title
, sibling
? sibling
->title
: "(all)");
620 stacking_lower(CLIENT_AS_WINDOW(client
));
624 ob_debug("Restack request BottomIf for client %s sibling "
626 client
->title
, sibling
? sibling
->title
: "(all)");
627 /* if this client occludes sibling (or anything if NULL), then
628 lower it to the bottom */
629 if (stacking_occludes(client
, sibling
)) {
630 stacking_lower(CLIENT_AS_WINDOW(client
));
635 ob_debug("Restack request Above for client %s sibling %s\n",
636 client
->title
, sibling
? sibling
->title
: "(all)");
637 stacking_raise(CLIENT_AS_WINDOW(client
));
641 ob_debug("Restack request TopIf for client %s sibling %s\n",
642 client
->title
, sibling
? sibling
->title
: "(all)");
643 if (stacking_occluded(client
, sibling
)) {
644 stacking_raise(CLIENT_AS_WINDOW(client
));
649 ob_debug("Restack request Opposite for client %s sibling "
651 client
->title
, sibling
? sibling
->title
: "(all)");
652 if (stacking_occluded(client
, sibling
)) {
653 stacking_raise(CLIENT_AS_WINDOW(client
));
656 else if (stacking_occludes(client
, sibling
)) {
657 stacking_lower(CLIENT_AS_WINDOW(client
));