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