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 PROP_SETA32(RootWindow(ob_display
, ob_screen
),
59 net_client_list_stacking
, window
, (gulong
*)windows
, i
);
64 static void do_restack(GList
*wins
, GList
*before
)
72 /* pls only restack stuff in the same layer at a time */
73 for (it
= wins
; it
; it
= next
) {
74 next
= g_list_next(it
);
76 g_assert (window_layer(it
->data
) == window_layer(next
->data
));
79 g_assert(window_layer(it
->data
) >= window_layer(before
->data
));
82 win
= g_new(Window
, g_list_length(wins
) + 1);
84 if (before
== stacking_list
)
85 win
[0] = screen_support_win
;
87 win
[0] = window_top(g_list_last(stacking_list
)->data
);
89 win
[0] = window_top(g_list_previous(before
)->data
);
91 for (i
= 1, it
= wins
; it
; ++i
, it
= g_list_next(it
)) {
92 win
[i
] = window_top(it
->data
);
93 g_assert(win
[i
] != None
); /* better not call stacking shit before
94 setting your top level window value */
95 stacking_list
= g_list_insert_before(stacking_list
, before
, it
->data
);
99 /* some debug checking of the stacking list's order */
100 for (it
= stacking_list
; ; it
= next
) {
101 next
= g_list_next(it
);
103 g_assert(window_layer(it
->data
) >= window_layer(next
->data
));
108 XRestackWindows(ob_display
, win
, i
);
114 void stacking_temp_raise(ObWindow
*window
)
120 /* don't use this for internal windows..! it would lower them.. */
121 g_assert(window_layer(window
) < OB_STACKING_LAYER_INTERNAL
);
123 /* find the window to drop it underneath */
124 win
[0] = screen_support_win
;
125 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
126 ObWindow
*w
= it
->data
;
127 if (window_layer(w
) >= OB_STACKING_LAYER_INTERNAL
)
128 win
[0] = window_top(w
);
133 win
[1] = window_top(window
);
134 start
= event_start_ignore_all_enters();
135 XRestackWindows(ob_display
, win
, 2);
136 event_end_ignore_all_enters(start
);
138 pause_changes
= TRUE
;
141 void stacking_restore(void)
148 win
= g_new(Window
, g_list_length(stacking_list
) + 1);
149 win
[0] = screen_support_win
;
150 for (i
= 1, it
= stacking_list
; it
; ++i
, it
= g_list_next(it
))
151 win
[i
] = window_top(it
->data
);
152 start
= event_start_ignore_all_enters();
153 XRestackWindows(ob_display
, win
, i
);
154 event_end_ignore_all_enters(start
);
157 pause_changes
= FALSE
;
160 static void do_raise(GList
*wins
)
163 GList
*layer
[OB_NUM_STACKING_LAYERS
] = {NULL
};
166 for (it
= wins
; it
; it
= g_list_next(it
)) {
169 l
= window_layer(it
->data
);
170 layer
[l
] = g_list_append(layer
[l
], it
->data
);
174 for (i
= OB_NUM_STACKING_LAYERS
- 1; i
>= 0; --i
) {
176 for (; it
; it
= g_list_next(it
)) {
177 /* look for the top of the layer */
178 if (window_layer(it
->data
) <= (ObStackingLayer
) i
)
181 do_restack(layer
[i
], it
);
182 g_list_free(layer
[i
]);
187 static void do_lower(GList
*wins
)
190 GList
*layer
[OB_NUM_STACKING_LAYERS
] = {NULL
};
193 for (it
= wins
; it
; it
= g_list_next(it
)) {
196 l
= window_layer(it
->data
);
197 layer
[l
] = g_list_append(layer
[l
], it
->data
);
201 for (i
= OB_NUM_STACKING_LAYERS
- 1; i
>= 0; --i
) {
203 for (; it
; it
= g_list_next(it
)) {
204 /* look for the top of the next layer down */
205 if (window_layer(it
->data
) < (ObStackingLayer
) i
)
208 do_restack(layer
[i
], it
);
209 g_list_free(layer
[i
]);
214 static void restack_windows(ObClient
*selected
, gboolean raise
)
216 GList
*it
, *last
, *below
, *above
, *next
;
219 GList
*group_helpers
= NULL
;
220 GList
*group_modals
= NULL
;
221 GList
*group_trans
= NULL
;
222 GList
*modals
= NULL
;
228 /* if a window is modal for another single window, then raise it to the
229 top too, the same is done with the focus order */
230 while (selected
->modal
&& (p
= client_direct_parent(selected
)))
234 /* remove first so we can't run into ourself */
235 it
= g_list_find(stacking_list
, selected
);
237 stacking_list
= g_list_delete_link(stacking_list
, it
);
239 /* go from the bottom of the stacking list up. don't move any other windows
240 when lowering, we call this for each window independently */
242 for (it
= g_list_last(stacking_list
); it
; it
= next
) {
243 next
= g_list_previous(it
);
245 if (WINDOW_IS_CLIENT(it
->data
)) {
246 ObClient
*ch
= it
->data
;
248 /* only move windows in the same stacking layer */
249 if (ch
->layer
== selected
->layer
&&
250 /* looking for windows that are transients, and so would
251 remain above the selected window */
252 client_search_transient(selected
, ch
))
254 if (client_is_direct_child(selected
, ch
)) {
256 modals
= g_list_prepend(modals
, ch
);
258 trans
= g_list_prepend(trans
, ch
);
260 else if (client_helper(ch
)) {
261 if (selected
->transient
) {
262 /* helpers do not stay above transient windows */
265 group_helpers
= g_list_prepend(group_helpers
, ch
);
269 group_modals
= g_list_prepend(group_modals
, ch
);
271 group_trans
= g_list_prepend(group_trans
, ch
);
273 stacking_list
= g_list_delete_link(stacking_list
, it
);
279 /* put modals above other direct transients */
280 wins
= g_list_concat(modals
, trans
);
282 /* put helpers below direct transients */
283 wins
= g_list_concat(wins
, group_helpers
);
285 /* put the selected window right below these children */
286 wins
= g_list_append(wins
, selected
);
288 /* if selected window is transient for group then raise it above others */
289 if (selected
->transient_for_group
) {
290 /* if it's modal, raise it above those also */
291 if (selected
->modal
) {
292 wins
= g_list_concat(wins
, group_modals
);
295 wins
= g_list_concat(wins
, group_trans
);
299 /* find where to put the selected window, start from bottom of list,
300 this is the window below everything we are re-adding to the list */
302 for (it
= g_list_last(stacking_list
); it
; it
= g_list_previous(it
))
304 if (window_layer(it
->data
) < selected
->layer
) {
308 /* if lowering, stop at the beginning of the layer */
311 /* if raising, stop at the end of the layer */
312 if (window_layer(it
->data
) > selected
->layer
)
318 /* save this position in the stacking list */
321 /* find where to put the group transients, start from the top of list */
322 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
323 /* skip past higher layers */
324 if (window_layer(it
->data
) > selected
->layer
)
326 /* if we reach the end of the layer (how?) then don't go further */
327 if (window_layer(it
->data
) < selected
->layer
)
329 /* stop when we reach the first window in the group */
330 if (WINDOW_IS_CLIENT(it
->data
)) {
331 ObClient
*c
= it
->data
;
332 if (c
->group
== selected
->group
)
335 /* if we don't hit any other group members, stop here because this
336 is where we are putting the selected window (and its children) */
341 /* save this position, this is the top of the group of windows between the
342 group transient ones we're restacking and the others up above that we're
345 we actually want to save 1 position _above_ that, for for loops to work
346 nicely, so move back one position in the list while saving it
348 above
= it
? g_list_previous(it
) : g_list_last(stacking_list
);
350 /* put the windows inside the gap to the other windows we're stacking
351 into the restacking list, go from the bottom up so that we can use
353 if (below
) it
= g_list_previous(below
);
354 else it
= g_list_last(stacking_list
);
355 for (; it
!= above
; it
= next
) {
356 next
= g_list_previous(it
);
357 wins
= g_list_prepend(wins
, it
->data
);
358 stacking_list
= g_list_delete_link(stacking_list
, it
);
361 /* group transients go above the rest of the stuff acquired to now */
362 wins
= g_list_concat(group_trans
, wins
);
363 /* group modals go on the very top */
364 wins
= g_list_concat(group_modals
, wins
);
366 do_restack(wins
, below
);
369 /* lower our parents after us, so they go below us */
370 if (!raise
&& selected
->parents
) {
371 GSList
*parents_copy
, *sit
;
372 GSList
*reorder
= NULL
;
374 parents_copy
= g_slist_copy(selected
->parents
);
376 /* go thru stacking list backwards so we can use g_slist_prepend */
377 for (it
= g_list_last(stacking_list
); it
&& parents_copy
;
378 it
= g_list_previous(it
))
379 if ((sit
= g_slist_find(parents_copy
, it
->data
))) {
380 reorder
= g_slist_prepend(reorder
, sit
->data
);
381 parents_copy
= g_slist_delete_link(parents_copy
, sit
);
383 g_assert(parents_copy
== NULL
);
385 /* call restack for each of these to lower them */
386 for (sit
= reorder
; sit
; sit
= g_slist_next(sit
))
387 restack_windows(sit
->data
, raise
);
391 void stacking_raise(ObWindow
*window
)
393 if (WINDOW_IS_CLIENT(window
)) {
395 selected
= WINDOW_AS_CLIENT(window
);
396 restack_windows(selected
, TRUE
);
399 wins
= g_list_append(NULL
, window
);
400 stacking_list
= g_list_remove(stacking_list
, window
);
406 void stacking_lower(ObWindow
*window
)
408 if (WINDOW_IS_CLIENT(window
)) {
410 selected
= WINDOW_AS_CLIENT(window
);
411 restack_windows(selected
, FALSE
);
414 wins
= g_list_append(NULL
, window
);
415 stacking_list
= g_list_remove(stacking_list
, window
);
421 void stacking_below(ObWindow
*window
, ObWindow
*below
)
423 GList
*wins
, *before
;
425 if (window_layer(window
) != window_layer(below
))
428 wins
= g_list_append(NULL
, window
);
429 stacking_list
= g_list_remove(stacking_list
, window
);
430 before
= g_list_next(g_list_find(stacking_list
, below
));
431 do_restack(wins
, before
);
435 void stacking_add(ObWindow
*win
)
437 g_assert(screen_support_win
!= None
); /* make sure I dont break this in the
440 stacking_list
= g_list_append(stacking_list
, win
);
444 static GList
*find_highest_relative(ObClient
*client
)
448 if (client
->parents
) {
452 /* get all top level relatives of this client */
453 top
= client_search_all_top_parents_layer(client
);
455 /* go from the top of the stacking order down */
456 for (it
= stacking_list
; !ret
&& it
; it
= g_list_next(it
)) {
457 if (WINDOW_IS_CLIENT(it
->data
)) {
458 ObClient
*c
= it
->data
;
459 /* only look at windows in the same layer and that are
461 if (c
->layer
== client
->layer
&&
463 (c
->desktop
== client
->desktop
||
464 c
->desktop
== DESKTOP_ALL
||
465 client
->desktop
== DESKTOP_ALL
))
469 /* go through each top level parent and see it this window
470 is related to them */
471 for (sit
= top
; !ret
&& sit
; sit
= g_slist_next(sit
)) {
472 ObClient
*topc
= sit
->data
;
474 /* are they related ? */
475 if (topc
== c
|| client_search_transient(topc
, c
))
485 void stacking_add_nonintrusive(ObWindow
*win
)
488 GList
*it_below
= NULL
; /* this client will be below us */
492 if (!WINDOW_IS_CLIENT(win
)) {
493 stacking_add(win
); /* no special rules for others */
497 client
= WINDOW_AS_CLIENT(win
);
499 /* insert above its highest parent (or its highest child !) */
500 it_below
= find_highest_relative(client
);
503 /* nothing to put it directly above, so try find the focused client
504 to put it underneath it */
505 if (focus_client
&& client
!= focus_client
&&
506 focus_client
->layer
== client
->layer
)
508 it_below
= g_list_find(stacking_list
, focus_client
);
509 /* this can give NULL, but it means the focused window is on the
510 bottom of the stacking order, so go to the bottom in that case,
512 it_below
= g_list_next(it_below
);
515 /* There is no window to put this directly above, so put it at the
516 top, so you know it is there.
518 It used to do this only if the window was focused and lower
521 We also put it at the top not the bottom to fix a bug with
522 fullscreen windows. When focusLast is off and followsMouse is
523 on, when you switch desktops, the fullscreen window loses
524 focus and goes into its lower layer. If this puts it at the
525 bottom then when you come back to the desktop, the window is
526 at the bottom and won't get focus back.
528 it_below
= stacking_list
;
532 /* make sure it's not in the wrong layer though ! */
533 for (; it_below
; it_below
= g_list_next(it_below
)) {
534 /* stop when the window is not in a higher layer than the window
535 it is going above (it_below) */
536 if (client
->layer
>= window_layer(it_below
->data
))
539 for (; it_below
!= stacking_list
; it_below
= it_above
) {
540 /* stop when the window is not in a lower layer than the
541 window it is going under (it_above) */
542 it_above
= it_below
?
543 g_list_previous(it_below
) : g_list_last(stacking_list
);
544 if (client
->layer
<= window_layer(it_above
->data
))
548 wins
= g_list_append(NULL
, win
);
549 do_restack(wins
, it_below
);
553 /*! Returns TRUE if client is occluded by the sibling. If sibling is NULL it
554 tries against all other clients.
556 static gboolean
stacking_occluded(ObClient
*client
, ObClient
*sibling
)
559 gboolean occluded
= FALSE
;
560 gboolean found
= FALSE
;
562 /* no need for any looping in this case */
563 if (sibling
&& client
->layer
!= sibling
->layer
)
566 for (it
= stacking_list
; it
;
567 it
= (found
? g_list_previous(it
) :g_list_next(it
)))
568 if (WINDOW_IS_CLIENT(it
->data
)) {
569 ObClient
*c
= it
->data
;
570 if (found
&& !c
->iconic
&&
571 (c
->desktop
== DESKTOP_ALL
|| client
->desktop
== DESKTOP_ALL
||
572 c
->desktop
== client
->desktop
) &&
573 !client_search_transient(client
, c
))
575 if (RECT_INTERSECTS_RECT(c
->frame
->area
, client
->frame
->area
))
577 if (sibling
!= NULL
) {
583 else if (c
->layer
== client
->layer
) {
587 else if (c
->layer
> client
->layer
)
588 break; /* we past its layer */
591 else if (c
== client
)
597 /*! Returns TRUE if client occludes the sibling. If sibling is NULL it tries
598 against all other clients.
600 static gboolean
stacking_occludes(ObClient
*client
, ObClient
*sibling
)
603 gboolean occludes
= FALSE
;
604 gboolean found
= FALSE
;
606 /* no need for any looping in this case */
607 if (sibling
&& client
->layer
!= sibling
->layer
)
610 for (it
= stacking_list
; it
; it
= g_list_next(it
))
611 if (WINDOW_IS_CLIENT(it
->data
)) {
612 ObClient
*c
= it
->data
;
613 if (found
&& !c
->iconic
&&
614 (c
->desktop
== DESKTOP_ALL
|| client
->desktop
== DESKTOP_ALL
||
615 c
->desktop
== client
->desktop
) &&
616 !client_search_transient(c
, client
))
618 if (RECT_INTERSECTS_RECT(c
->frame
->area
, client
->frame
->area
))
620 if (sibling
!= NULL
) {
626 else if (c
->layer
== client
->layer
) {
630 else if (c
->layer
< client
->layer
)
631 break; /* we past its layer */
634 else if (c
== client
)
640 gboolean
stacking_restack_request(ObClient
*client
, ObClient
*sibling
,
643 gboolean ret
= FALSE
;
645 if (sibling
&& ((client
->desktop
!= sibling
->desktop
&&
646 client
->desktop
!= DESKTOP_ALL
&&
647 sibling
->desktop
!= DESKTOP_ALL
) ||
650 ob_debug("Setting restack sibling to NULL, they are not on the same "
651 "desktop or it is iconified\n");
657 ob_debug("Restack request Below for client %s sibling %s\n",
658 client
->title
, sibling
? sibling
->title
: "(all)");
660 stacking_lower(CLIENT_AS_WINDOW(client
));
664 ob_debug("Restack request BottomIf for client %s sibling "
666 client
->title
, sibling
? sibling
->title
: "(all)");
667 /* if this client occludes sibling (or anything if NULL), then
668 lower it to the bottom */
669 if (stacking_occludes(client
, sibling
)) {
670 stacking_lower(CLIENT_AS_WINDOW(client
));
675 ob_debug("Restack request Above for client %s sibling %s\n",
676 client
->title
, sibling
? sibling
->title
: "(all)");
677 stacking_raise(CLIENT_AS_WINDOW(client
));
681 ob_debug("Restack request TopIf for client %s sibling %s\n",
682 client
->title
, sibling
? sibling
->title
: "(all)");
683 if (stacking_occluded(client
, sibling
)) {
684 stacking_raise(CLIENT_AS_WINDOW(client
));
689 ob_debug("Restack request Opposite for client %s sibling "
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
));