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 GList
*stacking_list_tail
= NULL
;
33 /*! When true, stacking changes will not be reflected on the screen. This is
34 to freeze the on-screen stacking order while a window is being temporarily
35 raised during focus cycling */
36 static gboolean pause_changes
= FALSE
;
38 void stacking_set_list(void)
40 Window
*windows
= NULL
;
44 /* on shutdown, don't update the properties, so that we can read it back
45 in on startup and re-stack the windows as they were before we shut down
47 if (ob_state() == OB_STATE_EXITING
) return;
49 /* create an array of the window ids (from bottom to top,
52 windows
= g_new(Window
, g_list_length(stacking_list
));
53 for (it
= g_list_last(stacking_list
); it
; it
= g_list_previous(it
)) {
54 if (WINDOW_IS_CLIENT(it
->data
))
55 windows
[i
++] = WINDOW_AS_CLIENT(it
->data
)->window
;
59 OBT_PROP_SETA32(obt_root(ob_screen
), NET_CLIENT_LIST_STACKING
, WINDOW
,
65 static void do_restack(GList
*wins
, GList
*before
)
75 /* pls only restack stuff in the same layer at a time */
76 for (it
= wins
; it
; it
= next
) {
77 next
= g_list_next(it
);
79 g_assert (window_layer(it
->data
) == window_layer(next
->data
));
82 g_assert(window_layer(it
->data
) >= window_layer(before
->data
));
85 win
= g_new(Window
, g_list_length(wins
) + 1);
87 if (before
== stacking_list
)
88 win
[0] = screen_support_win
;
90 win
[0] = window_top(g_list_last(stacking_list
)->data
);
92 win
[0] = window_top(g_list_previous(before
)->data
);
94 for (i
= 1, it
= wins
; it
; ++i
, it
= g_list_next(it
)) {
95 win
[i
] = window_top(it
->data
);
96 g_assert(win
[i
] != None
); /* better not call stacking shit before
97 setting your top level window value */
98 stacking_list
= g_list_insert_before(stacking_list
, before
, it
->data
);
102 /* some debug checking of the stacking list's order */
103 for (it
= stacking_list
; ; it
= next
) {
104 next
= g_list_next(it
);
106 g_assert(window_layer(it
->data
) >= window_layer(next
->data
));
111 XRestackWindows(obt_display
, win
, i
);
117 void stacking_temp_raise(ObWindow
*window
)
123 /* don't use this for internal windows..! it would lower them.. */
124 g_assert(window_layer(window
) < OB_STACKING_LAYER_INTERNAL
);
126 /* find the window to drop it underneath */
127 win
[0] = screen_support_win
;
128 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
129 ObWindow
*w
= it
->data
;
130 if (window_layer(w
) >= OB_STACKING_LAYER_INTERNAL
)
131 win
[0] = window_top(w
);
136 win
[1] = window_top(window
);
137 start
= event_start_ignore_all_enters();
138 XRestackWindows(obt_display
, win
, 2);
139 event_end_ignore_all_enters(start
);
141 pause_changes
= TRUE
;
144 void stacking_restore(void)
151 win
= g_new(Window
, g_list_length(stacking_list
) + 1);
152 win
[0] = screen_support_win
;
153 for (i
= 1, it
= stacking_list
; it
; ++i
, it
= g_list_next(it
))
154 win
[i
] = window_top(it
->data
);
155 start
= event_start_ignore_all_enters();
156 XRestackWindows(obt_display
, win
, i
);
157 event_end_ignore_all_enters(start
);
160 pause_changes
= FALSE
;
163 static void do_raise(GList
*wins
)
166 GList
*layer
[OB_NUM_STACKING_LAYERS
] = {NULL
};
169 for (it
= wins
; it
; it
= g_list_next(it
)) {
172 l
= window_layer(it
->data
);
173 layer
[l
] = g_list_append(layer
[l
], it
->data
);
177 for (i
= OB_NUM_STACKING_LAYERS
- 1; i
>= 0; --i
) {
179 for (; it
; it
= g_list_next(it
)) {
180 /* look for the top of the layer */
181 if (window_layer(it
->data
) <= (ObStackingLayer
) i
)
184 do_restack(layer
[i
], it
);
185 g_list_free(layer
[i
]);
190 static void do_lower(GList
*wins
)
193 GList
*layer
[OB_NUM_STACKING_LAYERS
] = {NULL
};
196 for (it
= wins
; it
; it
= g_list_next(it
)) {
199 l
= window_layer(it
->data
);
200 layer
[l
] = g_list_append(layer
[l
], it
->data
);
204 for (i
= OB_NUM_STACKING_LAYERS
- 1; i
>= 0; --i
) {
206 for (; it
; it
= g_list_next(it
)) {
207 /* look for the top of the next layer down */
208 if (window_layer(it
->data
) < (ObStackingLayer
) i
)
211 do_restack(layer
[i
], it
);
212 g_list_free(layer
[i
]);
217 static void restack_windows(ObClient
*selected
, gboolean raise
)
219 GList
*it
, *last
, *below
, *above
, *next
;
222 GList
*group_helpers
= NULL
;
223 GList
*group_modals
= NULL
;
224 GList
*group_trans
= NULL
;
225 GList
*modals
= NULL
;
231 /* if a window is modal for another single window, then raise it to the
232 top too, the same is done with the focus order */
233 while (selected
->modal
&& (p
= client_direct_parent(selected
)))
237 /* remove first so we can't run into ourself */
238 it
= g_list_find(stacking_list
, selected
);
240 stacking_list
= g_list_delete_link(stacking_list
, it
);
242 /* go from the bottom of the stacking list up. don't move any other windows
243 when lowering, we call this for each window independently */
245 for (it
= g_list_last(stacking_list
); it
; it
= next
) {
246 next
= g_list_previous(it
);
248 if (WINDOW_IS_CLIENT(it
->data
)) {
249 ObClient
*ch
= it
->data
;
251 /* only move windows in the same stacking layer */
252 if (ch
->layer
== selected
->layer
&&
253 /* looking for windows that are transients, and so would
254 remain above the selected window */
255 client_search_transient(selected
, ch
))
257 if (client_is_direct_child(selected
, ch
)) {
259 modals
= g_list_prepend(modals
, ch
);
261 trans
= g_list_prepend(trans
, ch
);
263 else if (client_helper(ch
)) {
264 if (selected
->transient
) {
265 /* helpers do not stay above transient windows */
268 group_helpers
= g_list_prepend(group_helpers
, ch
);
272 group_modals
= g_list_prepend(group_modals
, ch
);
274 group_trans
= g_list_prepend(group_trans
, ch
);
276 stacking_list
= g_list_delete_link(stacking_list
, it
);
282 /* put modals above other direct transients */
283 wins
= g_list_concat(modals
, trans
);
285 /* put helpers below direct transients */
286 wins
= g_list_concat(wins
, group_helpers
);
288 /* put the selected window right below these children */
289 wins
= g_list_append(wins
, selected
);
291 /* if selected window is transient for group then raise it above others */
292 if (selected
->transient_for_group
) {
293 /* if it's modal, raise it above those also */
294 if (selected
->modal
) {
295 wins
= g_list_concat(wins
, group_modals
);
298 wins
= g_list_concat(wins
, group_trans
);
302 /* find where to put the selected window, start from bottom of list,
303 this is the window below everything we are re-adding to the list */
305 for (it
= g_list_last(stacking_list
); it
; it
= g_list_previous(it
))
307 if (window_layer(it
->data
) < selected
->layer
) {
311 /* if lowering, stop at the beginning of the layer */
314 /* if raising, stop at the end of the layer */
315 if (window_layer(it
->data
) > selected
->layer
)
321 /* save this position in the stacking list */
324 /* find where to put the group transients, start from the top of list */
325 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
326 /* skip past higher layers */
327 if (window_layer(it
->data
) > selected
->layer
)
329 /* if we reach the end of the layer (how?) then don't go further */
330 if (window_layer(it
->data
) < selected
->layer
)
332 /* stop when we reach the first window in the group */
333 if (WINDOW_IS_CLIENT(it
->data
)) {
334 ObClient
*c
= it
->data
;
335 if (c
->group
== selected
->group
)
338 /* if we don't hit any other group members, stop here because this
339 is where we are putting the selected window (and its children) */
344 /* save this position, this is the top of the group of windows between the
345 group transient ones we're restacking and the others up above that we're
348 we actually want to save 1 position _above_ that, for for loops to work
349 nicely, so move back one position in the list while saving it
351 above
= it
? g_list_previous(it
) : g_list_last(stacking_list
);
353 /* put the windows inside the gap to the other windows we're stacking
354 into the restacking list, go from the bottom up so that we can use
356 if (below
) it
= g_list_previous(below
);
357 else it
= g_list_last(stacking_list
);
358 for (; it
!= above
; it
= next
) {
359 next
= g_list_previous(it
);
360 wins
= g_list_prepend(wins
, it
->data
);
361 stacking_list
= g_list_delete_link(stacking_list
, it
);
364 /* group transients go above the rest of the stuff acquired to now */
365 wins
= g_list_concat(group_trans
, wins
);
366 /* group modals go on the very top */
367 wins
= g_list_concat(group_modals
, wins
);
369 do_restack(wins
, below
);
372 /* lower our parents after us, so they go below us */
373 if (!raise
&& selected
->parents
) {
374 GSList
*parents_copy
, *sit
;
375 GSList
*reorder
= NULL
;
377 parents_copy
= g_slist_copy(selected
->parents
);
379 /* go thru stacking list backwards so we can use g_slist_prepend */
380 for (it
= g_list_last(stacking_list
); it
&& parents_copy
;
381 it
= g_list_previous(it
))
382 if ((sit
= g_slist_find(parents_copy
, it
->data
))) {
383 reorder
= g_slist_prepend(reorder
, sit
->data
);
384 parents_copy
= g_slist_delete_link(parents_copy
, sit
);
386 g_assert(parents_copy
== NULL
);
388 /* call restack for each of these to lower them */
389 for (sit
= reorder
; sit
; sit
= g_slist_next(sit
))
390 restack_windows(sit
->data
, raise
);
394 void stacking_raise(ObWindow
*window
)
396 if (WINDOW_IS_CLIENT(window
)) {
398 selected
= WINDOW_AS_CLIENT(window
);
399 restack_windows(selected
, TRUE
);
402 wins
= g_list_append(NULL
, window
);
403 stacking_list
= g_list_remove(stacking_list
, window
);
407 stacking_list_tail
= g_list_last(stacking_list
);
410 void stacking_lower(ObWindow
*window
)
412 if (WINDOW_IS_CLIENT(window
)) {
414 selected
= WINDOW_AS_CLIENT(window
);
415 restack_windows(selected
, FALSE
);
418 wins
= g_list_append(NULL
, window
);
419 stacking_list
= g_list_remove(stacking_list
, window
);
423 stacking_list_tail
= g_list_last(stacking_list
);
426 void stacking_below(ObWindow
*window
, ObWindow
*below
)
428 GList
*wins
, *before
;
430 if (window_layer(window
) != window_layer(below
))
433 wins
= g_list_append(NULL
, window
);
434 stacking_list
= g_list_remove(stacking_list
, window
);
435 before
= g_list_next(g_list_find(stacking_list
, below
));
436 do_restack(wins
, before
);
438 stacking_list_tail
= g_list_last(stacking_list
);
441 void stacking_add(ObWindow
*win
)
443 g_assert(screen_support_win
!= None
); /* make sure I dont break this in the
445 /* don't add windows that are being unmanaged ! */
446 if (WINDOW_IS_CLIENT(win
)) g_assert(WINDOW_AS_CLIENT(win
)->managed
);
448 stacking_list
= g_list_append(stacking_list
, win
);
451 /* stacking_list_tail set by stacking_raise() */
454 static GList
*find_highest_relative(ObClient
*client
)
458 if (client
->parents
) {
462 /* get all top level relatives of this client */
463 top
= client_search_all_top_parents_layer(client
);
465 /* go from the top of the stacking order down */
466 for (it
= stacking_list
; !ret
&& it
; it
= g_list_next(it
)) {
467 if (WINDOW_IS_CLIENT(it
->data
)) {
468 ObClient
*c
= it
->data
;
469 /* only look at windows in the same layer and that are
471 if (c
->layer
== client
->layer
&&
473 (c
->desktop
== client
->desktop
||
474 c
->desktop
== DESKTOP_ALL
||
475 client
->desktop
== DESKTOP_ALL
))
479 /* go through each top level parent and see it this window
480 is related to them */
481 for (sit
= top
; !ret
&& sit
; sit
= g_slist_next(sit
)) {
482 ObClient
*topc
= sit
->data
;
484 /* are they related ? */
485 if (topc
== c
|| client_search_transient(topc
, c
))
495 void stacking_add_nonintrusive(ObWindow
*win
)
498 GList
*it_below
= NULL
; /* this client will be below us */
502 if (!WINDOW_IS_CLIENT(win
)) {
503 stacking_add(win
); /* no special rules for others */
507 client
= WINDOW_AS_CLIENT(win
);
509 /* don't add windows that are being unmanaged ! */
510 g_assert(client
->managed
);
512 /* insert above its highest parent (or its highest child !) */
513 it_below
= find_highest_relative(client
);
516 /* nothing to put it directly above, so try find the focused client
517 to put it underneath it */
518 if (focus_client
&& client
!= focus_client
&&
519 focus_client
->layer
== client
->layer
)
521 it_below
= g_list_find(stacking_list
, focus_client
);
522 /* this can give NULL, but it means the focused window is on the
523 bottom of the stacking order, so go to the bottom in that case,
525 it_below
= g_list_next(it_below
);
528 /* There is no window to put this directly above, so put it at the
529 top, so you know it is there.
531 It used to do this only if the window was focused and lower
534 We also put it at the top not the bottom to fix a bug with
535 fullscreen windows. When focusLast is off and followsMouse is
536 on, when you switch desktops, the fullscreen window loses
537 focus and goes into its lower layer. If this puts it at the
538 bottom then when you come back to the desktop, the window is
539 at the bottom and won't get focus back.
541 it_below
= stacking_list
;
545 /* make sure it's not in the wrong layer though ! */
546 for (; it_below
; it_below
= g_list_next(it_below
)) {
547 /* stop when the window is not in a higher layer than the window
548 it is going above (it_below) */
549 if (client
->layer
>= window_layer(it_below
->data
))
552 for (; it_below
!= stacking_list
; it_below
= it_above
) {
553 /* stop when the window is not in a lower layer than the
554 window it is going under (it_above) */
555 it_above
= it_below
?
556 g_list_previous(it_below
) : g_list_last(stacking_list
);
557 if (client
->layer
<= window_layer(it_above
->data
))
561 wins
= g_list_append(NULL
, win
);
562 do_restack(wins
, it_below
);
564 stacking_list_tail
= g_list_last(stacking_list
);
567 /*! Returns TRUE if client is occluded by the sibling. If sibling is NULL it
568 tries against all other clients.
570 static gboolean
stacking_occluded(ObClient
*client
, ObClient
*sibling
)
573 gboolean occluded
= FALSE
;
575 /* no need for any looping in this case */
576 if (sibling
&& client
->layer
!= sibling
->layer
)
579 for (it
= g_list_previous(g_list_find(stacking_list
, client
)); it
;
580 it
= g_list_previous(it
))
581 if (WINDOW_IS_CLIENT(it
->data
)) {
582 ObClient
*c
= it
->data
;
584 (c
->desktop
== DESKTOP_ALL
|| client
->desktop
== DESKTOP_ALL
||
585 c
->desktop
== client
->desktop
) &&
586 !client_search_transient(client
, c
))
588 if (RECT_INTERSECTS_RECT(c
->frame
->area
, client
->frame
->area
))
590 if (sibling
!= NULL
) {
596 else if (c
->layer
== client
->layer
) {
600 else if (c
->layer
> client
->layer
)
601 break; /* we past its layer */
608 /*! Returns TRUE if client occludes the sibling. If sibling is NULL it tries
609 against all other clients.
611 static gboolean
stacking_occludes(ObClient
*client
, ObClient
*sibling
)
614 gboolean occludes
= FALSE
;
616 /* no need for any looping in this case */
617 if (sibling
&& client
->layer
!= sibling
->layer
)
620 for (it
= g_list_next(g_list_find(stacking_list
, client
));
621 it
; it
= g_list_next(it
))
622 if (WINDOW_IS_CLIENT(it
->data
)) {
623 ObClient
*c
= it
->data
;
625 (c
->desktop
== DESKTOP_ALL
|| client
->desktop
== DESKTOP_ALL
||
626 c
->desktop
== client
->desktop
) &&
627 !client_search_transient(c
, client
))
629 if (RECT_INTERSECTS_RECT(c
->frame
->area
, client
->frame
->area
))
631 if (sibling
!= NULL
) {
637 else if (c
->layer
== client
->layer
) {
641 else if (c
->layer
< client
->layer
)
642 break; /* we past its layer */
649 gboolean
stacking_restack_request(ObClient
*client
, ObClient
*sibling
,
652 gboolean ret
= FALSE
;
654 if (sibling
&& ((client
->desktop
!= sibling
->desktop
&&
655 client
->desktop
!= DESKTOP_ALL
&&
656 sibling
->desktop
!= DESKTOP_ALL
) ||
659 ob_debug("Setting restack sibling to NULL, they are not on the same "
660 "desktop or it is iconified");
666 ob_debug("Restack request Below for client %s sibling %s",
667 client
->title
, sibling
? sibling
->title
: "(all)");
669 stacking_lower(CLIENT_AS_WINDOW(client
));
673 ob_debug("Restack request BottomIf for client %s sibling %s",
674 client
->title
, sibling
? sibling
->title
: "(all)");
675 /* if this client occludes sibling (or anything if NULL), then
676 lower it to the bottom */
677 if (stacking_occludes(client
, sibling
)) {
678 stacking_lower(CLIENT_AS_WINDOW(client
));
683 ob_debug("Restack request Above for client %s sibling %s",
684 client
->title
, sibling
? sibling
->title
: "(all)");
685 stacking_raise(CLIENT_AS_WINDOW(client
));
689 ob_debug("Restack request TopIf for client %s sibling %s",
690 client
->title
, sibling
? sibling
->title
: "(all)");
691 if (stacking_occluded(client
, sibling
)) {
692 stacking_raise(CLIENT_AS_WINDOW(client
));
697 ob_debug("Restack request Opposite for client %s sibling %s",
698 client
->title
, sibling
? sibling
->title
: "(all)");
699 if (stacking_occluded(client
, sibling
)) {
700 stacking_raise(CLIENT_AS_WINDOW(client
));
703 else if (stacking_occludes(client
, sibling
)) {
704 stacking_lower(CLIENT_AS_WINDOW(client
));