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