]> Dogcows Code - chaz/openbox/blob - openbox/stacking.c
better stacking_add_nonintrusive.
[chaz/openbox] / 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-2007 Dana 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 /* 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);
187 }
188 g_assert(top == NULL);
189
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);
193 return;
194 }
195 }
196
197 /* remove first so we can't run into ourself */
198 it = g_list_find(stacking_list, selected);
199 g_assert(it);
200 stacking_list = g_list_delete_link(stacking_list, it);
201
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);
205
206 if (WINDOW_IS_CLIENT(it->data)) {
207 ObClient *ch = it->data;
208
209 /* only move windows in the same stacking layer */
210 if (ch->layer == selected->layer &&
211 client_search_transient(selected, ch))
212 {
213 if (client_is_direct_child(selected, ch)) {
214 if (ch->modal)
215 modals = g_list_prepend(modals, ch);
216 else
217 trans = g_list_prepend(trans, ch);
218 }
219 else {
220 if (ch->modal)
221 group_modals = g_list_prepend(group_modals, ch);
222 else
223 group_trans = g_list_prepend(group_trans, ch);
224 }
225 stacking_list = g_list_delete_link(stacking_list, it);
226 }
227 }
228 }
229
230 /* put transients of the selected window right above it */
231 wins = g_list_concat(modals, trans);
232 wins = g_list_append(wins, selected);
233
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);
239 group_modals = NULL;
240 }
241 wins = g_list_concat(wins, group_trans);
242 group_trans = NULL;
243 }
244
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 */
247 last = NULL;
248 for (it = g_list_last(stacking_list); it; it = g_list_previous(it))
249 {
250 if (window_layer(it->data) < selected->layer) {
251 last = it;
252 continue;
253 }
254 /* if lowering, stop at the beginning of the layer */
255 if (!raise)
256 break;
257 /* if raising, stop at the end of the layer */
258 if (window_layer(it->data) > selected->layer)
259 break;
260
261 last = it;
262 }
263
264 /* save this position in the stacking list */
265 below = last;
266
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)
271 continue;
272 /* if we reach the end of the layer (how?) then don't go further */
273 if (window_layer(it->data) < selected->layer)
274 break;
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)
279 break;
280 }
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) */
283 if (it == below)
284 break;
285 }
286
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
289 restacking
290
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
293 */
294 above = it ? g_list_previous(it) : g_list_last(stacking_list);
295
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
298 g_list_prepend */
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);
305 }
306
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);
311
312 do_restack(wins, below);
313 g_list_free(wins);
314 }
315
316 void stacking_raise(ObWindow *window)
317 {
318 if (WINDOW_IS_CLIENT(window)) {
319 ObClient *selected;
320 selected = WINDOW_AS_CLIENT(window);
321 restack_windows(selected, TRUE);
322 } else {
323 GList *wins;
324 wins = g_list_append(NULL, window);
325 stacking_list = g_list_remove(stacking_list, window);
326 do_raise(wins);
327 g_list_free(wins);
328 }
329 }
330
331 void stacking_lower(ObWindow *window)
332 {
333 if (WINDOW_IS_CLIENT(window)) {
334 ObClient *selected;
335 selected = WINDOW_AS_CLIENT(window);
336 restack_windows(selected, FALSE);
337 } else {
338 GList *wins;
339 wins = g_list_append(NULL, window);
340 stacking_list = g_list_remove(stacking_list, window);
341 do_lower(wins);
342 g_list_free(wins);
343 }
344 }
345
346 void stacking_below(ObWindow *window, ObWindow *below)
347 {
348 GList *wins, *before;
349
350 if (window_layer(window) != window_layer(below))
351 return;
352
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);
357 g_list_free(wins);
358 }
359
360 void stacking_add(ObWindow *win)
361 {
362 g_assert(screen_support_win != None); /* make sure I dont break this in the
363 future */
364
365 stacking_list = g_list_append(stacking_list, win);
366 stacking_raise(win);
367 }
368
369 static GList *find_highest_relative(ObClient *client)
370 {
371 GList *ret = NULL;
372
373 if (client->transient_for) {
374 GList *it;
375 GSList *top;
376
377 /* get all top level relatives of this client */
378 top = client_search_all_top_parents(client);
379
380 /* go from the top of the stacking order down */
381 for (it = stacking_list; !ret && it; it = g_list_next(it)) {
382 if (WINDOW_IS_CLIENT(it->data)) {
383 ObClient *c = it->data;
384 /* only look at windows in the same layer */
385 if (c->layer == client->layer) {
386 GSList *sit;
387
388 /* go through each top level parent and see it this window
389 is related to them */
390 for (sit = top; !ret && sit; sit = g_slist_next(sit)) {
391 ObClient *topc = sit->data;
392
393 /* are they related ? */
394 if (topc == c || client_search_transient(topc, c))
395 ret = it;
396 }
397 }
398 }
399 }
400 }
401 return ret;
402 }
403
404 void stacking_add_nonintrusive(ObWindow *win)
405 {
406 ObClient *client;
407 GList *it_below = NULL;
408
409 if (!WINDOW_IS_CLIENT(win)) {
410 stacking_add(win); /* no special rules for others */
411 return;
412 }
413
414 client = WINDOW_AS_CLIENT(win);
415
416 /* insert above its highest parent (or its highest child !) */
417 it_below = find_highest_relative(client);
418
419 if (!it_below) {
420 /* nothing to put it directly above, so try find the focused client to
421 put it underneath it */
422 if (focus_client && focus_client->layer == client->layer) {
423 if ((it_below = g_list_find(stacking_list, focus_client)))
424 it_below = it_below->next;
425 }
426 }
427 if (!it_below) {
428 /* there is no window to put this directly above, so put it at the
429 bottom */
430 stacking_list = g_list_prepend(stacking_list, win);
431 stacking_lower(win);
432 } else {
433 /* make sure it's not in the wrong layer though ! */
434 for (; it_below; it_below = g_list_next(it_below))
435 {
436 /* stop when the window is not in a higher layer than the window
437 it is going above (it_below) */
438 if (client->layer >= window_layer(it_below->data))
439 break;
440 }
441 for (; it_below != stacking_list;
442 it_below = g_list_previous(it_below))
443 {
444 /* stop when the window is not in a lower layer than the
445 window it is going under (it_above) */
446 GList *it_above = g_list_previous(it_below);
447 if (client->layer <= window_layer(it_above->data))
448 break;
449 }
450
451 GList *wins = g_list_append(NULL, win);
452 do_restack(wins, it_below);
453 g_list_free(wins);
454 }
455 }
This page took 0.058613 seconds and 5 git commands to generate.