/* This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include #include "libs/fvwmlib.h" #include "fvwm.h" #include "externs.h" #include "cursor.h" #include "functions.h" #include "bindings.h" #include "misc.h" #include "screen.h" #include "defaults.h" #include "module_interface.h" #include "focus.h" #include "stack.h" #include "events.h" #include "borders.h" #include "virtual.h" #include "icons.h" #include "gnome.h" /* ----------------------------- stack ring code --------------------------- */ /* If more than this many transients are in a single branch of a transient * tree, they will end up in more or less random stacking order. */ #define MAX_TRANSIENTS_IN_BRANCH 200000 /* Same for total levels of transients. */ #define MAX_TRANSIENT_LEVELS 10000 /* This number must fit in a signed int! */ #define LOWER_PENALTY (MAX_TRANSIENTS_IN_BRANCH * MAX_TRANSIENT_LEVELS) static void __RaiseOrLowerWindow( FvwmWindow *t, Bool do_lower, Bool allow_recursion, Bool is_new_window); static void RaiseOrLowerWindow( FvwmWindow *t, Bool do_lower, Bool allow_recursion, Bool is_new_window); #if 0 static void ResyncFvwmStackRing(void); #endif static void ResyncXStackingOrder(void); static void BroadcastRestack(FvwmWindow *s1, FvwmWindow *s2); static int collect_transients_recursive( FvwmWindow *t, FvwmWindow *list_head, int layer, Bool do_lower); #define DEBUG_STACK_RING 1 #ifdef DEBUG_STACK_RING /* debugging function */ static void dump_stack_ring(void) { FvwmWindow *t1; if (!debugging_stack_ring) return; XBell(dpy, 0); fprintf(stderr,"dumping stack ring:\n"); for (t1 = Scr.FvwmRoot.stack_next; t1 != &Scr.FvwmRoot; t1 = t1->stack_next) { fprintf(stderr," l=%d fw=0x%08x f=0x%08x '%s'\n", t1->layer, (int)t1, (int)t1->frame, t1->name); } return; } /* debugging function */ void verify_stack_ring_consistency(void) { Window root, parent, *children; unsigned int nchildren, i; FvwmWindow *t1, *t2; int last_layer; int last_index; if (!debugging_stack_ring) return; XSync(dpy, 0); t2 = Scr.FvwmRoot.stack_next; if (t2 == &Scr.FvwmRoot) return; last_layer = t2->layer; for (t1 = t2->stack_next; t1 != &Scr.FvwmRoot; t2 = t1, t1 = t1->stack_next) { if (t1->layer > last_layer) { fprintf( stderr, "vsrc: stack ring is corrupt! " "'%s' (layer %d) is above '%s' (layer %d/%d)\n", t1->name, t1->layer, t2->name, t2->layer, last_layer); dump_stack_ring(); return; } last_layer = t1->layer; } t2 = &Scr.FvwmRoot; for (t1 = t2->stack_next; t1 != &Scr.FvwmRoot; t2 = t1, t1 = t1->stack_next) { if (t1->stack_prev != t2) break; } if (t1 != &Scr.FvwmRoot || t1->stack_prev != t2) { fprintf( stderr, "vsrc: stack ring is corrupt - fvwm will probably crash! " "0x%08x -> 0x%08x but 0x%08x <- 0x%08x\n", (int)t2, (int)t1, (int)(t1->stack_prev), (int)t1); dump_stack_ring(); return; } MyXGrabServer(dpy); if (!XQueryTree(dpy, Scr.Root, &root, &parent, &children, &nchildren)) { MyXUngrabServer(dpy); return; } last_index = nchildren; for (t1 = Scr.FvwmRoot.stack_next; t1 != &Scr.FvwmRoot; t1 = t1->stack_next) { /* find window in window list */ for (i = 0; i < nchildren && t1->frame != children[i]; i++) ; if (i == nchildren) { fprintf(stderr,"vsrc: window already died: fw=0x%08x w=0x%08x '%s'\n", (int)t1, (int)t1->frame, t1->name); } else if (i >= last_index) { fprintf( stderr, "vsrc: window is at wrong position in stack ring: " "fw=0x%08x f=0x%08x '%s'\n", (int)t1, (int)t1->frame, t1->name); dump_stack_ring(); fprintf(stderr,"dumping X stacking order:\n"); for (i = nchildren; i-- > 0; ) { for (t1 = Scr.FvwmRoot.stack_next; t1 != &Scr.FvwmRoot; t1 = t1->stack_next) { /* only dump frame windows */ if (t1->frame == children[i]) { fprintf(stderr," f=0x%08x\n", (int)children[i]); break; } } } MyXUngrabServer(dpy); XFree(children); return; } last_index = i; } MyXUngrabServer(dpy); XFree(children); return; } #endif /* Remove a window from the stack ring */ void remove_window_from_stack_ring(FvwmWindow *t) { if (IS_SCHEDULED_FOR_DESTROY(t)) { return; } t->stack_prev->stack_next = t->stack_next; t->stack_next->stack_prev = t->stack_prev; /* not really necessary, but gives a little more saftey */ t->stack_prev = NULL; t->stack_next = NULL; return; } /* Add window t to the stack ring after window t */ void add_window_to_stack_ring_after(FvwmWindow *t, FvwmWindow *add_after_win) { if (IS_SCHEDULED_FOR_DESTROY(t)) { return; } if (t == add_after_win || t == add_after_win->stack_next) { /* tried to add the window before or after itself */ fvwm_msg(ERR, "add_window_to_stack_ring_after", "BUG: tried to add window '%s' %s itself in stack ring\n", t->name, (t == add_after_win) ? "after" : "before"); return; } t->stack_next = add_after_win->stack_next; add_after_win->stack_next->stack_prev = t; t->stack_prev = add_after_win; add_after_win->stack_next = t; return; } /* Add a whole ring of windows. The list_head itself will not be added. */ static void add_windowlist_to_stack_ring_after( FvwmWindow *list_head, FvwmWindow *add_after_win) { add_after_win->stack_next->stack_prev = list_head->stack_prev; list_head->stack_prev->stack_next = add_after_win->stack_next; add_after_win->stack_next = list_head->stack_next; list_head->stack_next->stack_prev = add_after_win; return; } FvwmWindow *get_next_window_in_stack_ring(FvwmWindow *t) { return t->stack_next; } FvwmWindow *get_prev_window_in_stack_ring(FvwmWindow *t) { return t->stack_prev; } FvwmWindow *get_transientfor_fvwmwindow(FvwmWindow *t) { FvwmWindow *s; if (!t || !IS_TRANSIENT(t) || t->transientfor == Scr.Root || t->transientfor == None) return NULL; for (s = Scr.FvwmRoot.next; s != NULL; s = s->next) { if (s->w == t->transientfor) { return (s == t) ? NULL : s; } } return NULL; } static FvwmWindow *get_transientfor_top_fvwmwindow(FvwmWindow *t) { FvwmWindow *s; s = t; while (s && IS_TRANSIENT(s) && DO_STACK_TRANSIENT_PARENT(s)) { s = get_transientfor_fvwmwindow(s); if (s) t = s; } return t; } /* Takes a window from the top of the stack ring and puts it at the appropriate * place. Called when new windows are created. */ Bool position_new_window_in_stack_ring(FvwmWindow *t, Bool do_lower) { if (t->stack_prev != &Scr.FvwmRoot) /* Not at top of stack ring, so it is already in place. add_window.c relies * on this. */ return False; /* RaiseWindow/LowerWindow will put the window in its layer */ RaiseOrLowerWindow(t, do_lower, False, True); return True; } /******************************************************************** * Raise a target and all higher FVWM-managed windows above any * override_redirects: * - locate the highest override_redirect above our target * - put all the FvwmWindows from the target to the highest FvwmWindow * below the highest override_redirect in the restack list * - configure our target window above the override_redirect sibling, * and restack. ********************************************************************/ static void raise_over_unmanaged(FvwmWindow *t) { Window junk; Window *tops; int i; unsigned int num; Window OR_Above = None; Window *wins; int count = 0; FvwmWindow *t2 = NULL; unsigned int flags; XWindowChanges changes; XWindowAttributes wa; if (!XQueryTree(dpy, Scr.Root, &junk, &junk, &tops, &num)) return; /******************************************************************** * Locate the highest override_redirect window above our target, and * the highest of our windows below it. ********************************************************************/ for (i = 0; i < num && tops[i] != t->frame; i++) { /* look for target window in list */ } for (; i < num; i++) { /* It might be just as well (and quicker) just to check for the absence of * an FvwmContext instead of for override_redirect... */ if (!XGetWindowAttributes(dpy, tops[i], &wa)) { continue; } if (wa.override_redirect == True && wa.class != InputOnly && tops[i] != Scr.NoFocusWin) { OR_Above = tops[i]; } } /* end for */ /******************************************************************** * Count the windows we need to restack, then build the stack list. ********************************************************************/ if (OR_Above) { for (count = 0, t2 = Scr.FvwmRoot.stack_next; t2 != &Scr.FvwmRoot; t2 = t2->stack_next) { count++; count += get_visible_icon_window_count(t2); if (t2 == t) { break; } } if (count > 0) { wins = (Window*) safemalloc (count * sizeof (Window)); for (i = 0, t2 = Scr.FvwmRoot.stack_next; t2 != &Scr.FvwmRoot; t2 = t2->stack_next) { wins[i++] = t2->frame; if (IS_ICONIFIED(t2) && ! IS_ICON_SUPPRESSED(t2)) { if (t2->icon_w != None) { wins[i++] = t2->icon_w; } if (t2->icon_pixmap_w != None) { wins[i++] = t2->icon_pixmap_w; } } if (t2 == t) { break; } } memset(&changes, '\0', sizeof(changes)); changes.sibling = OR_Above; changes.stack_mode = Above; flags = CWSibling|CWStackMode; XConfigureWindow (dpy, t->frame/*topwin*/, flags, &changes); XRestackWindows (dpy, wins, i); free (wins); } }/* end - we found an OR above our target */ XFree (tops); return; } static Bool must_move_transients( FvwmWindow *t, Bool do_lower) { if (IS_ICONIFIED(t)) { return False; } /* raise */ if ((!do_lower && DO_RAISE_TRANSIENT(t)) || (do_lower && DO_LOWER_TRANSIENT(t))) { Bool scanning_above_window = True; FvwmWindow *q; for (q = Scr.FvwmRoot.stack_next; q != &Scr.FvwmRoot && t->layer <= q->layer; q = q->stack_next) { if (t->layer < q->layer) { /* We're not interested in higher layers. */ continue; } else if (t == q) { /* We found our window. All further transients are below it. */ scanning_above_window = False; } else if (IS_TRANSIENT(q) && (q->transientfor == t->w)) { return True; } else if (scanning_above_window && !do_lower) { /* raise: The window is not raised, so itself and all transients will * be raised. */ return True; } } } return False; } static Window __get_stacking_sibling(FvwmWindow *fw, Bool do_stack_below) { Window w; /* default to frame window */ w = fw->frame; if (IS_ICONIFIED(fw) && do_stack_below == True) { /* override with icon windows when stacking below */ if (fw->icon_pixmap_w != None) { w = fw->icon_pixmap_w; } else if (fw->icon_w != None) { w = fw->icon_w; } } return w; } static void restack_windows( FvwmWindow *r, FvwmWindow *s, int count, Bool do_broadcast_all, Bool do_lower) { FvwmWindow *t; unsigned int flags; int i; XWindowChanges changes; Window *wins; int do_stack_above; int is_reversed; if (count <= 0) { for (count = 0, t = r->stack_next; t != s; t = t->stack_next) { count++; count += get_visible_icon_window_count(t); } } /* restack the windows between r and s */ wins = (Window*) safemalloc ((count + 3) * sizeof (Window)); i = 0; for (t = r->stack_next; t != s; t = t->stack_next) { if (i > count) { fvwm_msg (ERR, "restack_windows", "more transients than expected"); break; } wins[i++] = t->frame; if (IS_ICONIFIED(t) && !IS_ICON_SUPPRESSED(t)) { if(t->icon_w != None) wins[i++] = t->icon_w; if(t->icon_pixmap_w != None) wins[i++] = t->icon_pixmap_w; } } changes.sibling = __get_stacking_sibling(r, True); if (changes.sibling == None) { changes.sibling = __get_stacking_sibling(s, False); is_reversed = 1; } else { is_reversed = 0; } if (changes.sibling == None) { do_stack_above = !do_lower; flags = CWStackMode; } else { do_stack_above = 0; flags = CWStackMode | CWSibling; } changes.stack_mode = (do_stack_above ^ is_reversed) ? Above : Below; XConfigureWindow (dpy, r->stack_next->frame, flags, &changes); if (count > 1) { XRestackWindows (dpy, wins, count); } free(wins); if (do_broadcast_all) { /* send out M_RESTACK for all windows, to make sure we don't forget * anything. */ BroadcastRestackAllWindows(); } else { /* send out (one or more) M_RESTACK packets for windows between r and s */ BroadcastRestack(r, s); } return; } static void __sort_transient_ring(FvwmWindow *ring) { FvwmWindow *s; FvwmWindow *t; FvwmWindow *u; FvwmWindow *prev; if (ring->stack_next->stack_next == ring) { /* only one or zero windows */ return; } /* Implementation note: this sorting algorithm is about the most inefficient * possible. It just swaps the position of two adjacent windows in the ring * if they are in the wrong order. Since transient windows are rare, this * should not cause any notable performance hit. Because it is important that * the order of windows with the same key is not changed, we can not just use * qsort() here. */ for (t = ring->stack_next, prev = ring; t->stack_next != ring; prev = t->stack_prev) { s = t->stack_next; if (t->scratch.i < s->scratch.i) { /* swap windows */ u = s->stack_next; s->stack_next = t; t->stack_next = u; u = t->stack_prev; t->stack_prev = s; s->stack_prev = u; s->stack_prev->stack_next = s; t->stack_next->stack_prev = t; if (prev != ring) { /* move further up the ring? */ t = prev; } else { /* hit start of ring */ } } else { /* correct order, advance one window */ t = t->stack_next; } } return; } static Bool __restack_window( FvwmWindow *t, Bool do_lower, Bool do_restack_transients, Bool is_new_window) { FvwmWindow *s, *r, tmp_r; int count; int test_layer; /* detach t, so it doesn't make trouble in the loops */ remove_window_from_stack_ring(t); count = 0; if (do_restack_transients) { /* collect the transients in a temp list */ tmp_r.stack_prev = &tmp_r; tmp_r.stack_next = &tmp_r; count = collect_transients_recursive(t, &tmp_r, t->layer, do_lower); if (count == 0) { do_restack_transients = False; } } count += 1 + get_visible_icon_window_count(t); test_layer = t->layer; if (do_lower) { test_layer--; } /* now find the place to reinsert t and friends */ for (s = Scr.FvwmRoot.stack_next; s != &Scr.FvwmRoot; s = s->stack_next) { if (test_layer >= s->layer) { break; } } r = s->stack_prev; if (do_restack_transients) { /* re-sort the transient windows according to their scratch.i register */ __sort_transient_ring(&tmp_r); /* insert all transients between r and s. */ add_windowlist_to_stack_ring_after(&tmp_r, r); } /* ** Re-insert t - below transients */ add_window_to_stack_ring_after(t, s->stack_prev); if (is_new_window && IS_TRANSIENT(t) && DO_STACK_TRANSIENT_PARENT(t) && !IS_ICONIFIED(t)) { /* now that the new transient is properly positioned in the stack ring, * raise/lower it again so that its parent is raised/lowered too */ RaiseOrLowerWindow(t, do_lower, True, False); /* make sure the stacking order is correct - may be the sledge-hammer * method, but the recursion ist too hard to understand. */ ResyncXStackingOrder(); return True; } else { /* restack the windows between r and s */ restack_windows(r, s, count, do_restack_transients, do_lower); } return False; } /* Important note: __RaiseOrLowerWindow must *only* be called by * RaiseOrLowerWindow()! */ static void __RaiseOrLowerWindow( FvwmWindow *t, Bool do_lower, Bool allow_recursion, Bool is_new_window) { FvwmWindow *t2; Bool do_move_transients; /* Do not raise this window after command execution (see HandleButtonPress()). */ SET_SCHEDULED_FOR_RAISE(t, 0); /* New windows are simply raised/lowered without touching the transientfor * at first. Then, further down in the code, __RaiseOrLowerWindow() is called * again to raise/lower the transientfor if necessary. We can not do the * recursion stuff for new windows because the must_move_transients() call * needs a properly ordered stack ring - but the new window is still at the * front of the stack ring. */ if (allow_recursion && !is_new_window && !IS_ICONIFIED(t)) { /* * This part makes Raise/Lower on a Transient act on its Main and sibling * Transients. * * The recursion is limited to one level - which caters for most cases. * This code does not handle the case where there are trees of Main + * Transient (ie where a Main_window_with_Transients is itself Transient * for another window). * * Another strategy is required to handle trees of Main+Transients */ if (IS_TRANSIENT(t) && DO_STACK_TRANSIENT_PARENT(t)) { for (t2 = Scr.FvwmRoot.stack_next; t2 != &Scr.FvwmRoot; t2 = t2->stack_next) { if (t2->w == t->transientfor) { if (t2 == t) { break; } if (IS_ICONIFIED(t2) || t->layer != t2->layer) { break; } if (do_lower && (!IS_TRANSIENT(t2) || !DO_STACK_TRANSIENT_PARENT(t2))) { /* hit the highest level transient; lower this subtree below all * other subtrees of the same window */ t->scratch.i = -LOWER_PENALTY; } else { /* Add a bonus to the stack ring position for this branch of the * transient tree over all other branches. */ t->scratch.i = MAX_TRANSIENTS_IN_BRANCH; } __RaiseOrLowerWindow(t2, do_lower, True, False); if ((!do_lower && DO_RAISE_TRANSIENT(t2)) || (do_lower && DO_LOWER_TRANSIENT(t2)) ) { /* moving the parent moves our window already */ return; } } } } } if (is_new_window) { do_move_transients = False; } else { do_move_transients = must_move_transients(t, do_lower); } if (__restack_window( t, do_lower, do_move_transients, is_new_window) == True) { return; } if (!do_lower) { /* This hack raises the target and all higher FVWM windows over any style * grabfocusoff override_redirect windows that may be above it. This is * used to cope with ill-behaved applications that insist on using * long-lived override_redirects. */ if (Scr.bo.RaiseOverUnmanaged) { raise_over_unmanaged(t); } /* * The following is a hack to raise X windows over native windows * which is needed for some (all ?) X servers running under Windows * or Windows NT. */ if (Scr.bo.RaiseHackNeeded) { /* RBW - 09/20/1999. I find that trying to raise unmanaged windows causes problems with some apps. If this seems to work well for everyone, I'll remove the #if 0. */ #if 0 /* get *all* toplevels (even including override_redirects) */ XQueryTree(dpy, Scr.Root, &junk, &junk, &tops, &num); /* raise from tmp_win upwards to get them above NT windows */ for (i = 0; i < num; i++) { if (tops[i] == t->frame) found = True; if (found) XRaiseWindow (dpy, tops[i]); } XFree (tops); #endif for (t2 = t; t2 != &Scr.FvwmRoot; t2 = t2->stack_prev) { XRaiseWindow (dpy, t2->frame); } } /* This needs to be done after all the raise hacks. */ raisePanFrames(); /* If the window has been raised, make sure the decorations are updated * immediately in case we are in a complex function (e.g. raise, unshade). */ XSync(dpy, 0); handle_all_expose(); } return; } static void RaiseOrLowerWindow( FvwmWindow *t, Bool do_lower, Bool allow_recursion, Bool is_new_window) { FvwmWindow *fw; /* clean the auxiliary registers used in stacking transients */ for (fw = Scr.FvwmRoot.next; fw != NULL; fw = fw->next) { fw->scratch.i = 0; } __RaiseOrLowerWindow(t, do_lower, allow_recursion, is_new_window); return; } /* Raise t and its transients to the top of its layer. For the pager to work properly it is necessary that RaiseWindow *always* sends a proper M_RESTACK packet, even if the stacking order didn't change. */ void RaiseWindow(FvwmWindow *t) { BroadcastPacket(M_RAISE_WINDOW, 3, t->w, t->frame, (unsigned long)t); RaiseOrLowerWindow(t, False, True, False); focus_grab_buttons_on_pointer_window(); #ifdef DEBUG_STACK_RING verify_stack_ring_consistency(); #endif return; } void LowerWindow(FvwmWindow *t) { BroadcastPacket(M_LOWER_WINDOW, 3, t->w, t->frame, (unsigned long)t); RaiseOrLowerWindow(t, True, True, False); focus_grab_buttons_on_pointer_window(); #ifdef DEBUG_STACK_RING verify_stack_ring_consistency(); #endif return; } static Bool intersect (int x0, int y0, int w0, int h0, int x1, int y1, int w1, int h1) { return !((x0 >= x1 + w1) || (x0 + w0 <= x1) || (y0 >= y1 + h1) || (y0 + h0 <= y1)); } static Bool overlap_box (FvwmWindow *r, int x, int y, int w, int h) { if (IS_ICONIFIED(r)) { return ((r->icon_pixmap_w) && intersect (x, y, w, h, r->icon_g.x, r->icon_g.y, r->icon_p_width, r->icon_p_height)) || ((r->icon_w) && intersect (x, y, w, h, r->icon_xl_loc, r->icon_g.y + r->icon_p_height, r->icon_g.width, r->icon_g.height)); } else { return intersect (x, y, w, h, r->frame_g.x, r->frame_g.y, r->frame_g.width, r->frame_g.height); } } static Bool overlap (FvwmWindow *r, FvwmWindow *s) { if (r->Desk != s->Desk) { return 0; } if (IS_ICONIFIED(r)) { return ((r->icon_pixmap_w) && overlap_box (s, r->icon_g.x, r->icon_g.y, r->icon_p_width, r->icon_p_height)) || ((r->icon_w) && overlap_box (s, r->icon_xl_loc, r->icon_g.y + r->icon_p_height, r->icon_g.width, r->icon_g.height)); } else { return overlap_box (s, r->frame_g.x, r->frame_g.y, r->frame_g.width, r->frame_g.height); } } /* return true if stacking order changed */ Bool HandleUnusualStackmodes(unsigned int stack_mode, FvwmWindow *r, Window rw, FvwmWindow *s, Window sw) { Bool restack = 0; FvwmWindow *t; /* DBUG("HandleUnusualStackmodes", "called with %d, %lx\n", stack_mode, s);*/ if (((rw != r->w) ^ IS_ICONIFIED(r)) || (s && (((sw != s->w) ^ IS_ICONIFIED(s)) || (r->Desk != s->Desk)))) { /* one of the relevant windows is unmapped */ return 0; } switch (stack_mode) { case TopIf: for (t = r->stack_prev; (t != &Scr.FvwmRoot) && !restack; t = t->stack_prev) { restack = (((s == NULL) || (s == t)) && overlap (t, r)); } if (restack) { RaiseWindow (r); } break; case BottomIf: for (t = r->stack_next; (t != &Scr.FvwmRoot) && !restack; t = t->stack_next) { restack = (((s == NULL) || (s == t)) && overlap (t, r)); } if (restack) { LowerWindow (r); } break; case Opposite: restack = (HandleUnusualStackmodes (TopIf, r, rw, s, sw) || HandleUnusualStackmodes (BottomIf, r, rw, s, sw)); break; } /* DBUG("HandleUnusualStackmodes", "\t---> %d\n", restack);*/ #ifdef DEBUG_STACK_RING verify_stack_ring_consistency(); #endif return restack; } /* RBW - 01/07/1998 - this is here temporarily - I mean to move it to libfvwm eventually, along with some other chain manipulation functions. */ #if 0 /* ResyncFvwmStackRing - Rebuilds the stacking order ring of FVWM-managed windows. For use in cases where apps raise/lower their own windows in a way that makes it difficult to determine exactly where they ended up in the stacking order. - Based on code from Matthias Clasen. */ static void ResyncFvwmStackRing (void) { Window root, parent, *children; unsigned int nchildren, i; FvwmWindow *t1, *t2; MyXGrabServer (dpy); if (!XQueryTree (dpy, Scr.Root, &root, &parent, &children, &nchildren)) { MyXUngrabServer (dpy); return; } t2 = &Scr.FvwmRoot; for (i = 0; i < nchildren; i++) { for (t1 = Scr.FvwmRoot.next; t1 != NULL; t1 = t1->next) { if (IS_ICONIFIED(t1) && !IS_ICON_SUPPRESSED(t1)) { if (t1->icon_w == children[i] || t1->icon_pixmap_w == children[i]) { break; } } else { if (t1->frame == children[i]) { break; } } } if (t1 != NULL && t1 != t2) { /* * Move the window to its new position, working from the bottom up * (that's the way XQueryTree presents the list). */ /* Pluck from chain. */ remove_window_from_stack_ring(t1); add_window_to_stack_ring_after(t1, t2->stack_prev); if (t2 != &Scr.FvwmRoot && t2->layer > t1->layer) { /* oops, now our stack ring is out of order! */ /* emergency fix */ t1->layer = t2->layer; } t2 = t1; } } MyXUngrabServer (dpy); XFree (children); } #endif /* same as above but synchronizes the stacking order in X from the stack ring. */ static void ResyncXStackingOrder(void) { Window *wins; FvwmWindow *t; int count; int i; for (count = 0, t = Scr.FvwmRoot.next; t != NULL; count++, t = t->next) ; if (count > 0) { wins = (Window *)safemalloc(3 * count * sizeof (Window)); for (i = 0, t = Scr.FvwmRoot.stack_next; count--; t = t->stack_next) { wins[i++] = t->frame; if (IS_ICONIFIED(t) && !IS_ICON_SUPPRESSED(t)) { if (t->icon_w != None) wins[i++] = t->icon_w; if (t->icon_pixmap_w != None) wins[i++] = t->icon_pixmap_w; } } XRestackWindows(dpy, wins, i); free(wins); /* send out M_RESTACK for all windows, to make sure we don't forget * anything. */ BroadcastRestackAllWindows(); } } /* send RESTACK packets for all windows between s1 and s2 */ static void BroadcastRestack(FvwmWindow *s1, FvwmWindow *s2) { FvwmWindow *fw; int num; int i; int n; unsigned long *body, *bp, length; unsigned long max_wins_per_packet; extern Time lastTimestamp; if (s2 == &Scr.FvwmRoot) { s2 = s2->stack_prev; if (s2 == &Scr.FvwmRoot) return; } if (s1 == &Scr.FvwmRoot) { s1 = s1->stack_next; if (s1 == &Scr.FvwmRoot) return; /* s1 has been moved to the top of stack */ BroadcastPacket (M_RAISE_WINDOW, 3, s1->w, s1->frame, (unsigned long)s1); if (s1->stack_next == s2) { /* avoid sending empty RESTACK packet */ return; } } if (s1 == s2) { /* A useful M_RESTACK packet must contain at least two windows. */ return; } for (fw = s1, num = 1; fw != s2 && fw != &Scr.FvwmRoot; fw = fw->stack_next, num++) { /* nothing */ } max_wins_per_packet = (FvwmPacketMaxSize - FvwmPacketHeaderSize) / 3; /* split packet if it is too long */ for ( ; num > 1; s1 = fw, num -= n) { n = min(num, max_wins_per_packet) - 1; length = FvwmPacketHeaderSize + 3 * (n + 1); body = (unsigned long *)safemalloc(length * sizeof(unsigned long)); bp = body; *(bp++) = START_FLAG; *(bp++) = M_RESTACK; *(bp++) = length; *(bp++) = lastTimestamp; for (fw = s1, i = 0; i <= n; i++, fw = fw->stack_next) { *(bp++) = fw->w; *(bp++) = fw->frame; *(bp++) = (unsigned long)fw; } for (i = 0; i < npipes; i++) { PositiveWrite(i, body, length*sizeof(unsigned long)); } free(body); } #ifdef DEBUG_STACK_RING verify_stack_ring_consistency(); #endif return; } void BroadcastRestackAllWindows(void) { BroadcastRestack(Scr.FvwmRoot.stack_next, Scr.FvwmRoot.stack_prev); return; } /* send RESTACK packets for t, t->stack_prev and t->stack_next */ void BroadcastRestackThisWindow(FvwmWindow *t) { BroadcastRestack(t->stack_prev, t->stack_next); return; } /* ----------------------------- layer code -------------------------------- */ /* returns 0 if s and t are on the same layer, <1 if t is on a lower layer and * >1 if t is on a higher layer. */ int compare_window_layers(FvwmWindow *t, FvwmWindow *s) { return t->layer - s->layer; } void set_default_layer(FvwmWindow *t, int layer) { t->default_layer = layer; return; } void set_layer(FvwmWindow *t, int layer) { t->layer = layer; return; } int get_layer(FvwmWindow *t) { return t->layer; } /* This function recursively finds the transients of the window t and sets their * is_in_transient_subtree flag. If a layer is given, only windows in this * layer are checked. If the layer is < 0, all windows are considered. */ void mark_transient_subtree( FvwmWindow *t, int layer, int mark_mode, Bool do_ignore_icons, Bool use_window_group_hint) { FvwmWindow *s; FvwmWindow *start; FvwmWindow *end; Bool is_finished; if (layer >= 0 && t->layer != layer) return; /* find out on which windows to operate */ if (layer >= 0) { /* only work on the given layer */ start = &Scr.FvwmRoot; end = &Scr.FvwmRoot; for (s = Scr.FvwmRoot.stack_next; s != &Scr.FvwmRoot && s->layer >= layer; s = s->stack_next) { if (s->layer == layer) { if (start == &Scr.FvwmRoot) start = s; end = s->stack_next; } } } else { /* work on complete window list */ start = Scr.FvwmRoot.stack_next; end = &Scr.FvwmRoot; } /* clean the temporary flag in all windows and precalculate the transient * frame windows */ for (s = Scr.FvwmRoot.stack_next; s != &Scr.FvwmRoot; s = s->stack_next) { SET_IN_TRANSIENT_SUBTREE(s, 0); if (IS_TRANSIENT(s) && (layer < 0 || layer == s->layer)) { s->scratch.p = get_transientfor_fvwmwindow(s); } else { s->scratch.p = NULL; } } /* now loop over the windows and mark the ones we need to move */ SET_IN_TRANSIENT_SUBTREE(t, 1); is_finished = False; while (!is_finished) { FvwmWindow *r; FvwmWindow *u; /* recursively search for all transient windows */ is_finished = True; for (s = start; s != end; s = s->stack_next) { Bool use_group_hint = False; if (IS_IN_TRANSIENT_SUBTREE(s)) continue; if (use_window_group_hint && DO_ICONIFY_WINDOW_GROUPS(s) && s->wmhints && (s->wmhints->flags & WindowGroupHint) && (s->wmhints->window_group != None) && (s->wmhints->window_group != s->w) && (s->wmhints->window_group != Scr.Root)) { use_group_hint = True; } if (!IS_TRANSIENT(s) && !use_group_hint) continue; if (do_ignore_icons && IS_ICONIFIED(s)) continue; r = (FvwmWindow *)s->scratch.p; if (IS_TRANSIENT(s)) { if (r && IS_IN_TRANSIENT_SUBTREE(r) && ((mark_mode == MARK_ALL) || (mark_mode == MARK_LOWER && DO_LOWER_TRANSIENT(r)) || (mark_mode == MARK_RAISE && DO_RAISE_TRANSIENT(r)))) { /* have to move this one too */ SET_IN_TRANSIENT_SUBTREE(s, 1); /* used for stacking transients */ s->scratch.i += r->scratch.i + 1; /* need another scan through the list */ is_finished = False; continue; } } if (use_group_hint) { for (u = start; u != end; u = u->stack_next) { if (u->w == s->wmhints->window_group || (u->wmhints && (u->wmhints->flags & WindowGroupHint) && u->wmhints->window_group == s->wmhints->window_group)) { if (IS_IN_TRANSIENT_SUBTREE(u)) { /* have to move this one too */ SET_IN_TRANSIENT_SUBTREE(s, 1); /* need another scan through the list */ is_finished = False; } } } } } /* for */ } /* while */ return; } static int collect_transients_recursive( FvwmWindow *t, FvwmWindow *list_head, int layer, Bool do_lower) { FvwmWindow *s; int count = 0; mark_transient_subtree( t, layer, (do_lower) ? MARK_LOWER : MARK_RAISE, True, False); /* now collect the marked windows in a separate list */ for (s = Scr.FvwmRoot.stack_next; s != &Scr.FvwmRoot; ) { FvwmWindow *tmp; tmp = s->stack_next; if (IS_IN_TRANSIENT_SUBTREE(s)) { remove_window_from_stack_ring(s); add_window_to_stack_ring_after(s, list_head->stack_prev); count++; count += get_visible_icon_window_count(t); } s = tmp; } return count; } void new_layer(FvwmWindow *tmp_win, int layer) { FvwmWindow *s; FvwmWindow *target; FvwmWindow *prev; FvwmWindow list_head; int add_after_layer; int count; Bool do_lower; if (layer < 0) { layer = 0; } tmp_win = get_transientfor_top_fvwmwindow(tmp_win); if (layer == tmp_win->layer) return; list_head.stack_next = &list_head; list_head.stack_prev = &list_head; count = collect_transients_recursive( tmp_win, &list_head, tmp_win->layer, (layer < tmp_win->layer)); if (count == 0) { /* no windows to move */ return; } add_after_layer = layer; if (layer < tmp_win->layer) { /* lower below the windows in the new (lower) layer */ add_after_layer = layer; do_lower = True; } else { /* raise above the windows in the new (higher) layer */ add_after_layer = layer + 1; do_lower = False; } /* find the place to insert the windows */ for (target = Scr.FvwmRoot.stack_next; target != &Scr.FvwmRoot; target = target->stack_next) { if (target->layer < add_after_layer) { /* add all windows before the current window */ break; } } /* insert windows at new position */ add_windowlist_to_stack_ring_after(&list_head, target->stack_prev); prev = NULL; for (s = list_head.stack_next; prev != list_head.stack_prev; prev = s, s = s->stack_next) { s->layer = layer; GNOME_SetLayer(tmp_win); } /* move the windows without modifying their stacking order */ restack_windows( list_head.stack_next->stack_prev, target, count, (count > 1), do_lower); return; } /* ----------------------------- common functions -------------------------- */ /* RBW - 11/13/1998 - 2 new fields to init - stacking order chain. */ void init_stack_and_layers(void) { Scr.BottomLayer = DEFAULT_BOTTOM_LAYER; Scr.DefaultLayer = DEFAULT_DEFAULT_LAYER; Scr.TopLayer = DEFAULT_TOP_LAYER; Scr.FvwmRoot.stack_next = &Scr.FvwmRoot; Scr.FvwmRoot.stack_prev = &Scr.FvwmRoot; set_layer(&Scr.FvwmRoot, DEFAULT_ROOT_WINDOW_LAYER); return; } static Bool is_on_top_of_layer_ignore_rom(FvwmWindow *fw) { FvwmWindow *t; Bool ontop = True; if (IS_SCHEDULED_FOR_DESTROY(fw)) { /* stack ring members are no longer valid */ return False; } if (DO_RAISE_TRANSIENT(fw)) { mark_transient_subtree(fw, fw->layer, MARK_RAISE, True, False); } for (t = fw->stack_prev; t != &Scr.FvwmRoot; t = t->stack_prev) { if (t->layer > fw->layer) { break; } if (t->Desk != fw->Desk) { continue; } /* For RaiseOverUnmanaged we can not determine if the window is on top by * checking if the window overlaps another one. If it was below unmanaged * windows, but on top of its layer, it would be considered on top. */ if (Scr.bo.RaiseOverUnmanaged || overlap(fw, t)) { if (!DO_RAISE_TRANSIENT(fw) || (!IS_IN_TRANSIENT_SUBTREE(t) && t != fw)) { ontop = False; break; } } } return ontop; } Bool is_on_top_of_layer(FvwmWindow *fw) { if (Scr.bo.RaiseOverUnmanaged) { return False; } return is_on_top_of_layer_ignore_rom(fw); } /* ----------------------------- built in functions ------------------------ */ void CMD_Raise(F_CMD_ARGS) { if (DeferExecution(eventp,&w,&tmp_win,&context, CRS_SELECT,ButtonRelease)) return; RaiseWindow(tmp_win); } void CMD_Lower(F_CMD_ARGS) { if (DeferExecution(eventp,&w,&tmp_win,&context, CRS_SELECT, ButtonRelease)) return; LowerWindow(tmp_win); } void CMD_RaiseLower(F_CMD_ARGS) { Bool ontop; if (DeferExecution(eventp,&w,&tmp_win,&context, CRS_SELECT,ButtonRelease)) { #ifdef DEBUG_STACK_RING dump_stack_ring(); #endif return; } ontop = is_on_top_of_layer_ignore_rom(tmp_win); if (ontop) LowerWindow(tmp_win); else RaiseWindow(tmp_win); return; } void CMD_Layer(F_CMD_ARGS) { int n, layer, val[2]; char *token; if (DeferExecution(eventp,&w,&tmp_win,&context, CRS_SELECT,ButtonRelease)) return; if(tmp_win == NULL) return; token = PeekToken(action, NULL); if (StrEquals("default", token)) { layer = tmp_win->default_layer; } else { n = GetIntegerArguments(action, NULL, val, 2); layer = tmp_win->layer; if ((n == 1) || ((n == 2) && (val[0] != 0))) { layer += val[0]; } else if ((n == 2) && (val[1] >= 0)) { layer = val[1]; } else { layer = tmp_win->default_layer; } } if (layer < 0) { layer = 0; } new_layer(tmp_win, layer); #ifdef DEBUG_STACK_RING verify_stack_ring_consistency(); #endif } void CMD_DefaultLayers(F_CMD_ARGS) { char *bot = NULL; char *def = NULL; char *top = NULL; int i; bot = PeekToken(action, &action); if (bot) { i = atoi (bot); if (i < 0) { fvwm_msg(ERR,"DefaultLayers", "Layer must be non-negative." ); } else { Scr.BottomLayer = i; } } def = PeekToken(action, &action); if (def) { i = atoi (def); if (i < 0) { fvwm_msg(ERR,"DefaultLayers", "Layer must be non-negative." ); } else { Scr.DefaultLayer = i; } } top = PeekToken(action, &action); if (top) { i = atoi (top); if (i < 0) { fvwm_msg(ERR,"DefaultLayers", "Layer must be non-negative." ); } else { Scr.TopLayer = i; } } #ifdef DEBUG_STACK_RING verify_stack_ring_consistency(); #endif }