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 while (it && window_layer(it->data) == OB_STACKING_LAYER_ALL)
79 next = g_list_next(it);
80 while (next && window_layer(next->data) == OB_STACKING_LAYER_ALL)
81 next = g_list_next(next);
83 g_assert (window_layer(it->data) == window_layer(next->data));
86 g_assert(window_layer(it->data) >= window_layer(before->data));
89 win = g_new(Window, g_list_length(wins) + 1);
91 if (before == stacking_list)
92 win[0] = screen_support_win;
94 win[0] = window_top(g_list_last(stacking_list)->data);
96 win[0] = window_top(g_list_previous(before)->data);
98 for (i = 1, it = wins; it; ++i, it = g_list_next(it)) {
99 win[i] = window_top(it->data);
100 g_assert(win[i] != None); /* better not call stacking shit before
101 setting your top level window value */
102 stacking_list = g_list_insert_before(stacking_list, before, it->data);
106 /* some debug checking of the stacking list's order */
107 for (it = stacking_list; ; it = next) {
108 while (it && window_layer(it->data) == OB_STACKING_LAYER_ALL)
109 it = g_list_next(it);
110 next = g_list_next(it);
111 while (next && window_layer(next->data) == OB_STACKING_LAYER_ALL)
112 next = g_list_next(next);
114 g_assert(window_layer(it->data) >= window_layer(next->data));
119 XRestackWindows(obt_display, win, i);
125 void stacking_temp_raise(ObWindow *window)
131 /* don't use this for internal windows..! it would lower them.. */
132 g_assert(window_layer(window) < OB_STACKING_LAYER_INTERNAL);
134 /* find the window to drop it underneath */
135 win[0] = screen_support_win;
136 for (it = stacking_list; it; it = g_list_next(it)) {
137 ObWindow *w = it->data;
138 if (window_layer(w) >= OB_STACKING_LAYER_INTERNAL)
139 win[0] = window_top(w);
144 win[1] = window_top(window);
145 start = event_start_ignore_all_enters();
146 XRestackWindows(obt_display, win, 2);
147 event_end_ignore_all_enters(start);
149 pause_changes = TRUE;
152 void stacking_restore(void)
159 win = g_new(Window, g_list_length(stacking_list) + 1);
160 win[0] = screen_support_win;
161 for (i = 1, it = stacking_list; it; ++i, it = g_list_next(it))
162 win[i] = window_top(it->data);
163 start = event_start_ignore_all_enters();
164 XRestackWindows(obt_display, win, i);
165 event_end_ignore_all_enters(start);
168 pause_changes = FALSE;
171 static void do_raise(GList *wins)
174 GList *layer[OB_NUM_STACKING_LAYERS] = {NULL};
177 for (it = wins; it; it = g_list_next(it)) {
180 l = window_layer(it->data);
181 layer[l] = g_list_append(layer[l], it->data);
185 for (i = OB_NUM_STACKING_LAYERS - 1; i >= 0; --i) {
187 for (; it; it = g_list_next(it)) {
188 /* look for the top of the layer */
189 if (window_layer(it->data) <= (ObStackingLayer) i)
192 do_restack(layer[i], it);
193 g_list_free(layer[i]);
198 static void do_lower(GList *wins)
201 GList *layer[OB_NUM_STACKING_LAYERS] = {NULL};
204 for (it = wins; it; it = g_list_next(it)) {
207 l = window_layer(it->data);
208 layer[l] = g_list_append(layer[l], it->data);
212 for (i = OB_NUM_STACKING_LAYERS - 1; i >= 0; --i) {
214 for (; it; it = g_list_next(it)) {
215 /* look for the top of the next layer down */
216 if (window_layer(it->data) < (ObStackingLayer) i)
219 do_restack(layer[i], it);
220 g_list_free(layer[i]);
225 static void restack_windows(ObClient *selected, gboolean raise)
227 GList *it, *last, *below, *above, *next;
230 GList *group_helpers = NULL;
231 GList *group_modals = NULL;
232 GList *group_trans = NULL;
233 GList *modals = NULL;
239 /* if a window is modal for another single window, then raise it to the
240 top too, the same is done with the focus order */
241 while (selected->modal && (p = client_direct_parent(selected)))
245 /* remove first so we can't run into ourself */
246 it = g_list_find(stacking_list, selected);
248 stacking_list = g_list_delete_link(stacking_list, it);
250 /* go from the bottom of the stacking list up. don't move any other windows
251 when lowering, we call this for each window independently */
253 for (it = g_list_last(stacking_list); it; it = next) {
254 next = g_list_previous(it);
256 if (WINDOW_IS_CLIENT(it->data)) {
257 ObClient *ch = it->data;
259 /* only move windows in the same stacking layer */
260 if (ch->layer == selected->layer &&
261 /* looking for windows that are transients, and so would
262 remain above the selected window */
263 client_search_transient(selected, ch))
265 if (client_is_direct_child(selected, ch)) {
267 modals = g_list_prepend(modals, ch);
269 trans = g_list_prepend(trans, ch);
271 else if (client_helper(ch)) {
272 if (selected->transient) {
273 /* helpers do not stay above transient windows */
276 group_helpers = g_list_prepend(group_helpers, ch);
280 group_modals = g_list_prepend(group_modals, ch);
282 group_trans = g_list_prepend(group_trans, ch);
284 stacking_list = g_list_delete_link(stacking_list, it);
290 /* put modals above other direct transients */
291 wins = g_list_concat(modals, trans);
293 /* put helpers below direct transients */
294 wins = g_list_concat(wins, group_helpers);
296 /* put the selected window right below these children */
297 wins = g_list_append(wins, selected);
299 /* if selected window is transient for group then raise it above others */
300 if (selected->transient_for_group) {
301 /* if it's modal, raise it above those also */
302 if (selected->modal) {
303 wins = g_list_concat(wins, group_modals);
306 wins = g_list_concat(wins, group_trans);
310 /* find where to put the selected window, start from bottom of list,
311 this is the window below everything we are re-adding to the list */
313 for (it = g_list_last(stacking_list); it; it = g_list_previous(it))
315 if (window_layer(it->data) < selected->layer) {
319 /* if lowering, stop at the beginning of the layer */
322 /* if raising, stop at the end of the layer */
323 if (window_layer(it->data) > selected->layer)
329 /* save this position in the stacking list */
332 /* find where to put the group transients, start from the top of list */
333 for (it = stacking_list; it; it = g_list_next(it)) {
334 /* skip past higher layers */
335 if (window_layer(it->data) > selected->layer)
337 /* if we reach the end of the layer (how?) then don't go further */
338 if (window_layer(it->data) < selected->layer)
340 /* stop when we reach the first window in the group */
341 if (WINDOW_IS_CLIENT(it->data)) {
342 ObClient *c = it->data;
343 if (c->group == selected->group)
346 /* if we don't hit any other group members, stop here because this
347 is where we are putting the selected window (and its children) */
352 /* save this position, this is the top of the group of windows between the
353 group transient ones we're restacking and the others up above that we're
356 we actually want to save 1 position _above_ that, for for loops to work
357 nicely, so move back one position in the list while saving it
359 above = it ? g_list_previous(it) : g_list_last(stacking_list);
361 /* put the windows inside the gap to the other windows we're stacking
362 into the restacking list, go from the bottom up so that we can use
364 if (below) it = g_list_previous(below);
365 else it = g_list_last(stacking_list);
366 for (; it != above; it = next) {
367 next = g_list_previous(it);
368 wins = g_list_prepend(wins, it->data);
369 stacking_list = g_list_delete_link(stacking_list, it);
372 /* group transients go above the rest of the stuff acquired to now */
373 wins = g_list_concat(group_trans, wins);
374 /* group modals go on the very top */
375 wins = g_list_concat(group_modals, wins);
377 do_restack(wins, below);
380 /* lower our parents after us, so they go below us */
381 if (!raise && selected->parents) {
382 GSList *parents_copy, *sit;
383 GSList *reorder = NULL;
385 parents_copy = g_slist_copy(selected->parents);
387 /* go thru stacking list backwards so we can use g_slist_prepend */
388 for (it = g_list_last(stacking_list); it && parents_copy;
389 it = g_list_previous(it))
390 if ((sit = g_slist_find(parents_copy, it->data))) {
391 reorder = g_slist_prepend(reorder, sit->data);
392 parents_copy = g_slist_delete_link(parents_copy, sit);
394 g_assert(parents_copy == NULL);
396 /* call restack for each of these to lower them */
397 for (sit = reorder; sit; sit = g_slist_next(sit))
398 restack_windows(sit->data, raise);
402 void stacking_raise(ObWindow *window)
404 if (WINDOW_IS_CLIENT(window)) {
406 selected = WINDOW_AS_CLIENT(window);
407 restack_windows(selected, TRUE);
410 wins = g_list_append(NULL, window);
411 stacking_list = g_list_remove(stacking_list, window);
415 stacking_list_tail = g_list_last(stacking_list);
418 void stacking_lower(ObWindow *window)
420 if (WINDOW_IS_CLIENT(window)) {
422 selected = WINDOW_AS_CLIENT(window);
423 restack_windows(selected, FALSE);
426 wins = g_list_append(NULL, window);
427 stacking_list = g_list_remove(stacking_list, window);
431 stacking_list_tail = g_list_last(stacking_list);
434 void stacking_below(ObWindow *window, ObWindow *below)
436 GList *wins, *before;
438 if (window_layer(window) != window_layer(below))
441 wins = g_list_append(NULL, window);
442 stacking_list = g_list_remove(stacking_list, window);
443 before = g_list_next(g_list_find(stacking_list, below));
444 do_restack(wins, before);
446 stacking_list_tail = g_list_last(stacking_list);
449 void stacking_add(ObWindow *win)
451 g_assert(screen_support_win != None); /* make sure I dont break this in the
453 /* don't add windows that are being unmanaged ! */
454 if (WINDOW_IS_CLIENT(win)) g_assert(WINDOW_AS_CLIENT(win)->managed);
456 if (WINDOW_IS_UNMANAGED(win))
457 stacking_list = g_list_prepend(stacking_list, win);
459 stacking_list = g_list_append(stacking_list, win);
461 /* stacking_list_tail set by stacking_raise() */
465 static GList *find_highest_relative(ObClient *client)
469 if (client->parents) {
473 /* get all top level relatives of this client */
474 top = client_search_all_top_parents_layer(client);
476 /* go from the top of the stacking order down */
477 for (it = stacking_list; !ret && it; it = g_list_next(it)) {
478 if (WINDOW_IS_CLIENT(it->data)) {
479 ObClient *c = it->data;
480 /* only look at windows in the same layer and that are
482 if (c->layer == client->layer &&
484 (c->desktop == client->desktop ||
485 c->desktop == DESKTOP_ALL ||
486 client->desktop == DESKTOP_ALL))
490 /* go through each top level parent and see it this window
491 is related to them */
492 for (sit = top; !ret && sit; sit = g_slist_next(sit)) {
493 ObClient *topc = sit->data;
495 /* are they related ? */
496 if (topc == c || client_search_transient(topc, c))
506 void stacking_add_nonintrusive(ObWindow *win)
509 GList *it_below = NULL; /* this client will be below us */
513 if (!WINDOW_IS_CLIENT(win)) {
514 stacking_add(win); /* no special rules for others */
518 client = WINDOW_AS_CLIENT(win);
520 /* don't add windows that are being unmanaged ! */
521 g_assert(client->managed);
523 /* insert above its highest parent (or its highest child !) */
524 it_below = find_highest_relative(client);
527 /* nothing to put it directly above, so try find the focused client
528 to put it underneath it */
529 if (focus_client && client != focus_client &&
530 focus_client->layer == client->layer)
532 it_below = g_list_find(stacking_list, focus_client);
533 /* this can give NULL, but it means the focused window is on the
534 bottom of the stacking order, so go to the bottom in that case,
536 it_below = g_list_next(it_below);
539 /* There is no window to put this directly above, so put it at the
540 top, so you know it is there.
542 It used to do this only if the window was focused and lower
545 We also put it at the top not the bottom to fix a bug with
546 fullscreen windows. When focusLast is off and followsMouse is
547 on, when you switch desktops, the fullscreen window loses
548 focus and goes into its lower layer. If this puts it at the
549 bottom then when you come back to the desktop, the window is
550 at the bottom and won't get focus back.
552 it_below = stacking_list;
556 /* make sure it's not in the wrong layer though ! */
557 for (; it_below; it_below = g_list_next(it_below)) {
558 /* stop when the window is not in a higher layer than the window
559 it is going above (it_below) */
560 if (client->layer >= window_layer(it_below->data))
563 for (; it_below != stacking_list; it_below = it_above) {
564 /* stop when the window is not in a lower layer than the
565 window it is going under (it_above) */
566 it_above = it_below ?
567 g_list_previous(it_below) : g_list_last(stacking_list);
568 if (client->layer <= window_layer(it_above->data))
572 wins = g_list_append(NULL, win);
573 do_restack(wins, it_below);
575 stacking_list_tail = g_list_last(stacking_list);
578 /*! Returns TRUE if client is occluded by the sibling. If sibling is NULL it
579 tries against all other clients.
581 static gboolean stacking_occluded(ObClient *client, ObClient *sibling)
584 gboolean occluded = FALSE;
585 gboolean found = FALSE;
587 /* no need for any looping in this case */
588 if (sibling && client->layer != sibling->layer)
591 for (it = stacking_list; it;
592 it = (found ? g_list_previous(it) :g_list_next(it)))
593 if (WINDOW_IS_CLIENT(it->data)) {
594 ObClient *c = it->data;
595 if (found && !c->iconic &&
596 (c->desktop == DESKTOP_ALL || client->desktop == DESKTOP_ALL ||
597 c->desktop == client->desktop) &&
598 !client_search_transient(client, c))
600 if (RECT_INTERSECTS_RECT(c->frame->area, client->frame->area))
602 if (sibling != NULL) {
608 else if (c->layer == client->layer) {
612 else if (c->layer > client->layer)
613 break; /* we past its layer */
616 else if (c == client)
622 /*! Returns TRUE if client occludes the sibling. If sibling is NULL it tries
623 against all other clients.
625 static gboolean stacking_occludes(ObClient *client, ObClient *sibling)
628 gboolean occludes = FALSE;
629 gboolean found = FALSE;
631 /* no need for any looping in this case */
632 if (sibling && client->layer != sibling->layer)
635 for (it = stacking_list; it; it = g_list_next(it))
636 if (WINDOW_IS_CLIENT(it->data)) {
637 ObClient *c = it->data;
638 if (found && !c->iconic &&
639 (c->desktop == DESKTOP_ALL || client->desktop == DESKTOP_ALL ||
640 c->desktop == client->desktop) &&
641 !client_search_transient(c, client))
643 if (RECT_INTERSECTS_RECT(c->frame->area, client->frame->area))
645 if (sibling != NULL) {
651 else if (c->layer == client->layer) {
655 else if (c->layer < client->layer)
656 break; /* we past its layer */
659 else if (c == client)
665 gboolean stacking_restack_request(ObClient *client, ObClient *sibling,
668 gboolean ret = FALSE;
670 if (sibling && ((client->desktop != sibling->desktop &&
671 client->desktop != DESKTOP_ALL &&
672 sibling->desktop != DESKTOP_ALL) ||
675 ob_debug("Setting restack sibling to NULL, they are not on the same "
676 "desktop or it is iconified");
682 ob_debug("Restack request Below for client %s sibling %s",
683 client->title, sibling ? sibling->title : "(all)");
685 stacking_lower(CLIENT_AS_WINDOW(client));
689 ob_debug("Restack request BottomIf for client %s sibling %s",
690 client->title, sibling ? sibling->title : "(all)");
691 /* if this client occludes sibling (or anything if NULL), then
692 lower it to the bottom */
693 if (stacking_occludes(client, sibling)) {
694 stacking_lower(CLIENT_AS_WINDOW(client));
699 ob_debug("Restack request Above for client %s sibling %s",
700 client->title, sibling ? sibling->title : "(all)");
701 stacking_raise(CLIENT_AS_WINDOW(client));
705 ob_debug("Restack request TopIf for client %s sibling %s",
706 client->title, sibling ? sibling->title : "(all)");
707 if (stacking_occluded(client, sibling)) {
708 stacking_raise(CLIENT_AS_WINDOW(client));
713 ob_debug("Restack request Opposite for client %s sibling %s",
714 client->title, sibling ? sibling->title : "(all)");
715 if (stacking_occluded(client, sibling)) {
716 stacking_raise(CLIENT_AS_WINDOW(client));
719 else if (stacking_occludes(client, sibling)) {
720 stacking_lower(CLIENT_AS_WINDOW(client));