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