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
;
31 /*! When true, stacking changes will not be reflected on the screen. This is
32 to freeze the on-screen stacking order while a window is being temporarily
33 raised during focus cycling */
34 static gboolean pause_changes
= FALSE
;
36 void stacking_set_list(void)
38 Window
*windows
= NULL
;
42 /* on shutdown, don't update the properties, so that we can read it back
43 in on startup and re-stack the windows as they were before we shut down
45 if (ob_state() == OB_STATE_EXITING
) return;
47 /* create an array of the window ids (from bottom to top,
50 windows
= g_new(Window
, g_list_length(stacking_list
));
51 for (it
= g_list_last(stacking_list
); it
; it
= g_list_previous(it
)) {
52 if (WINDOW_IS_CLIENT(it
->data
))
53 windows
[i
++] = WINDOW_AS_CLIENT(it
->data
)->window
;
57 PROP_SETA32(RootWindow(ob_display
, ob_screen
),
58 net_client_list_stacking
, window
, (gulong
*)windows
, i
);
63 static void do_restack(GList
*wins
, GList
*before
)
71 /* pls only restack stuff in the same layer at a time */
72 for (it
= wins
; it
; it
= next
) {
73 next
= g_list_next(it
);
75 g_assert (window_layer(it
->data
) == window_layer(next
->data
));
78 g_assert(window_layer(it
->data
) >= window_layer(before
->data
));
81 win
= g_new(Window
, g_list_length(wins
) + 1);
83 if (before
== stacking_list
)
84 win
[0] = screen_support_win
;
86 win
[0] = window_top(g_list_last(stacking_list
)->data
);
88 win
[0] = window_top(g_list_previous(before
)->data
);
90 for (i
= 1, it
= wins
; it
; ++i
, it
= g_list_next(it
)) {
91 win
[i
] = window_top(it
->data
);
92 g_assert(win
[i
] != None
); /* better not call stacking shit before
93 setting your top level window value */
94 stacking_list
= g_list_insert_before(stacking_list
, before
, it
->data
);
98 /* some debug checking of the stacking list's order */
99 for (it
= stacking_list
; ; it
= next
) {
100 next
= g_list_next(it
);
102 g_assert(window_layer(it
->data
) >= window_layer(next
->data
));
107 XRestackWindows(ob_display
, win
, i
);
113 void stacking_temp_raise(ObWindow
*window
)
119 /* don't use this for internal windows..! it would lower them.. */
120 g_assert(window_layer(window
) < OB_STACKING_LAYER_INTERNAL
);
122 /* find the window to drop it underneath */
123 win
[0] = screen_support_win
;
124 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
125 ObWindow
*w
= it
->data
;
126 if (window_layer(w
) >= OB_STACKING_LAYER_INTERNAL
)
127 win
[0] = window_top(w
);
132 win
[1] = window_top(window
);
133 start
= event_start_ignore_all_enters();
134 XRestackWindows(ob_display
, win
, 2);
135 event_end_ignore_all_enters(start
);
137 pause_changes
= TRUE
;
140 void stacking_restore(void)
147 win
= g_new(Window
, g_list_length(stacking_list
) + 1);
148 win
[0] = screen_support_win
;
149 for (i
= 1, it
= stacking_list
; it
; ++i
, it
= g_list_next(it
))
150 win
[i
] = window_top(it
->data
);
151 start
= event_start_ignore_all_enters();
152 XRestackWindows(ob_display
, win
, i
);
153 event_end_ignore_all_enters(start
);
156 pause_changes
= FALSE
;
159 static void do_raise(GList
*wins
)
162 GList
*layer
[OB_NUM_STACKING_LAYERS
] = {NULL
};
165 for (it
= wins
; it
; it
= g_list_next(it
)) {
168 l
= window_layer(it
->data
);
169 layer
[l
] = g_list_append(layer
[l
], it
->data
);
173 for (i
= OB_NUM_STACKING_LAYERS
- 1; i
>= 0; --i
) {
175 for (; it
; it
= g_list_next(it
)) {
176 /* look for the top of the layer */
177 if (window_layer(it
->data
) <= (ObStackingLayer
) i
)
180 do_restack(layer
[i
], it
);
181 g_list_free(layer
[i
]);
186 static void do_lower(GList
*wins
)
189 GList
*layer
[OB_NUM_STACKING_LAYERS
] = {NULL
};
192 for (it
= wins
; it
; it
= g_list_next(it
)) {
195 l
= window_layer(it
->data
);
196 layer
[l
] = g_list_append(layer
[l
], it
->data
);
200 for (i
= OB_NUM_STACKING_LAYERS
- 1; i
>= 0; --i
) {
202 for (; it
; it
= g_list_next(it
)) {
203 /* look for the top of the next layer down */
204 if (window_layer(it
->data
) < (ObStackingLayer
) i
)
207 do_restack(layer
[i
], it
);
208 g_list_free(layer
[i
]);
213 static void restack_windows(ObClient
*selected
, gboolean raise
)
215 GList
*it
, *last
, *below
, *above
, *next
;
218 GList
*group_modals
= NULL
;
219 GList
*group_trans
= NULL
;
220 GList
*modals
= NULL
;
223 /* remove first so we can't run into ourself */
224 it
= g_list_find(stacking_list
, selected
);
226 stacking_list
= g_list_delete_link(stacking_list
, it
);
228 /* go from the bottom of the stacking list up. don't move any other windows
229 when lowering, we call this for each window independently */
231 for (it
= g_list_last(stacking_list
); it
; it
= next
) {
232 next
= g_list_previous(it
);
234 if (WINDOW_IS_CLIENT(it
->data
)) {
235 ObClient
*ch
= it
->data
;
237 /* only move windows in the same stacking layer */
238 if (ch
->layer
== selected
->layer
&&
239 client_search_transient(selected
, ch
))
241 if (client_is_direct_child(selected
, ch
)) {
243 modals
= g_list_prepend(modals
, ch
);
245 trans
= g_list_prepend(trans
, ch
);
249 group_modals
= g_list_prepend(group_modals
, ch
);
251 group_trans
= g_list_prepend(group_trans
, ch
);
253 stacking_list
= g_list_delete_link(stacking_list
, it
);
259 /* put transients of the selected window right above it */
260 wins
= g_list_concat(modals
, trans
);
261 wins
= g_list_append(wins
, selected
);
263 /* if selected window is transient for group then raise it above others */
264 if (selected
->transient_for_group
) {
265 /* if it's modal, raise it above those also */
266 if (selected
->modal
) {
267 wins
= g_list_concat(wins
, group_modals
);
270 wins
= g_list_concat(wins
, group_trans
);
274 /* find where to put the selected window, start from bottom of list,
275 this is the window below everything we are re-adding to the list */
277 for (it
= g_list_last(stacking_list
); it
; it
= g_list_previous(it
))
279 if (window_layer(it
->data
) < selected
->layer
) {
283 /* if lowering, stop at the beginning of the layer */
286 /* if raising, stop at the end of the layer */
287 if (window_layer(it
->data
) > selected
->layer
)
293 /* save this position in the stacking list */
296 /* find where to put the group transients, start from the top of list */
297 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
298 /* skip past higher layers */
299 if (window_layer(it
->data
) > selected
->layer
)
301 /* if we reach the end of the layer (how?) then don't go further */
302 if (window_layer(it
->data
) < selected
->layer
)
304 /* stop when we reach the first window in the group */
305 if (WINDOW_IS_CLIENT(it
->data
)) {
306 ObClient
*c
= it
->data
;
307 if (c
->group
== selected
->group
)
310 /* if we don't hit any other group members, stop here because this
311 is where we are putting the selected window (and its children) */
316 /* save this position, this is the top of the group of windows between the
317 group transient ones we're restacking and the others up above that we're
320 we actually want to save 1 position _above_ that, for for loops to work
321 nicely, so move back one position in the list while saving it
323 above
= it
? g_list_previous(it
) : g_list_last(stacking_list
);
325 /* put the windows inside the gap to the other windows we're stacking
326 into the restacking list, go from the bottom up so that we can use
328 if (below
) it
= g_list_previous(below
);
329 else it
= g_list_last(stacking_list
);
330 for (; it
!= above
; it
= next
) {
331 next
= g_list_previous(it
);
332 wins
= g_list_prepend(wins
, it
->data
);
333 stacking_list
= g_list_delete_link(stacking_list
, it
);
336 /* group transients go above the rest of the stuff acquired to now */
337 wins
= g_list_concat(group_trans
, wins
);
338 /* group modals go on the very top */
339 wins
= g_list_concat(group_modals
, wins
);
341 do_restack(wins
, below
);
344 /* lower our parents after us, so they go below us */
345 if (!raise
&& selected
->parents
) {
346 GSList
*parents_copy
, *sit
;
347 GSList
*reorder
= NULL
;
349 parents_copy
= g_slist_copy(selected
->parents
);
351 /* go thru stacking list backwards so we can use g_slist_prepend */
352 for (it
= g_list_last(stacking_list
); it
&& parents_copy
;
353 it
= g_list_previous(it
))
354 if ((sit
= g_slist_find(parents_copy
, it
->data
))) {
355 reorder
= g_slist_prepend(reorder
, sit
->data
);
356 parents_copy
= g_slist_delete_link(parents_copy
, sit
);
358 g_assert(parents_copy
== NULL
);
360 /* call restack for each of these to lower them */
361 for (sit
= reorder
; sit
; sit
= g_slist_next(sit
))
362 restack_windows(sit
->data
, raise
);
366 void stacking_raise(ObWindow
*window
)
368 if (WINDOW_IS_CLIENT(window
)) {
370 selected
= WINDOW_AS_CLIENT(window
);
371 restack_windows(selected
, TRUE
);
374 wins
= g_list_append(NULL
, window
);
375 stacking_list
= g_list_remove(stacking_list
, window
);
381 void stacking_lower(ObWindow
*window
)
383 if (WINDOW_IS_CLIENT(window
)) {
385 selected
= WINDOW_AS_CLIENT(window
);
386 restack_windows(selected
, FALSE
);
389 wins
= g_list_append(NULL
, window
);
390 stacking_list
= g_list_remove(stacking_list
, window
);
396 void stacking_below(ObWindow
*window
, ObWindow
*below
)
398 GList
*wins
, *before
;
400 if (window_layer(window
) != window_layer(below
))
403 wins
= g_list_append(NULL
, window
);
404 stacking_list
= g_list_remove(stacking_list
, window
);
405 before
= g_list_next(g_list_find(stacking_list
, below
));
406 do_restack(wins
, before
);
410 void stacking_add(ObWindow
*win
)
412 g_assert(screen_support_win
!= None
); /* make sure I dont break this in the
415 stacking_list
= g_list_append(stacking_list
, win
);
419 static GList
*find_highest_relative(ObClient
*client
)
423 if (client
->parents
) {
427 /* get all top level relatives of this client */
428 top
= client_search_all_top_parents_layer(client
);
430 /* go from the top of the stacking order down */
431 for (it
= stacking_list
; !ret
&& it
; it
= g_list_next(it
)) {
432 if (WINDOW_IS_CLIENT(it
->data
)) {
433 ObClient
*c
= it
->data
;
434 /* only look at windows in the same layer and that are
436 if (c
->layer
== client
->layer
&&
438 (c
->desktop
== client
->desktop
||
439 c
->desktop
== DESKTOP_ALL
||
440 client
->desktop
== DESKTOP_ALL
))
444 /* go through each top level parent and see it this window
445 is related to them */
446 for (sit
= top
; !ret
&& sit
; sit
= g_slist_next(sit
)) {
447 ObClient
*topc
= sit
->data
;
449 /* are they related ? */
450 if (topc
== c
|| client_search_transient(topc
, c
))
460 void stacking_add_nonintrusive(ObWindow
*win
)
463 GList
*it_below
= NULL
; /* this client will be below us */
467 if (!WINDOW_IS_CLIENT(win
)) {
468 stacking_add(win
); /* no special rules for others */
472 client
= WINDOW_AS_CLIENT(win
);
474 /* insert above its highest parent (or its highest child !) */
475 it_below
= find_highest_relative(client
);
478 /* nothing to put it directly above, so try find the focused client
479 to put it underneath it */
480 if (focus_client
&& client
!= focus_client
&&
481 focus_client
->layer
== client
->layer
)
483 it_below
= g_list_find(stacking_list
, focus_client
);
484 /* this can give NULL, but it means the focused window is on the
485 bottom of the stacking order, so go to the bottom in that case,
487 it_below
= g_list_next(it_below
);
490 /* There is no window to put this directly above, so put it at the
491 top, so you know it is there.
493 It used to do this only if the window was focused and lower
496 We also put it at the top not the bottom to fix a bug with
497 fullscreen windows. When focusLast is off and followsMouse is
498 on, when you switch desktops, the fullscreen window loses
499 focus and goes into its lower layer. If this puts it at the
500 bottom then when you come back to the desktop, the window is
501 at the bottom and won't get focus back.
503 it_below
= stacking_list
;
507 /* make sure it's not in the wrong layer though ! */
508 for (; it_below
; it_below
= g_list_next(it_below
)) {
509 /* stop when the window is not in a higher layer than the window
510 it is going above (it_below) */
511 if (client
->layer
>= window_layer(it_below
->data
))
514 for (; it_below
!= stacking_list
; it_below
= it_above
) {
515 /* stop when the window is not in a lower layer than the
516 window it is going under (it_above) */
517 it_above
= it_below
?
518 g_list_previous(it_below
) : g_list_last(stacking_list
);
519 if (client
->layer
<= window_layer(it_above
->data
))
523 wins
= g_list_append(NULL
, win
);
524 do_restack(wins
, it_below
);
528 /*! Returns TRUE if client is occluded by the sibling. If sibling is NULL it
529 tries against all other clients.
531 static gboolean
stacking_occluded(ObClient
*client
, ObClient
*sibling
)
534 gboolean occluded
= FALSE
;
535 gboolean found
= FALSE
;
537 /* no need for any looping in this case */
538 if (sibling
&& client
->layer
!= sibling
->layer
)
541 for (it
= stacking_list
; it
;
542 it
= (found
? g_list_previous(it
) :g_list_next(it
)))
543 if (WINDOW_IS_CLIENT(it
->data
)) {
544 ObClient
*c
= it
->data
;
545 if (found
&& !c
->iconic
&&
546 (c
->desktop
== DESKTOP_ALL
|| client
->desktop
== DESKTOP_ALL
||
547 c
->desktop
== client
->desktop
) &&
548 !client_search_transient(client
, c
))
550 if (RECT_INTERSECTS_RECT(c
->frame
->area
, client
->frame
->area
))
552 if (sibling
!= NULL
) {
558 else if (c
->layer
== client
->layer
) {
562 else if (c
->layer
> client
->layer
)
563 break; /* we past its layer */
566 else if (c
== client
)
572 /*! Returns TRUE if client occludes the sibling. If sibling is NULL it tries
573 against all other clients.
575 static gboolean
stacking_occludes(ObClient
*client
, ObClient
*sibling
)
578 gboolean occludes
= FALSE
;
579 gboolean found
= FALSE
;
581 /* no need for any looping in this case */
582 if (sibling
&& client
->layer
!= sibling
->layer
)
585 for (it
= stacking_list
; it
; it
= g_list_next(it
))
586 if (WINDOW_IS_CLIENT(it
->data
)) {
587 ObClient
*c
= it
->data
;
588 if (found
&& !c
->iconic
&&
589 (c
->desktop
== DESKTOP_ALL
|| client
->desktop
== DESKTOP_ALL
||
590 c
->desktop
== client
->desktop
) &&
591 !client_search_transient(c
, client
))
593 if (RECT_INTERSECTS_RECT(c
->frame
->area
, client
->frame
->area
))
595 if (sibling
!= NULL
) {
601 else if (c
->layer
== client
->layer
) {
605 else if (c
->layer
< client
->layer
)
606 break; /* we past its layer */
609 else if (c
== client
)
615 gboolean
stacking_restack_request(ObClient
*client
, ObClient
*sibling
,
618 gboolean ret
= FALSE
;
620 if (sibling
&& ((client
->desktop
!= sibling
->desktop
&&
621 client
->desktop
!= DESKTOP_ALL
&&
622 sibling
->desktop
!= DESKTOP_ALL
) ||
625 ob_debug("Setting restack sibling to NULL, they are not on the same "
626 "desktop or it is iconified\n");
632 ob_debug("Restack request Below for client %s sibling %s\n",
633 client
->title
, sibling
? sibling
->title
: "(all)");
635 stacking_lower(CLIENT_AS_WINDOW(client
));
639 ob_debug("Restack request BottomIf for client %s sibling "
641 client
->title
, sibling
? sibling
->title
: "(all)");
642 /* if this client occludes sibling (or anything if NULL), then
643 lower it to the bottom */
644 if (stacking_occludes(client
, sibling
)) {
645 stacking_lower(CLIENT_AS_WINDOW(client
));
650 ob_debug("Restack request Above for client %s sibling %s\n",
651 client
->title
, sibling
? sibling
->title
: "(all)");
652 stacking_raise(CLIENT_AS_WINDOW(client
));
656 ob_debug("Restack request TopIf for client %s sibling %s\n",
657 client
->title
, sibling
? sibling
->title
: "(all)");
658 if (stacking_occluded(client
, sibling
)) {
659 stacking_raise(CLIENT_AS_WINDOW(client
));
664 ob_debug("Restack request Opposite for client %s sibling "
666 client
->title
, sibling
? sibling
->title
: "(all)");
667 if (stacking_occluded(client
, sibling
)) {
668 stacking_raise(CLIENT_AS_WINDOW(client
));
671 else if (stacking_occludes(client
, sibling
)) {
672 stacking_lower(CLIENT_AS_WINDOW(client
));