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