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_modals
= NULL
;
222 GList
*group_trans
= NULL
;
223 GList
*modals
= NULL
;
229 /* if a window is modal for another single window, then raise it to the
230 top too, the same is done with the focus order */
231 while (selected
->modal
&& (p
= client_direct_parent(selected
)))
235 /* remove first so we can't run into ourself */
236 it
= g_list_find(stacking_list
, selected
);
238 stacking_list
= g_list_delete_link(stacking_list
, it
);
240 /* go from the bottom of the stacking list up. don't move any other windows
241 when lowering, we call this for each window independently */
243 for (it
= g_list_last(stacking_list
); it
; it
= next
) {
244 next
= g_list_previous(it
);
246 if (WINDOW_IS_CLIENT(it
->data
)) {
247 ObClient
*ch
= it
->data
;
249 /* only move windows in the same stacking layer */
250 if (ch
->layer
== selected
->layer
&&
251 client_search_transient(selected
, ch
))
253 if (client_is_direct_child(selected
, ch
)) {
255 modals
= g_list_prepend(modals
, ch
);
257 trans
= g_list_prepend(trans
, ch
);
261 group_modals
= g_list_prepend(group_modals
, ch
);
263 group_trans
= g_list_prepend(group_trans
, ch
);
265 stacking_list
= g_list_delete_link(stacking_list
, it
);
271 /* put transients of the selected window right above it */
272 wins
= g_list_concat(modals
, trans
);
273 wins
= g_list_append(wins
, selected
);
275 /* if selected window is transient for group then raise it above others */
276 if (selected
->transient_for_group
) {
277 /* if it's modal, raise it above those also */
278 if (selected
->modal
) {
279 wins
= g_list_concat(wins
, group_modals
);
282 wins
= g_list_concat(wins
, group_trans
);
286 /* find where to put the selected window, start from bottom of list,
287 this is the window below everything we are re-adding to the list */
289 for (it
= g_list_last(stacking_list
); it
; it
= g_list_previous(it
))
291 if (window_layer(it
->data
) < selected
->layer
) {
295 /* if lowering, stop at the beginning of the layer */
298 /* if raising, stop at the end of the layer */
299 if (window_layer(it
->data
) > selected
->layer
)
305 /* save this position in the stacking list */
308 /* find where to put the group transients, start from the top of list */
309 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
310 /* skip past higher layers */
311 if (window_layer(it
->data
) > selected
->layer
)
313 /* if we reach the end of the layer (how?) then don't go further */
314 if (window_layer(it
->data
) < selected
->layer
)
316 /* stop when we reach the first window in the group */
317 if (WINDOW_IS_CLIENT(it
->data
)) {
318 ObClient
*c
= it
->data
;
319 if (c
->group
== selected
->group
)
322 /* if we don't hit any other group members, stop here because this
323 is where we are putting the selected window (and its children) */
328 /* save this position, this is the top of the group of windows between the
329 group transient ones we're restacking and the others up above that we're
332 we actually want to save 1 position _above_ that, for for loops to work
333 nicely, so move back one position in the list while saving it
335 above
= it
? g_list_previous(it
) : g_list_last(stacking_list
);
337 /* put the windows inside the gap to the other windows we're stacking
338 into the restacking list, go from the bottom up so that we can use
340 if (below
) it
= g_list_previous(below
);
341 else it
= g_list_last(stacking_list
);
342 for (; it
!= above
; it
= next
) {
343 next
= g_list_previous(it
);
344 wins
= g_list_prepend(wins
, it
->data
);
345 stacking_list
= g_list_delete_link(stacking_list
, it
);
348 /* group transients go above the rest of the stuff acquired to now */
349 wins
= g_list_concat(group_trans
, wins
);
350 /* group modals go on the very top */
351 wins
= g_list_concat(group_modals
, wins
);
353 do_restack(wins
, below
);
356 /* lower our parents after us, so they go below us */
357 if (!raise
&& selected
->parents
) {
358 GSList
*parents_copy
, *sit
;
359 GSList
*reorder
= NULL
;
361 parents_copy
= g_slist_copy(selected
->parents
);
363 /* go thru stacking list backwards so we can use g_slist_prepend */
364 for (it
= g_list_last(stacking_list
); it
&& parents_copy
;
365 it
= g_list_previous(it
))
366 if ((sit
= g_slist_find(parents_copy
, it
->data
))) {
367 reorder
= g_slist_prepend(reorder
, sit
->data
);
368 parents_copy
= g_slist_delete_link(parents_copy
, sit
);
370 g_assert(parents_copy
== NULL
);
372 /* call restack for each of these to lower them */
373 for (sit
= reorder
; sit
; sit
= g_slist_next(sit
))
374 restack_windows(sit
->data
, raise
);
378 void stacking_raise(ObWindow
*window
)
380 if (WINDOW_IS_CLIENT(window
)) {
382 selected
= WINDOW_AS_CLIENT(window
);
383 restack_windows(selected
, TRUE
);
386 wins
= g_list_append(NULL
, window
);
387 stacking_list
= g_list_remove(stacking_list
, window
);
393 void stacking_lower(ObWindow
*window
)
395 if (WINDOW_IS_CLIENT(window
)) {
397 selected
= WINDOW_AS_CLIENT(window
);
398 restack_windows(selected
, FALSE
);
401 wins
= g_list_append(NULL
, window
);
402 stacking_list
= g_list_remove(stacking_list
, window
);
408 void stacking_below(ObWindow
*window
, ObWindow
*below
)
410 GList
*wins
, *before
;
412 if (window_layer(window
) != window_layer(below
))
415 wins
= g_list_append(NULL
, window
);
416 stacking_list
= g_list_remove(stacking_list
, window
);
417 before
= g_list_next(g_list_find(stacking_list
, below
));
418 do_restack(wins
, before
);
422 void stacking_add(ObWindow
*win
)
424 g_assert(screen_support_win
!= None
); /* make sure I dont break this in the
427 stacking_list
= g_list_append(stacking_list
, win
);
431 static GList
*find_highest_relative(ObClient
*client
)
435 if (client
->parents
) {
439 /* get all top level relatives of this client */
440 top
= client_search_all_top_parents_layer(client
);
442 /* go from the top of the stacking order down */
443 for (it
= stacking_list
; !ret
&& it
; it
= g_list_next(it
)) {
444 if (WINDOW_IS_CLIENT(it
->data
)) {
445 ObClient
*c
= it
->data
;
446 /* only look at windows in the same layer and that are
448 if (c
->layer
== client
->layer
&&
450 (c
->desktop
== client
->desktop
||
451 c
->desktop
== DESKTOP_ALL
||
452 client
->desktop
== DESKTOP_ALL
))
456 /* go through each top level parent and see it this window
457 is related to them */
458 for (sit
= top
; !ret
&& sit
; sit
= g_slist_next(sit
)) {
459 ObClient
*topc
= sit
->data
;
461 /* are they related ? */
462 if (topc
== c
|| client_search_transient(topc
, c
))
472 void stacking_add_nonintrusive(ObWindow
*win
)
475 GList
*it_below
= NULL
; /* this client will be below us */
479 if (!WINDOW_IS_CLIENT(win
)) {
480 stacking_add(win
); /* no special rules for others */
484 client
= WINDOW_AS_CLIENT(win
);
486 /* insert above its highest parent (or its highest child !) */
487 it_below
= find_highest_relative(client
);
490 /* nothing to put it directly above, so try find the focused client
491 to put it underneath it */
492 if (focus_client
&& client
!= focus_client
&&
493 focus_client
->layer
== client
->layer
)
495 it_below
= g_list_find(stacking_list
, focus_client
);
496 /* this can give NULL, but it means the focused window is on the
497 bottom of the stacking order, so go to the bottom in that case,
499 it_below
= g_list_next(it_below
);
502 /* There is no window to put this directly above, so put it at the
503 top, so you know it is there.
505 It used to do this only if the window was focused and lower
508 We also put it at the top not the bottom to fix a bug with
509 fullscreen windows. When focusLast is off and followsMouse is
510 on, when you switch desktops, the fullscreen window loses
511 focus and goes into its lower layer. If this puts it at the
512 bottom then when you come back to the desktop, the window is
513 at the bottom and won't get focus back.
515 it_below
= stacking_list
;
519 /* make sure it's not in the wrong layer though ! */
520 for (; it_below
; it_below
= g_list_next(it_below
)) {
521 /* stop when the window is not in a higher layer than the window
522 it is going above (it_below) */
523 if (client
->layer
>= window_layer(it_below
->data
))
526 for (; it_below
!= stacking_list
; it_below
= it_above
) {
527 /* stop when the window is not in a lower layer than the
528 window it is going under (it_above) */
529 it_above
= it_below
?
530 g_list_previous(it_below
) : g_list_last(stacking_list
);
531 if (client
->layer
<= window_layer(it_above
->data
))
535 wins
= g_list_append(NULL
, win
);
536 do_restack(wins
, it_below
);
540 /*! Returns TRUE if client is occluded by the sibling. If sibling is NULL it
541 tries against all other clients.
543 static gboolean
stacking_occluded(ObClient
*client
, ObClient
*sibling
)
546 gboolean occluded
= FALSE
;
547 gboolean found
= FALSE
;
549 /* no need for any looping in this case */
550 if (sibling
&& client
->layer
!= sibling
->layer
)
553 for (it
= stacking_list
; it
;
554 it
= (found
? g_list_previous(it
) :g_list_next(it
)))
555 if (WINDOW_IS_CLIENT(it
->data
)) {
556 ObClient
*c
= it
->data
;
557 if (found
&& !c
->iconic
&&
558 (c
->desktop
== DESKTOP_ALL
|| client
->desktop
== DESKTOP_ALL
||
559 c
->desktop
== client
->desktop
) &&
560 !client_search_transient(client
, c
))
562 if (RECT_INTERSECTS_RECT(c
->frame
->area
, client
->frame
->area
))
564 if (sibling
!= NULL
) {
570 else if (c
->layer
== client
->layer
) {
574 else if (c
->layer
> client
->layer
)
575 break; /* we past its layer */
578 else if (c
== client
)
584 /*! Returns TRUE if client occludes the sibling. If sibling is NULL it tries
585 against all other clients.
587 static gboolean
stacking_occludes(ObClient
*client
, ObClient
*sibling
)
590 gboolean occludes
= FALSE
;
591 gboolean found
= FALSE
;
593 /* no need for any looping in this case */
594 if (sibling
&& client
->layer
!= sibling
->layer
)
597 for (it
= stacking_list
; it
; it
= g_list_next(it
))
598 if (WINDOW_IS_CLIENT(it
->data
)) {
599 ObClient
*c
= it
->data
;
600 if (found
&& !c
->iconic
&&
601 (c
->desktop
== DESKTOP_ALL
|| client
->desktop
== DESKTOP_ALL
||
602 c
->desktop
== client
->desktop
) &&
603 !client_search_transient(c
, client
))
605 if (RECT_INTERSECTS_RECT(c
->frame
->area
, client
->frame
->area
))
607 if (sibling
!= NULL
) {
613 else if (c
->layer
== client
->layer
) {
617 else if (c
->layer
< client
->layer
)
618 break; /* we past its layer */
621 else if (c
== client
)
627 gboolean
stacking_restack_request(ObClient
*client
, ObClient
*sibling
,
630 gboolean ret
= FALSE
;
632 if (sibling
&& ((client
->desktop
!= sibling
->desktop
&&
633 client
->desktop
!= DESKTOP_ALL
&&
634 sibling
->desktop
!= DESKTOP_ALL
) ||
637 ob_debug("Setting restack sibling to NULL, they are not on the same "
638 "desktop or it is iconified");
644 ob_debug("Restack request Below for client %s sibling %s",
645 client
->title
, sibling
? sibling
->title
: "(all)");
647 stacking_lower(CLIENT_AS_WINDOW(client
));
651 ob_debug("Restack request BottomIf for client %s sibling %s",
652 client
->title
, sibling
? sibling
->title
: "(all)");
653 /* if this client occludes sibling (or anything if NULL), then
654 lower it to the bottom */
655 if (stacking_occludes(client
, sibling
)) {
656 stacking_lower(CLIENT_AS_WINDOW(client
));
661 ob_debug("Restack request Above for client %s sibling %s",
662 client
->title
, sibling
? sibling
->title
: "(all)");
663 stacking_raise(CLIENT_AS_WINDOW(client
));
667 ob_debug("Restack request TopIf for client %s sibling %s",
668 client
->title
, sibling
? sibling
->title
: "(all)");
669 if (stacking_occluded(client
, sibling
)) {
670 stacking_raise(CLIENT_AS_WINDOW(client
));
675 ob_debug("Restack request Opposite for client %s sibling %s",
676 client
->title
, sibling
? sibling
->title
: "(all)");
677 if (stacking_occluded(client
, sibling
)) {
678 stacking_raise(CLIENT_AS_WINDOW(client
));
681 else if (stacking_occludes(client
, sibling
)) {
682 stacking_lower(CLIENT_AS_WINDOW(client
));