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.
32 GList *stacking_list = NULL;
33 GList *stacking_list_tail = NULL;
34 /*! When true, stacking changes will not be reflected on the screen. This is
35 to freeze the on-screen stacking order while a window is being temporarily
36 raised during focus cycling */
37 static gboolean pause_changes = FALSE;
39 void stacking_set_list(void)
41 Window *windows = NULL;
45 /* on shutdown, don't update the properties, so that we can read it back
46 in on startup and re-stack the windows as they were before we shut down
48 if (ob_state() == OB_STATE_EXITING) return;
50 /* create an array of the window ids (from bottom to top,
53 windows = g_new(Window, g_list_length(stacking_list));
54 for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
55 if (WINDOW_IS_CLIENT(it->data))
56 windows[i++] = WINDOW_AS_CLIENT(it->data)->window;
60 OBT_PROP_SETA32(obt_root(ob_screen), NET_CLIENT_LIST_STACKING, WINDOW,
66 static void do_restack(GList *wins, GList *before)
76 /* pls only restack stuff in the same layer at a time */
77 for (it = wins; it; it = next) {
78 next = g_list_next(it);
80 g_assert (window_layer(it->data) == window_layer(next->data));
83 g_assert(window_layer(it->data) >= window_layer(before->data));
86 win = g_new(Window, g_list_length(wins) + 1);
88 if (before == stacking_list)
89 win[0] = screen_support_win;
91 win[0] = window_top(g_list_last(stacking_list)->data);
93 win[0] = window_top(g_list_previous(before)->data);
95 for (i = 1, it = wins; it; ++i, it = g_list_next(it)) {
96 win[i] = window_top(it->data);
97 g_assert(win[i] != None); /* better not call stacking shit before
98 setting your top level window value */
99 stacking_list = g_list_insert_before(stacking_list, before, it->data);
103 /* some debug checking of the stacking list's order */
104 for (it = stacking_list; ; it = next) {
105 next = g_list_next(it);
107 g_assert(window_layer(it->data) >= window_layer(next->data));
112 XRestackWindows(obt_display, win, i);
118 void stacking_temp_raise(ObWindow *window)
124 /* don't use this for internal windows..! it would lower them.. */
125 g_assert(window_layer(window) < OB_STACKING_LAYER_INTERNAL);
127 /* find the window to drop it underneath */
128 win[0] = screen_support_win;
129 for (it = stacking_list; it; it = g_list_next(it)) {
130 ObWindow *w = it->data;
131 if (window_layer(w) >= OB_STACKING_LAYER_INTERNAL)
132 win[0] = window_top(w);
137 win[1] = window_top(window);
138 start = event_start_ignore_all_enters();
139 XRestackWindows(obt_display, win, 2);
140 event_end_ignore_all_enters(start);
142 pause_changes = TRUE;
145 void stacking_restore(void)
152 win = g_new(Window, g_list_length(stacking_list) + 1);
153 win[0] = screen_support_win;
154 for (i = 1, it = stacking_list; it; ++i, it = g_list_next(it))
155 win[i] = window_top(it->data);
156 start = event_start_ignore_all_enters();
157 XRestackWindows(obt_display, win, i);
158 event_end_ignore_all_enters(start);
161 pause_changes = FALSE;
164 static void do_raise(GList *wins)
167 GList *layer[OB_NUM_STACKING_LAYERS] = {NULL};
170 for (it = wins; it; it = g_list_next(it)) {
173 l = window_layer(it->data);
174 layer[l] = g_list_append(layer[l], it->data);
178 for (i = OB_NUM_STACKING_LAYERS - 1; i >= 0; --i) {
180 for (; it; it = g_list_next(it)) {
181 /* look for the top of the layer */
182 if (window_layer(it->data) <= (ObStackingLayer) i)
185 do_restack(layer[i], it);
186 g_list_free(layer[i]);
191 static void do_lower(GList *wins)
194 GList *layer[OB_NUM_STACKING_LAYERS] = {NULL};
197 for (it = wins; it; it = g_list_next(it)) {
200 l = window_layer(it->data);
201 layer[l] = g_list_append(layer[l], it->data);
205 for (i = OB_NUM_STACKING_LAYERS - 1; i >= 0; --i) {
207 for (; it; it = g_list_next(it)) {
208 /* look for the top of the next layer down */
209 if (window_layer(it->data) < (ObStackingLayer) i)
212 do_restack(layer[i], it);
213 g_list_free(layer[i]);
218 static void restack_windows(ObClient *selected, gboolean raise)
220 GList *it, *last, *below, *above, *next;
223 GList *group_helpers = NULL;
224 GList *group_modals = NULL;
225 GList *group_trans = NULL;
226 GList *modals = NULL;
232 /* if a window is modal for another single window, then raise it to the
233 top too, the same is done with the focus order */
234 while (selected->modal && (p = client_direct_parent(selected)))
238 /* remove first so we can't run into ourself */
239 it = g_list_find(stacking_list, selected);
241 stacking_list = g_list_delete_link(stacking_list, it);
243 /* go from the bottom of the stacking list up. don't move any other windows
244 when lowering, we call this for each window independently */
246 for (it = g_list_last(stacking_list); it; it = next) {
247 next = g_list_previous(it);
249 if (WINDOW_IS_CLIENT(it->data)) {
250 ObClient *ch = it->data;
252 /* only move windows in the same stacking layer */
253 if (ch->layer == selected->layer &&
254 /* looking for windows that are transients, and so would
255 remain above the selected window */
256 client_search_transient(selected, ch))
258 if (client_is_direct_child(selected, ch)) {
260 modals = g_list_prepend(modals, ch);
262 trans = g_list_prepend(trans, ch);
264 else if (client_helper(ch)) {
265 if (selected->transient) {
266 /* helpers do not stay above transient windows */
269 group_helpers = g_list_prepend(group_helpers, ch);
273 group_modals = g_list_prepend(group_modals, ch);
275 group_trans = g_list_prepend(group_trans, ch);
277 stacking_list = g_list_delete_link(stacking_list, it);
283 /* put modals above other direct transients */
284 wins = g_list_concat(modals, trans);
286 /* put helpers below direct transients */
287 wins = g_list_concat(wins, group_helpers);
289 /* put the selected window right below these children */
290 wins = g_list_append(wins, selected);
292 /* if selected window is transient for group then raise it above others */
293 if (selected->transient_for_group) {
294 /* if it's modal, raise it above those also */
295 if (selected->modal) {
296 wins = g_list_concat(wins, group_modals);
299 wins = g_list_concat(wins, group_trans);
303 /* find where to put the selected window, start from bottom of list,
304 this is the window below everything we are re-adding to the list */
306 for (it = g_list_last(stacking_list); it; it = g_list_previous(it))
308 if (window_layer(it->data) < selected->layer) {
312 /* if lowering, stop at the beginning of the layer */
315 /* if raising, stop at the end of the layer */
316 if (window_layer(it->data) > selected->layer)
322 /* save this position in the stacking list */
325 /* find where to put the group transients, start from the top of list */
326 for (it = stacking_list; it; it = g_list_next(it)) {
327 /* skip past higher layers */
328 if (window_layer(it->data) > selected->layer)
330 /* if we reach the end of the layer (how?) then don't go further */
331 if (window_layer(it->data) < selected->layer)
333 /* stop when we reach the first window in the group */
334 if (WINDOW_IS_CLIENT(it->data)) {
335 ObClient *c = it->data;
336 if (c->group == selected->group)
339 /* if we don't hit any other group members, stop here because this
340 is where we are putting the selected window (and its children) */
345 /* save this position, this is the top of the group of windows between the
346 group transient ones we're restacking and the others up above that we're
349 we actually want to save 1 position _above_ that, for for loops to work
350 nicely, so move back one position in the list while saving it
352 above = it ? g_list_previous(it) : g_list_last(stacking_list);
354 /* put the windows inside the gap to the other windows we're stacking
355 into the restacking list, go from the bottom up so that we can use
357 if (below) it = g_list_previous(below);
358 else it = g_list_last(stacking_list);
359 for (; it != above; it = next) {
360 next = g_list_previous(it);
361 wins = g_list_prepend(wins, it->data);
362 stacking_list = g_list_delete_link(stacking_list, it);
365 /* group transients go above the rest of the stuff acquired to now */
366 wins = g_list_concat(group_trans, wins);
367 /* group modals go on the very top */
368 wins = g_list_concat(group_modals, wins);
370 do_restack(wins, below);
373 /* lower our parents after us, so they go below us */
374 if (!raise && selected->parents) {
375 GSList *parents_copy, *sit;
376 GSList *reorder = NULL;
378 parents_copy = g_slist_copy(selected->parents);
380 /* go thru stacking list backwards so we can use g_slist_prepend */
381 for (it = g_list_last(stacking_list); it && parents_copy;
382 it = g_list_previous(it))
383 if ((sit = g_slist_find(parents_copy, it->data))) {
384 reorder = g_slist_prepend(reorder, sit->data);
385 parents_copy = g_slist_delete_link(parents_copy, sit);
387 g_assert(parents_copy == NULL);
389 /* call restack for each of these to lower them */
390 for (sit = reorder; sit; sit = g_slist_next(sit))
391 restack_windows(sit->data, raise);
395 void stacking_raise(ObWindow *window)
397 if (WINDOW_IS_CLIENT(window)) {
399 selected = WINDOW_AS_CLIENT(window);
400 restack_windows(selected, TRUE);
403 wins = g_list_append(NULL, window);
404 stacking_list = g_list_remove(stacking_list, window);
408 stacking_list_tail = g_list_last(stacking_list);
411 void stacking_lower(ObWindow *window)
413 if (WINDOW_IS_CLIENT(window)) {
415 selected = WINDOW_AS_CLIENT(window);
416 restack_windows(selected, FALSE);
419 wins = g_list_append(NULL, window);
420 stacking_list = g_list_remove(stacking_list, window);
424 stacking_list_tail = g_list_last(stacking_list);
427 void stacking_above(ObWindow *window, ObWindow *above)
429 GList *wins, *before;
431 if (window_layer(window) != window_layer(above))
434 wins = g_list_append(NULL, window);
435 stacking_list = g_list_remove(stacking_list, window);
436 before = g_list_previous(g_list_find(stacking_list, above));
437 do_restack(wins, before);
441 void stacking_below(ObWindow *window, ObWindow *below)
443 GList *wins, *before;
445 if (window_layer(window) != window_layer(below))
448 wins = g_list_append(NULL, window);
449 stacking_list = g_list_remove(stacking_list, window);
450 before = g_list_next(g_list_find(stacking_list, below));
451 do_restack(wins, before);
453 stacking_list_tail = g_list_last(stacking_list);
456 void stacking_add(ObWindow *win)
458 g_assert(screen_support_win != None); /* make sure I dont break this in the
460 /* don't add windows that are being unmanaged ! */
461 if (WINDOW_IS_CLIENT(win)) g_assert(WINDOW_AS_CLIENT(win)->managed);
463 stacking_list = g_list_append(stacking_list, win);
466 /* stacking_list_tail set by stacking_raise() */
469 static GList *find_highest_relative(ObClient *client)
473 if (client->parents) {
477 /* get all top level relatives of this client */
478 top = client_search_all_top_parents_layer(client);
480 /* go from the top of the stacking order down */
481 for (it = stacking_list; !ret && it; it = g_list_next(it)) {
482 if (WINDOW_IS_CLIENT(it->data)) {
483 ObClient *c = it->data;
484 /* only look at windows in the same layer and that are
486 if (c->layer == client->layer &&
488 (c->desktop == client->desktop ||
489 c->desktop == DESKTOP_ALL ||
490 client->desktop == DESKTOP_ALL))
494 /* go through each top level parent and see it this window
495 is related to them */
496 for (sit = top; !ret && sit; sit = g_slist_next(sit)) {
497 ObClient *topc = sit->data;
499 /* are they related ? */
500 if (topc == c || client_search_transient(topc, c))
510 void stacking_add_nonintrusive(ObWindow *win)
513 GList *it_below = NULL; /* this client will be below us */
517 if (!WINDOW_IS_CLIENT(win)) {
518 stacking_add(win); /* no special rules for others */
522 client = WINDOW_AS_CLIENT(win);
524 /* don't add windows that are being unmanaged ! */
525 g_assert(client->managed);
527 /* insert above its highest parent (or its highest child !) */
528 it_below = find_highest_relative(client);
531 /* nothing to put it directly above, so try find the focused client
532 to put it underneath it */
533 if (focus_client && client != focus_client &&
534 focus_client->layer == client->layer)
536 it_below = g_list_find(stacking_list, focus_client);
537 /* this can give NULL, but it means the focused window is on the
538 bottom of the stacking order, so go to the bottom in that case,
540 it_below = g_list_next(it_below);
543 /* There is no window to put this directly above, so put it at the
544 top, so you know it is there.
546 It used to do this only if the window was focused and lower
549 We also put it at the top not the bottom to fix a bug with
550 fullscreen windows. When focusLast is off and followsMouse is
551 on, when you switch desktops, the fullscreen window loses
552 focus and goes into its lower layer. If this puts it at the
553 bottom then when you come back to the desktop, the window is
554 at the bottom and won't get focus back.
556 it_below = stacking_list;
560 /* make sure it's not in the wrong layer though ! */
561 for (; it_below; it_below = g_list_next(it_below)) {
562 /* stop when the window is not in a higher layer than the window
563 it is going above (it_below) */
564 if (client->layer >= window_layer(it_below->data))
567 for (; it_below != stacking_list; it_below = it_above) {
568 /* stop when the window is not in a lower layer than the
569 window it is going under (it_above) */
570 it_above = it_below ?
571 g_list_previous(it_below) : g_list_last(stacking_list);
572 if (client->layer <= window_layer(it_above->data))
576 wins = g_list_append(NULL, win);
577 do_restack(wins, it_below);
579 stacking_list_tail = g_list_last(stacking_list);
582 ObWindow *stacking_occluded(ObClient *client, ObWindow *sibling_win)
585 ObWindow *occluded = NULL;
586 ObClient *sibling = NULL;
588 if (sibling_win && WINDOW_IS_CLIENT(sibling_win))
589 sibling = WINDOW_AS_CLIENT(sibling_win);
591 /* no need for any looping in this case */
592 if (sibling && client->layer != sibling->layer)
595 for (it = g_list_previous(g_list_find(stacking_list, client)); it;
596 it = g_list_previous(it))
597 if (WINDOW_IS_CLIENT(it->data)) {
598 ObClient *c = it->data;
600 (c->desktop == DESKTOP_ALL || client->desktop == DESKTOP_ALL ||
601 c->desktop == client->desktop) &&
602 !client_search_transient(client, c))
604 if (RECT_INTERSECTS_RECT(c->frame->area, client->frame->area))
606 if (sibling != NULL) {
608 occluded = sibling_win;
612 else if (c->layer == client->layer) {
613 occluded = CLIENT_AS_WINDOW(c);
616 else if (c->layer > client->layer)
617 break; /* we past its layer */
621 else if (WINDOW_IS_DOCK(it->data)) {
622 ObDock *dock = it->data;
623 if (RECT_INTERSECTS_RECT(dock->area, client->frame->area))
625 if (sibling_win != NULL) {
626 if (DOCK_AS_WINDOW(dock) == sibling_win) {
627 occluded = sibling_win;
632 occluded = DOCK_AS_WINDOW(dock);
638 ObWindow *stacking_occludes(ObClient *client, ObWindow *sibling_win)
641 ObWindow *occludes = NULL;
642 ObClient *sibling = NULL;
644 if (sibling_win && WINDOW_IS_CLIENT(sibling_win))
645 sibling = WINDOW_AS_CLIENT(sibling_win);
647 /* no need for any looping in this case */
648 if (sibling && client->layer != sibling->layer)
651 for (it = g_list_next(g_list_find(stacking_list, client));
652 it; it = g_list_next(it))
653 if (WINDOW_IS_CLIENT(it->data)) {
654 ObClient *c = it->data;
656 (c->desktop == DESKTOP_ALL || client->desktop == DESKTOP_ALL ||
657 c->desktop == client->desktop) &&
658 !client_search_transient(c, client))
660 if (RECT_INTERSECTS_RECT(c->frame->area, client->frame->area))
662 if (sibling != NULL) {
664 occludes = sibling_win;
668 else if (c->layer == client->layer) {
669 occludes = CLIENT_AS_WINDOW(c);
672 else if (c->layer < client->layer)
673 break; /* we past its layer */
677 else if (WINDOW_IS_DOCK(it->data)) {
678 ObDock *dock = it->data;
679 if (RECT_INTERSECTS_RECT(dock->area, client->frame->area))
681 if (sibling_win != NULL) {
682 if (DOCK_AS_WINDOW(dock) == sibling_win) {
683 occludes = sibling_win;
688 occludes = DOCK_AS_WINDOW(dock);
694 gboolean stacking_restack_request(ObClient *client, ObWindow *sibling_win,
697 gboolean ret = FALSE;
701 if (sibling_win && WINDOW_IS_CLIENT(sibling_win))
702 sibling = WINDOW_AS_CLIENT(sibling_win);
704 if (sibling && ((client->desktop != sibling->desktop &&
705 client->desktop != DESKTOP_ALL &&
706 sibling->desktop != DESKTOP_ALL) ||
709 ob_debug("Setting restack sibling to NULL, they are not on the same "
710 "desktop or it is iconified");
716 ob_debug("Restack request Below for client %s sibling %s",
717 client->title, sibling ? sibling->title : "(all)");
719 stacking_lower(CLIENT_AS_WINDOW(client));
723 ob_debug("Restack request BottomIf for client %s sibling %s",
724 client->title, sibling ? sibling->title : "(all)");
725 /* if this client occludes sibling (or anything if NULL), then
726 lower it to the bottom */
727 if (stacking_occludes(client, sibling_win)) {
728 stacking_lower(CLIENT_AS_WINDOW(client));
733 ob_debug("Restack request Above for client %s sibling %s",
734 client->title, sibling ? sibling->title : "(all)");
735 stacking_raise(CLIENT_AS_WINDOW(client));
739 ob_debug("Restack request TopIf for client %s sibling %s",
740 client->title, sibling ? sibling->title : "(all)");
741 if (stacking_occluded(client, sibling_win)) {
742 stacking_raise(CLIENT_AS_WINDOW(client));
747 ob_debug("Restack request Opposite for client %s sibling %s",
748 client->title, sibling ? sibling->title : "(all)");
749 if (stacking_occluded(client, sibling_win)) {
750 stacking_raise(CLIENT_AS_WINDOW(client));
753 else if (stacking_occludes(client, sibling_win)) {
754 stacking_lower(CLIENT_AS_WINDOW(client));