]> Dogcows Code - chaz/openbox/blob - openbox/stacking.c
Rewrite the stacking code. It's a lot faster now, I should think. It's def a more...
[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 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, gboolean group)
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, group);
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 continue;
247 /* if lowering, stop at the beginning of the layer */
248 if (!raise)
249 break;
250 /* if raising, stop at the end of the layer */
251 if (window_layer(it->data) > selected->layer)
252 break;
253
254 last = it;
255 }
256
257 /* save this position in the stacking list */
258 below = last;
259
260 /* find where to put the group transients, start from the top of list */
261 for (it = stacking_list; it; it = g_list_next(it)) {
262 /* skip past higher layers */
263 if (window_layer(it->data) > selected->layer)
264 continue;
265 /* if we reach the end of the layer (how?) then don't go further */
266 if (window_layer(it->data) < selected->layer)
267 break;
268 /* stop when we reach the first window in the group */
269 if (WINDOW_IS_CLIENT(it->data)) {
270 ObClient *c = it->data;
271 if (c->group == selected->group)
272 break;
273 }
274 /* if we don't hit any other group members, stop here because this
275 is where we are putting the selected window (and its children) */
276 if (it == below)
277 break;
278 }
279
280 /* save this position, this is the top of the group of windows between the
281 group transient ones we're restacking and the others up above that we're
282 restacking
283
284 we actually want to save 1 position _above_ that, for for loops to work
285 nicely, so move back one position in the list while saving it
286 */
287 above = it ? g_list_previous(it) : g_list_last(stacking_list);
288
289 /* put the windows inside the gap to the other windows we're stacking
290 into the restacking list, go from the bottom up so that we can use
291 g_list_prepend */
292 if (below) it = g_list_previous(below);
293 else it = g_list_last(stacking_list);
294 for (; it != above; it = next) {
295 next = g_list_previous(it);
296 wins = g_list_prepend(wins, it->data);
297 stacking_list = g_list_delete_link(stacking_list, it);
298 }
299
300 /* group transients go above the rest of the stuff acquired to now */
301 wins = g_list_concat(group_trans, wins);
302 /* group modals go on the very top */
303 wins = g_list_concat(group_modals, wins);
304
305 do_restack(wins, below);
306 g_list_free(wins);
307 }
308
309 void stacking_raise(ObWindow *window, gboolean group)
310 {
311 if (WINDOW_IS_CLIENT(window)) {
312 ObClient *selected;
313 selected = WINDOW_AS_CLIENT(window);
314 restack_windows(selected, TRUE, group);
315 } else {
316 GList *wins;
317 wins = g_list_append(NULL, window);
318 stacking_list = g_list_remove(stacking_list, window);
319 do_raise(wins);
320 g_list_free(wins);
321 }
322 }
323
324 void stacking_lower(ObWindow *window, gboolean group)
325 {
326 if (WINDOW_IS_CLIENT(window)) {
327 ObClient *selected;
328 selected = WINDOW_AS_CLIENT(window);
329 restack_windows(selected, FALSE, group);
330 } else {
331 GList *wins;
332 wins = g_list_append(NULL, window);
333 stacking_list = g_list_remove(stacking_list, window);
334 do_lower(wins);
335 g_list_free(wins);
336 }
337 }
338
339 void stacking_below(ObWindow *window, ObWindow *below)
340 {
341 GList *wins, *before;
342
343 if (window_layer(window) != window_layer(below))
344 return;
345
346 wins = g_list_append(NULL, window);
347 stacking_list = g_list_remove(stacking_list, window);
348 before = g_list_next(g_list_find(stacking_list, below));
349 do_restack(wins, before);
350 g_list_free(wins);
351 }
352
353 void stacking_add(ObWindow *win)
354 {
355 g_assert(screen_support_win != None); /* make sure I dont break this in the
356 future */
357
358 stacking_list = g_list_append(stacking_list, win);
359 stacking_raise(win, FALSE);
360 }
361
362 void stacking_add_nonintrusive(ObWindow *win)
363 {
364 ObClient *client;
365 ObClient *parent = NULL;
366 GList *it_below = NULL;
367
368 if (!WINDOW_IS_CLIENT(win)) {
369 stacking_add(win); /* no special rules for others */
370 return;
371 }
372
373 client = WINDOW_AS_CLIENT(win);
374
375 /* insert above its highest parent */
376 if (client->transient_for) {
377 if (client->transient_for != OB_TRAN_GROUP) {
378 parent = client->transient_for;
379 } else {
380 GSList *sit;
381 GList *it;
382
383 if (client->group)
384 for (it = stacking_list; !parent && it; it = g_list_next(it)) {
385 if ((sit = g_slist_find(client->group->members, it->data)))
386 for (sit = client->group->members; !parent && sit;
387 sit = g_slist_next(sit))
388 {
389 ObClient *c = sit->data;
390 /* checking transient_for prevents infinate loops! */
391 if (sit->data == it->data && !c->transient_for)
392 parent = it->data;
393 }
394 }
395 }
396 }
397
398 if (!(it_below = g_list_find(stacking_list, parent))) {
399 /* no parent to put above, try find the focused client to go
400 under */
401 if (focus_client && focus_client->layer == client->layer) {
402 if ((it_below = g_list_find(stacking_list, focus_client)))
403 it_below = it_below->next;
404 }
405 }
406 if (!it_below) {
407 /* out of ideas, just add it normally... */
408 stacking_add(win);
409 } else {
410 /* make sure it's not in the wrong layer though ! */
411 for (; it_below; it_below = g_list_next(it_below))
412 {
413 /* stop when the window is not in a higher layer than the window
414 it is going above (it_below) */
415 if (client->layer >= window_layer(it_below->data))
416 break;
417 }
418 for (; it_below != stacking_list;
419 it_below = g_list_previous(it_below))
420 {
421 /* stop when the window is not in a lower layer than the
422 window it is going under (it_above) */
423 GList *it_above = g_list_previous(it_below);
424 if (client->layer <= window_layer(it_above->data))
425 break;
426 }
427
428 GList *wins = g_list_append(NULL, win);
429 do_restack(wins, it_below);
430 g_list_free(wins);
431 }
432 }
This page took 0.057059 seconds and 5 git commands to generate.