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.
29 GList *stacking_list = NULL;
31 void stacking_set_list()
33 Window *windows = NULL;
37 /* on shutdown, don't update the properties, so that we can read it back
38 in on startup and re-stack the windows as they were before we shut down
40 if (ob_state() == OB_STATE_EXITING) return;
42 /* create an array of the window ids (from bottom to top,
45 windows = g_new(Window, g_list_length(stacking_list));
46 for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
47 if (WINDOW_IS_CLIENT(it->data))
48 windows[i++] = WINDOW_AS_CLIENT(it->data)->window;
52 PROP_SETA32(RootWindow(ob_display, ob_screen),
53 net_client_list_stacking, window, (gulong*)windows, i);
58 static void do_restack(GList *wins, GList *before)
66 /* pls only restack stuff in the same layer at a time */
67 for (it = wins; it; it = next) {
68 next = g_list_next(it);
70 g_assert (window_layer(it->data) == window_layer(next->data));
73 g_assert(window_layer(it->data) >= window_layer(before->data));
76 win = g_new(Window, g_list_length(wins) + 1);
78 if (before == stacking_list)
79 win[0] = screen_support_win;
81 win[0] = window_top(g_list_last(stacking_list)->data);
83 win[0] = window_top(g_list_previous(before)->data);
85 for (i = 1, it = wins; it; ++i, it = g_list_next(it)) {
86 win[i] = window_top(it->data);
87 g_assert(win[i] != None); /* better not call stacking shit before
88 setting your top level window value */
89 stacking_list = g_list_insert_before(stacking_list, before, it->data);
93 /* some debug checking of the stacking list's order */
94 for (it = stacking_list; ; it = next) {
95 next = g_list_next(it);
97 g_assert(window_layer(it->data) >= window_layer(next->data));
101 XRestackWindows(ob_display, win, i);
107 static void do_raise(GList *wins)
110 GList *layer[OB_NUM_STACKING_LAYERS] = {NULL};
113 for (it = wins; it; it = g_list_next(it)) {
116 l = window_layer(it->data);
117 layer[l] = g_list_append(layer[l], it->data);
121 for (i = OB_NUM_STACKING_LAYERS - 1; i >= 0; --i) {
123 for (; it; it = g_list_next(it)) {
124 /* look for the top of the layer */
125 if (window_layer(it->data) <= (ObStackingLayer) i)
128 do_restack(layer[i], it);
129 g_list_free(layer[i]);
134 static void do_lower(GList *wins)
137 GList *layer[OB_NUM_STACKING_LAYERS] = {NULL};
140 for (it = wins; it; it = g_list_next(it)) {
143 l = window_layer(it->data);
144 layer[l] = g_list_append(layer[l], it->data);
148 for (i = OB_NUM_STACKING_LAYERS - 1; i >= 0; --i) {
150 for (; it; it = g_list_next(it)) {
151 /* look for the top of the next layer down */
152 if (window_layer(it->data) < (ObStackingLayer) i)
155 do_restack(layer[i], it);
156 g_list_free(layer[i]);
161 static void restack_windows(ObClient *selected, gboolean raise)
163 GList *it, *last, *below, *above, *next;
166 GList *group_modals = NULL;
167 GList *group_trans = NULL;
168 GList *modals = NULL;
171 if (!raise && selected->transient_for) {
172 GSList *top, *top_it;
173 GSList *top_reorder = NULL;
175 /* if it's a transient lowering, lower its parents so that we can lower
176 this window, or it won't move */
177 top = client_search_all_top_parents(selected);
179 /* that is, if it has any parents */
180 if (!(top->data == selected && top->next == NULL)) {
181 /* go thru stacking list backwards so we can use g_slist_prepend */
182 for (it = g_list_last(stacking_list); it && top;
183 it = g_list_previous(it))
184 if ((top_it = g_slist_find(top, it->data))) {
185 top_reorder = g_slist_prepend(top_reorder, top_it->data);
186 top = g_slist_delete_link(top, top_it);
188 g_assert(top == NULL);
190 /* call restack for each of these to lower them */
191 for (top_it = top_reorder; top_it; top_it = g_slist_next(top_it))
192 restack_windows(top_it->data, raise);
197 /* remove first so we can't run into ourself */
198 it = g_list_find(stacking_list, selected);
200 stacking_list = g_list_delete_link(stacking_list, it);
202 /* go from the bottom of the stacking list up */
203 for (it = g_list_last(stacking_list); it; it = next) {
204 next = g_list_previous(it);
206 if (WINDOW_IS_CLIENT(it->data)) {
207 ObClient *ch = it->data;
209 /* only move windows in the same stacking layer */
210 if (ch->layer == selected->layer &&
211 client_search_transient(selected, ch))
213 if (client_is_direct_child(selected, ch)) {
215 modals = g_list_prepend(modals, ch);
217 trans = g_list_prepend(trans, ch);
221 group_modals = g_list_prepend(group_modals, ch);
223 group_trans = g_list_prepend(group_trans, ch);
225 stacking_list = g_list_delete_link(stacking_list, it);
230 /* put transients of the selected window right above it */
231 wins = g_list_concat(modals, trans);
232 wins = g_list_append(wins, selected);
234 /* if selected window is transient for group then raise it above others */
235 if (selected->transient_for == OB_TRAN_GROUP) {
236 /* if it's modal, raise it above those also */
237 if (selected->modal) {
238 wins = g_list_concat(wins, group_modals);
241 wins = g_list_concat(wins, group_trans);
245 /* find where to put the selected window, start from bottom of list,
246 this is the window below everything we are re-adding to the list */
248 for (it = g_list_last(stacking_list); it; it = g_list_previous(it))
250 if (window_layer(it->data) < selected->layer) {
254 /* if lowering, stop at the beginning of the layer */
257 /* if raising, stop at the end of the layer */
258 if (window_layer(it->data) > selected->layer)
264 /* save this position in the stacking list */
267 /* find where to put the group transients, start from the top of list */
268 for (it = stacking_list; it; it = g_list_next(it)) {
269 /* skip past higher layers */
270 if (window_layer(it->data) > selected->layer)
272 /* if we reach the end of the layer (how?) then don't go further */
273 if (window_layer(it->data) < selected->layer)
275 /* stop when we reach the first window in the group */
276 if (WINDOW_IS_CLIENT(it->data)) {
277 ObClient *c = it->data;
278 if (c->group == selected->group)
281 /* if we don't hit any other group members, stop here because this
282 is where we are putting the selected window (and its children) */
287 /* save this position, this is the top of the group of windows between the
288 group transient ones we're restacking and the others up above that we're
291 we actually want to save 1 position _above_ that, for for loops to work
292 nicely, so move back one position in the list while saving it
294 above = it ? g_list_previous(it) : g_list_last(stacking_list);
296 /* put the windows inside the gap to the other windows we're stacking
297 into the restacking list, go from the bottom up so that we can use
299 if (below) it = g_list_previous(below);
300 else it = g_list_last(stacking_list);
301 for (; it != above; it = next) {
302 next = g_list_previous(it);
303 wins = g_list_prepend(wins, it->data);
304 stacking_list = g_list_delete_link(stacking_list, it);
307 /* group transients go above the rest of the stuff acquired to now */
308 wins = g_list_concat(group_trans, wins);
309 /* group modals go on the very top */
310 wins = g_list_concat(group_modals, wins);
312 do_restack(wins, below);
316 void stacking_raise(ObWindow *window)
318 if (WINDOW_IS_CLIENT(window)) {
320 selected = WINDOW_AS_CLIENT(window);
321 restack_windows(selected, TRUE);
324 wins = g_list_append(NULL, window);
325 stacking_list = g_list_remove(stacking_list, window);
331 void stacking_lower(ObWindow *window)
333 if (WINDOW_IS_CLIENT(window)) {
335 selected = WINDOW_AS_CLIENT(window);
336 restack_windows(selected, FALSE);
339 wins = g_list_append(NULL, window);
340 stacking_list = g_list_remove(stacking_list, window);
346 void stacking_below(ObWindow *window, ObWindow *below)
348 GList *wins, *before;
350 if (window_layer(window) != window_layer(below))
353 wins = g_list_append(NULL, window);
354 stacking_list = g_list_remove(stacking_list, window);
355 before = g_list_next(g_list_find(stacking_list, below));
356 do_restack(wins, before);
360 void stacking_add(ObWindow *win)
362 g_assert(screen_support_win != None); /* make sure I dont break this in the
365 stacking_list = g_list_append(stacking_list, win);
369 void stacking_add_nonintrusive(ObWindow *win)
372 ObClient *parent = NULL;
373 GList *it_below = NULL;
375 if (!WINDOW_IS_CLIENT(win)) {
376 stacking_add(win); /* no special rules for others */
380 client = WINDOW_AS_CLIENT(win);
382 /* insert above its highest parent */
383 if (client->transient_for) {
384 if (client->transient_for != OB_TRAN_GROUP) {
385 parent = client->transient_for;
391 for (it = stacking_list; !parent && it; it = g_list_next(it)) {
392 if ((sit = g_slist_find(client->group->members, it->data)))
393 for (sit = client->group->members; !parent && sit;
394 sit = g_slist_next(sit))
396 ObClient *c = sit->data;
397 /* checking transient_for prevents infinate loops!
399 if (sit->data == it->data && !c->transient_for)
406 if (!(it_below = g_list_find(stacking_list, parent))) {
407 /* no parent to put above, try find the focused client to go
409 if (focus_client && focus_client->layer == client->layer) {
410 if ((it_below = g_list_find(stacking_list, focus_client)))
411 it_below = it_below->next;
415 /* there is no window to put this directly above, so put it at the
417 stacking_list = g_list_prepend(stacking_list, win);
420 /* make sure it's not in the wrong layer though ! */
421 for (; it_below; it_below = g_list_next(it_below))
423 /* stop when the window is not in a higher layer than the window
424 it is going above (it_below) */
425 if (client->layer >= window_layer(it_below->data))
428 for (; it_below != stacking_list;
429 it_below = g_list_previous(it_below))
431 /* stop when the window is not in a lower layer than the
432 window it is going under (it_above) */
433 GList *it_above = g_list_previous(it_below);
434 if (client->layer <= window_layer(it_above->data))
438 GList *wins = g_list_append(NULL, win);
439 do_restack(wins, it_below);