]> Dogcows Code - chaz/openbox/blob - openbox/stacking.c
comment typo
[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 "event.h"
29 #include "debug.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 PROP_SETA32(RootWindow(ob_display, ob_screen),
59 net_client_list_stacking, window, (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(ob_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(ob_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(ob_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 /* remove first so we can't run into ourself */
225 it = g_list_find(stacking_list, selected);
226 g_assert(it);
227 stacking_list = g_list_delete_link(stacking_list, it);
228
229 /* go from the bottom of the stacking list up. don't move any other windows
230 when lowering, we call this for each window independently */
231 if (raise) {
232 for (it = g_list_last(stacking_list); it; it = next) {
233 next = g_list_previous(it);
234
235 if (WINDOW_IS_CLIENT(it->data)) {
236 ObClient *ch = it->data;
237
238 /* only move windows in the same stacking layer */
239 if (ch->layer == selected->layer &&
240 client_search_transient(selected, ch))
241 {
242 if (client_is_direct_child(selected, ch)) {
243 if (ch->modal)
244 modals = g_list_prepend(modals, ch);
245 else
246 trans = g_list_prepend(trans, ch);
247 }
248 else {
249 if (ch->modal)
250 group_modals = g_list_prepend(group_modals, ch);
251 else
252 group_trans = g_list_prepend(group_trans, ch);
253 }
254 stacking_list = g_list_delete_link(stacking_list, it);
255 }
256 }
257 }
258 }
259
260 /* put transients of the selected window right above it */
261 wins = g_list_concat(modals, trans);
262 wins = g_list_append(wins, selected);
263
264 /* if selected window is transient for group then raise it above others */
265 if (selected->transient_for_group) {
266 /* if it's modal, raise it above those also */
267 if (selected->modal) {
268 wins = g_list_concat(wins, group_modals);
269 group_modals = NULL;
270 }
271 wins = g_list_concat(wins, group_trans);
272 group_trans = NULL;
273 }
274
275 /* find where to put the selected window, start from bottom of list,
276 this is the window below everything we are re-adding to the list */
277 last = NULL;
278 for (it = g_list_last(stacking_list); it; it = g_list_previous(it))
279 {
280 if (window_layer(it->data) < selected->layer) {
281 last = it;
282 continue;
283 }
284 /* if lowering, stop at the beginning of the layer */
285 if (!raise)
286 break;
287 /* if raising, stop at the end of the layer */
288 if (window_layer(it->data) > selected->layer)
289 break;
290
291 last = it;
292 }
293
294 /* save this position in the stacking list */
295 below = last;
296
297 /* find where to put the group transients, start from the top of list */
298 for (it = stacking_list; it; it = g_list_next(it)) {
299 /* skip past higher layers */
300 if (window_layer(it->data) > selected->layer)
301 continue;
302 /* if we reach the end of the layer (how?) then don't go further */
303 if (window_layer(it->data) < selected->layer)
304 break;
305 /* stop when we reach the first window in the group */
306 if (WINDOW_IS_CLIENT(it->data)) {
307 ObClient *c = it->data;
308 if (c->group == selected->group)
309 break;
310 }
311 /* if we don't hit any other group members, stop here because this
312 is where we are putting the selected window (and its children) */
313 if (it == below)
314 break;
315 }
316
317 /* save this position, this is the top of the group of windows between the
318 group transient ones we're restacking and the others up above that we're
319 restacking
320
321 we actually want to save 1 position _above_ that, for for loops to work
322 nicely, so move back one position in the list while saving it
323 */
324 above = it ? g_list_previous(it) : g_list_last(stacking_list);
325
326 /* put the windows inside the gap to the other windows we're stacking
327 into the restacking list, go from the bottom up so that we can use
328 g_list_prepend */
329 if (below) it = g_list_previous(below);
330 else it = g_list_last(stacking_list);
331 for (; it != above; it = next) {
332 next = g_list_previous(it);
333 wins = g_list_prepend(wins, it->data);
334 stacking_list = g_list_delete_link(stacking_list, it);
335 }
336
337 /* group transients go above the rest of the stuff acquired to now */
338 wins = g_list_concat(group_trans, wins);
339 /* group modals go on the very top */
340 wins = g_list_concat(group_modals, wins);
341
342 do_restack(wins, below);
343 g_list_free(wins);
344
345 /* lower our parents after us, so they go below us */
346 if (!raise && selected->parents) {
347 GSList *parents_copy, *sit;
348 GSList *reorder = NULL;
349
350 parents_copy = g_slist_copy(selected->parents);
351
352 /* go thru stacking list backwards so we can use g_slist_prepend */
353 for (it = g_list_last(stacking_list); it && parents_copy;
354 it = g_list_previous(it))
355 if ((sit = g_slist_find(parents_copy, it->data))) {
356 reorder = g_slist_prepend(reorder, sit->data);
357 parents_copy = g_slist_delete_link(parents_copy, sit);
358 }
359 g_assert(parents_copy == NULL);
360
361 /* call restack for each of these to lower them */
362 for (sit = reorder; sit; sit = g_slist_next(sit))
363 restack_windows(sit->data, raise);
364 }
365 }
366
367 void stacking_raise(ObWindow *window)
368 {
369 if (WINDOW_IS_CLIENT(window)) {
370 ObClient *selected;
371 selected = WINDOW_AS_CLIENT(window);
372 restack_windows(selected, TRUE);
373 } else {
374 GList *wins;
375 wins = g_list_append(NULL, window);
376 stacking_list = g_list_remove(stacking_list, window);
377 do_raise(wins);
378 g_list_free(wins);
379 }
380 }
381
382 void stacking_lower(ObWindow *window)
383 {
384 if (WINDOW_IS_CLIENT(window)) {
385 ObClient *selected;
386 selected = WINDOW_AS_CLIENT(window);
387 restack_windows(selected, FALSE);
388 } else {
389 GList *wins;
390 wins = g_list_append(NULL, window);
391 stacking_list = g_list_remove(stacking_list, window);
392 do_lower(wins);
393 g_list_free(wins);
394 }
395 }
396
397 void stacking_below(ObWindow *window, ObWindow *below)
398 {
399 GList *wins, *before;
400
401 if (window_layer(window) != window_layer(below))
402 return;
403
404 wins = g_list_append(NULL, window);
405 stacking_list = g_list_remove(stacking_list, window);
406 before = g_list_next(g_list_find(stacking_list, below));
407 do_restack(wins, before);
408 g_list_free(wins);
409 }
410
411 void stacking_add(ObWindow *win)
412 {
413 g_assert(screen_support_win != None); /* make sure I dont break this in the
414 future */
415
416 stacking_list = g_list_append(stacking_list, win);
417 stacking_raise(win);
418 }
419
420 static GList *find_highest_relative(ObClient *client)
421 {
422 GList *ret = NULL;
423
424 if (client->parents) {
425 GList *it;
426 GSList *top;
427
428 /* get all top level relatives of this client */
429 top = client_search_all_top_parents_layer(client);
430
431 /* go from the top of the stacking order down */
432 for (it = stacking_list; !ret && it; it = g_list_next(it)) {
433 if (WINDOW_IS_CLIENT(it->data)) {
434 ObClient *c = it->data;
435 /* only look at windows in the same layer and that are
436 visible */
437 if (c->layer == client->layer &&
438 !c->iconic &&
439 (c->desktop == client->desktop ||
440 c->desktop == DESKTOP_ALL ||
441 client->desktop == DESKTOP_ALL))
442 {
443 GSList *sit;
444
445 /* go through each top level parent and see it this window
446 is related to them */
447 for (sit = top; !ret && sit; sit = g_slist_next(sit)) {
448 ObClient *topc = sit->data;
449
450 /* are they related ? */
451 if (topc == c || client_search_transient(topc, c))
452 ret = it;
453 }
454 }
455 }
456 }
457 }
458 return ret;
459 }
460
461 void stacking_add_nonintrusive(ObWindow *win)
462 {
463 ObClient *client;
464 GList *it_below = NULL; /* this client will be below us */
465 GList *it_above;
466 GList *wins;
467
468 if (!WINDOW_IS_CLIENT(win)) {
469 stacking_add(win); /* no special rules for others */
470 return;
471 }
472
473 client = WINDOW_AS_CLIENT(win);
474
475 /* insert above its highest parent (or its highest child !) */
476 it_below = find_highest_relative(client);
477
478 if (!it_below) {
479 /* nothing to put it directly above, so try find the focused client
480 to put it underneath it */
481 if (focus_client && client != focus_client &&
482 focus_client->layer == client->layer)
483 {
484 it_below = g_list_find(stacking_list, focus_client);
485 /* this can give NULL, but it means the focused window is on the
486 bottom of the stacking order, so go to the bottom in that case,
487 below it */
488 it_below = g_list_next(it_below);
489 }
490 else {
491 /* There is no window to put this directly above, so put it at the
492 top, so you know it is there.
493
494 It used to do this only if the window was focused and lower
495 it otherwise.
496
497 We also put it at the top not the bottom to fix a bug with
498 fullscreen windows. When focusLast is off and followsMouse is
499 on, when you switch desktops, the fullscreen window loses
500 focus and goes into its lower layer. If this puts it at the
501 bottom then when you come back to the desktop, the window is
502 at the bottom and won't get focus back.
503 */
504 it_below = stacking_list;
505 }
506 }
507
508 /* make sure it's not in the wrong layer though ! */
509 for (; it_below; it_below = g_list_next(it_below)) {
510 /* stop when the window is not in a higher layer than the window
511 it is going above (it_below) */
512 if (client->layer >= window_layer(it_below->data))
513 break;
514 }
515 for (; it_below != stacking_list; it_below = it_above) {
516 /* stop when the window is not in a lower layer than the
517 window it is going under (it_above) */
518 it_above = it_below ?
519 g_list_previous(it_below) : g_list_last(stacking_list);
520 if (client->layer <= window_layer(it_above->data))
521 break;
522 }
523
524 wins = g_list_append(NULL, win);
525 do_restack(wins, it_below);
526 g_list_free(wins);
527 }
528
529 /*! Returns TRUE if client is occluded by the sibling. If sibling is NULL it
530 tries against all other clients.
531 */
532 static gboolean stacking_occluded(ObClient *client, ObClient *sibling)
533 {
534 GList *it;
535 gboolean occluded = FALSE;
536 gboolean found = FALSE;
537
538 /* no need for any looping in this case */
539 if (sibling && client->layer != sibling->layer)
540 return occluded;
541
542 for (it = stacking_list; it;
543 it = (found ? g_list_previous(it) :g_list_next(it)))
544 if (WINDOW_IS_CLIENT(it->data)) {
545 ObClient *c = it->data;
546 if (found && !c->iconic &&
547 (c->desktop == DESKTOP_ALL || client->desktop == DESKTOP_ALL ||
548 c->desktop == client->desktop) &&
549 !client_search_transient(client, c))
550 {
551 if (RECT_INTERSECTS_RECT(c->frame->area, client->frame->area))
552 {
553 if (sibling != NULL) {
554 if (c == sibling) {
555 occluded = TRUE;
556 break;
557 }
558 }
559 else if (c->layer == client->layer) {
560 occluded = TRUE;
561 break;
562 }
563 else if (c->layer > client->layer)
564 break; /* we past its layer */
565 }
566 }
567 else if (c == client)
568 found = TRUE;
569 }
570 return occluded;
571 }
572
573 /*! Returns TRUE if client occludes the sibling. If sibling is NULL it tries
574 against all other clients.
575 */
576 static gboolean stacking_occludes(ObClient *client, ObClient *sibling)
577 {
578 GList *it;
579 gboolean occludes = FALSE;
580 gboolean found = FALSE;
581
582 /* no need for any looping in this case */
583 if (sibling && client->layer != sibling->layer)
584 return occludes;
585
586 for (it = stacking_list; it; it = g_list_next(it))
587 if (WINDOW_IS_CLIENT(it->data)) {
588 ObClient *c = it->data;
589 if (found && !c->iconic &&
590 (c->desktop == DESKTOP_ALL || client->desktop == DESKTOP_ALL ||
591 c->desktop == client->desktop) &&
592 !client_search_transient(c, client))
593 {
594 if (RECT_INTERSECTS_RECT(c->frame->area, client->frame->area))
595 {
596 if (sibling != NULL) {
597 if (c == sibling) {
598 occludes = TRUE;
599 break;
600 }
601 }
602 else if (c->layer == client->layer) {
603 occludes = TRUE;
604 break;
605 }
606 else if (c->layer < client->layer)
607 break; /* we past its layer */
608 }
609 }
610 else if (c == client)
611 found = TRUE;
612 }
613 return occludes;
614 }
615
616 gboolean stacking_restack_request(ObClient *client, ObClient *sibling,
617 gint detail)
618 {
619 gboolean ret = FALSE;
620
621 if (sibling && ((client->desktop != sibling->desktop &&
622 client->desktop != DESKTOP_ALL &&
623 sibling->desktop != DESKTOP_ALL) ||
624 sibling->iconic))
625 {
626 ob_debug("Setting restack sibling to NULL, they are not on the same "
627 "desktop or it is iconified\n");
628 sibling = NULL;
629 }
630
631 switch (detail) {
632 case Below:
633 ob_debug("Restack request Below for client %s sibling %s\n",
634 client->title, sibling ? sibling->title : "(all)");
635 /* just lower it */
636 stacking_lower(CLIENT_AS_WINDOW(client));
637 ret = TRUE;
638 break;
639 case BottomIf:
640 ob_debug("Restack request BottomIf for client %s sibling "
641 "%s\n",
642 client->title, sibling ? sibling->title : "(all)");
643 /* if this client occludes sibling (or anything if NULL), then
644 lower it to the bottom */
645 if (stacking_occludes(client, sibling)) {
646 stacking_lower(CLIENT_AS_WINDOW(client));
647 ret = TRUE;
648 }
649 break;
650 case Above:
651 ob_debug("Restack request Above for client %s sibling %s\n",
652 client->title, sibling ? sibling->title : "(all)");
653 stacking_raise(CLIENT_AS_WINDOW(client));
654 ret = TRUE;
655 break;
656 case TopIf:
657 ob_debug("Restack request TopIf for client %s sibling %s\n",
658 client->title, sibling ? sibling->title : "(all)");
659 if (stacking_occluded(client, sibling)) {
660 stacking_raise(CLIENT_AS_WINDOW(client));
661 ret = TRUE;
662 }
663 break;
664 case Opposite:
665 ob_debug("Restack request Opposite for client %s sibling "
666 "%s\n",
667 client->title, sibling ? sibling->title : "(all)");
668 if (stacking_occluded(client, sibling)) {
669 stacking_raise(CLIENT_AS_WINDOW(client));
670 ret = TRUE;
671 }
672 else if (stacking_occludes(client, sibling)) {
673 stacking_lower(CLIENT_AS_WINDOW(client));
674 ret = TRUE;
675 }
676 break;
677 }
678 return ret;
679 }
This page took 0.062717 seconds and 4 git commands to generate.