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_above(ObWindow *window, ObWindow *above)
428 GList *wins, *before;
430 if (window_layer(window) != window_layer(above))
433 wins = g_list_append(NULL, window);
434 stacking_list = g_list_remove(stacking_list, window);
435 before = g_list_previous(g_list_find(stacking_list, above));
436 do_restack(wins, before);
440 void stacking_below(ObWindow *window, ObWindow *below)
442 GList *wins, *before;
444 if (window_layer(window) != window_layer(below))
447 wins = g_list_append(NULL, window);
448 stacking_list = g_list_remove(stacking_list, window);
449 before = g_list_next(g_list_find(stacking_list, below));
450 do_restack(wins, before);
452 stacking_list_tail = g_list_last(stacking_list);
455 void stacking_add(ObWindow *win)
457 g_assert(screen_support_win != None); /* make sure I dont break this in the
459 /* don't add windows that are being unmanaged ! */
460 if (WINDOW_IS_CLIENT(win)) g_assert(WINDOW_AS_CLIENT(win)->managed);
462 stacking_list = g_list_append(stacking_list, win);
465 /* stacking_list_tail set by stacking_raise() */
468 static GList *find_highest_relative(ObClient *client)
472 if (client->parents) {
476 /* get all top level relatives of this client */
477 top = client_search_all_top_parents_layer(client);
479 /* go from the top of the stacking order down */
480 for (it = stacking_list; !ret && it; it = g_list_next(it)) {
481 if (WINDOW_IS_CLIENT(it->data)) {
482 ObClient *c = it->data;
483 /* only look at windows in the same layer and that are
485 if (c->layer == client->layer &&
487 (c->desktop == client->desktop ||
488 c->desktop == DESKTOP_ALL ||
489 client->desktop == DESKTOP_ALL))
493 /* go through each top level parent and see it this window
494 is related to them */
495 for (sit = top; !ret && sit; sit = g_slist_next(sit)) {
496 ObClient *topc = sit->data;
498 /* are they related ? */
499 if (topc == c || client_search_transient(topc, c))
509 void stacking_add_nonintrusive(ObWindow *win)
512 GList *it_below = NULL; /* this client will be below us */
516 if (!WINDOW_IS_CLIENT(win)) {
517 stacking_add(win); /* no special rules for others */
521 client = WINDOW_AS_CLIENT(win);
523 /* don't add windows that are being unmanaged ! */
524 g_assert(client->managed);
526 /* insert above its highest parent (or its highest child !) */
527 it_below = find_highest_relative(client);
530 /* nothing to put it directly above, so try find the focused client
531 to put it underneath it */
532 if (focus_client && client != focus_client &&
533 focus_client->layer == client->layer)
535 it_below = g_list_find(stacking_list, focus_client);
536 /* this can give NULL, but it means the focused window is on the
537 bottom of the stacking order, so go to the bottom in that case,
539 it_below = g_list_next(it_below);
542 /* There is no window to put this directly above, so put it at the
543 top, so you know it is there.
545 It used to do this only if the window was focused and lower
548 We also put it at the top not the bottom to fix a bug with
549 fullscreen windows. When focusLast is off and followsMouse is
550 on, when you switch desktops, the fullscreen window loses
551 focus and goes into its lower layer. If this puts it at the
552 bottom then when you come back to the desktop, the window is
553 at the bottom and won't get focus back.
555 it_below = stacking_list;
559 /* make sure it's not in the wrong layer though ! */
560 for (; it_below; it_below = g_list_next(it_below)) {
561 /* stop when the window is not in a higher layer than the window
562 it is going above (it_below) */
563 if (client->layer >= window_layer(it_below->data))
566 for (; it_below != stacking_list; it_below = it_above) {
567 /* stop when the window is not in a lower layer than the
568 window it is going under (it_above) */
569 it_above = it_below ?
570 g_list_previous(it_below) : g_list_last(stacking_list);
571 if (client->layer <= window_layer(it_above->data))
575 wins = g_list_append(NULL, win);
576 do_restack(wins, it_below);
578 stacking_list_tail = g_list_last(stacking_list);
581 ObClient *stacking_occluded(ObClient *client, ObClient *sibling)
584 ObClient *occluded = NULL;
586 /* no need for any looping in this case */
587 if (sibling && client->layer != sibling->layer)
590 for (it = g_list_previous(g_list_find(stacking_list, client)); it;
591 it = g_list_previous(it))
592 if (WINDOW_IS_CLIENT(it->data)) {
593 ObClient *c = it->data;
595 (c->desktop == DESKTOP_ALL || client->desktop == DESKTOP_ALL ||
596 c->desktop == client->desktop) &&
597 !client_search_transient(client, c))
599 if (RECT_INTERSECTS_RECT(c->frame->area, client->frame->area))
601 if (sibling != NULL) {
607 else if (c->layer == client->layer) {
611 else if (c->layer > client->layer)
612 break; /* we past its layer */
619 ObClient *stacking_occludes(ObClient *client, ObClient *sibling)
622 ObClient *occludes = NULL;
624 /* no need for any looping in this case */
625 if (sibling && client->layer != sibling->layer)
628 for (it = g_list_next(g_list_find(stacking_list, client));
629 it; it = g_list_next(it))
630 if (WINDOW_IS_CLIENT(it->data)) {
631 ObClient *c = it->data;
633 (c->desktop == DESKTOP_ALL || client->desktop == DESKTOP_ALL ||
634 c->desktop == client->desktop) &&
635 !client_search_transient(c, client))
637 if (RECT_INTERSECTS_RECT(c->frame->area, client->frame->area))
639 if (sibling != NULL) {
645 else if (c->layer == client->layer) {
649 else if (c->layer < client->layer)
650 break; /* we past its layer */
657 gboolean stacking_restack_request(ObClient *client, ObClient *sibling,
660 gboolean ret = FALSE;
662 if (sibling && ((client->desktop != sibling->desktop &&
663 client->desktop != DESKTOP_ALL &&
664 sibling->desktop != DESKTOP_ALL) ||
667 ob_debug("Setting restack sibling to NULL, they are not on the same "
668 "desktop or it is iconified");
674 ob_debug("Restack request Below for client %s sibling %s",
675 client->title, sibling ? sibling->title : "(all)");
677 stacking_lower(CLIENT_AS_WINDOW(client));
681 ob_debug("Restack request BottomIf for client %s sibling %s",
682 client->title, sibling ? sibling->title : "(all)");
683 /* if this client occludes sibling (or anything if NULL), then
684 lower it to the bottom */
685 if (stacking_occludes(client, sibling)) {
686 stacking_lower(CLIENT_AS_WINDOW(client));
691 ob_debug("Restack request Above for client %s sibling %s",
692 client->title, sibling ? sibling->title : "(all)");
693 stacking_raise(CLIENT_AS_WINDOW(client));
697 ob_debug("Restack request TopIf 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));
705 ob_debug("Restack request Opposite 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));
711 else if (stacking_occludes(client, sibling)) {
712 stacking_lower(CLIENT_AS_WINDOW(client));