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
)
118 /* don't use this for internal windows..! it would lower them.. */
119 g_assert(window_layer(window
) < OB_STACKING_LAYER_INTERNAL
);
121 /* find the window to drop it underneath */
122 win
[0] = screen_support_win
;
123 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
124 ObWindow
*w
= it
->data
;
125 if (window_layer(w
) >= OB_STACKING_LAYER_INTERNAL
)
126 win
[0] = window_top(w
);
131 win
[1] = window_top(window
);
132 XRestackWindows(ob_display
, win
, 2);
134 pause_changes
= TRUE
;
137 void stacking_restore(void)
143 win
= g_new(Window
, g_list_length(stacking_list
) + 1);
144 win
[0] = screen_support_win
;
145 for (i
= 1, it
= stacking_list
; it
; ++i
, it
= g_list_next(it
))
146 win
[i
] = window_top(it
->data
);
147 XRestackWindows(ob_display
, win
, i
);
150 pause_changes
= FALSE
;
153 static void do_raise(GList
*wins
)
156 GList
*layer
[OB_NUM_STACKING_LAYERS
] = {NULL
};
159 for (it
= wins
; it
; it
= g_list_next(it
)) {
162 l
= window_layer(it
->data
);
163 layer
[l
] = g_list_append(layer
[l
], it
->data
);
167 for (i
= OB_NUM_STACKING_LAYERS
- 1; i
>= 0; --i
) {
169 for (; it
; it
= g_list_next(it
)) {
170 /* look for the top of the layer */
171 if (window_layer(it
->data
) <= (ObStackingLayer
) i
)
174 do_restack(layer
[i
], it
);
175 g_list_free(layer
[i
]);
180 static void do_lower(GList
*wins
)
183 GList
*layer
[OB_NUM_STACKING_LAYERS
] = {NULL
};
186 for (it
= wins
; it
; it
= g_list_next(it
)) {
189 l
= window_layer(it
->data
);
190 layer
[l
] = g_list_append(layer
[l
], it
->data
);
194 for (i
= OB_NUM_STACKING_LAYERS
- 1; i
>= 0; --i
) {
196 for (; it
; it
= g_list_next(it
)) {
197 /* look for the top of the next layer down */
198 if (window_layer(it
->data
) < (ObStackingLayer
) i
)
201 do_restack(layer
[i
], it
);
202 g_list_free(layer
[i
]);
207 static void restack_windows(ObClient
*selected
, gboolean raise
)
209 GList
*it
, *last
, *below
, *above
, *next
;
212 GList
*group_modals
= NULL
;
213 GList
*group_trans
= NULL
;
214 GList
*modals
= NULL
;
217 /* remove first so we can't run into ourself */
218 it
= g_list_find(stacking_list
, selected
);
220 stacking_list
= g_list_delete_link(stacking_list
, it
);
222 /* go from the bottom of the stacking list up. don't move any other windows
223 when lowering, we call this for each window independently */
225 for (it
= g_list_last(stacking_list
); it
; it
= next
) {
226 next
= g_list_previous(it
);
228 if (WINDOW_IS_CLIENT(it
->data
)) {
229 ObClient
*ch
= it
->data
;
231 /* only move windows in the same stacking layer */
232 if (ch
->layer
== selected
->layer
&&
233 client_search_transient(selected
, ch
))
235 if (client_is_direct_child(selected
, ch
)) {
237 modals
= g_list_prepend(modals
, ch
);
239 trans
= g_list_prepend(trans
, ch
);
243 group_modals
= g_list_prepend(group_modals
, ch
);
245 group_trans
= g_list_prepend(group_trans
, ch
);
247 stacking_list
= g_list_delete_link(stacking_list
, it
);
253 /* put transients of the selected window right above it */
254 wins
= g_list_concat(modals
, trans
);
255 wins
= g_list_append(wins
, selected
);
257 /* if selected window is transient for group then raise it above others */
258 if (selected
->transient_for_group
) {
259 /* if it's modal, raise it above those also */
260 if (selected
->modal
) {
261 wins
= g_list_concat(wins
, group_modals
);
264 wins
= g_list_concat(wins
, group_trans
);
268 /* find where to put the selected window, start from bottom of list,
269 this is the window below everything we are re-adding to the list */
271 for (it
= g_list_last(stacking_list
); it
; it
= g_list_previous(it
))
273 if (window_layer(it
->data
) < selected
->layer
) {
277 /* if lowering, stop at the beginning of the layer */
280 /* if raising, stop at the end of the layer */
281 if (window_layer(it
->data
) > selected
->layer
)
287 /* save this position in the stacking list */
290 /* find where to put the group transients, start from the top of list */
291 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
292 /* skip past higher layers */
293 if (window_layer(it
->data
) > selected
->layer
)
295 /* if we reach the end of the layer (how?) then don't go further */
296 if (window_layer(it
->data
) < selected
->layer
)
298 /* stop when we reach the first window in the group */
299 if (WINDOW_IS_CLIENT(it
->data
)) {
300 ObClient
*c
= it
->data
;
301 if (c
->group
== selected
->group
)
304 /* if we don't hit any other group members, stop here because this
305 is where we are putting the selected window (and its children) */
310 /* save this position, this is the top of the group of windows between the
311 group transient ones we're restacking and the others up above that we're
314 we actually want to save 1 position _above_ that, for for loops to work
315 nicely, so move back one position in the list while saving it
317 above
= it
? g_list_previous(it
) : g_list_last(stacking_list
);
319 /* put the windows inside the gap to the other windows we're stacking
320 into the restacking list, go from the bottom up so that we can use
322 if (below
) it
= g_list_previous(below
);
323 else it
= g_list_last(stacking_list
);
324 for (; it
!= above
; it
= next
) {
325 next
= g_list_previous(it
);
326 wins
= g_list_prepend(wins
, it
->data
);
327 stacking_list
= g_list_delete_link(stacking_list
, it
);
330 /* group transients go above the rest of the stuff acquired to now */
331 wins
= g_list_concat(group_trans
, wins
);
332 /* group modals go on the very top */
333 wins
= g_list_concat(group_modals
, wins
);
335 do_restack(wins
, below
);
338 /* lower our parents after us, so they go below us */
339 if (!raise
&& selected
->parents
) {
340 GSList
*parents_copy
, *sit
;
341 GSList
*reorder
= NULL
;
343 parents_copy
= g_slist_copy(selected
->parents
);
345 /* go thru stacking list backwards so we can use g_slist_prepend */
346 for (it
= g_list_last(stacking_list
); it
&& parents_copy
;
347 it
= g_list_previous(it
))
348 if ((sit
= g_slist_find(parents_copy
, it
->data
))) {
349 reorder
= g_slist_prepend(reorder
, sit
->data
);
350 parents_copy
= g_slist_delete_link(parents_copy
, sit
);
352 g_assert(parents_copy
== NULL
);
354 /* call restack for each of these to lower them */
355 for (sit
= reorder
; sit
; sit
= g_slist_next(sit
))
356 restack_windows(sit
->data
, raise
);
360 void stacking_raise(ObWindow
*window
)
362 if (WINDOW_IS_CLIENT(window
)) {
364 selected
= WINDOW_AS_CLIENT(window
);
365 restack_windows(selected
, TRUE
);
368 wins
= g_list_append(NULL
, window
);
369 stacking_list
= g_list_remove(stacking_list
, window
);
375 void stacking_lower(ObWindow
*window
)
377 if (WINDOW_IS_CLIENT(window
)) {
379 selected
= WINDOW_AS_CLIENT(window
);
380 restack_windows(selected
, FALSE
);
383 wins
= g_list_append(NULL
, window
);
384 stacking_list
= g_list_remove(stacking_list
, window
);
390 void stacking_below(ObWindow
*window
, ObWindow
*below
)
392 GList
*wins
, *before
;
394 if (window_layer(window
) != window_layer(below
))
397 wins
= g_list_append(NULL
, window
);
398 stacking_list
= g_list_remove(stacking_list
, window
);
399 before
= g_list_next(g_list_find(stacking_list
, below
));
400 do_restack(wins
, before
);
404 void stacking_add(ObWindow
*win
)
406 g_assert(screen_support_win
!= None
); /* make sure I dont break this in the
409 stacking_list
= g_list_append(stacking_list
, win
);
413 static GList
*find_highest_relative(ObClient
*client
)
417 if (client
->parents
) {
421 /* get all top level relatives of this client */
422 top
= client_search_all_top_parents_layer(client
);
424 /* go from the top of the stacking order down */
425 for (it
= stacking_list
; !ret
&& it
; it
= g_list_next(it
)) {
426 if (WINDOW_IS_CLIENT(it
->data
)) {
427 ObClient
*c
= it
->data
;
428 /* only look at windows in the same layer and that are
430 if (c
->layer
== client
->layer
&&
432 (c
->desktop
== client
->desktop
||
433 c
->desktop
== DESKTOP_ALL
||
434 client
->desktop
== DESKTOP_ALL
))
438 /* go through each top level parent and see it this window
439 is related to them */
440 for (sit
= top
; !ret
&& sit
; sit
= g_slist_next(sit
)) {
441 ObClient
*topc
= sit
->data
;
443 /* are they related ? */
444 if (topc
== c
|| client_search_transient(topc
, c
))
454 void stacking_add_nonintrusive(ObWindow
*win
)
457 GList
*it_below
= NULL
; /* this client will be below us */
461 if (!WINDOW_IS_CLIENT(win
)) {
462 stacking_add(win
); /* no special rules for others */
466 client
= WINDOW_AS_CLIENT(win
);
468 /* insert above its highest parent (or its highest child !) */
469 it_below
= find_highest_relative(client
);
472 /* nothing to put it directly above, so try find the focused client
473 to put it underneath it */
474 if (focus_client
&& client
!= focus_client
&&
475 focus_client
->layer
== client
->layer
)
477 it_below
= g_list_find(stacking_list
, focus_client
);
478 /* this can give NULL, but it means the focused window is on the
479 bottom of the stacking order, so go to the bottom in that case,
481 it_below
= g_list_next(it_below
);
484 /* There is no window to put this directly above, so put it at the
485 top, so you know it is there.
487 It used to do this only if the window was focused and lower
490 We also put it at the top not the bottom to fix a bug with
491 fullscreen windows. When focusLast is off and followsMouse is
492 on, when you switch desktops, the fullscreen window loses
493 focus and goes into its lower layer. If this puts it at the
494 bottom then when you come back to the desktop, the window is
495 at the bottom and won't get focus back.
497 it_below
= stacking_list
;
501 /* make sure it's not in the wrong layer though ! */
502 for (; it_below
; it_below
= g_list_next(it_below
)) {
503 /* stop when the window is not in a higher layer than the window
504 it is going above (it_below) */
505 if (client
->layer
>= window_layer(it_below
->data
))
508 for (; it_below
!= stacking_list
; it_below
= it_above
) {
509 /* stop when the window is not in a lower layer than the
510 window it is going under (it_above) */
511 it_above
= it_below
?
512 g_list_previous(it_below
) : g_list_last(stacking_list
);
513 if (client
->layer
<= window_layer(it_above
->data
))
517 wins
= g_list_append(NULL
, win
);
518 do_restack(wins
, it_below
);
522 /*! Returns TRUE if client is occluded by the sibling. If sibling is NULL it
523 tries against all other clients.
525 static gboolean
stacking_occluded(ObClient
*client
, ObClient
*sibling
)
528 gboolean occluded
= FALSE
;
529 gboolean found
= FALSE
;
531 /* no need for any looping in this case */
532 if (sibling
&& client
->layer
!= sibling
->layer
)
535 for (it
= stacking_list
; it
;
536 it
= (found
? g_list_previous(it
) :g_list_next(it
)))
537 if (WINDOW_IS_CLIENT(it
->data
)) {
538 ObClient
*c
= it
->data
;
539 if (found
&& !c
->iconic
&&
540 (c
->desktop
== DESKTOP_ALL
|| client
->desktop
== DESKTOP_ALL
||
541 c
->desktop
== client
->desktop
) &&
542 !client_search_transient(client
, c
))
544 if (RECT_INTERSECTS_RECT(c
->frame
->area
, client
->frame
->area
))
546 if (sibling
!= NULL
) {
552 else if (c
->layer
== client
->layer
) {
556 else if (c
->layer
> client
->layer
)
557 break; /* we past its layer */
560 else if (c
== client
)
566 /*! Returns TRUE if client occludes the sibling. If sibling is NULL it tries
567 against all other clients.
569 static gboolean
stacking_occludes(ObClient
*client
, ObClient
*sibling
)
572 gboolean occludes
= FALSE
;
573 gboolean found
= FALSE
;
575 /* no need for any looping in this case */
576 if (sibling
&& client
->layer
!= sibling
->layer
)
579 for (it
= stacking_list
; it
; it
= g_list_next(it
))
580 if (WINDOW_IS_CLIENT(it
->data
)) {
581 ObClient
*c
= it
->data
;
582 if (found
&& !c
->iconic
&&
583 (c
->desktop
== DESKTOP_ALL
|| client
->desktop
== DESKTOP_ALL
||
584 c
->desktop
== client
->desktop
) &&
585 !client_search_transient(c
, client
))
587 if (RECT_INTERSECTS_RECT(c
->frame
->area
, client
->frame
->area
))
589 if (sibling
!= NULL
) {
595 else if (c
->layer
== client
->layer
) {
599 else if (c
->layer
< client
->layer
)
600 break; /* we past its layer */
603 else if (c
== client
)
609 gboolean
stacking_restack_request(ObClient
*client
, ObClient
*sibling
,
612 gboolean ret
= FALSE
;
614 if (sibling
&& ((client
->desktop
!= sibling
->desktop
&&
615 client
->desktop
!= DESKTOP_ALL
&&
616 sibling
->desktop
!= DESKTOP_ALL
) ||
619 ob_debug("Setting restack sibling to NULL, they are not on the same "
620 "desktop or it is iconified\n");
626 ob_debug("Restack request Below for client %s sibling %s\n",
627 client
->title
, sibling
? sibling
->title
: "(all)");
629 stacking_lower(CLIENT_AS_WINDOW(client
));
633 ob_debug("Restack request BottomIf for client %s sibling "
635 client
->title
, sibling
? sibling
->title
: "(all)");
636 /* if this client occludes sibling (or anything if NULL), then
637 lower it to the bottom */
638 if (stacking_occludes(client
, sibling
)) {
639 stacking_lower(CLIENT_AS_WINDOW(client
));
644 ob_debug("Restack request Above for client %s sibling %s\n",
645 client
->title
, sibling
? sibling
->title
: "(all)");
646 stacking_raise(CLIENT_AS_WINDOW(client
));
650 ob_debug("Restack request TopIf for client %s sibling %s\n",
651 client
->title
, sibling
? sibling
->title
: "(all)");
652 if (stacking_occluded(client
, sibling
)) {
653 stacking_raise(CLIENT_AS_WINDOW(client
));
658 ob_debug("Restack request Opposite for client %s sibling "
660 client
->title
, sibling
? sibling
->title
: "(all)");
661 if (stacking_occluded(client
, sibling
)) {
662 stacking_raise(CLIENT_AS_WINDOW(client
));
665 else if (stacking_occludes(client
, sibling
)) {
666 stacking_lower(CLIENT_AS_WINDOW(client
));