]> Dogcows Code - chaz/openbox/blob - openbox/stacking.c
let you lower siblings below eachother
[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 #include "debug.h"
29
30 GList *stacking_list = NULL;
31
32 void stacking_set_list()
33 {
34 Window *windows = NULL;
35 GList *it;
36 guint i = 0;
37
38 /* on shutdown, don't update the properties, so that we can read it back
39 in on startup and re-stack the windows as they were before we shut down
40 */
41 if (ob_state() == OB_STATE_EXITING) return;
42
43 /* create an array of the window ids (from bottom to top,
44 reverse order!) */
45 if (stacking_list) {
46 windows = g_new(Window, g_list_length(stacking_list));
47 for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
48 if (WINDOW_IS_CLIENT(it->data))
49 windows[i++] = WINDOW_AS_CLIENT(it->data)->window;
50 }
51 }
52
53 PROP_SETA32(RootWindow(ob_display, ob_screen),
54 net_client_list_stacking, window, (gulong*)windows, i);
55
56 g_free(windows);
57 }
58
59 static void do_restack(GList *wins, GList *before)
60 {
61 GList *it;
62 Window *win;
63 gint i;
64
65 #ifdef DEBUG
66 GList *next;
67 /* pls only restack stuff in the same layer at a time */
68 for (it = wins; it; it = next) {
69 next = g_list_next(it);
70 if (!next) break;
71 g_assert (window_layer(it->data) == window_layer(next->data));
72 }
73 if (before)
74 g_assert(window_layer(it->data) >= window_layer(before->data));
75 #endif
76
77 win = g_new(Window, g_list_length(wins) + 1);
78
79 if (before == stacking_list)
80 win[0] = screen_support_win;
81 else if (!before)
82 win[0] = window_top(g_list_last(stacking_list)->data);
83 else
84 win[0] = window_top(g_list_previous(before)->data);
85
86 for (i = 1, it = wins; it; ++i, it = g_list_next(it)) {
87 win[i] = window_top(it->data);
88 g_assert(win[i] != None); /* better not call stacking shit before
89 setting your top level window value */
90 stacking_list = g_list_insert_before(stacking_list, before, it->data);
91 }
92
93 #ifdef DEBUG
94 /* some debug checking of the stacking list's order */
95 for (it = stacking_list; ; it = next) {
96 next = g_list_next(it);
97 if (!next) break;
98 g_assert(window_layer(it->data) >= window_layer(next->data));
99 }
100 #endif
101
102 XRestackWindows(ob_display, win, i);
103 g_free(win);
104
105 stacking_set_list();
106 }
107
108 static void do_raise(GList *wins)
109 {
110 GList *it;
111 GList *layer[OB_NUM_STACKING_LAYERS] = {NULL};
112 gint i;
113
114 for (it = wins; it; it = g_list_next(it)) {
115 ObStackingLayer l;
116
117 l = window_layer(it->data);
118 layer[l] = g_list_append(layer[l], it->data);
119 }
120
121 it = stacking_list;
122 for (i = OB_NUM_STACKING_LAYERS - 1; i >= 0; --i) {
123 if (layer[i]) {
124 for (; it; it = g_list_next(it)) {
125 /* look for the top of the layer */
126 if (window_layer(it->data) <= (ObStackingLayer) i)
127 break;
128 }
129 do_restack(layer[i], it);
130 g_list_free(layer[i]);
131 }
132 }
133 }
134
135 static void do_lower(GList *wins)
136 {
137 GList *it;
138 GList *layer[OB_NUM_STACKING_LAYERS] = {NULL};
139 gint i;
140
141 for (it = wins; it; it = g_list_next(it)) {
142 ObStackingLayer l;
143
144 l = window_layer(it->data);
145 layer[l] = g_list_append(layer[l], it->data);
146 }
147
148 it = stacking_list;
149 for (i = OB_NUM_STACKING_LAYERS - 1; i >= 0; --i) {
150 if (layer[i]) {
151 for (; it; it = g_list_next(it)) {
152 /* look for the top of the next layer down */
153 if (window_layer(it->data) < (ObStackingLayer) i)
154 break;
155 }
156 do_restack(layer[i], it);
157 g_list_free(layer[i]);
158 }
159 }
160 }
161
162 static void restack_windows(ObClient *selected, gboolean raise)
163 {
164 GList *it, *last, *below, *above, *next;
165 GList *wins = NULL;
166
167 GList *group_modals = NULL;
168 GList *group_trans = NULL;
169 GList *modals = NULL;
170 GList *trans = NULL;
171
172 if (!raise && selected->parents) {
173 GSList *top, *top_it;
174 GSList *top_reorder = NULL;
175
176 /* if it's a transient lowering, lower its parents so that we can lower
177 this window, or it won't move */
178 top = client_search_all_top_parents_layer(selected);
179
180 /* that is, if it has any parents */
181 if (!(top->data == selected && top->next == NULL)) {
182 /* place the window being lowered on the bottom so it'll be
183 below any of its peers that it can */
184 stacking_list = g_list_remove(stacking_list, selected);
185 stacking_list = g_list_append(stacking_list, selected);
186
187 /* go thru stacking list backwards so we can use g_slist_prepend */
188 for (it = g_list_last(stacking_list); it && top;
189 it = g_list_previous(it))
190 if ((top_it = g_slist_find(top, it->data))) {
191 top_reorder = g_slist_prepend(top_reorder, top_it->data);
192 top = g_slist_delete_link(top, top_it);
193 }
194 g_assert(top == NULL);
195
196 /* call restack for each of these to lower them */
197 for (top_it = top_reorder; top_it; top_it = g_slist_next(top_it))
198 restack_windows(top_it->data, raise);
199 return;
200 }
201 }
202
203 /* remove first so we can't run into ourself */
204 it = g_list_find(stacking_list, selected);
205 g_assert(it);
206 stacking_list = g_list_delete_link(stacking_list, it);
207
208 /* go from the bottom of the stacking list up */
209 for (it = g_list_last(stacking_list); it; it = next) {
210 next = g_list_previous(it);
211
212 if (WINDOW_IS_CLIENT(it->data)) {
213 ObClient *ch = it->data;
214
215 /* only move windows in the same stacking layer */
216 if (ch->layer == selected->layer &&
217 client_search_transient(selected, ch))
218 {
219 if (client_is_direct_child(selected, ch)) {
220 if (ch->modal)
221 modals = g_list_prepend(modals, ch);
222 else
223 trans = g_list_prepend(trans, ch);
224 }
225 else {
226 if (ch->modal)
227 group_modals = g_list_prepend(group_modals, ch);
228 else
229 group_trans = g_list_prepend(group_trans, ch);
230 }
231 stacking_list = g_list_delete_link(stacking_list, it);
232 }
233 }
234 }
235
236 /* put transients of the selected window right above it */
237 wins = g_list_concat(modals, trans);
238 wins = g_list_append(wins, selected);
239
240 /* if selected window is transient for group then raise it above others */
241 if (selected->transient_for_group) {
242 /* if it's modal, raise it above those also */
243 if (selected->modal) {
244 wins = g_list_concat(wins, group_modals);
245 group_modals = NULL;
246 }
247 wins = g_list_concat(wins, group_trans);
248 group_trans = NULL;
249 }
250
251 /* find where to put the selected window, start from bottom of list,
252 this is the window below everything we are re-adding to the list */
253 last = NULL;
254 for (it = g_list_last(stacking_list); it; it = g_list_previous(it))
255 {
256 if (window_layer(it->data) < selected->layer) {
257 last = it;
258 continue;
259 }
260 /* if lowering, stop at the beginning of the layer */
261 if (!raise)
262 break;
263 /* if raising, stop at the end of the layer */
264 if (window_layer(it->data) > selected->layer)
265 break;
266
267 last = it;
268 }
269
270 /* save this position in the stacking list */
271 below = last;
272
273 /* find where to put the group transients, start from the top of list */
274 for (it = stacking_list; it; it = g_list_next(it)) {
275 /* skip past higher layers */
276 if (window_layer(it->data) > selected->layer)
277 continue;
278 /* if we reach the end of the layer (how?) then don't go further */
279 if (window_layer(it->data) < selected->layer)
280 break;
281 /* stop when we reach the first window in the group */
282 if (WINDOW_IS_CLIENT(it->data)) {
283 ObClient *c = it->data;
284 if (c->group == selected->group)
285 break;
286 }
287 /* if we don't hit any other group members, stop here because this
288 is where we are putting the selected window (and its children) */
289 if (it == below)
290 break;
291 }
292
293 /* save this position, this is the top of the group of windows between the
294 group transient ones we're restacking and the others up above that we're
295 restacking
296
297 we actually want to save 1 position _above_ that, for for loops to work
298 nicely, so move back one position in the list while saving it
299 */
300 above = it ? g_list_previous(it) : g_list_last(stacking_list);
301
302 /* put the windows inside the gap to the other windows we're stacking
303 into the restacking list, go from the bottom up so that we can use
304 g_list_prepend */
305 if (below) it = g_list_previous(below);
306 else it = g_list_last(stacking_list);
307 for (; it != above; it = next) {
308 next = g_list_previous(it);
309 wins = g_list_prepend(wins, it->data);
310 stacking_list = g_list_delete_link(stacking_list, it);
311 }
312
313 /* group transients go above the rest of the stuff acquired to now */
314 wins = g_list_concat(group_trans, wins);
315 /* group modals go on the very top */
316 wins = g_list_concat(group_modals, wins);
317
318 do_restack(wins, below);
319 g_list_free(wins);
320 }
321
322 void stacking_raise(ObWindow *window)
323 {
324 if (WINDOW_IS_CLIENT(window)) {
325 ObClient *selected;
326 selected = WINDOW_AS_CLIENT(window);
327 restack_windows(selected, TRUE);
328 } else {
329 GList *wins;
330 wins = g_list_append(NULL, window);
331 stacking_list = g_list_remove(stacking_list, window);
332 do_raise(wins);
333 g_list_free(wins);
334 }
335 }
336
337 void stacking_lower(ObWindow *window)
338 {
339 if (WINDOW_IS_CLIENT(window)) {
340 ObClient *selected;
341 selected = WINDOW_AS_CLIENT(window);
342 restack_windows(selected, FALSE);
343 } else {
344 GList *wins;
345 wins = g_list_append(NULL, window);
346 stacking_list = g_list_remove(stacking_list, window);
347 do_lower(wins);
348 g_list_free(wins);
349 }
350 }
351
352 void stacking_below(ObWindow *window, ObWindow *below)
353 {
354 GList *wins, *before;
355
356 if (window_layer(window) != window_layer(below))
357 return;
358
359 wins = g_list_append(NULL, window);
360 stacking_list = g_list_remove(stacking_list, window);
361 before = g_list_next(g_list_find(stacking_list, below));
362 do_restack(wins, before);
363 g_list_free(wins);
364 }
365
366 void stacking_add(ObWindow *win)
367 {
368 g_assert(screen_support_win != None); /* make sure I dont break this in the
369 future */
370
371 stacking_list = g_list_append(stacking_list, win);
372 stacking_raise(win);
373 }
374
375 static GList *find_highest_relative(ObClient *client)
376 {
377 GList *ret = NULL;
378
379 if (client->parents) {
380 GList *it;
381 GSList *top;
382
383 /* get all top level relatives of this client */
384 top = client_search_all_top_parents_layer(client);
385
386 /* go from the top of the stacking order down */
387 for (it = stacking_list; !ret && it; it = g_list_next(it)) {
388 if (WINDOW_IS_CLIENT(it->data)) {
389 ObClient *c = it->data;
390 /* only look at windows in the same layer and that are
391 visible */
392 if (c->layer == client->layer &&
393 !c->iconic &&
394 (c->desktop == client->desktop ||
395 c->desktop == DESKTOP_ALL ||
396 client->desktop == DESKTOP_ALL))
397 {
398 GSList *sit;
399
400 /* go through each top level parent and see it this window
401 is related to them */
402 for (sit = top; !ret && sit; sit = g_slist_next(sit)) {
403 ObClient *topc = sit->data;
404
405 /* are they related ? */
406 if (topc == c || client_search_transient(topc, c))
407 ret = it;
408 }
409 }
410 }
411 }
412 }
413 return ret;
414 }
415
416 void stacking_add_nonintrusive(ObWindow *win)
417 {
418 ObClient *client;
419 GList *it_below = NULL; /* this client will be below us */
420 GList *it_above;
421
422 if (!WINDOW_IS_CLIENT(win)) {
423 stacking_add(win); /* no special rules for others */
424 return;
425 }
426
427 client = WINDOW_AS_CLIENT(win);
428
429 /* insert above its highest parent (or its highest child !) */
430 it_below = find_highest_relative(client);
431
432 if (!it_below) {
433 /* nothing to put it directly above, so try find the focused client
434 to put it underneath it */
435 if (focus_client && client != focus_client &&
436 focus_client->layer == client->layer)
437 {
438 it_below = g_list_find(stacking_list, focus_client);
439 /* this can give NULL, but it means the focused window is on the
440 bottom of the stacking order, so go to the bottom in that case,
441 below it */
442 it_below = g_list_next(it_below);
443 }
444 else {
445 /* There is no window to put this directly above, so put it at the
446 top, so you know it is there.
447
448 It used to do this only if the window was focused and lower
449 it otherwise.
450
451 We also put it at the top not the bottom to fix a bug with
452 fullscreen windows. When focusLast is off and followsMouse is
453 on, when you switch desktops, the fullscreen window loses
454 focus and goes into its lower layer. If this puts it at the
455 bottom then when you come back to the desktop, the window is
456 at the bottom and won't get focus back.
457 */
458 it_below = stacking_list;
459 }
460 }
461
462 /* make sure it's not in the wrong layer though ! */
463 for (; it_below; it_below = g_list_next(it_below)) {
464 /* stop when the window is not in a higher layer than the window
465 it is going above (it_below) */
466 if (client->layer >= window_layer(it_below->data))
467 break;
468 }
469 for (; it_below != stacking_list; it_below = it_above) {
470 /* stop when the window is not in a lower layer than the
471 window it is going under (it_above) */
472 it_above = it_below ?
473 g_list_previous(it_below) : g_list_last(stacking_list);
474 if (client->layer <= window_layer(it_above->data))
475 break;
476 }
477
478 GList *wins = g_list_append(NULL, win);
479 do_restack(wins, it_below);
480 g_list_free(wins);
481 }
482
483 /*! Returns TRUE if client is occluded by the sibling. If sibling is NULL it
484 tries against all other clients.
485 */
486 static gboolean stacking_occluded(ObClient *client, ObClient *sibling)
487 {
488 GList *it;
489 gboolean occluded = FALSE;
490 gboolean found = FALSE;
491
492 /* no need for any looping in this case */
493 if (sibling && client->layer != sibling->layer)
494 return occluded;
495
496 for (it = stacking_list; it;
497 it = (found ? g_list_previous(it) :g_list_next(it)))
498 if (WINDOW_IS_CLIENT(it->data)) {
499 ObClient *c = it->data;
500 if (found && !c->iconic &&
501 (c->desktop == DESKTOP_ALL || client->desktop == DESKTOP_ALL ||
502 c->desktop == client->desktop))
503 {
504 if (RECT_INTERSECTS_RECT(c->frame->area, client->frame->area))
505 {
506 if (sibling != NULL) {
507 if (c == sibling) {
508 occluded = TRUE;
509 break;
510 }
511 }
512 else if (c->layer == client->layer) {
513 occluded = TRUE;
514 break;
515 }
516 else if (c->layer > client->layer)
517 break; /* we past its layer */
518 }
519 }
520 else if (c == client)
521 found = TRUE;
522 }
523 return occluded;
524 }
525
526 /*! Returns TRUE if client occludes the sibling. If sibling is NULL it tries
527 against all other clients.
528 */
529 static gboolean stacking_occludes(ObClient *client, ObClient *sibling)
530 {
531 GList *it;
532 gboolean occludes = FALSE;
533 gboolean found = FALSE;
534
535 /* no need for any looping in this case */
536 if (sibling && client->layer != sibling->layer)
537 return occludes;
538
539 for (it = stacking_list; it; it = g_list_next(it))
540 if (WINDOW_IS_CLIENT(it->data)) {
541 ObClient *c = it->data;
542 if (found && !c->iconic &&
543 (c->desktop == DESKTOP_ALL || client->desktop == DESKTOP_ALL ||
544 c->desktop == client->desktop))
545 {
546 if (RECT_INTERSECTS_RECT(c->frame->area, client->frame->area))
547 {
548 if (sibling != NULL) {
549 if (c == sibling) {
550 occludes = TRUE;
551 break;
552 }
553 }
554 else if (c->layer == client->layer) {
555 occludes = TRUE;
556 break;
557 }
558 else if (c->layer < client->layer)
559 break; /* we past its layer */
560 }
561 }
562 else if (c == client)
563 found = TRUE;
564 }
565 return occludes;
566 }
567
568 gboolean stacking_restack_request(ObClient *client, ObClient *sibling,
569 gint detail, gboolean activate)
570 {
571 gboolean ret = FALSE;
572
573 if (sibling && ((client->desktop != sibling->desktop &&
574 client->desktop != DESKTOP_ALL &&
575 sibling->desktop != DESKTOP_ALL) ||
576 sibling->iconic))
577 {
578 ob_debug("Setting restack sibling to NULL, they are not on the same "
579 "desktop or it is iconified\n");
580 sibling = NULL;
581 }
582
583 switch (detail) {
584 case Below:
585 ob_debug("Restack request Below for client %s sibling %s\n",
586 client->title, sibling ? sibling->title : "(all)");
587 /* just lower it */
588 stacking_lower(CLIENT_AS_WINDOW(client));
589 ret = TRUE;
590 break;
591 case BottomIf:
592 ob_debug("Restack request BottomIf for client %s sibling "
593 "%s\n",
594 client->title, sibling ? sibling->title : "(all)");
595 /* if this client occludes sibling (or anything if NULL), then
596 lower it to the bottom */
597 if (stacking_occludes(client, sibling)) {
598 stacking_lower(CLIENT_AS_WINDOW(client));
599 ret = TRUE;
600 }
601 break;
602 case Above:
603 ob_debug("Restack request Above for client %s sibling %s\n",
604 client->title, sibling ? sibling->title : "(all)");
605 if (activate && !client->iconic && client_normal(client))
606 /* use user=TRUE because it is impossible to get a timestamp
607 for this */
608 client_activate(client, FALSE, TRUE);
609 else
610 stacking_raise(CLIENT_AS_WINDOW(client));
611 ret = TRUE;
612 break;
613 case TopIf:
614 ob_debug("Restack request TopIf for client %s sibling %s\n",
615 client->title, sibling ? sibling->title : "(all)");
616 if (stacking_occluded(client, sibling)) {
617 if (activate && !client->iconic && client_normal(client))
618 /* use user=TRUE because it is impossible to get a timestamp
619 for this */
620 client_activate(client, FALSE, TRUE);
621 else
622 stacking_raise(CLIENT_AS_WINDOW(client));
623 ret = TRUE;
624 }
625 break;
626 case Opposite:
627 ob_debug("Restack request Opposite for client %s sibling "
628 "%s\n",
629 client->title, sibling ? sibling->title : "(all)");
630 if (stacking_occluded(client, sibling)) {
631 if (activate && !client->iconic && client_normal(client))
632 /* use user=TRUE because it is impossible to get a timestamp
633 for this */
634 client_activate(client, FALSE, TRUE);
635 else
636 stacking_raise(CLIENT_AS_WINDOW(client));
637 ret = TRUE;
638 }
639 else if (stacking_occludes(client, sibling)) {
640 stacking_lower(CLIENT_AS_WINDOW(client));
641 ret = TRUE;
642 }
643 break;
644 }
645 return ret;
646 }
This page took 0.065903 seconds and 5 git commands to generate.