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.
31 GList
*stacking_list
= NULL
;
32 /*! When true, stacking changes will not be reflected on the screen. This is
33 to freeze the on-screen stacking order while a window is being temporarily
34 raised during focus cycling */
35 static gboolean pause_changes
= FALSE
;
37 void stacking_set_list(void)
39 Window
*windows
= NULL
;
43 /* on shutdown, don't update the properties, so that we can read it back
44 in on startup and re-stack the windows as they were before we shut down
46 if (ob_state() == OB_STATE_EXITING
) return;
48 /* create an array of the window ids (from bottom to top,
51 windows
= g_new(Window
, g_list_length(stacking_list
));
52 for (it
= g_list_last(stacking_list
); it
; it
= g_list_previous(it
)) {
53 if (WINDOW_IS_CLIENT(it
->data
))
54 windows
[i
++] = WINDOW_AS_CLIENT(it
->data
)->window
;
58 OBT_PROP_SETA32(obt_root(ob_screen
), NET_CLIENT_LIST_STACKING
, WINDOW
,
64 static void do_restack(GList
*wins
, GList
*before
)
74 /* pls only restack stuff in the same layer at a time */
75 for (it
= wins
; it
; it
= next
) {
76 next
= g_list_next(it
);
78 g_assert (window_layer(it
->data
) == window_layer(next
->data
));
81 g_assert(window_layer(it
->data
) >= window_layer(before
->data
));
84 win
= g_new(Window
, g_list_length(wins
) + 1);
86 if (before
== stacking_list
)
87 win
[0] = screen_support_win
;
89 win
[0] = window_top(g_list_last(stacking_list
)->data
);
91 win
[0] = window_top(g_list_previous(before
)->data
);
93 for (i
= 1, it
= wins
; it
; ++i
, it
= g_list_next(it
)) {
94 win
[i
] = window_top(it
->data
);
95 g_assert(win
[i
] != None
); /* better not call stacking shit before
96 setting your top level window value */
97 stacking_list
= g_list_insert_before(stacking_list
, before
, it
->data
);
101 /* some debug checking of the stacking list's order */
102 for (it
= stacking_list
; ; it
= next
) {
103 next
= g_list_next(it
);
105 g_assert(window_layer(it
->data
) >= window_layer(next
->data
));
110 XRestackWindows(obt_display
, win
, i
);
116 void stacking_temp_raise(ObWindow
*window
)
122 /* don't use this for internal windows..! it would lower them.. */
123 g_assert(window_layer(window
) < OB_STACKING_LAYER_INTERNAL
);
125 /* find the window to drop it underneath */
126 win
[0] = screen_support_win
;
127 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
128 ObWindow
*w
= it
->data
;
129 if (window_layer(w
) >= OB_STACKING_LAYER_INTERNAL
)
130 win
[0] = window_top(w
);
135 win
[1] = window_top(window
);
136 start
= event_start_ignore_all_enters();
137 XRestackWindows(obt_display
, win
, 2);
138 event_end_ignore_all_enters(start
);
140 pause_changes
= TRUE
;
143 void stacking_restore(void)
150 win
= g_new(Window
, g_list_length(stacking_list
) + 1);
151 win
[0] = screen_support_win
;
152 for (i
= 1, it
= stacking_list
; it
; ++i
, it
= g_list_next(it
))
153 win
[i
] = window_top(it
->data
);
154 start
= event_start_ignore_all_enters();
155 XRestackWindows(obt_display
, win
, i
);
156 event_end_ignore_all_enters(start
);
159 pause_changes
= FALSE
;
162 static void do_raise(GList
*wins
)
165 GList
*layer
[OB_NUM_STACKING_LAYERS
] = {NULL
};
168 for (it
= wins
; it
; it
= g_list_next(it
)) {
171 l
= window_layer(it
->data
);
172 layer
[l
] = g_list_append(layer
[l
], it
->data
);
176 for (i
= OB_NUM_STACKING_LAYERS
- 1; i
>= 0; --i
) {
178 for (; it
; it
= g_list_next(it
)) {
179 /* look for the top of the layer */
180 if (window_layer(it
->data
) <= (ObStackingLayer
) i
)
183 do_restack(layer
[i
], it
);
184 g_list_free(layer
[i
]);
189 static void do_lower(GList
*wins
)
192 GList
*layer
[OB_NUM_STACKING_LAYERS
] = {NULL
};
195 for (it
= wins
; it
; it
= g_list_next(it
)) {
198 l
= window_layer(it
->data
);
199 layer
[l
] = g_list_append(layer
[l
], it
->data
);
203 for (i
= OB_NUM_STACKING_LAYERS
- 1; i
>= 0; --i
) {
205 for (; it
; it
= g_list_next(it
)) {
206 /* look for the top of the next layer down */
207 if (window_layer(it
->data
) < (ObStackingLayer
) i
)
210 do_restack(layer
[i
], it
);
211 g_list_free(layer
[i
]);
216 static void restack_windows(ObClient
*selected
, gboolean raise
)
218 GList
*it
, *last
, *below
, *above
, *next
;
221 GList
*group_helpers
= NULL
;
222 GList
*group_modals
= NULL
;
223 GList
*group_trans
= NULL
;
224 GList
*modals
= NULL
;
230 /* if a window is modal for another single window, then raise it to the
231 top too, the same is done with the focus order */
232 while (selected
->modal
&& (p
= client_direct_parent(selected
)))
236 /* remove first so we can't run into ourself */
237 it
= g_list_find(stacking_list
, selected
);
239 stacking_list
= g_list_delete_link(stacking_list
, it
);
241 /* go from the bottom of the stacking list up. don't move any other windows
242 when lowering, we call this for each window independently */
244 for (it
= g_list_last(stacking_list
); it
; it
= next
) {
245 next
= g_list_previous(it
);
247 if (WINDOW_IS_CLIENT(it
->data
)) {
248 ObClient
*ch
= it
->data
;
250 /* only move windows in the same stacking layer */
251 if (ch
->layer
== selected
->layer
&&
252 /* looking for windows that are transients, and so would
253 remain above the selected window */
254 client_search_transient(selected
, ch
))
256 if (client_is_direct_child(selected
, ch
)) {
258 modals
= g_list_prepend(modals
, ch
);
260 trans
= g_list_prepend(trans
, ch
);
262 else if (client_helper(ch
)) {
263 if (selected
->transient
) {
264 /* helpers do not stay above transient windows */
267 group_helpers
= g_list_prepend(group_helpers
, ch
);
271 group_modals
= g_list_prepend(group_modals
, ch
);
273 group_trans
= g_list_prepend(group_trans
, ch
);
275 stacking_list
= g_list_delete_link(stacking_list
, it
);
281 /* put modals above other direct transients */
282 wins
= g_list_concat(modals
, trans
);
284 /* put helpers below direct transients */
285 wins
= g_list_concat(wins
, group_helpers
);
287 /* put the selected window right below these children */
288 wins
= g_list_append(wins
, selected
);
290 /* if selected window is transient for group then raise it above others */
291 if (selected
->transient_for_group
) {
292 /* if it's modal, raise it above those also */
293 if (selected
->modal
) {
294 wins
= g_list_concat(wins
, group_modals
);
297 wins
= g_list_concat(wins
, group_trans
);
301 /* find where to put the selected window, start from bottom of list,
302 this is the window below everything we are re-adding to the list */
304 for (it
= g_list_last(stacking_list
); it
; it
= g_list_previous(it
))
306 if (window_layer(it
->data
) < selected
->layer
) {
310 /* if lowering, stop at the beginning of the layer */
313 /* if raising, stop at the end of the layer */
314 if (window_layer(it
->data
) > selected
->layer
)
320 /* save this position in the stacking list */
323 /* find where to put the group transients, start from the top of list */
324 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
325 /* skip past higher layers */
326 if (window_layer(it
->data
) > selected
->layer
)
328 /* if we reach the end of the layer (how?) then don't go further */
329 if (window_layer(it
->data
) < selected
->layer
)
331 /* stop when we reach the first window in the group */
332 if (WINDOW_IS_CLIENT(it
->data
)) {
333 ObClient
*c
= it
->data
;
334 if (c
->group
== selected
->group
)
337 /* if we don't hit any other group members, stop here because this
338 is where we are putting the selected window (and its children) */
343 /* save this position, this is the top of the group of windows between the
344 group transient ones we're restacking and the others up above that we're
347 we actually want to save 1 position _above_ that, for for loops to work
348 nicely, so move back one position in the list while saving it
350 above
= it
? g_list_previous(it
) : g_list_last(stacking_list
);
352 /* put the windows inside the gap to the other windows we're stacking
353 into the restacking list, go from the bottom up so that we can use
355 if (below
) it
= g_list_previous(below
);
356 else it
= g_list_last(stacking_list
);
357 for (; it
!= above
; it
= next
) {
358 next
= g_list_previous(it
);
359 wins
= g_list_prepend(wins
, it
->data
);
360 stacking_list
= g_list_delete_link(stacking_list
, it
);
363 /* group transients go above the rest of the stuff acquired to now */
364 wins
= g_list_concat(group_trans
, wins
);
365 /* group modals go on the very top */
366 wins
= g_list_concat(group_modals
, wins
);
368 do_restack(wins
, below
);
371 /* lower our parents after us, so they go below us */
372 if (!raise
&& selected
->parents
) {
373 GSList
*parents_copy
, *sit
;
374 GSList
*reorder
= NULL
;
376 parents_copy
= g_slist_copy(selected
->parents
);
378 /* go thru stacking list backwards so we can use g_slist_prepend */
379 for (it
= g_list_last(stacking_list
); it
&& parents_copy
;
380 it
= g_list_previous(it
))
381 if ((sit
= g_slist_find(parents_copy
, it
->data
))) {
382 reorder
= g_slist_prepend(reorder
, sit
->data
);
383 parents_copy
= g_slist_delete_link(parents_copy
, sit
);
385 g_assert(parents_copy
== NULL
);
387 /* call restack for each of these to lower them */
388 for (sit
= reorder
; sit
; sit
= g_slist_next(sit
))
389 restack_windows(sit
->data
, raise
);
393 void stacking_raise(ObWindow
*window
)
395 if (WINDOW_IS_CLIENT(window
)) {
397 selected
= WINDOW_AS_CLIENT(window
);
398 restack_windows(selected
, TRUE
);
401 wins
= g_list_append(NULL
, window
);
402 stacking_list
= g_list_remove(stacking_list
, window
);
408 void stacking_lower(ObWindow
*window
)
410 if (WINDOW_IS_CLIENT(window
)) {
412 selected
= WINDOW_AS_CLIENT(window
);
413 restack_windows(selected
, FALSE
);
416 wins
= g_list_append(NULL
, window
);
417 stacking_list
= g_list_remove(stacking_list
, window
);
423 void stacking_below(ObWindow
*window
, ObWindow
*below
)
425 GList
*wins
, *before
;
427 if (window_layer(window
) != window_layer(below
))
430 wins
= g_list_append(NULL
, window
);
431 stacking_list
= g_list_remove(stacking_list
, window
);
432 before
= g_list_next(g_list_find(stacking_list
, below
));
433 do_restack(wins
, before
);
437 void stacking_add(ObWindow
*win
)
439 g_assert(screen_support_win
!= None
); /* make sure I dont break this in the
442 stacking_list
= g_list_append(stacking_list
, win
);
446 static GList
*find_highest_relative(ObClient
*client
)
450 if (client
->parents
) {
454 /* get all top level relatives of this client */
455 top
= client_search_all_top_parents_layer(client
);
457 /* go from the top of the stacking order down */
458 for (it
= stacking_list
; !ret
&& it
; it
= g_list_next(it
)) {
459 if (WINDOW_IS_CLIENT(it
->data
)) {
460 ObClient
*c
= it
->data
;
461 /* only look at windows in the same layer and that are
463 if (c
->layer
== client
->layer
&&
465 (c
->desktop
== client
->desktop
||
466 c
->desktop
== DESKTOP_ALL
||
467 client
->desktop
== DESKTOP_ALL
))
471 /* go through each top level parent and see it this window
472 is related to them */
473 for (sit
= top
; !ret
&& sit
; sit
= g_slist_next(sit
)) {
474 ObClient
*topc
= sit
->data
;
476 /* are they related ? */
477 if (topc
== c
|| client_search_transient(topc
, c
))
487 void stacking_add_nonintrusive(ObWindow
*win
)
490 GList
*it_below
= NULL
; /* this client will be below us */
494 if (!WINDOW_IS_CLIENT(win
)) {
495 stacking_add(win
); /* no special rules for others */
499 client
= WINDOW_AS_CLIENT(win
);
501 /* insert above its highest parent (or its highest child !) */
502 it_below
= find_highest_relative(client
);
505 /* nothing to put it directly above, so try find the focused client
506 to put it underneath it */
507 if (focus_client
&& client
!= focus_client
&&
508 focus_client
->layer
== client
->layer
)
510 it_below
= g_list_find(stacking_list
, focus_client
);
511 /* this can give NULL, but it means the focused window is on the
512 bottom of the stacking order, so go to the bottom in that case,
514 it_below
= g_list_next(it_below
);
517 /* There is no window to put this directly above, so put it at the
518 top, so you know it is there.
520 It used to do this only if the window was focused and lower
523 We also put it at the top not the bottom to fix a bug with
524 fullscreen windows. When focusLast is off and followsMouse is
525 on, when you switch desktops, the fullscreen window loses
526 focus and goes into its lower layer. If this puts it at the
527 bottom then when you come back to the desktop, the window is
528 at the bottom and won't get focus back.
530 it_below
= stacking_list
;
534 /* make sure it's not in the wrong layer though ! */
535 for (; it_below
; it_below
= g_list_next(it_below
)) {
536 /* stop when the window is not in a higher layer than the window
537 it is going above (it_below) */
538 if (client
->layer
>= window_layer(it_below
->data
))
541 for (; it_below
!= stacking_list
; it_below
= it_above
) {
542 /* stop when the window is not in a lower layer than the
543 window it is going under (it_above) */
544 it_above
= it_below
?
545 g_list_previous(it_below
) : g_list_last(stacking_list
);
546 if (client
->layer
<= window_layer(it_above
->data
))
550 wins
= g_list_append(NULL
, win
);
551 do_restack(wins
, it_below
);
555 /*! Returns TRUE if client is occluded by the sibling. If sibling is NULL it
556 tries against all other clients.
558 static gboolean
stacking_occluded(ObClient
*client
, ObClient
*sibling
)
561 gboolean occluded
= FALSE
;
562 gboolean found
= FALSE
;
564 /* no need for any looping in this case */
565 if (sibling
&& client
->layer
!= sibling
->layer
)
568 for (it
= stacking_list
; it
;
569 it
= (found
? g_list_previous(it
) :g_list_next(it
)))
570 if (WINDOW_IS_CLIENT(it
->data
)) {
571 ObClient
*c
= it
->data
;
572 if (found
&& !c
->iconic
&&
573 (c
->desktop
== DESKTOP_ALL
|| client
->desktop
== DESKTOP_ALL
||
574 c
->desktop
== client
->desktop
) &&
575 !client_search_transient(client
, c
))
577 if (RECT_INTERSECTS_RECT(c
->frame
->area
, client
->frame
->area
))
579 if (sibling
!= NULL
) {
585 else if (c
->layer
== client
->layer
) {
589 else if (c
->layer
> client
->layer
)
590 break; /* we past its layer */
593 else if (c
== client
)
599 /*! Returns TRUE if client occludes the sibling. If sibling is NULL it tries
600 against all other clients.
602 static gboolean
stacking_occludes(ObClient
*client
, ObClient
*sibling
)
605 gboolean occludes
= FALSE
;
606 gboolean found
= FALSE
;
608 /* no need for any looping in this case */
609 if (sibling
&& client
->layer
!= sibling
->layer
)
612 for (it
= stacking_list
; it
; it
= g_list_next(it
))
613 if (WINDOW_IS_CLIENT(it
->data
)) {
614 ObClient
*c
= it
->data
;
615 if (found
&& !c
->iconic
&&
616 (c
->desktop
== DESKTOP_ALL
|| client
->desktop
== DESKTOP_ALL
||
617 c
->desktop
== client
->desktop
) &&
618 !client_search_transient(c
, client
))
620 if (RECT_INTERSECTS_RECT(c
->frame
->area
, client
->frame
->area
))
622 if (sibling
!= NULL
) {
628 else if (c
->layer
== client
->layer
) {
632 else if (c
->layer
< client
->layer
)
633 break; /* we past its layer */
636 else if (c
== client
)
642 gboolean
stacking_restack_request(ObClient
*client
, ObClient
*sibling
,
645 gboolean ret
= FALSE
;
647 if (sibling
&& ((client
->desktop
!= sibling
->desktop
&&
648 client
->desktop
!= DESKTOP_ALL
&&
649 sibling
->desktop
!= DESKTOP_ALL
) ||
652 ob_debug("Setting restack sibling to NULL, they are not on the same "
653 "desktop or it is iconified");
659 ob_debug("Restack request Below for client %s sibling %s",
660 client
->title
, sibling
? sibling
->title
: "(all)");
662 stacking_lower(CLIENT_AS_WINDOW(client
));
666 ob_debug("Restack request BottomIf for client %s sibling %s",
667 client
->title
, sibling
? sibling
->title
: "(all)");
668 /* if this client occludes sibling (or anything if NULL), then
669 lower it to the bottom */
670 if (stacking_occludes(client
, sibling
)) {
671 stacking_lower(CLIENT_AS_WINDOW(client
));
676 ob_debug("Restack request Above for client %s sibling %s",
677 client
->title
, sibling
? sibling
->title
: "(all)");
678 stacking_raise(CLIENT_AS_WINDOW(client
));
682 ob_debug("Restack request TopIf for client %s sibling %s",
683 client
->title
, sibling
? sibling
->title
: "(all)");
684 if (stacking_occluded(client
, sibling
)) {
685 stacking_raise(CLIENT_AS_WINDOW(client
));
690 ob_debug("Restack request Opposite for client %s sibling %s",
691 client
->title
, sibling
? sibling
->title
: "(all)");
692 if (stacking_occluded(client
, sibling
)) {
693 stacking_raise(CLIENT_AS_WINDOW(client
));
696 else if (stacking_occludes(client
, sibling
)) {
697 stacking_lower(CLIENT_AS_WINDOW(client
));