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