that was a silly mistake. fullscreen windows amongst others will not crash now :D
[mikachu/openbox.git] / openbox / stacking.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    stacking.c for the Openbox window manager
4    Copyright (c) 2006        Mikael Magnusson
5    Copyright (c) 2003        Ben Jansens
6
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.
11
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.
16
17    See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "openbox.h"
21 #include "prop.h"
22 #include "screen.h"
23 #include "focus.h"
24 #include "client.h"
25 #include "group.h"
26 #include "frame.h"
27 #include "window.h"
28
29 GList  *stacking_list = NULL;
30
31 void stacking_set_list()
32 {
33     Window *windows = NULL;
34     GList *it;
35     guint i = 0;
36
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
39     */
40     if (ob_state() == OB_STATE_EXITING) return;
41
42     /* create an array of the window ids (from bottom to top,
43        reverse order!) */
44     if (stacking_list) {
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;
49         }
50     }
51
52     PROP_SETA32(RootWindow(ob_display, ob_screen),
53                 net_client_list_stacking, window, (gulong*)windows, i);
54
55     g_free(windows);
56 }
57
58 static void do_restack(GList *wins, GList *before)
59 {
60     GList *it;
61     Window *win;
62     gint i;
63
64 #ifdef DEBUG
65     GList *next;
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);
69         if (!next) break;
70         g_assert (window_layer(it->data) == window_layer(next->data));
71     }
72     if (before)
73         g_assert(window_layer(it->data) >= window_layer(before->data));
74 #endif
75
76     win = g_new(Window, g_list_length(wins) + 1);
77
78     if (before == stacking_list)
79         win[0] = screen_support_win;
80     else if (!before)
81         win[0] = window_top(g_list_last(stacking_list)->data);
82     else
83         win[0] = window_top(g_list_previous(before)->data);
84
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);
90     }
91
92 #ifdef DEBUG
93     /* some debug checking of the stacking list's order */
94     for (it = stacking_list; ; it = next) {
95         next = g_list_next(it);
96         if (!next) break;
97         g_assert(window_layer(it->data) >= window_layer(next->data));
98     }
99 #endif
100
101     XRestackWindows(ob_display, win, i);
102     g_free(win);
103
104     stacking_set_list();
105 }
106
107 static void do_raise(GList *wins)
108 {
109     GList *it;
110     GList *layer[OB_NUM_STACKING_LAYERS] = {NULL};
111     gint i;
112
113     for (it = wins; it; it = g_list_next(it)) {
114         ObStackingLayer l;
115
116         l = window_layer(it->data);
117         layer[l] = g_list_append(layer[l], it->data);
118     }
119
120     it = stacking_list;
121     for (i = OB_NUM_STACKING_LAYERS - 1; i >= 0; --i) {
122         if (layer[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)
126                     break;
127             }
128             do_restack(layer[i], it);
129             g_list_free(layer[i]);
130         }
131     }
132 }
133
134 static void do_lower(GList *wins)
135 {
136     GList *it;
137     GList *layer[OB_NUM_STACKING_LAYERS] = {NULL};
138     gint i;
139
140     for (it = wins; it; it = g_list_next(it)) {
141         ObStackingLayer l;
142
143         l = window_layer(it->data);
144         layer[l] = g_list_append(layer[l], it->data);
145     }
146
147     it = stacking_list;
148     for (i = OB_NUM_STACKING_LAYERS - 1; i >= 0; --i) {
149         if (layer[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)
153                     break;
154             }
155             do_restack(layer[i], it);
156             g_list_free(layer[i]);
157         }
158     }
159 }
160
161 static void restack_windows(ObClient *selected, gboolean raise)
162 {
163     GList *it, *last, *below, *above, *next;
164     GList *wins = NULL;
165
166     GList *group_modals = NULL;
167     GList *group_trans = NULL;
168     GList *modals = NULL;
169     GList *trans = NULL;
170
171     if (!raise && selected->transient_for) {
172         GSList *top, *top_it;
173         GSList *top_reorder = NULL;
174         
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);
178
179         /* go thru stacking list backwards so we can use g_slist_prepend */
180         for (it = g_list_last(stacking_list); it && top;
181              it = g_list_previous(it))
182             if ((top_it = g_slist_find(top, it->data))) {
183                 top_reorder = g_slist_prepend(top_reorder, top_it->data);
184                 top = g_slist_delete_link(top, top_it);
185             }
186         g_assert(top == NULL);
187
188         /* call restack for each of these to lower them */
189         for (top_it = top_reorder; top_it; top_it = g_slist_next(top_it))
190             restack_windows(top_it->data, raise);
191         return;
192     }
193
194     /* remove first so we can't run into ourself */
195     it = g_list_find(stacking_list, selected);
196     g_assert(it);
197     stacking_list = g_list_delete_link(stacking_list, it);
198
199     /* go from the bottom of the stacking list up */
200     for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
201         if (WINDOW_IS_CLIENT(it->data)) {
202             ObClient *ch = it->data;
203
204             /* only move windows in the same stacking layer */
205             if (ch->layer == selected->layer &&
206                 client_search_transient(selected, ch))
207             {
208                 if (client_is_direct_child(selected, ch)) {
209                     if (ch->modal)
210                         modals = g_list_prepend(modals, ch);
211                     else
212                         trans = g_list_prepend(trans, ch);
213                 }
214                 else {
215                     if (ch->modal)
216                         group_modals = g_list_prepend(group_modals, ch);
217                     else
218                         group_trans = g_list_prepend(group_trans, ch);
219                 }
220                 stacking_list = g_list_delete_link(stacking_list, it);
221             }
222         }
223     }
224
225     /* put transients of the selected window right above it */
226     wins = g_list_concat(modals, trans);
227     wins = g_list_append(wins, selected);
228
229     /* if selected window is transient for group then raise it above others */
230     if (selected->transient_for == OB_TRAN_GROUP) {
231         /* if it's modal, raise it above those also */
232         if (selected->modal) {
233             wins = g_list_concat(wins, group_modals);
234             group_modals = NULL;
235         }
236         wins = g_list_concat(wins, group_trans);
237         group_trans = NULL;
238     }
239
240     /* find where to put the selected window, start from bottom of list,
241        this is the window below everything we are re-adding to the list */
242     last = NULL;
243     for (it = g_list_last(stacking_list); it; it = g_list_previous(it))
244     {
245         if (window_layer(it->data) < selected->layer) {
246             last = it;
247             continue;
248         }
249         /* if lowering, stop at the beginning of the layer */
250         if (!raise)
251             break;
252         /* if raising, stop at the end of the layer */
253         if (window_layer(it->data) > selected->layer)
254             break;
255
256         last = it;
257     }
258
259     /* save this position in the stacking list */
260     below = last;
261
262     /* find where to put the group transients, start from the top of list */
263     for (it = stacking_list; it; it = g_list_next(it)) {
264         /* skip past higher layers */
265         if (window_layer(it->data) > selected->layer)
266             continue;
267         /* if we reach the end of the layer (how?) then don't go further */
268         if (window_layer(it->data) < selected->layer)
269             break;
270         /* stop when we reach the first window in the group */
271         if (WINDOW_IS_CLIENT(it->data)) {
272             ObClient *c = it->data;
273             if (c->group == selected->group)
274                 break;
275         }
276         /* if we don't hit any other group members, stop here because this
277            is where we are putting the selected window (and its children) */
278         if (it == below)
279             break;
280     }
281
282     /* save this position, this is the top of the group of windows between the
283        group transient ones we're restacking and the others up above that we're
284        restacking
285
286        we actually want to save 1 position _above_ that, for for loops to work
287        nicely, so move back one position in the list while saving it
288     */
289     above = it ? g_list_previous(it) : g_list_last(stacking_list);
290
291     /* put the windows inside the gap to the other windows we're stacking
292        into the restacking list, go from the bottom up so that we can use
293        g_list_prepend */
294     if (below) it = g_list_previous(below);
295     else       it = g_list_last(stacking_list);
296     for (; it != above; it = next) {
297         next = g_list_previous(it);
298         wins = g_list_prepend(wins, it->data);
299         stacking_list = g_list_delete_link(stacking_list, it);
300     }
301
302     /* group transients go above the rest of the stuff acquired to now */
303     wins = g_list_concat(group_trans, wins);
304     /* group modals go on the very top */
305     wins = g_list_concat(group_modals, wins);
306
307     do_restack(wins, below);
308     g_list_free(wins);
309 }
310
311 void stacking_raise(ObWindow *window)
312 {
313     if (WINDOW_IS_CLIENT(window)) {
314         ObClient *selected;
315         selected = WINDOW_AS_CLIENT(window);
316         restack_windows(selected, TRUE);
317     } else {
318         GList *wins;
319         wins = g_list_append(NULL, window);
320         stacking_list = g_list_remove(stacking_list, window);
321         do_raise(wins);
322         g_list_free(wins);
323     }
324 }
325
326 void stacking_lower(ObWindow *window)
327 {
328     if (WINDOW_IS_CLIENT(window)) {
329         ObClient *selected;
330         selected = WINDOW_AS_CLIENT(window);
331         restack_windows(selected, FALSE);
332     } else {
333         GList *wins;
334         wins = g_list_append(NULL, window);
335         stacking_list = g_list_remove(stacking_list, window);
336         do_lower(wins);
337         g_list_free(wins);
338     }
339 }
340
341 void stacking_below(ObWindow *window, ObWindow *below)
342 {
343     GList *wins, *before;
344
345     if (window_layer(window) != window_layer(below))
346         return;
347
348     wins = g_list_append(NULL, window);
349     stacking_list = g_list_remove(stacking_list, window);
350     before = g_list_next(g_list_find(stacking_list, below));
351     do_restack(wins, before);
352     g_list_free(wins);
353 }
354
355 void stacking_add(ObWindow *win)
356 {
357     g_assert(screen_support_win != None); /* make sure I dont break this in the
358                                              future */
359
360     stacking_list = g_list_append(stacking_list, win);
361     stacking_raise(win);
362 }
363
364 void stacking_add_nonintrusive(ObWindow *win)
365 {
366     ObClient *client;
367     ObClient *parent = NULL;
368     GList *it_below = NULL;
369
370     if (!WINDOW_IS_CLIENT(win)) {
371         stacking_add(win); /* no special rules for others */
372         return;
373     }
374
375     client = WINDOW_AS_CLIENT(win);
376
377     /* insert above its highest parent */
378     if (client->transient_for) {
379         if (client->transient_for != OB_TRAN_GROUP) {
380             parent = client->transient_for;
381         } else {
382             GSList *sit;
383             GList *it;
384
385             if (client->group)
386                 for (it = stacking_list; !parent && it; it = g_list_next(it)) {
387                     if ((sit = g_slist_find(client->group->members, it->data)))
388                 for (sit = client->group->members; !parent && sit;
389                      sit = g_slist_next(sit))
390                 {
391                     ObClient *c = sit->data;
392                     /* checking transient_for prevents infinate loops! */
393                     if (sit->data == it->data && !c->transient_for)
394                         parent = it->data;
395                 }
396             }
397         }
398     }
399
400     if (!(it_below = g_list_find(stacking_list, parent))) {
401         /* no parent to put above, try find the focused client to go
402            under */
403         if (focus_client && focus_client->layer == client->layer) {
404             if ((it_below = g_list_find(stacking_list, focus_client)))
405                 it_below = it_below->next;
406         }
407     }
408     if (!it_below) {
409         /* out of ideas, just add it normally... */
410         stacking_add(win);
411     } else {
412         /* make sure it's not in the wrong layer though ! */
413         for (; it_below; it_below = g_list_next(it_below))
414         {
415             /* stop when the window is not in a higher layer than the window
416                it is going above (it_below) */
417             if (client->layer >= window_layer(it_below->data))
418                 break;
419         }
420         for (; it_below != stacking_list;
421              it_below = g_list_previous(it_below))
422         {
423             /* stop when the window is not in a lower layer than the
424                window it is going under (it_above) */
425             GList *it_above = g_list_previous(it_below);
426             if (client->layer <= window_layer(it_above->data))
427                 break;
428         }
429
430         GList *wins = g_list_append(NULL, win);
431         do_restack(wins, it_below);
432         g_list_free(wins);
433     }
434 }