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