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