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.
33 GList *stacking_list = NULL;
34 GList *stacking_list_tail = NULL;
35 /*! When true, stacking changes will not be reflected on the screen. This is
36 to freeze the on-screen stacking order while a window is being temporarily
37 raised during focus cycling */
38 static gboolean pause_changes = FALSE;
40 void stacking_set_list(void)
42 Window *windows = NULL;
46 /* on shutdown, don't update the properties, so that we can read it back
47 in on startup and re-stack the windows as they were before we shut down
49 if (ob_state() == OB_STATE_EXITING) return;
51 /* create an array of the window ids (from bottom to top,
54 windows = g_new(Window, g_list_length(stacking_list));
55 for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
56 if (WINDOW_IS_CLIENT(it->data))
57 windows[i++] = WINDOW_AS_CLIENT(it->data)->window;
61 OBT_PROP_SETA32(obt_root(ob_screen), NET_CLIENT_LIST_STACKING, WINDOW,
67 static void do_restack(GList *wins, GList *before)
77 /* pls only restack stuff in the same layer at a time */
78 for (it = wins; it; it = next) {
79 next = g_list_next(it);
81 g_assert (window_layer(it->data) == window_layer(next->data));
84 g_assert(window_layer(it->data) >= window_layer(before->data));
87 win = g_new(Window, g_list_length(wins) + 1);
89 if (before == stacking_list)
90 win[0] = screen_support_win;
92 win[0] = window_top(g_list_last(stacking_list)->data);
94 win[0] = window_top(g_list_previous(before)->data);
96 for (i = 1, it = wins; it; ++i, it = g_list_next(it)) {
97 win[i] = window_top(it->data);
98 g_assert(win[i] != None); /* better not call stacking shit before
99 setting your top level window value */
100 stacking_list = g_list_insert_before(stacking_list, before, it->data);
104 /* some debug checking of the stacking list's order */
105 for (it = stacking_list; ; it = next) {
106 next = g_list_next(it);
108 g_assert(window_layer(it->data) >= window_layer(next->data));
113 XRestackWindows(obt_display, win, i);
119 void stacking_temp_raise(ObWindow *window)
125 /* don't use this for internal windows..! it would lower them.. */
126 g_assert(window_layer(window) < OB_STACKING_LAYER_INTERNAL);
128 /* find the window to drop it underneath */
129 win[0] = screen_support_win;
130 for (it = stacking_list; it; it = g_list_next(it)) {
131 ObWindow *w = it->data;
132 if (window_layer(w) >= OB_STACKING_LAYER_INTERNAL)
133 win[0] = window_top(w);
138 win[1] = window_top(window);
139 start = event_start_ignore_all_enters();
140 XRestackWindows(obt_display, win, 2);
141 event_end_ignore_all_enters(start);
143 pause_changes = TRUE;
146 void stacking_restore(void)
153 win = g_new(Window, g_list_length(stacking_list) + 1);
154 win[0] = screen_support_win;
155 for (i = 1, it = stacking_list; it; ++i, it = g_list_next(it))
156 win[i] = window_top(it->data);
157 start = event_start_ignore_all_enters();
158 XRestackWindows(obt_display, win, i);
159 event_end_ignore_all_enters(start);
162 pause_changes = FALSE;
165 static void do_raise(GList *wins)
168 GList *layer[OB_NUM_STACKING_LAYERS] = {NULL};
171 for (it = wins; it; it = g_list_next(it)) {
174 l = window_layer(it->data);
175 layer[l] = g_list_append(layer[l], it->data);
179 for (i = OB_NUM_STACKING_LAYERS - 1; i >= 0; --i) {
181 for (; it; it = g_list_next(it)) {
182 /* look for the top of the layer */
183 if (window_layer(it->data) <= (ObStackingLayer) i)
186 do_restack(layer[i], it);
187 g_list_free(layer[i]);
192 static void do_lower(GList *wins)
195 GList *layer[OB_NUM_STACKING_LAYERS] = {NULL};
198 for (it = wins; it; it = g_list_next(it)) {
201 l = window_layer(it->data);
202 layer[l] = g_list_append(layer[l], it->data);
206 for (i = OB_NUM_STACKING_LAYERS - 1; i >= 0; --i) {
208 for (; it; it = g_list_next(it)) {
209 /* look for the top of the next layer down */
210 if (window_layer(it->data) < (ObStackingLayer) i)
213 do_restack(layer[i], it);
214 g_list_free(layer[i]);
219 static void restack_windows(ObClient *selected, gboolean raise)
221 GList *it, *last, *below, *above, *next;
224 GList *group_helpers = NULL;
225 GList *group_modals = NULL;
226 GList *group_trans = NULL;
227 GList *modals = NULL;
233 /* if a window is modal for another single window, then raise it to the
234 top too, the same is done with the focus order */
235 while (selected->modal && (p = client_direct_parent(selected)))
239 /* remove first so we can't run into ourself */
240 it = g_list_find(stacking_list, selected);
242 stacking_list = g_list_delete_link(stacking_list, it);
244 /* go from the bottom of the stacking list up. don't move any other windows
245 when lowering, we call this for each window independently */
247 for (it = g_list_last(stacking_list); it; it = next) {
248 next = g_list_previous(it);
250 if (WINDOW_IS_CLIENT(it->data)) {
251 ObClient *ch = it->data;
253 /* only move windows in the same stacking layer */
254 if (ch->layer == selected->layer &&
255 /* looking for windows that are transients, and so would
256 remain above the selected window */
257 client_search_transient(selected, ch))
259 if (client_is_direct_child(selected, ch)) {
261 modals = g_list_prepend(modals, ch);
263 trans = g_list_prepend(trans, ch);
265 else if (client_helper(ch)) {
266 if (selected->transient) {
267 /* helpers do not stay above transient windows */
270 group_helpers = g_list_prepend(group_helpers, ch);
274 group_modals = g_list_prepend(group_modals, ch);
276 group_trans = g_list_prepend(group_trans, ch);
278 stacking_list = g_list_delete_link(stacking_list, it);
284 /* put modals above other direct transients */
285 wins = g_list_concat(modals, trans);
287 /* put helpers below direct transients */
288 wins = g_list_concat(wins, group_helpers);
290 /* put the selected window right below these children */
291 wins = g_list_append(wins, selected);
293 /* if selected window is transient for group then raise it above others */
294 if (selected->transient_for_group) {
295 /* if it's modal, raise it above those also */
296 if (selected->modal) {
297 wins = g_list_concat(wins, group_modals);
300 wins = g_list_concat(wins, group_trans);
304 /* find where to put the selected window, start from bottom of list,
305 this is the window below everything we are re-adding to the list */
307 for (it = g_list_last(stacking_list); it; it = g_list_previous(it))
309 if (window_layer(it->data) < selected->layer) {
313 /* if lowering, stop at the beginning of the layer */
316 /* if raising, stop at the end of the layer */
317 if (window_layer(it->data) > selected->layer)
323 /* save this position in the stacking list */
326 /* find where to put the group transients, start from the top of list */
327 for (it = stacking_list; it; it = g_list_next(it)) {
328 /* skip past higher layers */
329 if (window_layer(it->data) > selected->layer)
331 /* if we reach the end of the layer (how?) then don't go further */
332 if (window_layer(it->data) < selected->layer)
334 /* stop when we reach the first window in the group */
335 if (WINDOW_IS_CLIENT(it->data)) {
336 ObClient *c = it->data;
337 if (c->group == selected->group)
340 /* if we don't hit any other group members, stop here because this
341 is where we are putting the selected window (and its children) */
346 /* save this position, this is the top of the group of windows between the
347 group transient ones we're restacking and the others up above that we're
350 we actually want to save 1 position _above_ that, for for loops to work
351 nicely, so move back one position in the list while saving it
353 above = it ? g_list_previous(it) : g_list_last(stacking_list);
355 /* put the windows inside the gap to the other windows we're stacking
356 into the restacking list, go from the bottom up so that we can use
358 if (below) it = g_list_previous(below);
359 else it = g_list_last(stacking_list);
360 for (; it != above; it = next) {
361 next = g_list_previous(it);
362 wins = g_list_prepend(wins, it->data);
363 stacking_list = g_list_delete_link(stacking_list, it);
366 /* group transients go above the rest of the stuff acquired to now */
367 wins = g_list_concat(group_trans, wins);
368 /* group modals go on the very top */
369 wins = g_list_concat(group_modals, wins);
371 do_restack(wins, below);
374 /* lower our parents after us, so they go below us */
375 if (!raise && selected->parents) {
376 GSList *parents_copy, *sit;
377 GSList *reorder = NULL;
379 parents_copy = g_slist_copy(selected->parents);
381 /* go thru stacking list backwards so we can use g_slist_prepend */
382 for (it = g_list_last(stacking_list); it && parents_copy;
383 it = g_list_previous(it))
384 if ((sit = g_slist_find(parents_copy, it->data))) {
385 reorder = g_slist_prepend(reorder, sit->data);
386 parents_copy = g_slist_delete_link(parents_copy, sit);
388 g_assert(parents_copy == NULL);
390 /* call restack for each of these to lower them */
391 for (sit = reorder; sit; sit = g_slist_next(sit))
392 restack_windows(sit->data, raise);
396 void stacking_raise(ObWindow *window)
398 if (WINDOW_IS_CLIENT(window)) {
400 selected = WINDOW_AS_CLIENT(window);
401 restack_windows(selected, TRUE);
404 wins = g_list_append(NULL, window);
405 stacking_list = g_list_remove(stacking_list, window);
409 stacking_list_tail = g_list_last(stacking_list);
412 void stacking_lower(ObWindow *window)
414 if (WINDOW_IS_CLIENT(window)) {
416 selected = WINDOW_AS_CLIENT(window);
417 restack_windows(selected, FALSE);
420 wins = g_list_append(NULL, window);
421 stacking_list = g_list_remove(stacking_list, window);
425 stacking_list_tail = g_list_last(stacking_list);
428 void stacking_below(ObWindow *window, ObWindow *below)
430 GList *wins, *before;
432 if (window_layer(window) != window_layer(below))
435 wins = g_list_append(NULL, window);
436 stacking_list = g_list_remove(stacking_list, window);
437 before = g_list_next(g_list_find(stacking_list, below));
438 do_restack(wins, before);
440 stacking_list_tail = g_list_last(stacking_list);
443 void stacking_add(ObWindow *win)
445 g_assert(screen_support_win != None); /* make sure I dont break this in the
447 /* don't add windows that are being unmanaged ! */
448 if (WINDOW_IS_CLIENT(win)) g_assert(WINDOW_AS_CLIENT(win)->managed);
450 stacking_list = g_list_append(stacking_list, win);
453 /* stacking_list_tail set by stacking_raise() */
456 static GList *find_highest_relative(ObClient *client)
460 if (client->parents) {
464 /* get all top level relatives of this client */
465 top = client_search_all_top_parents_layer(client);
467 /* go from the top of the stacking order down */
468 for (it = stacking_list; !ret && it; it = g_list_next(it)) {
469 if (WINDOW_IS_CLIENT(it->data)) {
470 ObClient *c = it->data;
471 /* only look at windows in the same layer and that are
473 if (c->layer == client->layer &&
475 (c->desktop == client->desktop ||
476 c->desktop == DESKTOP_ALL ||
477 client->desktop == DESKTOP_ALL))
481 /* go through each top level parent and see it this window
482 is related to them */
483 for (sit = top; !ret && sit; sit = g_slist_next(sit)) {
484 ObClient *topc = sit->data;
486 /* are they related ? */
487 if (topc == c || client_search_transient(topc, c))
497 void stacking_add_nonintrusive(ObWindow *win)
500 GList *it_below = NULL; /* this client will be below us */
504 if (!WINDOW_IS_CLIENT(win)) {
505 stacking_add(win); /* no special rules for others */
509 client = WINDOW_AS_CLIENT(win);
511 /* don't add windows that are being unmanaged ! */
512 g_assert(client->managed);
514 /* insert above its highest parent (or its highest child !) */
515 it_below = find_highest_relative(client);
518 /* nothing to put it directly above, so try find the focused client
519 to put it underneath it */
520 if (focus_client && client != focus_client &&
521 focus_client->layer == client->layer)
523 it_below = g_list_find(stacking_list, focus_client);
524 /* this can give NULL, but it means the focused window is on the
525 bottom of the stacking order, so go to the bottom in that case,
527 it_below = g_list_next(it_below);
530 /* There is no window to put this directly above, so put it at the
531 top, so you know it is there.
533 It used to do this only if the window was focused and lower
536 We also put it at the top not the bottom to fix a bug with
537 fullscreen windows. When focusLast is off and followsMouse is
538 on, when you switch desktops, the fullscreen window loses
539 focus and goes into its lower layer. If this puts it at the
540 bottom then when you come back to the desktop, the window is
541 at the bottom and won't get focus back.
543 it_below = stacking_list;
547 /* make sure it's not in the wrong layer though ! */
548 for (; it_below; it_below = g_list_next(it_below)) {
549 /* stop when the window is not in a higher layer than the window
550 it is going above (it_below) */
551 if (client->layer >= window_layer(it_below->data))
554 for (; it_below != stacking_list; it_below = it_above) {
555 /* stop when the window is not in a lower layer than the
556 window it is going under (it_above) */
557 it_above = it_below ?
558 g_list_previous(it_below) : g_list_last(stacking_list);
559 if (client->layer <= window_layer(it_above->data))
563 wins = g_list_append(NULL, win);
564 do_restack(wins, it_below);
566 stacking_list_tail = g_list_last(stacking_list);
569 /*! Returns TRUE if client is occluded by the sibling. If sibling is NULL it
570 tries against all other clients.
572 static gboolean stacking_occluded(ObClient *client, ObWindow *sibling_win)
575 gboolean occluded = FALSE;
576 ObClient *sibling = NULL;
578 if (sibling_win && WINDOW_IS_CLIENT(sibling_win))
579 sibling = WINDOW_AS_CLIENT(sibling_win);
581 /* no need for any looping in this case */
582 if (sibling && client->layer != sibling->layer)
585 for (it = g_list_previous(g_list_find(stacking_list, client)); it;
586 it = g_list_previous(it))
587 if (WINDOW_IS_CLIENT(it->data)) {
588 ObClient *c = it->data;
590 (c->desktop == DESKTOP_ALL || client->desktop == DESKTOP_ALL ||
591 c->desktop == client->desktop) &&
592 !client_search_transient(client, c))
594 if (RECT_INTERSECTS_RECT(c->frame->area, client->frame->area))
596 if (sibling != NULL) {
602 else if (c->layer == client->layer) {
606 else if (c->layer > client->layer)
607 break; /* we past its layer */
610 } else if (WINDOW_IS_DOCK(it->data)) {
611 ObDock *dock = it->data;
612 if (RECT_INTERSECTS_RECT(dock->area, client->frame->area))
614 if (sibling_win != NULL) {
615 if (DOCK_AS_WINDOW(dock) == sibling_win) {
620 else if (config_dock_layer == client->layer) {
629 /*! Returns TRUE if client occludes the sibling. If sibling is NULL it tries
630 against all other clients.
632 static gboolean stacking_occludes(ObClient *client, ObWindow *sibling_win)
635 gboolean occludes = FALSE;
636 ObClient *sibling = NULL;
638 if (sibling_win && WINDOW_IS_CLIENT(sibling_win))
639 sibling = WINDOW_AS_CLIENT(sibling_win);
641 /* no need for any looping in this case */
642 if (sibling && client->layer != sibling->layer)
645 for (it = g_list_next(g_list_find(stacking_list, client));
646 it; it = g_list_next(it))
647 if (WINDOW_IS_CLIENT(it->data)) {
648 ObClient *c = it->data;
650 (c->desktop == DESKTOP_ALL || client->desktop == DESKTOP_ALL ||
651 c->desktop == client->desktop) &&
652 !client_search_transient(c, client))
654 if (RECT_INTERSECTS_RECT(c->frame->area, client->frame->area))
656 if (sibling != NULL) {
662 else if (c->layer == client->layer) {
666 else if (c->layer < client->layer)
667 break; /* we past its layer */
671 else if (WINDOW_IS_DOCK(it->data)) {
672 ObDock *dock = it->data;
673 if (RECT_INTERSECTS_RECT(dock->area, client->frame->area))
675 if (sibling_win != NULL) {
676 if (DOCK_AS_WINDOW(dock) == sibling_win) {
681 else if (config_dock_layer == client->layer) {
690 gboolean stacking_restack_request(ObClient *client, ObWindow *sibling_win,
693 gboolean ret = FALSE;
695 ObClient *sibling = NULL;
697 if (sibling_win && WINDOW_IS_CLIENT(sibling_win))
698 sibling = WINDOW_AS_CLIENT(sibling_win);
700 if (sibling && ((client->desktop != sibling->desktop &&
701 client->desktop != DESKTOP_ALL &&
702 sibling->desktop != DESKTOP_ALL) ||
705 ob_debug("Setting restack sibling to NULL, they are not on the same "
706 "desktop or it is iconified");
712 ob_debug("Restack request Below for client %s sibling %s",
713 client->title, sibling ? sibling->title : "(all)");
715 stacking_lower(CLIENT_AS_WINDOW(client));
719 ob_debug("Restack request BottomIf for client %s sibling %s",
720 client->title, sibling ? sibling->title : "(all)");
721 /* if this client occludes sibling (or anything if NULL), then
722 lower it to the bottom */
723 if (stacking_occludes(client, sibling_win)) {
724 stacking_lower(CLIENT_AS_WINDOW(client));
729 ob_debug("Restack request Above for client %s sibling %s",
730 client->title, sibling ? sibling->title : "(all)");
731 stacking_raise(CLIENT_AS_WINDOW(client));
735 ob_debug("Restack request TopIf for client %s sibling %s",
736 client->title, sibling ? sibling->title : "(all)");
737 if (stacking_occluded(client, sibling_win)) {
738 stacking_raise(CLIENT_AS_WINDOW(client));
743 ob_debug("Restack request Opposite for client %s sibling %s",
744 client->title, sibling ? sibling->title : "(all)");
745 if (stacking_occluded(client, sibling_win)) {
746 stacking_raise(CLIENT_AS_WINDOW(client));
749 else if (stacking_occludes(client, sibling_win)) {
750 stacking_lower(CLIENT_AS_WINDOW(client));