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