]> Dogcows Code - chaz/openbox/blob - openbox/stacking.c
fix a mem leak
[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 /*! 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 }
This page took 0.066672 seconds and 4 git commands to generate.