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