* move common tests to function to avoid code duplication
[fvwm.git] / fvwm / stack.c
blob7ccb93795cad07f0e0709d2e9c55e1ca6721daa9
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>
24 #include "ftime.h"
26 #include "libs/fvwmlib.h"
27 #include <libs/gravity.h>
28 #include "fvwm.h"
29 #include "externs.h"
30 #include "cursor.h"
31 #include "functions.h"
32 #include "bindings.h"
33 #include "misc.h"
34 #include "screen.h"
35 #include "defaults.h"
36 #include "module_list.h"
37 #include "module_interface.h"
38 #include "focus.h"
39 #include "stack.h"
40 #include "events.h"
41 #include "borders.h"
42 #include "virtual.h"
43 #include "geometry.h"
44 #include "icons.h"
45 #include "gnome.h"
46 #include "ewmh.h"
47 #include "frame.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 -------------------------------- */
67 typedef enum
69 SM_RAISE = MARK_RAISE,
70 SM_LOWER = MARK_LOWER,
71 SM_RESTACK = MARK_RESTACK
72 } stack_mode_t;
74 /* ---------------------------- forward declarations ----------------------- */
76 static void __raise_or_lower_window(
77 FvwmWindow *t, stack_mode_t mode, Bool allow_recursion,
78 Bool is_new_window);
79 static void raise_or_lower_window(
80 FvwmWindow *t, stack_mode_t mode, Bool allow_recursion,
81 Bool is_new_window);
82 #if 0
83 static void ResyncFvwmStackRing(void);
84 #endif
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)
103 FvwmWindow *t1;
105 if (!debugging_stack_ring)
107 return;
109 XBell(dpy, 0);
110 fprintf(stderr,"dumping stack ring:\n");
111 for (
112 t1 = Scr.FvwmRoot.stack_next; t1 != &Scr.FvwmRoot;
113 t1 = t1->stack_next)
115 fprintf(stderr," l=%d fw=%p f=0x%08x '%s'\n", t1->layer,
116 t1, (int)FW_W_FRAME(t1), t1->name.name);
119 return;
122 /* debugging function */
123 void verify_stack_ring_consistency(void)
125 Window root, parent, *children;
126 unsigned int nchildren;
127 int i;
128 FvwmWindow *t1, *t2;
129 int last_layer;
130 int last_index;
132 if (!debugging_stack_ring)
134 return;
136 XFlush(dpy);
137 t2 = Scr.FvwmRoot.stack_next;
138 if (t2 == &Scr.FvwmRoot)
140 return;
142 last_layer = t2->layer;
144 for (
145 t1 = t2->stack_next; t1 != &Scr.FvwmRoot;
146 t2 = t1, t1 = t1->stack_next)
148 if (t1->layer > last_layer)
150 fprintf(
151 stderr,
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);
156 dump_stack_ring();
157 return;
159 last_layer = t1->layer;
161 t2 = &Scr.FvwmRoot;
162 for (
163 t1 = t2->stack_next; t1 != &Scr.FvwmRoot;
164 t2 = t1, t1 = t1->stack_next)
166 if (t1->stack_prev != t2)
168 break;
171 if (t1 != &Scr.FvwmRoot || t1->stack_prev != t2)
173 fprintf(
174 stderr,
175 "vsrc: stack ring is corrupt -"
176 " fvwm will probably crash! %p -> %p but %p <- %p",
177 t2, t1, t1->stack_prev, t1);
178 dump_stack_ring();
179 return;
181 MyXGrabServer(dpy);
182 if (!XQueryTree(dpy, Scr.Root, &root, &parent, &children, &nchildren))
184 MyXUngrabServer(dpy);
185 return;
188 last_index = nchildren;
189 for (
190 t1 = Scr.FvwmRoot.stack_next; t1 != &Scr.FvwmRoot;
191 t1 = t1->stack_next)
193 /* find window in window list */
194 for (
195 i = 0; i < nchildren && FW_W_FRAME(t1) != children[i];
196 i++)
198 /* nothing to do here */
200 if (i == nchildren)
202 fprintf(
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)
209 fprintf(
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),
213 t1->name.name);
214 dump_stack_ring();
215 fprintf(stderr,"dumping X stacking order:\n");
216 for (i = nchildren; i-- > 0; )
218 for (
219 t1 = Scr.FvwmRoot.stack_next;
220 t1 != &Scr.FvwmRoot;
221 t1 = t1->stack_next)
223 /* only dump frame windows */
224 if (FW_W_FRAME(t1) == children[i])
226 fprintf(
227 stderr, " f=0x%08x\n",
228 (int)children[i]);
229 break;
233 MyXUngrabServer(dpy);
234 XFree(children);
235 return;
237 last_index = i;
239 MyXUngrabServer(dpy);
240 XFree(children);
242 return;
244 #endif
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;
255 return;
258 static FvwmWindow *get_transientfor_top_fvwmwindow(FvwmWindow *t)
260 FvwmWindow *s;
262 s = t;
263 while (s && IS_TRANSIENT(s) && DO_STACK_TRANSIENT_PARENT(s))
265 s = get_transientfor_fvwmwindow(s);
266 if (s)
268 t = s;
272 return t;
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,
282 * and restack.
284 static void raise_over_unmanaged(FvwmWindow *t)
286 int i;
287 Window OR_Above = None;
288 Window *wins;
289 int count = 0;
290 FvwmWindow *t2 = NULL;
291 unsigned int flags;
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))
303 for (
304 count = 0, t2 = Scr.FvwmRoot.stack_next;
305 t2 != &Scr.FvwmRoot; t2 = t2->stack_next)
307 count++;
308 count += get_visible_icon_window_count(t2);
309 if (t2 == t)
311 break;
315 if (count > 0)
317 wins = (Window*) safemalloc (count * sizeof (Window));
318 for (
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)
332 wins[i++] =
333 FW_W_ICON_PIXMAP(t2);
336 if (t2 == t)
338 break;
342 memset(&changes, '\0', sizeof(changes));
343 changes.sibling = OR_Above;
344 changes.stack_mode = Above;
345 flags = CWSibling|CWStackMode;
347 XConfigureWindow(
348 dpy, FW_W_FRAME(t)/*topwin*/, flags, &changes);
349 if (i > 1)
351 XRestackWindows(dpy, wins, i);
353 free (wins);
355 }/* end - we found an OR above our target */
357 return;
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)
367 return True;
370 if (DO_LOWER_TRANSIENT(t))
372 if (mode == SM_LOWER || mode == SM_RESTACK)
374 return True;
378 return False;
381 static Bool __must_move_transients(
382 FvwmWindow *t, stack_mode_t mode)
384 if (IS_ICONIFIED(t))
386 return False;
388 /* raise */
389 if (__is_restack_transients_needed(t, mode) == True)
391 Bool scanning_above_window = True;
392 FvwmWindow *q;
394 for (
395 q = Scr.FvwmRoot.stack_next;
396 q != &Scr.FvwmRoot && t->layer <= q->layer;
397 q = q->stack_next)
399 if (t->layer < q->layer)
401 /* We're not interested in higher layers. */
402 continue;
404 else if (mode == SM_RESTACK && IS_TRANSIENT(q) &&
405 FW_W_TRANSIENTFOR(q) == FW_W(t))
407 return True;
409 else if (t == q)
411 /* We found our window. All further transients
412 * are below it. */
413 scanning_above_window = False;
415 else if (IS_TRANSIENT(q) &&
416 FW_W_TRANSIENTFOR(q) == FW_W(t))
418 return True;
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. */
424 return True;
429 return False;
432 static Window __get_stacking_sibling(FvwmWindow *fw, Bool do_stack_below)
434 Window w;
436 /* default to frame window */
437 w = FW_W_FRAME(fw);
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);
451 return w;
454 static void __sort_transient_ring(FvwmWindow *ring)
456 FvwmWindow *s;
457 FvwmWindow *t;
458 FvwmWindow *u;
459 FvwmWindow *prev;
461 if (ring->stack_next->stack_next == ring)
463 /* only one or zero windows */
464 return;
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.
473 for (
474 t = ring->stack_next, prev = ring; t->stack_next != ring;
475 prev = t->stack_prev)
477 s = t->stack_next;
478 if (t->scratch.i < s->scratch.i)
480 /* swap windows */
481 u = s->stack_next;
482 s->stack_next = t;
483 t->stack_next = u;
484 u = t->stack_prev;
485 t->stack_prev = s;
486 s->stack_prev = u;
487 s->stack_prev->stack_next = s;
488 t->stack_next->stack_prev = t;
489 if (prev != ring)
491 /* move further up the ring? */
492 t = prev;
494 else
496 /* hit start of ring */
499 else
501 /* correct order, advance one window */
502 t = t->stack_next;
506 return;
509 static void __restack_window_list(
510 FvwmWindow *r, FvwmWindow *s, int count, Bool do_broadcast_all,
511 Bool do_lower)
513 FvwmWindow *t;
514 unsigned int flags;
515 int i;
516 XWindowChanges changes;
517 Window *wins;
518 int do_stack_above;
519 int is_reversed;
521 if (count <= 0)
523 for (count = 0, t = r->stack_next; t != s; t = t->stack_next)
525 count++;
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)
533 if (i > count)
535 fvwm_msg(
536 ERR, "__restack_window_list",
537 "more transients than expected");
538 break;
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);
557 is_reversed = 1;
559 else
561 is_reversed = 0;
563 if (changes.sibling == None)
565 do_stack_above = !do_lower;
566 flags = CWStackMode;
568 else
570 do_stack_above = 0;
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);
575 if (count > 1)
577 XRestackWindows(dpy, wins, count);
579 free(wins);
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();
587 else
589 /* send out (one or more) M_RESTACK packets for windows
590 * between r and s */
591 BroadcastRestack(r, s);
594 return;
597 FvwmWindow *__get_window_to_insert_after(FvwmWindow *fw, stack_mode_t mode)
599 int test_layer;
600 FvwmWindow *s;
602 switch (mode)
604 case SM_LOWER:
605 test_layer = fw->layer - 1;
606 break;
607 default:
608 case SM_RAISE:
609 case SM_RESTACK:
610 test_layer = fw->layer;
611 break;
613 for (
614 s = Scr.FvwmRoot.stack_next; s != &Scr.FvwmRoot;
615 s = s->stack_next)
617 if (s == fw)
619 continue;
621 if (test_layer >= s->layer)
623 break;
627 return s;
630 static void __mark_group_member(
631 FvwmWindow *fw, FvwmWindow *start, FvwmWindow *end)
633 FvwmWindow *t;
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);
649 return;
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;
658 FvwmWindow *r;
660 if (IS_IN_TRANSIENT_SUBTREE(s))
662 return False;
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)
675 return False;
677 if (do_ignore_icons && IS_ICONIFIED(s))
679 return False;
681 r = (FvwmWindow *)s->scratch.p;
682 if (IS_TRANSIENT(s))
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;
693 return True;
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 */
702 return True;
706 return False;
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)
717 FvwmWindow *s;
718 FvwmWindow *start;
719 FvwmWindow *end;
720 int min_i;
721 Bool has_passed_root;
722 Bool is_in_gap;
723 int mark_mode;
725 switch (mode)
727 case SM_RAISE:
728 mark_mode = MARK_RAISE;
729 break;
730 case SM_LOWER:
731 mark_mode = MARK_LOWER;
732 break;
733 default:
734 return False;
737 if (layer >= 0 && t->layer != layer)
739 return True;
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 */
744 if (layer >= 0)
746 /* only work on the given layer */
747 start = &Scr.FvwmRoot;
748 end = &Scr.FvwmRoot;
749 for (
750 s = Scr.FvwmRoot.stack_prev;
751 s != &Scr.FvwmRoot && s->layer <= layer;
752 s = s->stack_prev)
754 if (s->layer == layer)
756 if (start == &Scr.FvwmRoot)
758 start = s;
760 end = s->stack_prev;
764 else
766 /* work on complete window list */
767 start = Scr.FvwmRoot.stack_prev;
768 end = &Scr.FvwmRoot;
770 /* clean the temporary flag in all windows and precalculate the
771 * transient frame windows */
772 for (
773 s = Scr.FvwmRoot.stack_next; s != &Scr.FvwmRoot;
774 s = s->stack_next)
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);
781 else
783 s->scratch.p = NULL;
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);
790 min_i = INT_MIN;
791 has_passed_root = False;
792 is_in_gap = False;
794 if (mode == SM_LOWER && t != start)
796 return False;
799 for (s = start; s != end && !(is_in_gap && mode == SM_RAISE);
800 s = s->stack_prev)
802 if (t == s)
804 has_passed_root = True;
806 else
808 if (
809 __mark_transient_subtree_test(
810 s, start, end, mark_mode,
811 do_ignore_icons,
812 use_window_group_hint))
814 if (is_in_gap || !has_passed_root)
816 return False;
818 else if (s->scratch.i < min_i)
820 return False;
822 min_i = s->scratch.i;
824 else
826 if (has_passed_root)
828 is_in_gap = True;
832 } /* for */
833 if (is_in_gap && mode == SM_RAISE)
835 return False;
838 return True;
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,
844 Bool is_new_window)
846 if (is_new_window)
848 return True;
851 if (mode == SM_RESTACK)
853 return True;
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);
871 return True;
874 static Bool __restack_window(
875 FvwmWindow *t, stack_mode_t mode, Bool do_restack_transients,
876 Bool is_new_window)
878 FvwmWindow *s = NULL;
879 FvwmWindow *r = NULL;
880 FvwmWindow tmp_r;
881 int count;
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)
891 s = t->stack_next;
892 while (IS_IN_TRANSIENT_SUBTREE(s))
894 s = s->stack_next;
896 BroadcastRestack(t->stack_prev, s);
899 /* return True: no need to do raise hacks if nothing canged */
900 return True;
903 count = 0;
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);
911 if (count == 0)
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)
920 s = t->stack_next;
922 else
924 s = __get_window_to_insert_after(t, mode);
926 remove_window_from_stack_ring(t);
927 r = s->stack_prev;
928 if (do_restack_transients)
930 /* re-sort the transient windows according to their scratch.i
931 * register */
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
951 * understand. */
952 ResyncXStackingOrder();
953 return True;
955 else
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);
963 return False;
966 static Bool __raise_lower_recursion(
967 FvwmWindow *t, stack_mode_t mode)
969 FvwmWindow *t2;
971 for (
972 t2 = Scr.FvwmRoot.stack_next; t2 != &Scr.FvwmRoot;
973 t2 = t2->stack_next)
975 if (FW_W(t2) == FW_W_TRANSIENTFOR(t))
977 if (t2 == t)
979 return False;
981 if (IS_ICONIFIED(t2) || t->layer != t2->layer)
983 break;
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
991 * same window */
992 t->scratch.i = -LOWER_PENALTY;
994 else
996 /* Add a bonus to the stack ring position for
997 * this branch of the transient tree over all
998 * other branches. */
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 */
1005 return True;
1010 return False;
1013 static void __raise_or_lower_window(
1014 FvwmWindow *t, stack_mode_t mode, Bool allow_recursion,
1015 Bool is_new_window)
1017 FvwmWindow *t2;
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
1040 * window). */
1041 if (IS_TRANSIENT(t) && DO_STACK_TRANSIENT_PARENT(t))
1043 if (__raise_lower_recursion(t, mode) == True)
1045 return;
1050 if (is_new_window)
1052 do_move_transients = False;
1054 else
1056 do_move_transients = __must_move_transients(t, mode);
1058 if (__restack_window(
1059 t, mode, do_move_transients, is_new_window) == True)
1061 return;
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
1084 * the #if 0. */
1085 #if 0
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))
1095 found = True;
1097 if (found)
1099 XRaiseWindow (dpy, tops[i]);
1102 XFree (tops);
1103 #endif
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. */
1110 raisePanFrames();
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). */
1114 XFlush(dpy);
1115 handle_all_expose();
1118 return;
1121 static void raise_or_lower_window(
1122 FvwmWindow *t, stack_mode_t mode, Bool allow_recursion,
1123 Bool is_new_window)
1125 FvwmWindow *fw;
1127 /* clean the auxiliary registers used in stacking transients */
1128 for (fw = Scr.FvwmRoot.next; fw != NULL; fw = fw->next)
1130 fw->scratch.i = 0;
1132 __raise_or_lower_window(t, mode, allow_recursion, is_new_window);
1134 return;
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)
1146 rectangle g1;
1147 rectangle g2;
1148 Bool rc;
1150 if (r->Desk != s->Desk)
1152 return False;
1154 rc = get_visible_window_or_icon_geometry(r, &g1);
1155 if (rc == False)
1157 return False;
1159 rc = get_visible_window_or_icon_geometry(s, &g2);
1160 if (rc == False)
1162 return False;
1164 rc = intersect(
1165 g1.x, g1.y, g1.width, g1.height,
1166 g2.x, g2.y, g2.width, g2.height);
1168 return rc;
1171 #if 0
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;
1183 int i;
1184 FvwmWindow *t1, *t2;
1186 MyXGrabServer (dpy);
1188 if (!XQueryTree (dpy, Scr.Root, &root, &parent, &children, &nchildren))
1190 MyXUngrabServer (dpy);
1191 return;
1194 t2 = &Scr.FvwmRoot;
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])
1204 break;
1207 else
1209 if (FW_W_FRAME(t1) == children[i])
1211 break;
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
1220 * the list). */
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! */
1227 /* emergency fix */
1228 t1->layer = t2->layer;
1230 t2 = t1;
1234 MyXUngrabServer (dpy);
1236 XFree (children);
1238 #endif
1240 /* same as above but synchronizes the stacking order in X from the stack ring.
1242 static void ResyncXStackingOrder(void)
1244 Window *wins;
1245 FvwmWindow *t;
1246 int count;
1247 int i;
1249 for (count = 0, t = Scr.FvwmRoot.next; t != NULL; count++, t = t->next)
1251 /* nothing to do here */
1253 if (count > 0)
1255 wins = (Window *)safemalloc(3 * count * sizeof (Window));
1256 for (
1257 i = 0, t = Scr.FvwmRoot.stack_next; count--;
1258 t = t->stack_next)
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);
1274 free(wins);
1275 /* send out M_RESTACK for all windows, to make sure we don't
1276 * forget anything. */
1277 BroadcastRestackAllWindows();
1280 return;
1283 /* send RESTACK packets for all windows between s1 and s2 */
1284 static void BroadcastRestack(FvwmWindow *s1, FvwmWindow *s2)
1286 FvwmWindow *fw;
1287 int num;
1288 int i;
1289 int n;
1290 fmodule *module;
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)
1299 return;
1302 if (s1 == &Scr.FvwmRoot)
1304 s1 = s1->stack_next;
1305 if (s1 == &Scr.FvwmRoot)
1307 return;
1309 /* s1 has been moved to the top of stack */
1310 BroadcastPacket(
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 */
1316 return;
1319 if (s1 == s2)
1321 /* A useful M_RESTACK packet must contain at least two windows.
1323 return;
1325 for (
1326 fw = s1, num = 1; fw != s2 && fw != &Scr.FvwmRoot;
1327 fw = fw->stack_next, num++)
1329 /* nothing */
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));
1339 bp = body;
1340 *(bp++) = START_FLAG;
1341 *(bp++) = M_RESTACK;
1342 *(bp++) = length;
1343 *(bp++) = fev_get_evtime();
1344 for (fw = s1, i = 0; i <= n; i++, fw = fw->stack_next)
1346 *(bp++) = FW_W(fw);
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))
1353 PositiveWrite(
1354 module, body, length*sizeof(unsigned long));
1356 free(body);
1358 #ifdef DEBUG_STACK_RING
1359 verify_stack_ring_consistency();
1360 #endif
1362 return;
1365 static int collect_transients_recursive(
1366 FvwmWindow *t, FvwmWindow *list_head, int layer, stack_mode_t mode,
1367 Bool do_include_target_window)
1369 FvwmWindow *s;
1370 int count = 0;
1371 int m;
1373 switch (mode)
1375 case SM_LOWER:
1376 m = MARK_LOWER;
1377 break;
1378 case SM_RAISE:
1379 m = MARK_RAISE;
1380 break;
1381 case SM_RESTACK:
1382 m = MARK_RESTACK;
1383 break;
1384 default:
1385 /* can not happen */
1386 m = MARK_RAISE;
1387 break;
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; )
1393 FvwmWindow *tmp;
1395 if (s == t && do_include_target_window == False)
1397 /* ignore the target window */
1398 s = s->stack_next;
1399 continue;
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);
1407 count++;
1408 count += get_visible_icon_window_count(t);
1410 s = tmp;
1413 return count;
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.
1423 Bool ontop = True;
1424 Window junk;
1425 Window *tops;
1426 int i;
1427 unsigned int num;
1428 Window OR_Above = None;
1429 XWindowAttributes wa;
1431 if (fw->Desk != Scr.CurrentDesk)
1433 return True;
1435 if (!XQueryTree(dpy, Scr.Root, &junk, &junk, &tops, &num))
1437 return ontop;
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))
1455 continue;
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])))
1465 OR_Above = tops[i];
1467 } /* end for */
1469 if (OR_Above) {
1470 *umtop = OR_Above;
1471 ontop = False;
1474 XFree (tops);
1477 return ontop;
1480 static Bool is_on_top_of_layer_ignore_rom(FvwmWindow *fw)
1482 FvwmWindow *t;
1483 Bool ontop = True;
1485 if (IS_SCHEDULED_FOR_DESTROY(fw))
1487 /* stack ring members are no longer valid */
1488 return False;
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)
1498 break;
1500 if (t->Desk != fw->Desk)
1502 continue;
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))
1513 ontop = False;
1514 break;
1519 return ontop;
1522 static Bool __is_on_top_of_layer(FvwmWindow *fw, Bool client_entered)
1524 Window junk;
1525 Bool ontop = False;
1526 if (Scr.bo.do_raise_over_unmanaged)
1529 #define EXPERIMENTAL_ROU_HANDLING
1530 #ifdef EXPERIMENTAL_ROU_HANDLING
1532 RBW - 2002/08/15 -
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
1537 this one.
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))
1548 if (client_entered)
1549 /* FIXME! - perhaps we should only do if MFCR */
1551 #ifdef ROUDEBUG
1552 printf("RBW-iotol - %8.8lx is on top,"
1553 " checking server tree. ***\n",
1554 FW_W_CLIENT(fw));
1555 #endif
1556 ontop = is_above_unmanaged(fw, &junk);
1557 #ifdef ROUDEBUG
1558 printf(" returning %d\n", (int) ontop);
1559 #endif
1561 else
1563 #ifdef ROUDEBUG
1564 printf("RBW-iotol - %8.8lx is on top,"
1565 " *** NOT checking server tree.\n",
1566 FW_W_CLIENT(fw));
1567 #endif
1568 ontop = True;
1570 return ontop;
1572 else
1574 return False;
1576 #else
1577 return False; /* Old pre-2002/08/22 handling. */
1578 #endif
1580 else
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))
1593 return;
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;
1601 return;
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))
1609 return;
1611 if (t == add_after_win || t == add_after_win->stack_next)
1613 /* tried to add the window before or after itself */
1614 fvwm_msg(
1615 ERR, "add_window_to_stack_ring_after",
1616 "BUG: tried to add window '%s' %s itself in stack"
1617 " ring\n",
1618 t->name.name, (t == add_after_win) ?
1619 "after" : "before");
1620 return;
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;
1627 return;
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)
1642 FvwmWindow *s;
1644 if (!t || !IS_TRANSIENT(t) || FW_W_TRANSIENTFOR(t) == Scr.Root ||
1645 FW_W_TRANSIENTFOR(t) == None)
1647 return NULL;
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;
1657 return NULL;
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. */
1668 return False;
1670 /* RaiseWindow/LowerWindow will put the window in its layer */
1671 raise_or_lower_window(t, (do_lower) ? SM_LOWER : SM_RAISE, False, True);
1673 return 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)
1681 BroadcastPacket(
1682 M_RAISE_WINDOW, 3, (long)FW_W(t), (long)FW_W_FRAME(t),
1683 (unsigned long)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();
1688 #endif
1689 return;
1692 void LowerWindow(FvwmWindow *t)
1694 BroadcastPacket(
1695 M_LOWER_WINDOW, 3, (long)FW_W(t), (long)FW_W_FRAME(t),
1696 (unsigned long)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();
1701 #endif
1702 return;
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();
1711 #endif
1712 return;
1715 /* return true if stacking order changed */
1716 Bool HandleUnusualStackmodes(
1717 unsigned int stack_mode, FvwmWindow *r, Window rw, FvwmWindow *s,
1718 Window sw)
1720 int do_restack = 0;
1721 FvwmWindow *t;
1723 /* DBUG("HandleUnusualStackmodes", "called with %d, %lx\n",
1724 stack_mode, s);*/
1726 if (
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 */
1732 return 0;
1735 switch (stack_mode)
1737 case TopIf:
1738 for (
1739 t = r->stack_prev; t != &Scr.FvwmRoot && !do_restack;
1740 t = t->stack_prev)
1742 do_restack = ((s == NULL || s == t) && overlap(t, r));
1744 if (do_restack)
1746 RaiseWindow (r);
1748 break;
1749 case BottomIf:
1750 for (
1751 t = r->stack_next; t != &Scr.FvwmRoot && !do_restack;
1752 t = t->stack_next)
1754 do_restack = ((s == NULL || s == t) && overlap(t, r));
1756 if (do_restack)
1758 LowerWindow (r);
1760 break;
1761 case Opposite:
1762 do_restack = (
1763 HandleUnusualStackmodes(TopIf, r, rw, s, sw) ||
1764 HandleUnusualStackmodes(BottomIf, r, rw, s, sw));
1765 break;
1767 /* DBUG("HandleUnusualStackmodes", "\t---> %d\n", do_restack);*/
1768 #ifdef DEBUG_STACK_RING
1769 verify_stack_ring_consistency();
1770 #endif
1771 return do_restack;
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);
1782 return;
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);
1789 return;
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;
1802 return;
1805 void set_layer(FvwmWindow *t, int layer)
1807 t->layer = layer;
1808 return;
1811 int get_layer(FvwmWindow *t)
1813 return t->layer;
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)
1824 FvwmWindow *s;
1825 FvwmWindow *start;
1826 FvwmWindow *end;
1827 Bool is_finished;
1829 if (layer >= 0 && t->layer != layer)
1831 return;
1833 /* find out on which windows to operate */
1834 if (layer >= 0)
1836 /* only work on the given layer */
1837 start = &Scr.FvwmRoot;
1838 end = &Scr.FvwmRoot;
1839 for (
1840 s = Scr.FvwmRoot.stack_next;
1841 s != &Scr.FvwmRoot && s->layer >= layer;
1842 s = s->stack_next)
1844 if (s == t)
1846 /* ignore the target window */
1847 continue;
1849 if (s->layer == layer)
1851 if (start == &Scr.FvwmRoot)
1853 start = s;
1855 end = s->stack_next;
1859 else
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)
1869 for (
1870 s = Scr.FvwmRoot.stack_next; s != &Scr.FvwmRoot;
1871 s = s->stack_next)
1873 SET_IN_TRANSIENT_SUBTREE(s, 0);
1874 if (
1875 IS_TRANSIENT(s) &&
1876 (layer < 0 || layer == s->layer))
1878 s->scratch.p = get_transientfor_fvwmwindow(s);
1880 else
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 */
1894 is_finished = True;
1895 for (s = start; s != end; s = s->stack_next)
1898 if (
1899 __mark_transient_subtree_test(
1900 s, start, end, mark_mode,
1901 do_ignore_icons,
1902 use_window_group_hint))
1904 is_finished = False;
1906 } /* for */
1907 } /* while */
1909 return;
1912 void new_layer(FvwmWindow *fw, int layer)
1914 FvwmWindow *s;
1915 FvwmWindow *target;
1916 FvwmWindow *prev;
1917 FvwmWindow list_head;
1918 int add_after_layer;
1919 int count;
1920 int old_layer;
1921 Bool do_lower;
1923 if (layer < 0)
1925 layer = 0;
1927 fw = get_transientfor_top_fvwmwindow(fw);
1928 if (layer == fw->layer)
1930 return;
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);
1938 if (count == 0)
1940 /* no windows to move */
1941 return;
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;
1948 do_lower = True;
1950 else
1952 /* raise above the windows in the new (higher) layer */
1953 add_after_layer = layer + 1;
1954 do_lower = False;
1956 /* find the place to insert the windows */
1957 for (
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 */
1964 break;
1967 /* insert windows at new position */
1968 add_windowlist_to_stack_ring_after(&list_head, target->stack_prev);
1969 prev = NULL;
1970 for (
1971 s = list_head.stack_next; prev != list_head.stack_prev;
1972 prev = s, s = s->stack_next)
1974 s->layer = layer;
1975 /* redraw title and buttons to update layer buttons */
1976 border_draw_decorations(
1977 s, PART_TITLEBAR, (Scr.Hilite == fw), True, CLEAR_NONE,
1978 NULL, NULL);
1979 GNOME_SetLayer(fw);
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),
1985 do_lower);
1986 focus_grab_buttons_on_layer(layer);
1987 focus_grab_buttons_on_layer(old_layer);
1989 return;
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);
2001 return;
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);
2020 return;
2023 void CMD_Lower(F_CMD_ARGS)
2025 LowerWindow(exc->w.fw);
2027 return;
2030 void CMD_RestackTransients(F_CMD_ARGS)
2032 RestackWindow(exc->w.fw);
2034 return;
2037 void CMD_RaiseLower(F_CMD_ARGS)
2039 Bool ontop;
2040 FvwmWindow * const fw = exc->w.fw;
2042 ontop = is_on_top_of_layer_ignore_rom(fw);
2043 if (ontop)
2045 LowerWindow(fw);
2047 else
2049 RaiseWindow(fw);
2052 return;
2055 void CMD_Layer(F_CMD_ARGS)
2057 int n, layer, val[2];
2058 char *token;
2059 FvwmWindow * const fw = exc->w.fw;
2061 if (fw == NULL)
2063 return;
2065 token = PeekToken(action, NULL);
2066 if (StrEquals("default", token))
2068 layer = fw->default_layer;
2070 else
2072 n = GetIntegerArguments(action, NULL, val, 2);
2074 layer = fw->layer;
2075 if ((n == 1) ||
2076 ((n == 2) && (val[0] != 0)))
2078 layer += val[0];
2080 else if ((n == 2) && (val[1] >= 0))
2082 layer = val[1];
2084 else
2086 layer = fw->default_layer;
2089 if (layer < 0)
2091 layer = 0;
2093 new_layer(fw, layer);
2094 #ifdef DEBUG_STACK_RING
2095 verify_stack_ring_consistency();
2096 #endif
2098 return;
2101 void CMD_DefaultLayers(F_CMD_ARGS)
2103 char *bot = NULL;
2104 char *def = NULL;
2105 char *top = NULL;
2106 int i;
2108 bot = PeekToken(action, &action);
2109 if (bot)
2111 i = atoi (bot);
2112 if (i < 0)
2114 fvwm_msg(
2115 ERR, "DefaultLayers",
2116 "Layer must be non-negative." );
2118 else
2120 Scr.BottomLayer = i;
2123 def = PeekToken(action, &action);
2124 if (def)
2126 i = atoi (def);
2127 if (i < 0)
2129 fvwm_msg(
2130 ERR, "DefaultLayers",
2131 "Layer must be non-negative." );
2133 else
2135 Scr.DefaultLayer = i;
2138 top = PeekToken(action, &action);
2139 if (top)
2141 i = atoi (top);
2142 if (i < 0)
2144 fvwm_msg(
2145 ERR, "DefaultLayers",
2146 "Layer must be non-negative." );
2148 else
2150 Scr.TopLayer = i;
2153 #ifdef DEBUG_STACK_RING
2154 verify_stack_ring_consistency();
2155 #endif
2157 return;