NEWS/Changelog for previous commit.
[fvwm.git] / fvwm / stack.c
blob6783fe905877ee77d8808d98f6d154fc2027731a
1 /* -*-c-*- */
2 /* This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 /* ---------------------------- included header files ---------------------- */
19 #include "config.h"
20 #include <stdio.h>
21 #include <signal.h>
22 #include <stdarg.h>
23 #include <limits.h>
25 #include "libs/ftime.h"
26 #include "libs/fvwmlib.h"
27 #include "libs/gravity.h"
28 #include "libs/Grab.h"
29 #include "libs/Parse.h"
30 #include "libs/defaults.h"
31 #include "fvwm.h"
32 #include "externs.h"
33 #include "cursor.h"
34 #include "functions.h"
35 #include "bindings.h"
36 #include "misc.h"
37 #include "screen.h"
38 #include "module_list.h"
39 #include "module_interface.h"
40 #include "focus.h"
41 #include "stack.h"
42 #include "events.h"
43 #include "borders.h"
44 #include "virtual.h"
45 #include "geometry.h"
46 #include "icons.h"
47 #include "gnome.h"
48 #include "ewmh.h"
49 #include "frame.h"
51 /* ---------------------------- local definitions -------------------------- */
53 /* If more than this many transients are in a single branch of a transient
54 * tree, they will end up in more or less random stacking order. */
55 #define MAX_TRANSIENTS_IN_BRANCH 200000
56 /* Same for total levels of transients. */
57 #define MAX_TRANSIENT_LEVELS 10000
58 /* This number must fit in a signed int! */
59 #define LOWER_PENALTY (MAX_TRANSIENTS_IN_BRANCH * MAX_TRANSIENT_LEVELS)
61 /* ---------------------------- local macros ------------------------------- */
63 /* ---------------------------- imports ------------------------------------ */
65 /* ---------------------------- included code files ------------------------ */
67 /* ---------------------------- local types -------------------------------- */
69 typedef enum
71 SM_RAISE = MARK_RAISE,
72 SM_LOWER = MARK_LOWER,
73 SM_RESTACK = MARK_RESTACK
74 } stack_mode_t;
76 /* ---------------------------- forward declarations ----------------------- */
78 static void __raise_or_lower_window(
79 FvwmWindow *t, stack_mode_t mode, Bool allow_recursion,
80 Bool is_new_window, Bool is_client_request);
81 static void raise_or_lower_window(
82 FvwmWindow *t, stack_mode_t mode, Bool allow_recursion,
83 Bool is_new_window, Bool is_client_request);
84 #if 0
85 static void ResyncFvwmStackRing(void);
86 #endif
87 static void ResyncXStackingOrder(void);
88 static void BroadcastRestack(FvwmWindow *s1, FvwmWindow *s2);
89 static Bool is_above_unmanaged(FvwmWindow *fw, Window *umtop);
90 static int collect_transients_recursive(
91 FvwmWindow *t, FvwmWindow *list_head, int layer, stack_mode_t mode,
92 Bool do_include_target_window);
94 /* ---------------------------- local variables ---------------------------- */
96 /* ---------------------------- exported variables (globals) --------------- */
98 /* ---------------------------- local functions ---------------------------- */
100 #define DEBUG_STACK_RING 1
101 #ifdef DEBUG_STACK_RING
102 /* debugging function */
103 static void dump_stack_ring(void)
105 FvwmWindow *t1;
107 if (!debugging_stack_ring)
109 return;
111 XBell(dpy, 0);
112 fprintf(stderr,"dumping stack ring:\n");
113 for (
114 t1 = Scr.FvwmRoot.stack_next; t1 != &Scr.FvwmRoot;
115 t1 = t1->stack_next)
117 fprintf(stderr," l=%d fw=%p f=0x%08x '%s'\n", t1->layer,
118 t1, (int)FW_W_FRAME(t1), t1->name.name);
121 return;
124 /* debugging function */
125 void verify_stack_ring_consistency(void)
127 Window root, parent, *children;
128 unsigned int nchildren;
129 int i;
130 FvwmWindow *t1, *t2;
131 int last_layer;
132 int last_index;
134 if (!debugging_stack_ring)
136 return;
138 XFlush(dpy);
139 t2 = Scr.FvwmRoot.stack_next;
140 if (t2 == &Scr.FvwmRoot)
142 return;
144 last_layer = t2->layer;
146 for (
147 t1 = t2->stack_next; t1 != &Scr.FvwmRoot;
148 t2 = t1, t1 = t1->stack_next)
150 if (t1->layer > last_layer)
152 fprintf(
153 stderr,
154 "vsrc: stack ring is corrupt! '%s' (layer %d)"
155 " is above '%s' (layer %d/%d)\n",
156 t1->name.name, t1->layer, t2->name.name,
157 t2->layer, last_layer);
158 dump_stack_ring();
159 return;
161 last_layer = t1->layer;
163 t2 = &Scr.FvwmRoot;
164 for (
165 t1 = t2->stack_next; t1 != &Scr.FvwmRoot;
166 t2 = t1, t1 = t1->stack_next)
168 if (t1->stack_prev != t2)
170 break;
173 if (t1 != &Scr.FvwmRoot || t1->stack_prev != t2)
175 fprintf(
176 stderr,
177 "vsrc: stack ring is corrupt -"
178 " fvwm will probably crash! %p -> %p but %p <- %p",
179 t2, t1, t1->stack_prev, t1);
180 dump_stack_ring();
181 return;
183 MyXGrabServer(dpy);
184 if (!XQueryTree(dpy, Scr.Root, &root, &parent, &children, &nchildren))
186 MyXUngrabServer(dpy);
187 return;
190 last_index = nchildren;
191 for (
192 t1 = Scr.FvwmRoot.stack_next; t1 != &Scr.FvwmRoot;
193 t1 = t1->stack_next)
195 /* find window in window list */
196 for (
197 i = 0; i < nchildren && FW_W_FRAME(t1) != children[i];
198 i++)
200 /* nothing to do here */
202 if (i == nchildren)
204 fprintf(
205 stderr,"vsrc: window already died:"
206 " fw=%p w=0x%08x '%s'\n",
207 t1, (int)FW_W_FRAME(t1), t1->name.name);
209 else if (i >= last_index)
211 fprintf(
212 stderr, "vsrc: window is at wrong position"
213 " in stack ring: fw=%p f=0x%08x '%s'\n",
214 t1, (int)FW_W_FRAME(t1),
215 t1->name.name);
216 dump_stack_ring();
217 fprintf(stderr,"dumping X stacking order:\n");
218 for (i = nchildren; i-- > 0; )
220 for (
221 t1 = Scr.FvwmRoot.stack_next;
222 t1 != &Scr.FvwmRoot;
223 t1 = t1->stack_next)
225 /* only dump frame windows */
226 if (FW_W_FRAME(t1) == children[i])
228 fprintf(
229 stderr, " f=0x%08x\n",
230 (int)children[i]);
231 break;
235 MyXUngrabServer(dpy);
236 XFree(children);
237 return;
239 last_index = i;
241 MyXUngrabServer(dpy);
242 XFree(children);
244 return;
246 #endif
248 /* Add a whole ring of windows. The list_head itself will not be added. */
249 static void add_windowlist_to_stack_ring_after(
250 FvwmWindow *list_head, FvwmWindow *add_after_win)
252 add_after_win->stack_next->stack_prev = list_head->stack_prev;
253 list_head->stack_prev->stack_next = add_after_win->stack_next;
254 add_after_win->stack_next = list_head->stack_next;
255 list_head->stack_next->stack_prev = add_after_win;
257 return;
260 static FvwmWindow *get_transientfor_top_fvwmwindow(FvwmWindow *t)
262 FvwmWindow *s;
264 s = t;
265 while (s && IS_TRANSIENT(s) && DO_STACK_TRANSIENT_PARENT(s))
267 s = get_transientfor_fvwmwindow(s);
268 if (s)
270 t = s;
274 return t;
278 * Raise a target and all higher fvwm-managed windows above any
279 * override_redirects:
280 * - locate the highest override_redirect above our target
281 * - put all the FvwmWindows from the target to the highest FvwmWindow
282 * below the highest override_redirect in the restack list
283 * - configure our target window above the override_redirect sibling,
284 * and restack.
286 static void raise_over_unmanaged(FvwmWindow *t)
288 int i;
289 Window OR_Above = None;
290 Window *wins;
291 int count = 0;
292 FvwmWindow *t2 = NULL;
293 unsigned int flags;
294 XWindowChanges changes;
298 * Locate the highest override_redirect window above our target, and
299 * the highest of our windows below it.
301 * Count the windows we need to restack, then build the stack list.
303 if (!is_above_unmanaged(t, &OR_Above))
305 for (
306 count = 0, t2 = Scr.FvwmRoot.stack_next;
307 t2 != &Scr.FvwmRoot; t2 = t2->stack_next)
309 count++;
310 count += get_visible_icon_window_count(t2);
311 if (t2 == t)
313 break;
317 if (count > 0)
319 wins = (Window*) safemalloc (count * sizeof (Window));
320 for (
321 i = 0, t2 = Scr.FvwmRoot.stack_next;
322 t2 != &Scr.FvwmRoot; t2 = t2->stack_next)
324 wins[i++] = FW_W_FRAME(t2);
325 if (IS_ICONIFIED(t2) &&
326 ! IS_ICON_SUPPRESSED(t2))
328 if (FW_W_ICON_TITLE(t2) != None)
330 wins[i++] = FW_W_ICON_TITLE(t2);
332 if (FW_W_ICON_PIXMAP(t2) != None)
334 wins[i++] =
335 FW_W_ICON_PIXMAP(t2);
338 if (t2 == t)
340 break;
344 memset(&changes, '\0', sizeof(changes));
345 changes.sibling = OR_Above;
346 changes.stack_mode = Above;
347 flags = CWSibling|CWStackMode;
349 XConfigureWindow(
350 dpy, FW_W_FRAME(t)/*topwin*/, flags, &changes);
351 if (i > 1)
353 XRestackWindows(dpy, wins, i);
355 free (wins);
357 }/* end - we found an OR above our target */
359 return;
362 static Bool __is_restack_transients_needed(
363 FvwmWindow *t, stack_mode_t mode)
365 if (DO_RAISE_TRANSIENT(t))
367 if (mode == SM_RAISE || mode == SM_RESTACK)
369 return True;
372 if (DO_LOWER_TRANSIENT(t))
374 if (mode == SM_LOWER || mode == SM_RESTACK)
376 return True;
380 return False;
383 static Bool __must_move_transients(
384 FvwmWindow *t, stack_mode_t mode)
386 if (IS_ICONIFIED(t))
388 return False;
390 /* raise */
391 if (__is_restack_transients_needed(t, mode) == True)
393 Bool scanning_above_window = True;
394 FvwmWindow *q;
396 for (
397 q = Scr.FvwmRoot.stack_next;
398 q != &Scr.FvwmRoot && t->layer <= q->layer;
399 q = q->stack_next)
401 if (t->layer < q->layer)
403 /* We're not interested in higher layers. */
404 continue;
406 else if (mode == SM_RESTACK && IS_TRANSIENT(q) &&
407 FW_W_TRANSIENTFOR(q) == FW_W(t))
409 return True;
411 else if (t == q)
413 /* We found our window. All further transients
414 * are below it. */
415 scanning_above_window = False;
417 else if (IS_TRANSIENT(q) &&
418 FW_W_TRANSIENTFOR(q) == FW_W(t))
420 return True;
422 else if (scanning_above_window && mode == SM_RAISE)
424 /* raise: The window is not raised, so itself
425 * and all transients will be raised. */
426 return True;
431 return False;
434 static Window __get_stacking_sibling(FvwmWindow *fw, Bool do_stack_below)
436 Window w;
438 /* default to frame window */
439 w = FW_W_FRAME(fw);
440 if (IS_ICONIFIED(fw) && do_stack_below == True)
442 /* override with icon windows when stacking below */
443 if (FW_W_ICON_PIXMAP(fw) != None)
445 w = FW_W_ICON_PIXMAP(fw);
447 else if (FW_W_ICON_TITLE(fw) != None)
449 w = FW_W_ICON_TITLE(fw);
453 return w;
456 static void __sort_transient_ring(FvwmWindow *ring)
458 FvwmWindow *s;
459 FvwmWindow *t;
460 FvwmWindow *u;
461 FvwmWindow *prev;
463 if (ring->stack_next->stack_next == ring)
465 /* only one or zero windows */
466 return;
468 /* Implementation note: this sorting algorithm is about the most
469 * inefficient possible. It just swaps the position of two adjacent
470 * windows in the ring if they are in the wrong order. Since
471 * transient windows are rare, this should not cause any notable
472 * performance hit. Because it is important that the order of windows
473 * with the same key is not changed, we can not just use qsort() here.
475 for (
476 t = ring->stack_next, prev = ring; t->stack_next != ring;
477 prev = t->stack_prev)
479 s = t->stack_next;
480 if (t->scratch.i < s->scratch.i)
482 /* swap windows */
483 u = s->stack_next;
484 s->stack_next = t;
485 t->stack_next = u;
486 u = t->stack_prev;
487 t->stack_prev = s;
488 s->stack_prev = u;
489 s->stack_prev->stack_next = s;
490 t->stack_next->stack_prev = t;
491 if (prev != ring)
493 /* move further up the ring? */
494 t = prev;
496 else
498 /* hit start of ring */
501 else
503 /* correct order, advance one window */
504 t = t->stack_next;
508 return;
511 static void __restack_window_list(
512 FvwmWindow *r, FvwmWindow *s, int count, Bool do_broadcast_all,
513 Bool do_lower)
515 FvwmWindow *t;
516 unsigned int flags;
517 int i;
518 XWindowChanges changes;
519 Window *wins;
520 int do_stack_above;
521 int is_reversed;
523 if (count <= 0)
525 for (count = 0, t = r->stack_next; t != s; t = t->stack_next)
527 count++;
528 count += get_visible_icon_window_count(t);
531 /* restack the windows between r and s */
532 wins = (Window *)safemalloc((count + 3) * sizeof(Window));
533 for (t = r->stack_next, i = 0; t != s; t = t->stack_next)
535 if (i > count)
537 fvwm_msg(
538 ERR, "__restack_window_list",
539 "more transients than expected");
540 break;
542 wins[i++] = FW_W_FRAME(t);
543 if (IS_ICONIFIED(t) && !IS_ICON_SUPPRESSED(t))
545 if (FW_W_ICON_TITLE(t) != None)
547 wins[i++] = FW_W_ICON_TITLE(t);
549 if (FW_W_ICON_PIXMAP(t) != None)
551 wins[i++] = FW_W_ICON_PIXMAP(t);
555 changes.sibling = __get_stacking_sibling(r, True);
556 if (changes.sibling == None)
558 changes.sibling = __get_stacking_sibling(s, False);
559 is_reversed = 1;
561 else
563 is_reversed = 0;
565 if (changes.sibling == None)
567 do_stack_above = !do_lower;
568 flags = CWStackMode;
570 else
572 do_stack_above = 0;
573 flags = CWStackMode | CWSibling;
575 changes.stack_mode = (do_stack_above ^ is_reversed) ? Above : Below;
576 XConfigureWindow(dpy, FW_W_FRAME(r->stack_next), flags, &changes);
577 if (count > 1)
579 XRestackWindows(dpy, wins, count);
581 free(wins);
582 EWMH_SetClientListStacking();
583 if (do_broadcast_all)
585 /* send out M_RESTACK for all windows, to make sure we don't
586 * forget anything. */
587 BroadcastRestackAllWindows();
589 else
591 /* send out (one or more) M_RESTACK packets for windows
592 * between r and s */
593 BroadcastRestack(r, s);
596 return;
599 FvwmWindow *__get_window_to_insert_after(FvwmWindow *fw, stack_mode_t mode)
601 int test_layer;
602 FvwmWindow *s;
604 switch (mode)
606 case SM_LOWER:
607 test_layer = fw->layer - 1;
608 break;
609 default:
610 case SM_RAISE:
611 case SM_RESTACK:
612 test_layer = fw->layer;
613 break;
615 for (
616 s = Scr.FvwmRoot.stack_next; s != &Scr.FvwmRoot;
617 s = s->stack_next)
619 if (s == fw)
621 continue;
623 if (test_layer >= s->layer)
625 break;
629 return s;
632 static void __mark_group_member(
633 FvwmWindow *fw, FvwmWindow *start, FvwmWindow *end)
635 FvwmWindow *t;
637 for (t = start; t != end; t = t->stack_next)
639 if (FW_W(t) == fw->wmhints->window_group ||
640 (t->wmhints && (t->wmhints->flags & WindowGroupHint) &&
641 t->wmhints->window_group == fw->wmhints->window_group))
643 if (IS_IN_TRANSIENT_SUBTREE(t))
645 /* have to move this one too */
646 SET_IN_TRANSIENT_SUBTREE(fw, 1);
651 return;
655 static Bool __mark_transient_subtree_test(
656 FvwmWindow *s, FvwmWindow *start, FvwmWindow *end, int mark_mode,
657 Bool do_ignore_icons, Bool use_window_group_hint)
659 Bool use_group_hint = False;
660 FvwmWindow *r;
662 if (IS_IN_TRANSIENT_SUBTREE(s))
664 return False;
666 if (use_window_group_hint &&
667 DO_ICONIFY_WINDOW_GROUPS(s) && s->wmhints &&
668 (s->wmhints->flags & WindowGroupHint) &&
669 (s->wmhints->window_group != None) &&
670 (s->wmhints->window_group != FW_W(s)) &&
671 (s->wmhints->window_group != Scr.Root))
673 use_group_hint = True;
675 if (!IS_TRANSIENT(s) && !use_group_hint)
677 return False;
679 if (do_ignore_icons && IS_ICONIFIED(s))
681 return False;
683 r = (FvwmWindow *)s->scratch.p;
684 if (IS_TRANSIENT(s))
686 if (r && IS_IN_TRANSIENT_SUBTREE(r) &&
687 ((mark_mode == MARK_ALL) ||
688 __is_restack_transients_needed(
689 r, (stack_mode_t)mark_mode) == True))
691 /* have to move this one too */
692 SET_IN_TRANSIENT_SUBTREE(s, 1);
693 /* used for stacking transients */
694 s->scratch.i += r->scratch.i + 1;
695 return True;
698 if (use_group_hint && !IS_IN_TRANSIENT_SUBTREE(s))
700 __mark_group_member(s, start, end);
701 if (IS_IN_TRANSIENT_SUBTREE(s))
703 /* need another scan through the list */
704 return True;
708 return False;
711 /* heavaly borrowed from mark_transient_subtree. This will mark a subtree as
712 * long as it is straight, and return true if the operation is succussful. It
713 * will abort and return False as soon as some inconsitance is hit. */
715 static Bool is_transient_subtree_straight(
716 FvwmWindow *t, int layer, stack_mode_t mode, Bool do_ignore_icons,
717 Bool use_window_group_hint)
719 FvwmWindow *s;
720 FvwmWindow *start;
721 FvwmWindow *end;
722 int min_i;
723 Bool is_in_gap;
724 int mark_mode;
726 switch (mode)
728 case SM_RAISE:
729 mark_mode = MARK_RAISE;
730 break;
731 case SM_LOWER:
732 mark_mode = MARK_LOWER;
733 break;
734 default:
735 return False;
738 if (layer >= 0 && t->layer != layer)
740 return True;
742 if (t->stack_prev == NULL || t->stack_next == NULL)
744 /* the window is not placed correctly in the stack ring
745 * (probably about to be destroyed) */
746 return False;
748 /* find out on which windows to operate */
749 /* iteration are done reverse (bottom up, since that's the way the
750 * transients wil be stacked if all is well */
751 if (layer >= 0)
753 /* only work on the given layer */
754 start = &Scr.FvwmRoot;
755 end = &Scr.FvwmRoot;
756 for (
757 s = Scr.FvwmRoot.stack_prev;
758 s != &Scr.FvwmRoot && s->layer <= layer;
759 s = s->stack_prev)
761 if (s->layer == layer)
763 if (start == &Scr.FvwmRoot)
765 start = s;
767 end = s->stack_prev;
771 else
773 /* work on complete window list */
774 start = Scr.FvwmRoot.stack_prev;
775 end = &Scr.FvwmRoot;
777 /* clean the temporary flag in all windows and precalculate the
778 * transient frame windows */
779 for (
780 s = Scr.FvwmRoot.stack_next; s != &Scr.FvwmRoot;
781 s = s->stack_next)
783 SET_IN_TRANSIENT_SUBTREE(s, 0);
784 if (IS_TRANSIENT(s) && (layer < 0 || layer == s->layer))
786 s->scratch.p = get_transientfor_fvwmwindow(s);
788 else
790 s->scratch.p = NULL;
793 /* Indicate that no cleening is needed */
794 Scr.FvwmRoot.scratch.i = 1;
795 /* now loop over the windows and mark the ones we need to move */
796 SET_IN_TRANSIENT_SUBTREE(t, 1);
797 min_i = INT_MIN;
798 is_in_gap = False;
800 if (mode == SM_LOWER && t != start)
802 return False;
805 /* check that all transients above the window are in a sorted line
806 * with no other windows between them */
807 for (s = t->stack_prev; s != end && !(is_in_gap && mode == SM_RAISE);
808 s = s->stack_prev)
810 if (
811 __mark_transient_subtree_test(
812 s, start, end, mark_mode,
813 do_ignore_icons,
814 use_window_group_hint))
816 if (is_in_gap)
818 return False;
820 else if (s->scratch.i < min_i)
822 return False;
824 min_i = s->scratch.i;
826 else
828 is_in_gap = True;
830 } /* for */
831 if (is_in_gap && mode == SM_RAISE)
833 return False;
835 /* check that there are no transients left beneth the window */
836 for (s = start; s != t; s = s->stack_prev)
838 if (
839 __mark_transient_subtree_test(
840 s, start, end, mark_mode,
841 do_ignore_icons,
842 use_window_group_hint))
844 return False;
848 return True;
851 /* function to test if all windows are at correct place from start. */
852 static Bool __is_restack_needed(
853 FvwmWindow *t, stack_mode_t mode, Bool do_restack_transients,
854 Bool is_new_window)
856 if (is_new_window)
858 return True;
860 else if (t->stack_prev == NULL || t->stack_next == NULL)
862 /* the window is about to be destroyed, and has been removed
863 * from the stack ring. No need to restack. */
864 return False;
867 if (mode == SM_RESTACK)
869 return True;
871 if (do_restack_transients)
873 return !is_transient_subtree_straight(
874 t, t->layer, mode, True, False);
876 else if (mode == SM_LOWER)
878 return (t->stack_next != &Scr.FvwmRoot &&
879 t->stack_next->layer == t->layer);
881 else if (mode == SM_RAISE)
883 return (t->stack_prev != &Scr.FvwmRoot &&
884 t->stack_prev->layer == t->layer);
887 return True;
890 static Bool __restack_window(
891 FvwmWindow *t, stack_mode_t mode, Bool do_restack_transients,
892 Bool is_new_window, Bool is_client_request)
894 FvwmWindow *s = NULL;
895 FvwmWindow *r = NULL;
896 FvwmWindow tmp_r;
897 int count;
899 if (!__is_restack_needed(
900 t, mode, do_restack_transients, is_new_window))
902 /* need to cancel out the effect of any M_RAISE/M_LOWER that
903 * might already be send out. This is ugly. Better would be to
904 * not send the messages in the first place. */
905 if (do_restack_transients)
907 s = t->stack_next;
908 if (s == NULL)
910 return True;
912 while (IS_IN_TRANSIENT_SUBTREE(s))
914 s = s->stack_next;
916 BroadcastRestack(t->stack_prev, s);
919 /* native/unmanaged windows might have raised. However some
920 * buggy clients will keep issuing requests if the raise hacks
921 * are done after processing their requests. */
922 return is_client_request;
925 count = 0;
926 if (do_restack_transients)
928 /* collect the transients in a temp list */
929 tmp_r.stack_prev = &tmp_r;
930 tmp_r.stack_next = &tmp_r;
931 count = collect_transients_recursive(
932 t, &tmp_r, t->layer, mode, False);
933 if (count == 0)
935 do_restack_transients = False;
938 count += 1 + get_visible_icon_window_count(t);
939 /* now find the place to reinsert t and friends */
940 if (mode == SM_RESTACK)
942 s = t->stack_next;
944 else
946 s = __get_window_to_insert_after(t, mode);
948 remove_window_from_stack_ring(t);
949 r = s->stack_prev;
950 if (do_restack_transients)
952 /* re-sort the transient windows according to their scratch.i
953 * register */
954 __sort_transient_ring(&tmp_r);
955 /* insert all transients between r and s. */
956 add_windowlist_to_stack_ring_after(&tmp_r, r);
960 ** Re-insert t - below transients
962 add_window_to_stack_ring_after(t, s->stack_prev);
964 if (is_new_window && IS_TRANSIENT(t) &&
965 DO_STACK_TRANSIENT_PARENT(t) && !IS_ICONIFIED(t))
967 /* now that the new transient is properly positioned in the
968 * stack ring, raise/lower it again so that its parent is
969 * raised/lowered too */
970 raise_or_lower_window(t, mode, True, False, is_client_request);
971 /* make sure the stacking order is correct - may be the
972 * sledge-hammer method, but the recursion ist too hard to
973 * understand. */
974 ResyncXStackingOrder();
976 /* if the transient is on the top of the top layer pan frames
977 * will have ended up under all windows after this. */
978 return (t->stack_prev != &Scr.FvwmRoot);
980 else
982 /* restack the windows between r and s */
983 __restack_window_list(
984 r, s, count, do_restack_transients,
985 mode == (SM_LOWER) ? True : False);
988 return False;
991 static Bool __raise_lower_recursion(
992 FvwmWindow *t, stack_mode_t mode, Bool is_client_request)
994 FvwmWindow *t2;
996 for (
997 t2 = Scr.FvwmRoot.stack_next; t2 != &Scr.FvwmRoot;
998 t2 = t2->stack_next)
1000 if (FW_W(t2) == FW_W_TRANSIENTFOR(t))
1002 if (t2 == t)
1004 return False;
1006 if (IS_ICONIFIED(t2) || t->layer != t2->layer)
1008 break;
1010 if (mode == SM_LOWER &&
1011 (!IS_TRANSIENT(t2) ||
1012 !DO_STACK_TRANSIENT_PARENT(t2)))
1014 /* hit the highest level transient; lower this
1015 * subtree below all other subtrees of the
1016 * same window */
1017 t->scratch.i = -LOWER_PENALTY;
1019 else
1021 /* Add a bonus to the stack ring position for
1022 * this branch of the transient tree over all
1023 * other branches. */
1024 t->scratch.i = MAX_TRANSIENTS_IN_BRANCH;
1026 __raise_or_lower_window(
1027 t2, mode, True, False, is_client_request);
1028 if (__is_restack_transients_needed(t2, mode))
1030 /* moving the parent moves our window already */
1031 return True;
1036 return False;
1039 static void __raise_or_lower_window(
1040 FvwmWindow *t, stack_mode_t mode, Bool allow_recursion,
1041 Bool is_new_window, Bool is_client_request)
1043 FvwmWindow *t2;
1044 Bool do_move_transients;
1046 /* Do not raise this window after command execution (see
1047 * HandleButtonPress()). */
1048 SET_SCHEDULED_FOR_RAISE(t, 0);
1050 /* New windows are simply raised/lowered without touching the
1051 * transientfor at first. Then, further down in the code,
1052 * __raise_or_lower_window() is called again to raise/lower the
1053 * transientfor if necessary. We can not do the recursion stuff for
1054 * new windows because the __must_move_transients() call needs a
1055 * properly ordered stack ring - but the new window is still at the
1056 * front of the stack ring. */
1057 if (allow_recursion && !is_new_window && !IS_ICONIFIED(t))
1059 /* This part makes Raise/Lower on a Transient act on its Main
1060 * and sibling Transients.
1062 * The recursion is limited to one level - which caters for
1063 * most cases. This code does not handle the case where there
1064 * are trees of Main + Transient (i.e. where a
1065 * Main_window_with_Transients is itself Transient for another
1066 * window). */
1067 if (IS_TRANSIENT(t) && DO_STACK_TRANSIENT_PARENT(t))
1069 if (__raise_lower_recursion(
1070 t, mode, is_client_request) == True)
1072 return;
1077 if (is_new_window)
1079 do_move_transients = False;
1081 else
1083 do_move_transients = __must_move_transients(t, mode);
1085 if (__restack_window(
1086 t, mode, do_move_transients, is_new_window,
1087 is_client_request) == True)
1089 return;
1092 if (mode == SM_RAISE)
1094 /* This hack raises the target and all higher fvwm windows over
1095 * any style grabfocusoff override_redirect windows that may be
1096 * above it. This is used to cope with ill-behaved applications
1097 * that insist on using long-lived override_redirects. */
1098 if (Scr.bo.do_raise_over_unmanaged)
1100 raise_over_unmanaged(t);
1104 * The following is a hack to raise X windows over native
1105 * windows which is needed for some (all ?) X servers running
1106 * under Windows or Windows NT. */
1107 if (Scr.bo.is_raise_hack_needed)
1109 /* RBW - 09/20/1999. I find that trying to raise
1110 * unmanaged windows causes problems with some apps. If
1111 * this seems to work well for everyone, I'll remove
1112 * the #if 0. */
1113 #if 0
1114 /* get *all* toplevels (even including
1115 * override_redirects) */
1116 XQueryTree(dpy, Scr.Root, &junk, &junk, &tops, &num);
1118 /* raise from fw upwards to get them above NT windows */
1119 for (i = 0; i < num; i++)
1121 if (tops[i] == FW_W_FRAME(t))
1123 found = True;
1125 if (found)
1127 XRaiseWindow (dpy, tops[i]);
1130 XFree (tops);
1131 #endif
1132 for (t2 = t; t2 != &Scr.FvwmRoot; t2 = t2->stack_prev)
1134 XRaiseWindow(dpy, FW_W_FRAME(t2));
1137 /* This needs to be done after all the raise hacks. */
1138 raisePanFrames();
1139 /* If the window has been raised, make sure the decorations are
1140 * updated immediately in case we are in a complex function
1141 * (e.g. raise, unshade). */
1142 XFlush(dpy);
1143 handle_all_expose();
1146 return;
1149 static void raise_or_lower_window(
1150 FvwmWindow *t, stack_mode_t mode, Bool allow_recursion,
1151 Bool is_new_window, Bool is_client_request)
1153 FvwmWindow *fw;
1155 /* clean the auxiliary registers used in stacking transients */
1156 for (fw = Scr.FvwmRoot.next; fw != NULL; fw = fw->next)
1158 fw->scratch.i = 0;
1160 __raise_or_lower_window(
1161 t, mode, allow_recursion, is_new_window, is_client_request);
1163 return;
1166 static Bool intersect(
1167 int x0, int y0, int w0, int h0, int x1, int y1, int w1, int h1)
1169 return !((x0 >= x1 + w1) || (x0 + w0 <= x1) ||
1170 (y0 >= y1 + h1) || (y0 + h0 <= y1));
1173 static Bool overlap(FvwmWindow *r, FvwmWindow *s)
1175 rectangle g1;
1176 rectangle g2;
1177 Bool rc;
1179 if (r->Desk != s->Desk)
1181 return False;
1183 rc = get_visible_window_or_icon_geometry(r, &g1);
1184 if (rc == False)
1186 return False;
1188 rc = get_visible_window_or_icon_geometry(s, &g2);
1189 if (rc == False)
1191 return False;
1193 rc = intersect(
1194 g1.x, g1.y, g1.width, g1.height,
1195 g2.x, g2.y, g2.width, g2.height);
1197 return rc;
1200 #if 0
1202 ResyncFvwmStackRing -
1203 Rebuilds the stacking order ring of fvwm-managed windows. For use in cases
1204 where apps raise/lower their own windows in a way that makes it difficult
1205 to determine exactly where they ended up in the stacking order.
1206 - Based on code from Matthias Clasen.
1208 static void ResyncFvwmStackRing (void)
1210 Window root, parent, *children;
1211 unsigned int nchildren;
1212 int i;
1213 FvwmWindow *t1, *t2;
1215 MyXGrabServer (dpy);
1217 if (!XQueryTree (dpy, Scr.Root, &root, &parent, &children, &nchildren))
1219 MyXUngrabServer (dpy);
1220 return;
1223 t2 = &Scr.FvwmRoot;
1224 for (i = 0; i < nchildren; i++)
1226 for (t1 = Scr.FvwmRoot.next; t1 != NULL; t1 = t1->next)
1228 if (IS_ICONIFIED(t1) && !IS_ICON_SUPPRESSED(t1))
1230 if (FW_W_ICON_TITLE(t1) == children[i] ||
1231 FW_W_ICON_PIXMAP(t1) == children[i])
1233 break;
1236 else
1238 if (FW_W_FRAME(t1) == children[i])
1240 break;
1245 if (t1 != NULL && t1 != t2)
1247 /* Move the window to its new position, working from
1248 * the bottom up (that's the way XQueryTree presents
1249 * the list). */
1250 /* Pluck from chain. */
1251 remove_window_from_stack_ring(t1);
1252 add_window_to_stack_ring_after(t1, t2->stack_prev);
1253 if (t2 != &Scr.FvwmRoot && t2->layer > t1->layer)
1255 /* oops, now our stack ring is out of order! */
1256 /* emergency fix */
1257 t1->layer = t2->layer;
1259 t2 = t1;
1263 MyXUngrabServer (dpy);
1265 XFree (children);
1267 #endif
1269 /* same as above but synchronizes the stacking order in X from the stack ring.
1271 static void ResyncXStackingOrder(void)
1273 Window *wins;
1274 FvwmWindow *t;
1275 int count;
1276 int i;
1278 for (count = 0, t = Scr.FvwmRoot.next; t != NULL; count++, t = t->next)
1280 /* nothing to do here */
1282 if (count > 0)
1284 wins = (Window *)safemalloc(3 * count * sizeof (Window));
1285 for (
1286 i = 0, t = Scr.FvwmRoot.stack_next; count--;
1287 t = t->stack_next)
1289 wins[i++] = FW_W_FRAME(t);
1290 if (IS_ICONIFIED(t) && !IS_ICON_SUPPRESSED(t))
1292 if (FW_W_ICON_TITLE(t) != None)
1294 wins[i++] = FW_W_ICON_TITLE(t);
1296 if (FW_W_ICON_PIXMAP(t) != None)
1298 wins[i++] = FW_W_ICON_PIXMAP(t);
1302 XRestackWindows(dpy, wins, i);
1303 free(wins);
1304 /* send out M_RESTACK for all windows, to make sure we don't
1305 * forget anything. */
1306 BroadcastRestackAllWindows();
1309 return;
1312 /* send RESTACK packets for all windows between s1 and s2 */
1313 static void BroadcastRestack(FvwmWindow *s1, FvwmWindow *s2)
1315 FvwmWindow *fw;
1316 int num;
1317 int i;
1318 int n;
1319 fmodule_list_itr moditr;
1320 fmodule *module;
1321 unsigned long *body, *bp, length;
1322 unsigned long max_wins_per_packet;
1324 if (s2 == &Scr.FvwmRoot)
1326 s2 = s2->stack_prev;
1327 if (s2 == &Scr.FvwmRoot)
1329 return;
1332 if (s1 == &Scr.FvwmRoot)
1334 s1 = s1->stack_next;
1335 if (s1 == &Scr.FvwmRoot)
1337 return;
1339 /* s1 has been moved to the top of stack */
1340 BroadcastPacket(
1341 M_RAISE_WINDOW, 3, (long)FW_W(s1),
1342 (long)FW_W_FRAME(s1), (unsigned long)s1);
1343 if (s1->stack_next == s2)
1345 /* avoid sending empty RESTACK packet */
1346 return;
1349 if (s1 == s2)
1351 /* A useful M_RESTACK packet must contain at least two windows.
1353 return;
1355 for (
1356 fw = s1, num = 1; fw != s2 && fw != &Scr.FvwmRoot;
1357 fw = fw->stack_next, num++)
1359 /* nothing */
1361 max_wins_per_packet = (FvwmPacketMaxSize - FvwmPacketHeaderSize) / 3;
1362 /* split packet if it is too long */
1363 for ( ; num > 1; s1 = fw, num -= n)
1365 n = min(num, max_wins_per_packet) - 1;
1366 length = FvwmPacketHeaderSize + 3 * (n + 1);
1367 body = (unsigned long *)safemalloc(
1368 length * sizeof(unsigned long));
1369 bp = body;
1370 *(bp++) = START_FLAG;
1371 *(bp++) = M_RESTACK;
1372 *(bp++) = length;
1373 *(bp++) = fev_get_evtime();
1374 for (fw = s1, i = 0; i <= n; i++, fw = fw->stack_next)
1376 *(bp++) = FW_W(fw);
1377 *(bp++) = FW_W_FRAME(fw);
1378 *(bp++) = (unsigned long)fw;
1380 /* The last window has to be in the header of the next part */
1381 fw = fw->stack_prev;
1382 module_list_itr_init(&moditr);
1383 while ( (module = module_list_itr_next(&moditr)) != NULL)
1385 PositiveWrite(
1386 module,body,length*sizeof(unsigned long));
1388 free(body);
1390 #ifdef DEBUG_STACK_RING
1391 verify_stack_ring_consistency();
1392 #endif
1394 return;
1397 static int collect_transients_recursive(
1398 FvwmWindow *t, FvwmWindow *list_head, int layer, stack_mode_t mode,
1399 Bool do_include_target_window)
1401 FvwmWindow *s;
1402 int count = 0;
1403 int m;
1405 switch (mode)
1407 case SM_LOWER:
1408 m = MARK_LOWER;
1409 break;
1410 case SM_RAISE:
1411 m = MARK_RAISE;
1412 break;
1413 case SM_RESTACK:
1414 m = MARK_RESTACK;
1415 break;
1416 default:
1417 /* can not happen */
1418 m = MARK_RAISE;
1419 break;
1421 mark_transient_subtree(t, layer, m, True, False);
1422 /* now collect the marked windows in a separate list */
1423 for (s = Scr.FvwmRoot.stack_next; s != &Scr.FvwmRoot; )
1425 FvwmWindow *tmp;
1427 if (s == t && do_include_target_window == False)
1429 /* ignore the target window */
1430 s = s->stack_next;
1431 continue;
1433 tmp = s->stack_next;
1434 if (IS_IN_TRANSIENT_SUBTREE(s))
1436 remove_window_from_stack_ring(s);
1437 add_window_to_stack_ring_after(
1438 s, list_head->stack_prev);
1439 count++;
1440 count += get_visible_icon_window_count(t);
1442 s = tmp;
1445 return count;
1448 static Bool is_above_unmanaged(FvwmWindow *fw, Window *umtop)
1451 Chase through the entire stack of the server's windows looking
1452 for any unmanaged window that's higher than the target.
1453 Called from raise_over_unmanaged and is_on_top_of_layer.
1455 Bool ontop = True;
1456 Window junk;
1457 Window *tops;
1458 int i;
1459 unsigned int num;
1460 Window OR_Above = None;
1461 XWindowAttributes wa;
1463 if (fw->Desk != Scr.CurrentDesk)
1465 return True;
1467 if (!XQueryTree(dpy, Scr.Root, &junk, &junk, &tops, &num))
1469 return ontop;
1473 * Locate the highest override_redirect window above our target, and
1474 * the highest of our windows below it.
1476 for (i = 0; i < num && tops[i] != FW_W_FRAME(fw); i++)
1478 /* look for target window in list */
1480 for (; i < num; i++)
1482 /* It might be just as well (and quicker) just to check for the
1483 * absence of an FvwmContext instead of for
1484 * override_redirect... */
1485 if (!XGetWindowAttributes(dpy, tops[i], &wa))
1487 continue;
1490 Don't forget to ignore the hidden frame resizing windows...
1492 if (wa.override_redirect == True
1493 && wa.class != InputOnly
1494 && tops[i] != Scr.NoFocusWin
1495 && (!is_frame_hide_window(tops[i])))
1497 OR_Above = tops[i];
1499 } /* end for */
1501 if (OR_Above) {
1502 *umtop = OR_Above;
1503 ontop = False;
1506 XFree (tops);
1509 return ontop;
1512 static Bool is_on_top_of_layer_ignore_rom(FvwmWindow *fw)
1514 FvwmWindow *t;
1515 Bool ontop = True;
1517 if (IS_SCHEDULED_FOR_DESTROY(fw))
1519 /* stack ring members are no longer valid */
1520 return False;
1522 if (DO_RAISE_TRANSIENT(fw))
1524 mark_transient_subtree(fw, fw->layer, MARK_RAISE, True, False);
1526 for (t = fw->stack_prev; t != &Scr.FvwmRoot; t = t->stack_prev)
1528 if (t->layer > fw->layer)
1530 break;
1532 if (t->Desk != fw->Desk)
1534 continue;
1536 /* For RaiseOverUnmanaged we can not determine if the window is
1537 * on top by checking if the window overlaps another one. If
1538 * it was below unmanaged windows, but on top of its layer, it
1539 * would be considered on top. */
1540 if (Scr.bo.do_raise_over_unmanaged || overlap(fw, t))
1542 if (!DO_RAISE_TRANSIENT(fw) ||
1543 (!IS_IN_TRANSIENT_SUBTREE(t) && t != fw))
1545 ontop = False;
1546 break;
1551 return ontop;
1554 static Bool __is_on_top_of_layer(FvwmWindow *fw, Bool client_entered)
1556 Window junk;
1557 Bool ontop = False;
1558 if (Scr.bo.do_raise_over_unmanaged)
1561 #define EXPERIMENTAL_ROU_HANDLING
1562 #ifdef EXPERIMENTAL_ROU_HANDLING
1564 RBW - 2002/08/15 -
1565 RaiseOverUnmanaged adds some overhead. The only way to let our
1566 caller know for sure whether we need to grab the mouse buttons
1567 because we may need to raise this window is to query the
1568 server's tree and look for any override_redirect windows above
1569 this one.
1570 But this function is called far too often to do this every
1571 time. Only if the window is at the top of the FvwmWindow
1572 stack do we need more information from the server; and then
1573 only at the last moment in HandleEnterNotify when we really
1574 need to know whether a raise will be needed if the user
1575 clicks in the client window.
1576 is_on_top_of_layer_and_above_unmanaged is called in that case.
1578 if (is_on_top_of_layer_ignore_rom(fw))
1580 if (client_entered)
1581 /* FIXME! - perhaps we should only do if MFCR */
1583 #ifdef ROUDEBUG
1584 printf("RBW-iotol - %8.8lx is on top,"
1585 " checking server tree. ***\n",
1586 FW_W_CLIENT(fw));
1587 #endif
1588 ontop = is_above_unmanaged(fw, &junk);
1589 #ifdef ROUDEBUG
1590 printf(" returning %d\n", (int) ontop);
1591 #endif
1593 else
1595 #ifdef ROUDEBUG
1596 printf("RBW-iotol - %8.8lx is on top,"
1597 " *** NOT checking server tree.\n",
1598 FW_W_CLIENT(fw));
1599 #endif
1600 ontop = True;
1602 return ontop;
1604 else
1606 return False;
1608 #else
1609 return False; /* Old pre-2002/08/22 handling. */
1610 #endif
1612 else
1614 return is_on_top_of_layer_ignore_rom(fw);
1618 /* ---------------------------- interface functions ------------------------ */
1620 /* Remove a window from the stack ring */
1621 void remove_window_from_stack_ring(FvwmWindow *t)
1623 if (IS_SCHEDULED_FOR_DESTROY(t))
1625 return;
1627 t->stack_prev->stack_next = t->stack_next;
1628 t->stack_next->stack_prev = t->stack_prev;
1629 /* not really necessary, but gives a little more saftey */
1630 t->stack_prev = NULL;
1631 t->stack_next = NULL;
1633 return;
1636 /* Add window t to the stack ring after window t */
1637 void add_window_to_stack_ring_after(FvwmWindow *t, FvwmWindow *add_after_win)
1639 if (IS_SCHEDULED_FOR_DESTROY(t))
1641 return;
1643 if (t == add_after_win || t == add_after_win->stack_next)
1645 /* tried to add the window before or after itself */
1646 fvwm_msg(
1647 ERR, "add_window_to_stack_ring_after",
1648 "BUG: tried to add window '%s' %s itself in stack"
1649 " ring\n",
1650 t->name.name, (t == add_after_win) ?
1651 "after" : "before");
1652 return;
1654 t->stack_next = add_after_win->stack_next;
1655 add_after_win->stack_next->stack_prev = t;
1656 t->stack_prev = add_after_win;
1657 add_after_win->stack_next = t;
1659 return;
1662 FvwmWindow *get_next_window_in_stack_ring(const FvwmWindow *t)
1664 return t->stack_next;
1667 FvwmWindow *get_prev_window_in_stack_ring(const FvwmWindow *t)
1669 return t->stack_prev;
1672 FvwmWindow *get_transientfor_fvwmwindow(const FvwmWindow *t)
1674 FvwmWindow *s;
1676 if (!t || !IS_TRANSIENT(t) || FW_W_TRANSIENTFOR(t) == Scr.Root ||
1677 FW_W_TRANSIENTFOR(t) == None)
1679 return NULL;
1681 for (s = Scr.FvwmRoot.next; s != NULL; s = s->next)
1683 if (FW_W(s) == FW_W_TRANSIENTFOR(t))
1685 return (s == t) ? NULL : s;
1689 return NULL;
1692 /* Takes a window from the top of the stack ring and puts it at the appropriate
1693 * place. Called when new windows are created. */
1694 Bool position_new_window_in_stack_ring(FvwmWindow *t, Bool do_lower)
1696 if (t->stack_prev != &Scr.FvwmRoot)
1698 /* Not at top of stack ring, so it is already in place.
1699 * add_window.c relies on this. */
1700 return False;
1702 /* RaiseWindow/LowerWindow will put the window in its layer */
1703 raise_or_lower_window(
1704 t, (do_lower) ? SM_LOWER : SM_RAISE, False, True, False);
1706 return True;
1709 /* Raise t and its transients to the top of its layer. For the pager to work
1710 * properly it is necessary that RaiseWindow *always* sends a proper M_RESTACK
1711 * packet, even if the stacking order didn't change. */
1712 void RaiseWindow(FvwmWindow *t, Bool is_client_request)
1714 BroadcastPacket(
1715 M_RAISE_WINDOW, 3, (long)FW_W(t), (long)FW_W_FRAME(t),
1716 (unsigned long)t);
1717 raise_or_lower_window(t, SM_RAISE, True, False, is_client_request);
1718 focus_grab_buttons_on_layer(t->layer);
1719 #ifdef DEBUG_STACK_RING
1720 verify_stack_ring_consistency();
1721 #endif
1722 return;
1725 void LowerWindow(FvwmWindow *t, Bool is_client_request)
1727 BroadcastPacket(
1728 M_LOWER_WINDOW, 3, (long)FW_W(t), (long)FW_W_FRAME(t),
1729 (unsigned long)t);
1730 raise_or_lower_window(t, SM_LOWER, True, False, is_client_request);
1731 focus_grab_buttons_on_layer(t->layer);
1732 #ifdef DEBUG_STACK_RING
1733 verify_stack_ring_consistency();
1734 #endif
1735 return;
1738 void RestackWindow(FvwmWindow *t, Bool is_client_request)
1740 raise_or_lower_window(t, SM_RESTACK, True, False, is_client_request);
1741 focus_grab_buttons_on_layer(t->layer);
1742 #ifdef DEBUG_STACK_RING
1743 verify_stack_ring_consistency();
1744 #endif
1745 return;
1748 /* return true if stacking order changed */
1749 Bool HandleUnusualStackmodes(
1750 unsigned int stack_mode, FvwmWindow *r, Window rw, FvwmWindow *s,
1751 Window sw)
1753 int do_restack = 0;
1754 FvwmWindow *t;
1756 /* DBUG("HandleUnusualStackmodes", "called with %d, %lx\n",
1757 stack_mode, s);*/
1759 if (
1760 ((rw != FW_W(r)) ^ IS_ICONIFIED(r)) ||
1761 (s && (((sw != FW_W(s)) ^ IS_ICONIFIED(s)) ||
1762 (r->Desk != s->Desk))))
1764 /* one of the relevant windows is unmapped */
1765 return 0;
1768 switch (stack_mode)
1770 case TopIf:
1771 for (
1772 t = r->stack_prev; t != &Scr.FvwmRoot && !do_restack;
1773 t = t->stack_prev)
1775 do_restack = ((s == NULL || s == t) && overlap(t, r));
1777 if (do_restack)
1779 RaiseWindow (r, True);
1781 break;
1782 case BottomIf:
1783 for (
1784 t = r->stack_next; t != &Scr.FvwmRoot && !do_restack;
1785 t = t->stack_next)
1787 do_restack = ((s == NULL || s == t) && overlap(t, r));
1789 if (do_restack)
1791 LowerWindow (r, True);
1793 break;
1794 case Opposite:
1795 do_restack = (
1796 HandleUnusualStackmodes(TopIf, r, rw, s, sw) ||
1797 HandleUnusualStackmodes(BottomIf, r, rw, s, sw));
1798 break;
1800 /* DBUG("HandleUnusualStackmodes", "\t---> %d\n", do_restack);*/
1801 #ifdef DEBUG_STACK_RING
1802 verify_stack_ring_consistency();
1803 #endif
1804 return do_restack;
1808 RBW - 01/07/1998 - this is here temporarily - I mean to move it to
1809 libfvwm eventually, along with some other chain manipulation functions.
1812 void BroadcastRestackAllWindows(void)
1814 BroadcastRestack(Scr.FvwmRoot.stack_next, Scr.FvwmRoot.stack_prev);
1815 return;
1818 /* send RESTACK packets for t, t->stack_prev and t->stack_next */
1819 void BroadcastRestackThisWindow(FvwmWindow *t)
1821 BroadcastRestack(t->stack_prev, t->stack_next);
1822 return;
1825 /* returns 0 if s and t are on the same layer, <1 if t is on a lower layer and
1826 * >1 if t is on a higher layer. */
1827 int compare_window_layers(FvwmWindow *t, FvwmWindow *s)
1829 return t->layer - s->layer;
1832 void set_default_layer(FvwmWindow *t, int layer)
1834 t->default_layer = layer;
1835 return;
1838 void set_layer(FvwmWindow *t, int layer)
1840 t->layer = layer;
1841 return;
1844 int get_layer(FvwmWindow *t)
1846 return t->layer;
1849 /* This function recursively finds the transients of the window t and sets their
1850 * is_in_transient_subtree flag. If a layer is given, only windows in this
1851 * layer are checked. If the layer is < 0, all windows are considered.
1853 void mark_transient_subtree(
1854 FvwmWindow *t, int layer, int mark_mode, Bool do_ignore_icons,
1855 Bool use_window_group_hint)
1857 FvwmWindow *s;
1858 FvwmWindow *start;
1859 FvwmWindow *end;
1860 Bool is_finished;
1862 if (layer >= 0 && t->layer != layer)
1864 return;
1866 /* find out on which windows to operate */
1867 if (layer >= 0)
1869 /* only work on the given layer */
1870 start = &Scr.FvwmRoot;
1871 end = &Scr.FvwmRoot;
1872 for (
1873 s = Scr.FvwmRoot.stack_next;
1874 s != &Scr.FvwmRoot && s->layer >= layer;
1875 s = s->stack_next)
1877 if (s == t)
1879 /* ignore the target window */
1880 continue;
1882 if (s->layer == layer)
1884 if (start == &Scr.FvwmRoot)
1886 start = s;
1888 end = s->stack_next;
1892 else
1894 /* work on complete window list */
1895 start = Scr.FvwmRoot.stack_next;
1896 end = &Scr.FvwmRoot;
1898 /* clean the temporary flag in all windows and precalculate the
1899 * transient frame windows */
1900 if (Scr.FvwmRoot.scratch.i == 0)
1902 for (
1903 s = Scr.FvwmRoot.stack_next; s != &Scr.FvwmRoot;
1904 s = s->stack_next)
1906 SET_IN_TRANSIENT_SUBTREE(s, 0);
1907 if (
1908 IS_TRANSIENT(s) &&
1909 (layer < 0 || layer == s->layer))
1911 s->scratch.p = get_transientfor_fvwmwindow(s);
1913 else
1915 s->scratch.p = NULL;
1919 Scr.FvwmRoot.scratch.i = 0;
1921 /* now loop over the windows and mark the ones we need to move */
1922 SET_IN_TRANSIENT_SUBTREE(t, 1);
1923 is_finished = False;
1924 while (!is_finished)
1926 /* recursively search for all transient windows */
1927 is_finished = True;
1928 for (s = start; s != end; s = s->stack_next)
1931 if (
1932 __mark_transient_subtree_test(
1933 s, start, end, mark_mode,
1934 do_ignore_icons,
1935 use_window_group_hint))
1937 is_finished = False;
1939 } /* for */
1940 } /* while */
1942 return;
1945 void new_layer(FvwmWindow *fw, int layer)
1947 FvwmWindow *s;
1948 FvwmWindow *target;
1949 FvwmWindow *prev;
1950 FvwmWindow list_head;
1951 int add_after_layer;
1952 int count;
1953 int old_layer;
1954 Bool do_lower;
1956 if (layer < 0)
1958 layer = 0;
1960 fw = get_transientfor_top_fvwmwindow(fw);
1961 if (layer == fw->layer)
1963 return;
1965 old_layer = fw->layer;
1966 list_head.stack_next = &list_head;
1967 list_head.stack_prev = &list_head;
1968 count = collect_transients_recursive(
1969 fw, &list_head, fw->layer,
1970 (layer < fw->layer) ? SM_LOWER : SM_RAISE, True);
1971 if (count == 0)
1973 /* no windows to move */
1974 return;
1976 add_after_layer = layer;
1977 if (layer < fw->layer)
1979 /* lower below the windows in the new (lower) layer */
1980 add_after_layer = layer;
1981 do_lower = True;
1983 else
1985 /* raise above the windows in the new (higher) layer */
1986 add_after_layer = layer + 1;
1987 do_lower = False;
1989 /* find the place to insert the windows */
1990 for (
1991 target = Scr.FvwmRoot.stack_next; target != &Scr.FvwmRoot;
1992 target = target->stack_next)
1994 if (target->layer < add_after_layer)
1996 /* add all windows before the current window */
1997 break;
2000 /* insert windows at new position */
2001 add_windowlist_to_stack_ring_after(&list_head, target->stack_prev);
2002 prev = NULL;
2003 for (
2004 s = list_head.stack_next; prev != list_head.stack_prev;
2005 prev = s, s = s->stack_next)
2007 s->layer = layer;
2008 /* redraw title and buttons to update layer buttons */
2009 border_draw_decorations(
2010 s, PART_TITLEBAR, (Scr.Hilite == fw), True, CLEAR_NONE,
2011 NULL, NULL);
2012 GNOME_SetLayer(fw);
2013 EWMH_SetWMState(fw, False);
2015 /* move the windows without modifying their stacking order */
2016 __restack_window_list(
2017 list_head.stack_next->stack_prev, target, count, (count > 1),
2018 do_lower);
2019 focus_grab_buttons_on_layer(layer);
2020 focus_grab_buttons_on_layer(old_layer);
2022 return;
2025 /* RBW - 11/13/1998 - 2 new fields to init - stacking order chain. */
2026 void init_stack_and_layers(void)
2028 Scr.BottomLayer = DEFAULT_BOTTOM_LAYER;
2029 Scr.DefaultLayer = DEFAULT_DEFAULT_LAYER;
2030 Scr.TopLayer = DEFAULT_TOP_LAYER;
2031 Scr.FvwmRoot.stack_next = &Scr.FvwmRoot;
2032 Scr.FvwmRoot.stack_prev = &Scr.FvwmRoot;
2033 set_layer(&Scr.FvwmRoot, DEFAULT_ROOT_WINDOW_LAYER);
2034 return;
2037 Bool is_on_top_of_layer(FvwmWindow *fw)
2039 return __is_on_top_of_layer(fw, False);
2042 Bool is_on_top_of_layer_and_above_unmanaged(FvwmWindow *fw)
2044 return __is_on_top_of_layer(fw, True);
2047 /* ----------------------------- built in functions ----------------------- */
2049 void CMD_Raise(F_CMD_ARGS)
2051 RaiseWindow(exc->w.fw, False);
2053 return;
2056 void CMD_Lower(F_CMD_ARGS)
2058 LowerWindow(exc->w.fw, False);
2060 return;
2063 void CMD_RestackTransients(F_CMD_ARGS)
2065 RestackWindow(exc->w.fw, False);
2067 return;
2070 void CMD_RaiseLower(F_CMD_ARGS)
2072 Bool ontop;
2073 FvwmWindow * const fw = exc->w.fw;
2075 ontop = is_on_top_of_layer_ignore_rom(fw);
2076 if (ontop)
2078 LowerWindow(fw, False);
2080 else
2082 RaiseWindow(fw, False);
2085 return;
2088 void CMD_Layer(F_CMD_ARGS)
2090 int n, layer, val[2];
2091 char *token;
2092 FvwmWindow * const fw = exc->w.fw;
2094 if (fw == NULL)
2096 return;
2098 token = PeekToken(action, NULL);
2099 if (StrEquals("default", token))
2101 layer = fw->default_layer;
2103 else
2105 n = GetIntegerArguments(action, NULL, val, 2);
2107 layer = fw->layer;
2108 if ((n == 1) ||
2109 ((n == 2) && (val[0] != 0)))
2111 layer += val[0];
2113 else if ((n == 2) && (val[1] >= 0))
2115 layer = val[1];
2117 else
2119 layer = fw->default_layer;
2122 if (layer < 0)
2124 layer = 0;
2126 new_layer(fw, layer);
2127 #ifdef DEBUG_STACK_RING
2128 verify_stack_ring_consistency();
2129 #endif
2131 return;
2134 void CMD_DefaultLayers(F_CMD_ARGS)
2136 char *bot = NULL;
2137 char *def = NULL;
2138 char *top = NULL;
2139 int i;
2141 bot = PeekToken(action, &action);
2142 if (bot)
2144 i = atoi (bot);
2145 if (i < 0)
2147 fvwm_msg(
2148 ERR, "DefaultLayers",
2149 "Layer must be non-negative." );
2151 else
2153 Scr.BottomLayer = i;
2156 def = PeekToken(action, &action);
2157 if (def)
2159 i = atoi (def);
2160 if (i < 0)
2162 fvwm_msg(
2163 ERR, "DefaultLayers",
2164 "Layer must be non-negative." );
2166 else
2168 Scr.DefaultLayer = i;
2171 top = PeekToken(action, &action);
2172 if (top)
2174 i = atoi (top);
2175 if (i < 0)
2177 fvwm_msg(
2178 ERR, "DefaultLayers",
2179 "Layer must be non-negative." );
2181 else
2183 Scr.TopLayer = i;
2186 #ifdef DEBUG_STACK_RING
2187 verify_stack_ring_consistency();
2188 #endif
2190 return;