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 ---------------------- */
26 #include "libs/fvwmlib.h"
27 #include <libs/gravity.h>
31 #include "functions.h"
36 #include "module_list.h"
37 #include "module_interface.h"
49 /* ---------------------------- local definitions -------------------------- */
51 /* If more than this many transients are in a single branch of a transient
52 * tree, they will end up in more or less random stacking order. */
53 #define MAX_TRANSIENTS_IN_BRANCH 200000
54 /* Same for total levels of transients. */
55 #define MAX_TRANSIENT_LEVELS 10000
56 /* This number must fit in a signed int! */
57 #define LOWER_PENALTY (MAX_TRANSIENTS_IN_BRANCH * MAX_TRANSIENT_LEVELS)
59 /* ---------------------------- local macros ------------------------------- */
61 /* ---------------------------- imports ------------------------------------ */
63 /* ---------------------------- included code files ------------------------ */
65 /* ---------------------------- local types -------------------------------- */
69 SM_RAISE
= MARK_RAISE
,
70 SM_LOWER
= MARK_LOWER
,
71 SM_RESTACK
= MARK_RESTACK
74 /* ---------------------------- forward declarations ----------------------- */
76 static void __raise_or_lower_window(
77 FvwmWindow
*t
, stack_mode_t mode
, Bool allow_recursion
,
79 static void raise_or_lower_window(
80 FvwmWindow
*t
, stack_mode_t mode
, Bool allow_recursion
,
83 static void ResyncFvwmStackRing(void);
85 static void ResyncXStackingOrder(void);
86 static void BroadcastRestack(FvwmWindow
*s1
, FvwmWindow
*s2
);
87 static Bool
is_above_unmanaged(FvwmWindow
*fw
, Window
*umtop
);
88 static int collect_transients_recursive(
89 FvwmWindow
*t
, FvwmWindow
*list_head
, int layer
, stack_mode_t mode
,
90 Bool do_include_target_window
);
92 /* ---------------------------- local variables ---------------------------- */
94 /* ---------------------------- exported variables (globals) --------------- */
96 /* ---------------------------- local functions ---------------------------- */
98 #define DEBUG_STACK_RING 1
99 #ifdef DEBUG_STACK_RING
100 /* debugging function */
101 static void dump_stack_ring(void)
105 if (!debugging_stack_ring
)
110 fprintf(stderr
,"dumping stack ring:\n");
112 t1
= Scr
.FvwmRoot
.stack_next
; t1
!= &Scr
.FvwmRoot
;
115 fprintf(stderr
," l=%d fw=%p f=0x%08x '%s'\n", t1
->layer
,
116 t1
, (int)FW_W_FRAME(t1
), t1
->name
.name
);
122 /* debugging function */
123 void verify_stack_ring_consistency(void)
125 Window root
, parent
, *children
;
126 unsigned int nchildren
;
132 if (!debugging_stack_ring
)
137 t2
= Scr
.FvwmRoot
.stack_next
;
138 if (t2
== &Scr
.FvwmRoot
)
142 last_layer
= t2
->layer
;
145 t1
= t2
->stack_next
; t1
!= &Scr
.FvwmRoot
;
146 t2
= t1
, t1
= t1
->stack_next
)
148 if (t1
->layer
> last_layer
)
152 "vsrc: stack ring is corrupt! '%s' (layer %d)"
153 " is above '%s' (layer %d/%d)\n",
154 t1
->name
.name
, t1
->layer
, t2
->name
.name
,
155 t2
->layer
, last_layer
);
159 last_layer
= t1
->layer
;
163 t1
= t2
->stack_next
; t1
!= &Scr
.FvwmRoot
;
164 t2
= t1
, t1
= t1
->stack_next
)
166 if (t1
->stack_prev
!= t2
)
171 if (t1
!= &Scr
.FvwmRoot
|| t1
->stack_prev
!= t2
)
175 "vsrc: stack ring is corrupt -"
176 " fvwm will probably crash! %p -> %p but %p <- %p",
177 t2
, t1
, t1
->stack_prev
, t1
);
182 if (!XQueryTree(dpy
, Scr
.Root
, &root
, &parent
, &children
, &nchildren
))
184 MyXUngrabServer(dpy
);
188 last_index
= nchildren
;
190 t1
= Scr
.FvwmRoot
.stack_next
; t1
!= &Scr
.FvwmRoot
;
193 /* find window in window list */
195 i
= 0; i
< nchildren
&& FW_W_FRAME(t1
) != children
[i
];
198 /* nothing to do here */
203 stderr
,"vsrc: window already died:"
204 " fw=%p w=0x%08x '%s'\n",
205 t1
, (int)FW_W_FRAME(t1
), t1
->name
.name
);
207 else if (i
>= last_index
)
210 stderr
, "vsrc: window is at wrong position"
211 " in stack ring: fw=%p f=0x%08x '%s'\n",
212 t1
, (int)FW_W_FRAME(t1
),
215 fprintf(stderr
,"dumping X stacking order:\n");
216 for (i
= nchildren
; i
-- > 0; )
219 t1
= Scr
.FvwmRoot
.stack_next
;
223 /* only dump frame windows */
224 if (FW_W_FRAME(t1
) == children
[i
])
227 stderr
, " f=0x%08x\n",
233 MyXUngrabServer(dpy
);
239 MyXUngrabServer(dpy
);
246 /* Add a whole ring of windows. The list_head itself will not be added. */
247 static void add_windowlist_to_stack_ring_after(
248 FvwmWindow
*list_head
, FvwmWindow
*add_after_win
)
250 add_after_win
->stack_next
->stack_prev
= list_head
->stack_prev
;
251 list_head
->stack_prev
->stack_next
= add_after_win
->stack_next
;
252 add_after_win
->stack_next
= list_head
->stack_next
;
253 list_head
->stack_next
->stack_prev
= add_after_win
;
258 static FvwmWindow
*get_transientfor_top_fvwmwindow(FvwmWindow
*t
)
263 while (s
&& IS_TRANSIENT(s
) && DO_STACK_TRANSIENT_PARENT(s
))
265 s
= get_transientfor_fvwmwindow(s
);
276 * Raise a target and all higher FVWM-managed windows above any
277 * override_redirects:
278 * - locate the highest override_redirect above our target
279 * - put all the FvwmWindows from the target to the highest FvwmWindow
280 * below the highest override_redirect in the restack list
281 * - configure our target window above the override_redirect sibling,
284 static void raise_over_unmanaged(FvwmWindow
*t
)
287 Window OR_Above
= None
;
290 FvwmWindow
*t2
= NULL
;
292 XWindowChanges changes
;
296 * Locate the highest override_redirect window above our target, and
297 * the highest of our windows below it.
299 * Count the windows we need to restack, then build the stack list.
301 if (!is_above_unmanaged(t
, &OR_Above
))
304 count
= 0, t2
= Scr
.FvwmRoot
.stack_next
;
305 t2
!= &Scr
.FvwmRoot
; t2
= t2
->stack_next
)
308 count
+= get_visible_icon_window_count(t2
);
317 wins
= (Window
*) safemalloc (count
* sizeof (Window
));
319 i
= 0, t2
= Scr
.FvwmRoot
.stack_next
;
320 t2
!= &Scr
.FvwmRoot
; t2
= t2
->stack_next
)
322 wins
[i
++] = FW_W_FRAME(t2
);
323 if (IS_ICONIFIED(t2
) &&
324 ! IS_ICON_SUPPRESSED(t2
))
326 if (FW_W_ICON_TITLE(t2
) != None
)
328 wins
[i
++] = FW_W_ICON_TITLE(t2
);
330 if (FW_W_ICON_PIXMAP(t2
) != None
)
333 FW_W_ICON_PIXMAP(t2
);
342 memset(&changes
, '\0', sizeof(changes
));
343 changes
.sibling
= OR_Above
;
344 changes
.stack_mode
= Above
;
345 flags
= CWSibling
|CWStackMode
;
348 dpy
, FW_W_FRAME(t
)/*topwin*/, flags
, &changes
);
351 XRestackWindows(dpy
, wins
, i
);
355 }/* end - we found an OR above our target */
360 static Bool
__is_restack_transients_needed(
361 FvwmWindow
*t
, stack_mode_t mode
)
363 if (DO_RAISE_TRANSIENT(t
))
365 if (mode
== SM_RAISE
|| mode
== SM_RESTACK
)
370 if (DO_LOWER_TRANSIENT(t
))
372 if (mode
== SM_LOWER
|| mode
== SM_RESTACK
)
381 static Bool
__must_move_transients(
382 FvwmWindow
*t
, stack_mode_t mode
)
389 if (__is_restack_transients_needed(t
, mode
) == True
)
391 Bool scanning_above_window
= True
;
395 q
= Scr
.FvwmRoot
.stack_next
;
396 q
!= &Scr
.FvwmRoot
&& t
->layer
<= q
->layer
;
399 if (t
->layer
< q
->layer
)
401 /* We're not interested in higher layers. */
404 else if (mode
== SM_RESTACK
&& IS_TRANSIENT(q
) &&
405 FW_W_TRANSIENTFOR(q
) == FW_W(t
))
411 /* We found our window. All further transients
413 scanning_above_window
= False
;
415 else if (IS_TRANSIENT(q
) &&
416 FW_W_TRANSIENTFOR(q
) == FW_W(t
))
420 else if (scanning_above_window
&& mode
== SM_RAISE
)
422 /* raise: The window is not raised, so itself
423 * and all transients will be raised. */
432 static Window
__get_stacking_sibling(FvwmWindow
*fw
, Bool do_stack_below
)
436 /* default to frame window */
438 if (IS_ICONIFIED(fw
) && do_stack_below
== True
)
440 /* override with icon windows when stacking below */
441 if (FW_W_ICON_PIXMAP(fw
) != None
)
443 w
= FW_W_ICON_PIXMAP(fw
);
445 else if (FW_W_ICON_TITLE(fw
) != None
)
447 w
= FW_W_ICON_TITLE(fw
);
454 static void __sort_transient_ring(FvwmWindow
*ring
)
461 if (ring
->stack_next
->stack_next
== ring
)
463 /* only one or zero windows */
466 /* Implementation note: this sorting algorithm is about the most
467 * inefficient possible. It just swaps the position of two adjacent
468 * windows in the ring if they are in the wrong order. Since
469 * transient windows are rare, this should not cause any notable
470 * performance hit. Because it is important that the order of windows
471 * with the same key is not changed, we can not just use qsort() here.
474 t
= ring
->stack_next
, prev
= ring
; t
->stack_next
!= ring
;
475 prev
= t
->stack_prev
)
478 if (t
->scratch
.i
< s
->scratch
.i
)
487 s
->stack_prev
->stack_next
= s
;
488 t
->stack_next
->stack_prev
= t
;
491 /* move further up the ring? */
496 /* hit start of ring */
501 /* correct order, advance one window */
509 static void __restack_window_list(
510 FvwmWindow
*r
, FvwmWindow
*s
, int count
, Bool do_broadcast_all
,
516 XWindowChanges changes
;
523 for (count
= 0, t
= r
->stack_next
; t
!= s
; t
= t
->stack_next
)
526 count
+= get_visible_icon_window_count(t
);
529 /* restack the windows between r and s */
530 wins
= (Window
*)safemalloc((count
+ 3) * sizeof(Window
));
531 for (t
= r
->stack_next
, i
= 0; t
!= s
; t
= t
->stack_next
)
536 ERR
, "__restack_window_list",
537 "more transients than expected");
540 wins
[i
++] = FW_W_FRAME(t
);
541 if (IS_ICONIFIED(t
) && !IS_ICON_SUPPRESSED(t
))
543 if (FW_W_ICON_TITLE(t
) != None
)
545 wins
[i
++] = FW_W_ICON_TITLE(t
);
547 if (FW_W_ICON_PIXMAP(t
) != None
)
549 wins
[i
++] = FW_W_ICON_PIXMAP(t
);
553 changes
.sibling
= __get_stacking_sibling(r
, True
);
554 if (changes
.sibling
== None
)
556 changes
.sibling
= __get_stacking_sibling(s
, False
);
563 if (changes
.sibling
== None
)
565 do_stack_above
= !do_lower
;
571 flags
= CWStackMode
| CWSibling
;
573 changes
.stack_mode
= (do_stack_above
^ is_reversed
) ? Above
: Below
;
574 XConfigureWindow(dpy
, FW_W_FRAME(r
->stack_next
), flags
, &changes
);
577 XRestackWindows(dpy
, wins
, count
);
580 EWMH_SetClientListStacking();
581 if (do_broadcast_all
)
583 /* send out M_RESTACK for all windows, to make sure we don't
584 * forget anything. */
585 BroadcastRestackAllWindows();
589 /* send out (one or more) M_RESTACK packets for windows
591 BroadcastRestack(r
, s
);
597 FvwmWindow
*__get_window_to_insert_after(FvwmWindow
*fw
, stack_mode_t mode
)
605 test_layer
= fw
->layer
- 1;
610 test_layer
= fw
->layer
;
614 s
= Scr
.FvwmRoot
.stack_next
; s
!= &Scr
.FvwmRoot
;
621 if (test_layer
>= s
->layer
)
630 static void __mark_group_member(
631 FvwmWindow
*fw
, FvwmWindow
*start
, FvwmWindow
*end
)
635 for (t
= start
; t
!= end
; t
= t
->stack_next
)
637 if (FW_W(t
) == fw
->wmhints
->window_group
||
638 (t
->wmhints
&& (t
->wmhints
->flags
& WindowGroupHint
) &&
639 t
->wmhints
->window_group
== fw
->wmhints
->window_group
))
641 if (IS_IN_TRANSIENT_SUBTREE(t
))
643 /* have to move this one too */
644 SET_IN_TRANSIENT_SUBTREE(fw
, 1);
653 static Bool
__mark_transient_subtree_test(
654 FvwmWindow
*s
, FvwmWindow
*start
, FvwmWindow
*end
, int mark_mode
,
655 Bool do_ignore_icons
, Bool use_window_group_hint
)
657 Bool use_group_hint
= False
;
660 if (IS_IN_TRANSIENT_SUBTREE(s
))
664 if (use_window_group_hint
&&
665 DO_ICONIFY_WINDOW_GROUPS(s
) && s
->wmhints
&&
666 (s
->wmhints
->flags
& WindowGroupHint
) &&
667 (s
->wmhints
->window_group
!= None
) &&
668 (s
->wmhints
->window_group
!= FW_W(s
)) &&
669 (s
->wmhints
->window_group
!= Scr
.Root
))
671 use_group_hint
= True
;
673 if (!IS_TRANSIENT(s
) && !use_group_hint
)
677 if (do_ignore_icons
&& IS_ICONIFIED(s
))
681 r
= (FvwmWindow
*)s
->scratch
.p
;
684 if (r
&& IS_IN_TRANSIENT_SUBTREE(r
) &&
685 ((mark_mode
== MARK_ALL
) ||
686 __is_restack_transients_needed(
687 r
, (stack_mode_t
)mark_mode
) == True
))
689 /* have to move this one too */
690 SET_IN_TRANSIENT_SUBTREE(s
, 1);
691 /* used for stacking transients */
692 s
->scratch
.i
+= r
->scratch
.i
+ 1;
696 if (use_group_hint
&& !IS_IN_TRANSIENT_SUBTREE(s
))
698 __mark_group_member(s
, start
, end
);
699 if (IS_IN_TRANSIENT_SUBTREE(s
))
701 /* need another scan through the list */
709 /* heavaly borrowed from mark_transient_subtree. This will mark a subtree as
710 * long as it is straight, and return true if the operation is succussful. It
711 * will abort and return False as soon as some inconsitance is hit. */
713 static Bool
is_transient_subtree_straight(
714 FvwmWindow
*t
, int layer
, stack_mode_t mode
, Bool do_ignore_icons
,
715 Bool use_window_group_hint
)
721 Bool has_passed_root
;
728 mark_mode
= MARK_RAISE
;
731 mark_mode
= MARK_LOWER
;
737 if (layer
>= 0 && t
->layer
!= layer
)
741 /* find out on which windows to operate */
742 /* iteration are done reverse (bottom up, since that's the way the
743 * transients wil be stacked if all is well */
746 /* only work on the given layer */
747 start
= &Scr
.FvwmRoot
;
750 s
= Scr
.FvwmRoot
.stack_prev
;
751 s
!= &Scr
.FvwmRoot
&& s
->layer
<= layer
;
754 if (s
->layer
== layer
)
756 if (start
== &Scr
.FvwmRoot
)
766 /* work on complete window list */
767 start
= Scr
.FvwmRoot
.stack_prev
;
770 /* clean the temporary flag in all windows and precalculate the
771 * transient frame windows */
773 s
= Scr
.FvwmRoot
.stack_next
; s
!= &Scr
.FvwmRoot
;
776 SET_IN_TRANSIENT_SUBTREE(s
, 0);
777 if (IS_TRANSIENT(s
) && (layer
< 0 || layer
== s
->layer
))
779 s
->scratch
.p
= get_transientfor_fvwmwindow(s
);
786 /* Indicate that no cleening is needed */
787 Scr
.FvwmRoot
.scratch
.i
= 1;
788 /* now loop over the windows and mark the ones we need to move */
789 SET_IN_TRANSIENT_SUBTREE(t
, 1);
791 has_passed_root
= False
;
794 if (mode
== SM_LOWER
&& t
!= start
)
799 for (s
= start
; s
!= end
&& !(is_in_gap
&& mode
== SM_RAISE
);
804 has_passed_root
= True
;
809 __mark_transient_subtree_test(
810 s
, start
, end
, mark_mode
,
812 use_window_group_hint
))
814 if (is_in_gap
|| !has_passed_root
)
818 else if (s
->scratch
.i
< min_i
)
822 min_i
= s
->scratch
.i
;
833 if (is_in_gap
&& mode
== SM_RAISE
)
841 /* function to test if all windows are at correct place from start. */
842 static Bool
__is_restack_needed(
843 FvwmWindow
*t
, stack_mode_t mode
, Bool do_restack_transients
,
851 if (mode
== SM_RESTACK
)
855 if (do_restack_transients
)
857 return !is_transient_subtree_straight(
858 t
, t
->layer
, mode
, True
, False
);
860 else if (mode
== SM_LOWER
)
862 return (t
->stack_next
!= &Scr
.FvwmRoot
&&
863 t
->stack_next
->layer
== t
->layer
);
865 else if (mode
== SM_RAISE
)
867 return (t
->stack_prev
!= &Scr
.FvwmRoot
&&
868 t
->stack_prev
->layer
== t
->layer
);
874 static Bool
__restack_window(
875 FvwmWindow
*t
, stack_mode_t mode
, Bool do_restack_transients
,
878 FvwmWindow
*s
= NULL
;
879 FvwmWindow
*r
= NULL
;
883 if (!__is_restack_needed(
884 t
, mode
, do_restack_transients
, is_new_window
))
886 /* need to cancel out the effect of any M_RAISE/M_LOWER that
887 * might already be send out. This is ugly. Better would be to
888 * not send the messages in the first place. */
889 if (do_restack_transients
)
892 while (IS_IN_TRANSIENT_SUBTREE(s
))
896 BroadcastRestack(t
->stack_prev
, s
);
899 /* return True: no need to do raise hacks if nothing canged */
904 if (do_restack_transients
)
906 /* collect the transients in a temp list */
907 tmp_r
.stack_prev
= &tmp_r
;
908 tmp_r
.stack_next
= &tmp_r
;
909 count
= collect_transients_recursive(
910 t
, &tmp_r
, t
->layer
, mode
, False
);
913 do_restack_transients
= False
;
916 count
+= 1 + get_visible_icon_window_count(t
);
917 /* now find the place to reinsert t and friends */
918 if (mode
== SM_RESTACK
)
924 s
= __get_window_to_insert_after(t
, mode
);
926 remove_window_from_stack_ring(t
);
928 if (do_restack_transients
)
930 /* re-sort the transient windows according to their scratch.i
932 __sort_transient_ring(&tmp_r
);
933 /* insert all transients between r and s. */
934 add_windowlist_to_stack_ring_after(&tmp_r
, r
);
938 ** Re-insert t - below transients
940 add_window_to_stack_ring_after(t
, s
->stack_prev
);
942 if (is_new_window
&& IS_TRANSIENT(t
) &&
943 DO_STACK_TRANSIENT_PARENT(t
) && !IS_ICONIFIED(t
))
945 /* now that the new transient is properly positioned in the
946 * stack ring, raise/lower it again so that its parent is
947 * raised/lowered too */
948 raise_or_lower_window(t
, mode
, True
, False
);
949 /* make sure the stacking order is correct - may be the
950 * sledge-hammer method, but the recursion ist too hard to
952 ResyncXStackingOrder();
957 /* restack the windows between r and s */
958 __restack_window_list(
959 r
, s
, count
, do_restack_transients
,
960 mode
== (SM_LOWER
) ? True
: False
);
966 static Bool
__raise_lower_recursion(
967 FvwmWindow
*t
, stack_mode_t mode
)
972 t2
= Scr
.FvwmRoot
.stack_next
; t2
!= &Scr
.FvwmRoot
;
975 if (FW_W(t2
) == FW_W_TRANSIENTFOR(t
))
981 if (IS_ICONIFIED(t2
) || t
->layer
!= t2
->layer
)
985 if (mode
== SM_LOWER
&&
986 (!IS_TRANSIENT(t2
) ||
987 !DO_STACK_TRANSIENT_PARENT(t2
)))
989 /* hit the highest level transient; lower this
990 * subtree below all other subtrees of the
992 t
->scratch
.i
= -LOWER_PENALTY
;
996 /* Add a bonus to the stack ring position for
997 * this branch of the transient tree over all
999 t
->scratch
.i
= MAX_TRANSIENTS_IN_BRANCH
;
1001 __raise_or_lower_window(t2
, mode
, True
, False
);
1002 if (__is_restack_transients_needed(t2
, mode
))
1004 /* moving the parent moves our window already */
1013 static void __raise_or_lower_window(
1014 FvwmWindow
*t
, stack_mode_t mode
, Bool allow_recursion
,
1018 Bool do_move_transients
;
1020 /* Do not raise this window after command execution (see
1021 * HandleButtonPress()). */
1022 SET_SCHEDULED_FOR_RAISE(t
, 0);
1024 /* New windows are simply raised/lowered without touching the
1025 * transientfor at first. Then, further down in the code,
1026 * __raise_or_lower_window() is called again to raise/lower the
1027 * transientfor if necessary. We can not do the recursion stuff for
1028 * new windows because the __must_move_transients() call needs a
1029 * properly ordered stack ring - but the new window is still at the
1030 * front of the stack ring. */
1031 if (allow_recursion
&& !is_new_window
&& !IS_ICONIFIED(t
))
1033 /* This part makes Raise/Lower on a Transient act on its Main
1034 * and sibling Transients.
1036 * The recursion is limited to one level - which caters for
1037 * most cases. This code does not handle the case where there
1038 * are trees of Main + Transient (i.e. where a
1039 * Main_window_with_Transients is itself Transient for another
1041 if (IS_TRANSIENT(t
) && DO_STACK_TRANSIENT_PARENT(t
))
1043 if (__raise_lower_recursion(t
, mode
) == True
)
1052 do_move_transients
= False
;
1056 do_move_transients
= __must_move_transients(t
, mode
);
1058 if (__restack_window(
1059 t
, mode
, do_move_transients
, is_new_window
) == True
)
1064 if (mode
== SM_RAISE
)
1066 /* This hack raises the target and all higher FVWM windows over
1067 * any style grabfocusoff override_redirect windows that may be
1068 * above it. This is used to cope with ill-behaved applications
1069 * that insist on using long-lived override_redirects. */
1070 if (Scr
.bo
.do_raise_over_unmanaged
)
1072 raise_over_unmanaged(t
);
1076 * The following is a hack to raise X windows over native
1077 * windows which is needed for some (all ?) X servers running
1078 * under Windows or Windows NT. */
1079 if (Scr
.bo
.is_raise_hack_needed
)
1081 /* RBW - 09/20/1999. I find that trying to raise
1082 * unmanaged windows causes problems with some apps. If
1083 * this seems to work well for everyone, I'll remove
1086 /* get *all* toplevels (even including
1087 * override_redirects) */
1088 XQueryTree(dpy
, Scr
.Root
, &junk
, &junk
, &tops
, &num
);
1090 /* raise from fw upwards to get them above NT windows */
1091 for (i
= 0; i
< num
; i
++)
1093 if (tops
[i
] == FW_W_FRAME(t
))
1099 XRaiseWindow (dpy
, tops
[i
]);
1104 for (t2
= t
; t2
!= &Scr
.FvwmRoot
; t2
= t2
->stack_prev
)
1106 XRaiseWindow (dpy
, FW_W_FRAME(t2
));
1109 /* This needs to be done after all the raise hacks. */
1111 /* If the window has been raised, make sure the decorations are
1112 * updated immediately in case we are in a complex function
1113 * (e.g. raise, unshade). */
1115 handle_all_expose();
1121 static void raise_or_lower_window(
1122 FvwmWindow
*t
, stack_mode_t mode
, Bool allow_recursion
,
1127 /* clean the auxiliary registers used in stacking transients */
1128 for (fw
= Scr
.FvwmRoot
.next
; fw
!= NULL
; fw
= fw
->next
)
1132 __raise_or_lower_window(t
, mode
, allow_recursion
, is_new_window
);
1137 static Bool
intersect(
1138 int x0
, int y0
, int w0
, int h0
, int x1
, int y1
, int w1
, int h1
)
1140 return !((x0
>= x1
+ w1
) || (x0
+ w0
<= x1
) ||
1141 (y0
>= y1
+ h1
) || (y0
+ h0
<= y1
));
1144 static Bool
overlap(FvwmWindow
*r
, FvwmWindow
*s
)
1150 if (r
->Desk
!= s
->Desk
)
1154 rc
= get_visible_window_or_icon_geometry(r
, &g1
);
1159 rc
= get_visible_window_or_icon_geometry(s
, &g2
);
1165 g1
.x
, g1
.y
, g1
.width
, g1
.height
,
1166 g2
.x
, g2
.y
, g2
.width
, g2
.height
);
1173 ResyncFvwmStackRing -
1174 Rebuilds the stacking order ring of FVWM-managed windows. For use in cases
1175 where apps raise/lower their own windows in a way that makes it difficult
1176 to determine exactly where they ended up in the stacking order.
1177 - Based on code from Matthias Clasen.
1179 static void ResyncFvwmStackRing (void)
1181 Window root
, parent
, *children
;
1182 unsigned int nchildren
;
1184 FvwmWindow
*t1
, *t2
;
1186 MyXGrabServer (dpy
);
1188 if (!XQueryTree (dpy
, Scr
.Root
, &root
, &parent
, &children
, &nchildren
))
1190 MyXUngrabServer (dpy
);
1195 for (i
= 0; i
< nchildren
; i
++)
1197 for (t1
= Scr
.FvwmRoot
.next
; t1
!= NULL
; t1
= t1
->next
)
1199 if (IS_ICONIFIED(t1
) && !IS_ICON_SUPPRESSED(t1
))
1201 if (FW_W_ICON_TITLE(t1
) == children
[i
] ||
1202 FW_W_ICON_PIXMAP(t1
) == children
[i
])
1209 if (FW_W_FRAME(t1
) == children
[i
])
1216 if (t1
!= NULL
&& t1
!= t2
)
1218 /* Move the window to its new position, working from
1219 * the bottom up (that's the way XQueryTree presents
1221 /* Pluck from chain. */
1222 remove_window_from_stack_ring(t1
);
1223 add_window_to_stack_ring_after(t1
, t2
->stack_prev
);
1224 if (t2
!= &Scr
.FvwmRoot
&& t2
->layer
> t1
->layer
)
1226 /* oops, now our stack ring is out of order! */
1228 t1
->layer
= t2
->layer
;
1234 MyXUngrabServer (dpy
);
1240 /* same as above but synchronizes the stacking order in X from the stack ring.
1242 static void ResyncXStackingOrder(void)
1249 for (count
= 0, t
= Scr
.FvwmRoot
.next
; t
!= NULL
; count
++, t
= t
->next
)
1251 /* nothing to do here */
1255 wins
= (Window
*)safemalloc(3 * count
* sizeof (Window
));
1257 i
= 0, t
= Scr
.FvwmRoot
.stack_next
; count
--;
1260 wins
[i
++] = FW_W_FRAME(t
);
1261 if (IS_ICONIFIED(t
) && !IS_ICON_SUPPRESSED(t
))
1263 if (FW_W_ICON_TITLE(t
) != None
)
1265 wins
[i
++] = FW_W_ICON_TITLE(t
);
1267 if (FW_W_ICON_PIXMAP(t
) != None
)
1269 wins
[i
++] = FW_W_ICON_PIXMAP(t
);
1273 XRestackWindows(dpy
, wins
, i
);
1275 /* send out M_RESTACK for all windows, to make sure we don't
1276 * forget anything. */
1277 BroadcastRestackAllWindows();
1283 /* send RESTACK packets for all windows between s1 and s2 */
1284 static void BroadcastRestack(FvwmWindow
*s1
, FvwmWindow
*s2
)
1291 unsigned long *body
, *bp
, length
;
1292 unsigned long max_wins_per_packet
;
1294 if (s2
== &Scr
.FvwmRoot
)
1296 s2
= s2
->stack_prev
;
1297 if (s2
== &Scr
.FvwmRoot
)
1302 if (s1
== &Scr
.FvwmRoot
)
1304 s1
= s1
->stack_next
;
1305 if (s1
== &Scr
.FvwmRoot
)
1309 /* s1 has been moved to the top of stack */
1311 M_RAISE_WINDOW
, 3, (long)FW_W(s1
),
1312 (long)FW_W_FRAME(s1
), (unsigned long)s1
);
1313 if (s1
->stack_next
== s2
)
1315 /* avoid sending empty RESTACK packet */
1321 /* A useful M_RESTACK packet must contain at least two windows.
1326 fw
= s1
, num
= 1; fw
!= s2
&& fw
!= &Scr
.FvwmRoot
;
1327 fw
= fw
->stack_next
, num
++)
1331 max_wins_per_packet
= (FvwmPacketMaxSize
- FvwmPacketHeaderSize
) / 3;
1332 /* split packet if it is too long */
1333 for ( ; num
> 1; s1
= fw
, num
-= n
)
1335 n
= min(num
, max_wins_per_packet
) - 1;
1336 length
= FvwmPacketHeaderSize
+ 3 * (n
+ 1);
1337 body
= (unsigned long *)safemalloc(
1338 length
* sizeof(unsigned long));
1340 *(bp
++) = START_FLAG
;
1341 *(bp
++) = M_RESTACK
;
1343 *(bp
++) = fev_get_evtime();
1344 for (fw
= s1
, i
= 0; i
<= n
; i
++, fw
= fw
->stack_next
)
1347 *(bp
++) = FW_W_FRAME(fw
);
1348 *(bp
++) = (unsigned long)fw
;
1350 module
= module_get_next(NULL
);
1351 for (; module
!= NULL
; module
= module_get_next(module
))
1354 module
, body
, length
*sizeof(unsigned long));
1358 #ifdef DEBUG_STACK_RING
1359 verify_stack_ring_consistency();
1365 static int collect_transients_recursive(
1366 FvwmWindow
*t
, FvwmWindow
*list_head
, int layer
, stack_mode_t mode
,
1367 Bool do_include_target_window
)
1385 /* can not happen */
1389 mark_transient_subtree(t
, layer
, m
, True
, False
);
1390 /* now collect the marked windows in a separate list */
1391 for (s
= Scr
.FvwmRoot
.stack_next
; s
!= &Scr
.FvwmRoot
; )
1395 if (s
== t
&& do_include_target_window
== False
)
1397 /* ignore the target window */
1401 tmp
= s
->stack_next
;
1402 if (IS_IN_TRANSIENT_SUBTREE(s
))
1404 remove_window_from_stack_ring(s
);
1405 add_window_to_stack_ring_after(
1406 s
, list_head
->stack_prev
);
1408 count
+= get_visible_icon_window_count(t
);
1416 static Bool
is_above_unmanaged(FvwmWindow
*fw
, Window
*umtop
)
1419 Chase through the entire stack of the server's windows looking
1420 for any unmanaged window that's higher than the target.
1421 Called from raise_over_unmanaged and is_on_top_of_layer.
1428 Window OR_Above
= None
;
1429 XWindowAttributes wa
;
1431 if (fw
->Desk
!= Scr
.CurrentDesk
)
1435 if (!XQueryTree(dpy
, Scr
.Root
, &junk
, &junk
, &tops
, &num
))
1441 * Locate the highest override_redirect window above our target, and
1442 * the highest of our windows below it.
1444 for (i
= 0; i
< num
&& tops
[i
] != FW_W_FRAME(fw
); i
++)
1446 /* look for target window in list */
1448 for (; i
< num
; i
++)
1450 /* It might be just as well (and quicker) just to check for the
1451 * absence of an FvwmContext instead of for
1452 * override_redirect... */
1453 if (!XGetWindowAttributes(dpy
, tops
[i
], &wa
))
1458 Don't forget to ignore the hidden frame resizing windows...
1460 if (wa
.override_redirect
== True
1461 && wa
.class != InputOnly
1462 && tops
[i
] != Scr
.NoFocusWin
1463 && (!is_frame_hide_window(tops
[i
])))
1480 static Bool
is_on_top_of_layer_ignore_rom(FvwmWindow
*fw
)
1485 if (IS_SCHEDULED_FOR_DESTROY(fw
))
1487 /* stack ring members are no longer valid */
1490 if (DO_RAISE_TRANSIENT(fw
))
1492 mark_transient_subtree(fw
, fw
->layer
, MARK_RAISE
, True
, False
);
1494 for (t
= fw
->stack_prev
; t
!= &Scr
.FvwmRoot
; t
= t
->stack_prev
)
1496 if (t
->layer
> fw
->layer
)
1500 if (t
->Desk
!= fw
->Desk
)
1504 /* For RaiseOverUnmanaged we can not determine if the window is
1505 * on top by checking if the window overlaps another one. If
1506 * it was below unmanaged windows, but on top of its layer, it
1507 * would be considered on top. */
1508 if (Scr
.bo
.do_raise_over_unmanaged
|| overlap(fw
, t
))
1510 if (!DO_RAISE_TRANSIENT(fw
) ||
1511 (!IS_IN_TRANSIENT_SUBTREE(t
) && t
!= fw
))
1522 static Bool
__is_on_top_of_layer(FvwmWindow
*fw
, Bool client_entered
)
1526 if (Scr
.bo
.do_raise_over_unmanaged
)
1529 #define EXPERIMENTAL_ROU_HANDLING
1530 #ifdef EXPERIMENTAL_ROU_HANDLING
1533 RaiseOverUnmanaged adds some overhead. The only way to let our
1534 caller know for sure whether we need to grab the mouse buttons
1535 because we may need to raise this window is to query the
1536 server's tree and look for any override_redirect windows above
1538 But this function is called far too often to do this every
1539 time. Only if the window is at the top of the FvwmWindow
1540 stack do we need more information from the server; and then
1541 only at the last moment in HandleEnterNotify when we really
1542 need to know whether a raise will be needed if the user
1543 clicks in the client window.
1544 is_on_top_of_layer_and_above_unmanaged is called in that case.
1546 if (is_on_top_of_layer_ignore_rom(fw
))
1549 /* FIXME! - perhaps we should only do if MFCR */
1552 printf("RBW-iotol - %8.8lx is on top,"
1553 " checking server tree. ***\n",
1556 ontop
= is_above_unmanaged(fw
, &junk
);
1558 printf(" returning %d\n", (int) ontop
);
1564 printf("RBW-iotol - %8.8lx is on top,"
1565 " *** NOT checking server tree.\n",
1577 return False
; /* Old pre-2002/08/22 handling. */
1582 return is_on_top_of_layer_ignore_rom(fw
);
1586 /* ---------------------------- interface functions ------------------------ */
1588 /* Remove a window from the stack ring */
1589 void remove_window_from_stack_ring(FvwmWindow
*t
)
1591 if (IS_SCHEDULED_FOR_DESTROY(t
))
1595 t
->stack_prev
->stack_next
= t
->stack_next
;
1596 t
->stack_next
->stack_prev
= t
->stack_prev
;
1597 /* not really necessary, but gives a little more saftey */
1598 t
->stack_prev
= NULL
;
1599 t
->stack_next
= NULL
;
1604 /* Add window t to the stack ring after window t */
1605 void add_window_to_stack_ring_after(FvwmWindow
*t
, FvwmWindow
*add_after_win
)
1607 if (IS_SCHEDULED_FOR_DESTROY(t
))
1611 if (t
== add_after_win
|| t
== add_after_win
->stack_next
)
1613 /* tried to add the window before or after itself */
1615 ERR
, "add_window_to_stack_ring_after",
1616 "BUG: tried to add window '%s' %s itself in stack"
1618 t
->name
.name
, (t
== add_after_win
) ?
1619 "after" : "before");
1622 t
->stack_next
= add_after_win
->stack_next
;
1623 add_after_win
->stack_next
->stack_prev
= t
;
1624 t
->stack_prev
= add_after_win
;
1625 add_after_win
->stack_next
= t
;
1630 FvwmWindow
*get_next_window_in_stack_ring(const FvwmWindow
*t
)
1632 return t
->stack_next
;
1635 FvwmWindow
*get_prev_window_in_stack_ring(const FvwmWindow
*t
)
1637 return t
->stack_prev
;
1640 FvwmWindow
*get_transientfor_fvwmwindow(const FvwmWindow
*t
)
1644 if (!t
|| !IS_TRANSIENT(t
) || FW_W_TRANSIENTFOR(t
) == Scr
.Root
||
1645 FW_W_TRANSIENTFOR(t
) == None
)
1649 for (s
= Scr
.FvwmRoot
.next
; s
!= NULL
; s
= s
->next
)
1651 if (FW_W(s
) == FW_W_TRANSIENTFOR(t
))
1653 return (s
== t
) ? NULL
: s
;
1660 /* Takes a window from the top of the stack ring and puts it at the appropriate
1661 * place. Called when new windows are created. */
1662 Bool
position_new_window_in_stack_ring(FvwmWindow
*t
, Bool do_lower
)
1664 if (t
->stack_prev
!= &Scr
.FvwmRoot
)
1666 /* Not at top of stack ring, so it is already in place.
1667 * add_window.c relies on this. */
1670 /* RaiseWindow/LowerWindow will put the window in its layer */
1671 raise_or_lower_window(t
, (do_lower
) ? SM_LOWER
: SM_RAISE
, False
, True
);
1676 /* Raise t and its transients to the top of its layer. For the pager to work
1677 * properly it is necessary that RaiseWindow *always* sends a proper M_RESTACK
1678 * packet, even if the stacking order didn't change. */
1679 void RaiseWindow(FvwmWindow
*t
)
1682 M_RAISE_WINDOW
, 3, (long)FW_W(t
), (long)FW_W_FRAME(t
),
1684 raise_or_lower_window(t
, SM_RAISE
, True
, False
);
1685 focus_grab_buttons_on_layer(t
->layer
);
1686 #ifdef DEBUG_STACK_RING
1687 verify_stack_ring_consistency();
1692 void LowerWindow(FvwmWindow
*t
)
1695 M_LOWER_WINDOW
, 3, (long)FW_W(t
), (long)FW_W_FRAME(t
),
1697 raise_or_lower_window(t
, SM_LOWER
, True
, False
);
1698 focus_grab_buttons_on_layer(t
->layer
);
1699 #ifdef DEBUG_STACK_RING
1700 verify_stack_ring_consistency();
1705 void RestackWindow(FvwmWindow
*t
)
1707 raise_or_lower_window(t
, SM_RESTACK
, True
, False
);
1708 focus_grab_buttons_on_layer(t
->layer
);
1709 #ifdef DEBUG_STACK_RING
1710 verify_stack_ring_consistency();
1715 /* return true if stacking order changed */
1716 Bool
HandleUnusualStackmodes(
1717 unsigned int stack_mode
, FvwmWindow
*r
, Window rw
, FvwmWindow
*s
,
1723 /* DBUG("HandleUnusualStackmodes", "called with %d, %lx\n",
1727 ((rw
!= FW_W(r
)) ^ IS_ICONIFIED(r
)) ||
1728 (s
&& (((sw
!= FW_W(s
)) ^ IS_ICONIFIED(s
)) ||
1729 (r
->Desk
!= s
->Desk
))))
1731 /* one of the relevant windows is unmapped */
1739 t
= r
->stack_prev
; t
!= &Scr
.FvwmRoot
&& !do_restack
;
1742 do_restack
= ((s
== NULL
|| s
== t
) && overlap(t
, r
));
1751 t
= r
->stack_next
; t
!= &Scr
.FvwmRoot
&& !do_restack
;
1754 do_restack
= ((s
== NULL
|| s
== t
) && overlap(t
, r
));
1763 HandleUnusualStackmodes(TopIf
, r
, rw
, s
, sw
) ||
1764 HandleUnusualStackmodes(BottomIf
, r
, rw
, s
, sw
));
1767 /* DBUG("HandleUnusualStackmodes", "\t---> %d\n", do_restack);*/
1768 #ifdef DEBUG_STACK_RING
1769 verify_stack_ring_consistency();
1775 RBW - 01/07/1998 - this is here temporarily - I mean to move it to
1776 libfvwm eventually, along with some other chain manipulation functions.
1779 void BroadcastRestackAllWindows(void)
1781 BroadcastRestack(Scr
.FvwmRoot
.stack_next
, Scr
.FvwmRoot
.stack_prev
);
1785 /* send RESTACK packets for t, t->stack_prev and t->stack_next */
1786 void BroadcastRestackThisWindow(FvwmWindow
*t
)
1788 BroadcastRestack(t
->stack_prev
, t
->stack_next
);
1792 /* returns 0 if s and t are on the same layer, <1 if t is on a lower layer and
1793 * >1 if t is on a higher layer. */
1794 int compare_window_layers(FvwmWindow
*t
, FvwmWindow
*s
)
1796 return t
->layer
- s
->layer
;
1799 void set_default_layer(FvwmWindow
*t
, int layer
)
1801 t
->default_layer
= layer
;
1805 void set_layer(FvwmWindow
*t
, int layer
)
1811 int get_layer(FvwmWindow
*t
)
1816 /* This function recursively finds the transients of the window t and sets their
1817 * is_in_transient_subtree flag. If a layer is given, only windows in this
1818 * layer are checked. If the layer is < 0, all windows are considered.
1820 void mark_transient_subtree(
1821 FvwmWindow
*t
, int layer
, int mark_mode
, Bool do_ignore_icons
,
1822 Bool use_window_group_hint
)
1829 if (layer
>= 0 && t
->layer
!= layer
)
1833 /* find out on which windows to operate */
1836 /* only work on the given layer */
1837 start
= &Scr
.FvwmRoot
;
1838 end
= &Scr
.FvwmRoot
;
1840 s
= Scr
.FvwmRoot
.stack_next
;
1841 s
!= &Scr
.FvwmRoot
&& s
->layer
>= layer
;
1846 /* ignore the target window */
1849 if (s
->layer
== layer
)
1851 if (start
== &Scr
.FvwmRoot
)
1855 end
= s
->stack_next
;
1861 /* work on complete window list */
1862 start
= Scr
.FvwmRoot
.stack_next
;
1863 end
= &Scr
.FvwmRoot
;
1865 /* clean the temporary flag in all windows and precalculate the
1866 * transient frame windows */
1867 if (Scr
.FvwmRoot
.scratch
.i
== 0)
1870 s
= Scr
.FvwmRoot
.stack_next
; s
!= &Scr
.FvwmRoot
;
1873 SET_IN_TRANSIENT_SUBTREE(s
, 0);
1876 (layer
< 0 || layer
== s
->layer
))
1878 s
->scratch
.p
= get_transientfor_fvwmwindow(s
);
1882 s
->scratch
.p
= NULL
;
1886 Scr
.FvwmRoot
.scratch
.i
= 0;
1888 /* now loop over the windows and mark the ones we need to move */
1889 SET_IN_TRANSIENT_SUBTREE(t
, 1);
1890 is_finished
= False
;
1891 while (!is_finished
)
1893 /* recursively search for all transient windows */
1895 for (s
= start
; s
!= end
; s
= s
->stack_next
)
1899 __mark_transient_subtree_test(
1900 s
, start
, end
, mark_mode
,
1902 use_window_group_hint
))
1904 is_finished
= False
;
1912 void new_layer(FvwmWindow
*fw
, int layer
)
1917 FvwmWindow list_head
;
1918 int add_after_layer
;
1927 fw
= get_transientfor_top_fvwmwindow(fw
);
1928 if (layer
== fw
->layer
)
1932 old_layer
= fw
->layer
;
1933 list_head
.stack_next
= &list_head
;
1934 list_head
.stack_prev
= &list_head
;
1935 count
= collect_transients_recursive(
1936 fw
, &list_head
, fw
->layer
,
1937 (layer
< fw
->layer
) ? SM_LOWER
: SM_RAISE
, True
);
1940 /* no windows to move */
1943 add_after_layer
= layer
;
1944 if (layer
< fw
->layer
)
1946 /* lower below the windows in the new (lower) layer */
1947 add_after_layer
= layer
;
1952 /* raise above the windows in the new (higher) layer */
1953 add_after_layer
= layer
+ 1;
1956 /* find the place to insert the windows */
1958 target
= Scr
.FvwmRoot
.stack_next
; target
!= &Scr
.FvwmRoot
;
1959 target
= target
->stack_next
)
1961 if (target
->layer
< add_after_layer
)
1963 /* add all windows before the current window */
1967 /* insert windows at new position */
1968 add_windowlist_to_stack_ring_after(&list_head
, target
->stack_prev
);
1971 s
= list_head
.stack_next
; prev
!= list_head
.stack_prev
;
1972 prev
= s
, s
= s
->stack_next
)
1975 /* redraw title and buttons to update layer buttons */
1976 border_draw_decorations(
1977 s
, PART_TITLEBAR
, (Scr
.Hilite
== fw
), True
, CLEAR_NONE
,
1980 EWMH_SetWMState(fw
, False
);
1982 /* move the windows without modifying their stacking order */
1983 __restack_window_list(
1984 list_head
.stack_next
->stack_prev
, target
, count
, (count
> 1),
1986 focus_grab_buttons_on_layer(layer
);
1987 focus_grab_buttons_on_layer(old_layer
);
1992 /* RBW - 11/13/1998 - 2 new fields to init - stacking order chain. */
1993 void init_stack_and_layers(void)
1995 Scr
.BottomLayer
= DEFAULT_BOTTOM_LAYER
;
1996 Scr
.DefaultLayer
= DEFAULT_DEFAULT_LAYER
;
1997 Scr
.TopLayer
= DEFAULT_TOP_LAYER
;
1998 Scr
.FvwmRoot
.stack_next
= &Scr
.FvwmRoot
;
1999 Scr
.FvwmRoot
.stack_prev
= &Scr
.FvwmRoot
;
2000 set_layer(&Scr
.FvwmRoot
, DEFAULT_ROOT_WINDOW_LAYER
);
2004 Bool
is_on_top_of_layer(FvwmWindow
*fw
)
2006 return __is_on_top_of_layer(fw
, False
);
2009 Bool
is_on_top_of_layer_and_above_unmanaged(FvwmWindow
*fw
)
2011 return __is_on_top_of_layer(fw
, True
);
2014 /* ----------------------------- built in functions ----------------------- */
2016 void CMD_Raise(F_CMD_ARGS
)
2018 RaiseWindow(exc
->w
.fw
);
2023 void CMD_Lower(F_CMD_ARGS
)
2025 LowerWindow(exc
->w
.fw
);
2030 void CMD_RestackTransients(F_CMD_ARGS
)
2032 RestackWindow(exc
->w
.fw
);
2037 void CMD_RaiseLower(F_CMD_ARGS
)
2040 FvwmWindow
* const fw
= exc
->w
.fw
;
2042 ontop
= is_on_top_of_layer_ignore_rom(fw
);
2055 void CMD_Layer(F_CMD_ARGS
)
2057 int n
, layer
, val
[2];
2059 FvwmWindow
* const fw
= exc
->w
.fw
;
2065 token
= PeekToken(action
, NULL
);
2066 if (StrEquals("default", token
))
2068 layer
= fw
->default_layer
;
2072 n
= GetIntegerArguments(action
, NULL
, val
, 2);
2076 ((n
== 2) && (val
[0] != 0)))
2080 else if ((n
== 2) && (val
[1] >= 0))
2086 layer
= fw
->default_layer
;
2093 new_layer(fw
, layer
);
2094 #ifdef DEBUG_STACK_RING
2095 verify_stack_ring_consistency();
2101 void CMD_DefaultLayers(F_CMD_ARGS
)
2108 bot
= PeekToken(action
, &action
);
2115 ERR
, "DefaultLayers",
2116 "Layer must be non-negative." );
2120 Scr
.BottomLayer
= i
;
2123 def
= PeekToken(action
, &action
);
2130 ERR
, "DefaultLayers",
2131 "Layer must be non-negative." );
2135 Scr
.DefaultLayer
= i
;
2138 top
= PeekToken(action
, &action
);
2145 ERR
, "DefaultLayers",
2146 "Layer must be non-negative." );
2153 #ifdef DEBUG_STACK_RING
2154 verify_stack_ring_consistency();