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