Subject: Layers

From: Matthias Clasen <[email-masked]>
Date: Wed, 24 Feb 1999 01:17:46 +0100

A layered stacking order for fvwm-2.2:

The patch below replaces StaysOnTop with the concept of layers.
Every window is in a layer. Fvwm arranges the layers on top of 
each other with layer 0 being the bottommost. The default layer is 0.

The following things are added by this patch:
 * a new style option "Layer n", where n can be any 
   nonnegative integer. Use of this option causes a window to be 
   placed in layer n.  
 * a new command "Layer rel [abs]" to move a window to another
   layer. It interprets its arguments in the same way as "Desk".
   As a special case, "Layer 0 -1" will move the window to its 
   "default" layer, ie the layer indicated by its style. 
 * a "Layers m [n]" WindowList option with the meaning that only 
   windows in layers between m and n are listed. n defaults to m.
 * a "Layer [n]" condition (for "Current" and related commands) 
   which is satisfied if the window is in layer n. n defaults to
   the layer of the focus window. 
 * a layer variable in the FvwmWindow struct.

The following things are removed by this patch:
 * the StaysOnTop/StaysPut style options (can be replaced by
   suitable "Layer" options)
 * the WindowList options NoOnTop/OnTop/OnlyOnTop (can also
   be replaced by suitable "Layer" options)
 * the Raised/!Raised conditions (didn't make sense to begin
   with)
 * the ONTOP and RAISED flags in the FvwmWindow struct
 * the KeepOnTop function

To apply the patch: 

cd fvwm-2.2/fvwm; patch -p 1 < this-mail

diff -c fvwm+session/add_window.c fvwm/add_window.c
*** fvwm+session/add_window.c	Tue Feb 23 21:11:05 1999
--- fvwm/add_window.c	Tue Feb 23 20:03:05 1999
***************
*** 202,207 ****
--- 202,210 ----
  
    tmp_win->IconBoxes = styles.IconBoxes; /* copy iconboxes ptr (if any) */
    tmp_win->buttons = styles.on_buttons; /* on and off buttons combined. */
+   /* FIXME: shouldn't transients inherit the layer ? */
+   tmp_win->layer = styles.layer;
+ 
  #ifdef USEDECOR
    /* search for a UseDecor tag in the Style */
    tmp_win->fl = NULL;
***************
*** 396,402 ****
    /* tmp_win->prev points to the last window in the list, tmp_win->next is NULL.
       Now fix the last window to point to tmp_win */
    tmp_win->prev->next = tmp_win;
- 
    /*
        RBW - 11/13/1998 - add it into the stacking order chain also.
        This chain is anchored at both ends on Scr.FvwmRoot, there are
--- 399,404 ----
***************
*** 742,748 ****
      XConfigureWindow(dpy, tmp_win->frame, vm, &xwc);
    }
  #endif
-   KeepOnTop();
    MyXUngrabServer(dpy);
  
    XGetGeometry(dpy, tmp_win->w, &JunkRoot, &JunkX, &JunkY,
--- 744,749 ----
***************
*** 1188,1192 ****
--- 1189,1197 ----
    if(nptr->IconBoxes != NULL) {         /* If style has iconboxes */
      styles->IconBoxes = nptr->IconBoxes; /* copy it */
    }
+   /* FIXME: must have a flag */
+  if (nptr->layer >= 0) {
+    styles->layer = nptr->layer;
+  }
    return;                               /* return */
  }
diff -c fvwm+session/builtins.c fvwm/builtins.c
*** fvwm+session/builtins.c	Tue Feb 23 21:10:45 1999
--- fvwm/builtins.c	Tue Feb 23 20:00:40 1999
***************
*** 219,225 ****
  #if 0 /* don't want to raise anymore either */
    RaiseWindow(t);
  #endif /* 0 */
-   KeepOnTop();
  
    /* If the window is still not visible, make it visible! */
    if(((t->frame_x + t->frame_height)< 0)||(t->frame_y + t->frame_width < 0)||
--- 219,224 ----
***************
*** 294,300 ****
      XWarpPointer(dpy, None, Scr.Root, 0, 0, 0, 0, x, y);
    }
    RaiseWindow(t);
-   KeepOnTop();
  
    /* If the window is still not visible, make it visible! */
    if(((t->frame_x + t->frame_height)< 0)||(t->frame_y + t->frame_width < 0)||
--- 293,298 ----
***************
*** 716,729 ****
    if (DeferExecution(eventp,&w,&tmp_win,&context, SELECT,ButtonRelease))
      return;
  
!   if(tmp_win)
!     RaiseWindow(tmp_win);
! 
!   LookInList(tmp_win, &styles);         /* get merged styles */
!   if (styles.on_flags & STAYSONTOP_FLAG) {
!     tmp_win->flags |= ONTOP;
!   }
!   KeepOnTop();
  }
  
  void lower_function(XEvent *eventp,Window w,FvwmWindow *tmp_win,
--- 714,720 ----
    if (DeferExecution(eventp,&w,&tmp_win,&context, SELECT,ButtonRelease))
      return;
  
!   RaiseWindow(tmp_win);
  }
  
  void lower_function(XEvent *eventp,Window w,FvwmWindow *tmp_win,
***************
*** 733,740 ****
      return;
  
    LowerWindow(tmp_win);
- 
-   tmp_win->flags &= ~ONTOP;
  }
  
  void destroy_function(XEvent *eventp,Window w,FvwmWindow *tmp_win,
--- 724,729 ----
***************
*** 1125,1147 ****
  
    if (DeferExecution(eventp,&w,&tmp_win,&context, SELECT,ButtonRelease))
      return;
-   if(tmp_win == NULL)
-     return;
  
    if((tmp_win == Scr.LastWindowRaised)||
       (tmp_win->flags & VISIBLE))
    {
      LowerWindow(tmp_win);
-     tmp_win->flags &= ~ONTOP;
    }
    else
    {
      RaiseWindow(tmp_win);
-     LookInList(tmp_win, &styles);       /* get merged styles */
-     if (styles.on_flags & STAYSONTOP_FLAG) {
-       tmp_win->flags |= ONTOP;
-     }
-     KeepOnTop();
    }
  }
  
--- 1114,1128 ----
***************
*** 4185,4190 ****
--- 4166,4172 ----
    mask->useCirculateHitIcon = 0;
    mask->onFlags = 0;
    mask->offFlags = 0;
+   mask->layer = -2; /* -2  means no layer condition, -1 means current */ 
  }
  
  /**********************************************************************
***************
*** 4226,4235 ****
        mask->onFlags |= TRANSIENT;
      else if(StrEquals(condition,"!Transient"))
        mask->offFlags |= TRANSIENT;
-     else if(StrEquals(condition,"Raised"))
-       mask->onFlags |= RAISED;
-     else if(StrEquals(condition,"!Raised"))
-       mask->offFlags |= RAISED;
      else if(StrEquals(condition,"CurrentDesk"))
        mask->needsCurrentDesk = 1;
      else if(StrEquals(condition,"CurrentPage"))
--- 4208,4213 ----
***************
*** 4244,4249 ****
--- 4222,4240 ----
        mask->useCirculateHit = 1;
      else if(StrEquals(condition,"CirculateHitIcon"))
        mask->useCirculateHitIcon = 1;
+     else if (StrEquals(condition, "Layer")) 
+     {  
+        if (sscanf(tmp,"%d",&mask->layer)) 
+        {
+ 	  tmp = GetNextToken (tmp, &condition);
+           if (mask->layer < 0) 
+             mask->layer = -2; /* silently ignore invalid layers */
+        }
+        else
+        {
+           mask->layer = -1; /* needs current layer */ 
+        }
+     }
      else if(!mask->needsName && !mask->needsNotName)
      {
        /* only 1st name to avoid mem leak */
***************
*** 4325,4330 ****
--- 4316,4327 ----
    if (mask->needsNotName && fMatches)
      return 0;
  
+   if ((mask->layer == -1) && (fw->layer != Scr.Focus->layer)) 
+     return 0;
+ 
+   if ((mask->layer >= 0) && (fw->layer != mask->layer))  
+     return 0;
+ 
    return 1;
  }
  
***************
*** 4863,4866 ****
--- 4860,4928 ----
    if (i>0 && rgpctMovementDefault[i-1] != 1.0) {
      rgpctMovementDefault[i++] = 1.0;
    }
+ }
+ 
+ void change_layer(XEvent *eventp,Window w,FvwmWindow *tmp_win,
+ 		  unsigned long context, char *action,int* Module)
+ {
+   int n, layer, val[2];
+   FvwmWindow *t2, *next;
+   name_list styles;
+   
+   if (DeferExecution(eventp,&w,&tmp_win,&context, SELECT,ButtonRelease))
+     return;
+   
+   if(tmp_win == NULL)
+     return;
+ 
+   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
+     {
+       /* a hack: Layer 0 -1 goes back to the "default" layer */
+       LookInList (tmp_win, &styles);
+       layer = styles.layer;
+     }
+ 
+   if (layer < 0) 
+     {
+       layer = 0;
+     } 
+ 
+   if (layer < tmp_win->layer)
+     {
+       tmp_win->layer = layer;
+       RaiseWindow(tmp_win);
+     } 
+   else if (layer > tmp_win->layer)
+     {
+ #ifndef DONT_RAISE_TRANSIENTS
+       /* this could be done much more efficiently */
+       for (t2 = Scr.FvwmRoot.stack_next; t2 != &Scr.FvwmRoot; t2 = next)
+ 	{
+ 	  next = t2->stack_next;
+ 	  if ((t2->flags & TRANSIENT) &&
+ 	      (t2->transientfor == tmp_win->w) &&
+ 	      (t2 != tmp_win) &&
+ 	      (t2->layer >= tmp_win->layer) &&
+               (t2->layer < layer))
+ 	    {
+ 	      t2->layer = layer;
+ 	      LowerWindow(t2);  
+ 	    }
+ 	}
+ #endif
+       tmp_win->layer = layer;
+       LowerWindow(tmp_win);
+     } 
  }
diff -c fvwm+session/events.c fvwm/events.c
*** fvwm+session/events.c	Tue Feb 23 21:11:05 1999
--- fvwm/events.c	Tue Feb 23 20:05:44 1999
***************
*** 832,839 ****
      {
        DeIconify(Tmp_win);
      }
-   if(!PPosOverride)
-     KeepOnTop();
  
  #ifdef SESSION 
    /* just to be on the safe side, we make sure that STARTICONIC
--- 832,837 ----
***************
*** 930,936 ****
    Tmp_win->flags &= ~MAP_PENDING;
    Tmp_win->flags &= ~ICONIFIED;
    Tmp_win->flags &= ~ICON_UNMAPPED;
-   KeepOnTop();
  }

--- 928,933 ----
***************
*** 1102,1108 ****
        RaiseWindow(Tmp_win);
      }
  
-     KeepOnTop();
      /* Why is this here? Seems to cause breakage with
       * non-focusing windows! */
      if(!(Tmp_win->flags & ICONIFIED))
--- 1099,1104 ----
***************
*** 1127,1133 ****
          GetContext(Tmp_win,&Event, &PressedW) == C_WINDOW)
      {
        RaiseWindow(Tmp_win);
-       KeepOnTop();
      }
      XSync(dpy,0);
      XAllowEvents(dpy,ReplayPointer,CurrentTime);
--- 1123,1128 ----
***************
*** 1384,1389 ****
--- 1379,1407 ----
                      (XFindContext (dpy, cre->above, FvwmContext,
                                     (caddr_t *) &otherwin) == XCSUCCESS))
                     ? otherwin->frame : cre->above);
+      /* we only allow clients to restack their windows within
+        their layer. Thus we don't handle TopIf, BottomIf or
+        Opposite modes. We could perhaps allow them and then
+        catch windows that have gone off to the end of the stack
+        and bring them back to the layer, but who cares ? Nobody will
+        use these modes anyway.
+     */
+     if (!(cre->value_mask & CWSibling))
+       {
+          switch (cre->detail)
+            {
+             case Above:
+               RaiseWindow (Tmp_win);
+               break;
+             case Below:
+               LowerWindow (Tmp_win);
+               break;
+            }
+       }
+     else if (otherwin &&
+       (otherwin->layer == Tmp_win->layer) &&
+       ((cre->detail == Above) || (cre->detail == Below)))
+       {
      xwc.stack_mode = cre->detail;
      XConfigureWindow (dpy, Tmp_win->frame,
                        cre->value_mask & (CWSibling | CWStackMode), &xwc);
***************
*** 1422,1427 ****
--- 1440,1446 ----
          ResyncFvwmStackRing();
        }
    }
+   }
  
  #ifdef SHAPE
    if (ShapesSupported)
***************
*** 1467,1473 ****
     */
    ConstrainSize(Tmp_win, &width, &height, False, 0, 0);
    SetupFrame (Tmp_win, x, y, width, height,sendEvent);
-   KeepOnTop();
  }
  
  /***********************************************************************
--- 1486,1491 ----
***************
*** 1514,1532 ****
  	Tmp_win->flags |= VISIBLE;
        else
  	Tmp_win->flags &= ~VISIBLE;
- 
-       /* For the most part, we'll raised partially obscured ONTOP windows
-        * here. The exception is ONTOP windows that are obscured by
-        * other ONTOP windows, which are raised in KeepOnTop(). This
-        * complicated set-up saves us from continually re-raising
-        * every on top window */
-       if(((vevent->state == VisibilityPartiallyObscured)||
- 	  (vevent->state == VisibilityFullyObscured))&&
- 	 (Tmp_win->flags&ONTOP)&&(Tmp_win->flags & RAISED))
- 	{
- 	  RaiseWindow(Tmp_win);
- 	  Tmp_win->flags &= ~RAISED;
- 	}
      }
  }
  
--- 1532,1537 ----
diff -c fvwm+session/functions.c fvwm/functions.c
*** fvwm+session/functions.c	Tue Feb 23 21:10:45 1999
--- fvwm/functions.c	Tue Feb 23 19:35:27 1999
***************
*** 117,122 ****
--- 117,123 ----
    {"IconPath",     setIconPath,      F_ICON_PATH,           FUNC_NO_WINDOW},
    {"Key",          ParseKeyEntry,    F_KEY,                 FUNC_NO_WINDOW},
    {"KillModule",   module_zapper,    F_ZAP,                 FUNC_NO_WINDOW},
+   {"Layer",        change_layer,     F_LAYER,               FUNC_NEEDS_WINDOW},
    {"Lower",        lower_function,   F_LOWER,               FUNC_NEEDS_WINDOW},
    {"Maximize",     Maximize,         F_MAXIMIZE,            FUNC_NEEDS_WINDOW},
    {"Menu",         staysup_func,     F_STAYSUP,             FUNC_NO_WINDOW},
diff -c fvwm+session/fvwm.c fvwm/fvwm.c
*** fvwm+session/fvwm.c	Tue Feb 23 21:11:06 1999
--- fvwm/fvwm.c	Tue Feb 23 20:00:29 1999
***************
*** 731,737 ****
    /* after the windows already on the screen are in place,
     * don't use PPosition */
    PPosOverride = False;
-   KeepOnTop();
    MyXUngrabServer(dpy);
    XSync(dpy,0); /* should we do this on initial capture? */
  }
--- 731,736 ----
***************
*** 1357,1362 ****
--- 1356,1362 ----
    /*  RBW - 11/13/1998 - 2 new fields to init - stacking order chain.  */
    Scr.FvwmRoot.stack_next  =  &Scr.FvwmRoot;
    Scr.FvwmRoot.stack_prev  =  &Scr.FvwmRoot;
+   Scr.FvwmRoot.layer = -1; 
  
    XGetWindowAttributes(dpy,Scr.Root,&(Scr.FvwmRoot.attr));
    Scr.root_pushes = 0;
diff -c fvwm+session/fvwm.h fvwm/fvwm.h
*** fvwm+session/fvwm.h	Tue Feb 23 21:11:06 1999
--- fvwm/fvwm.h	Tue Feb 23 20:00:40 1999
***************
*** 281,286 ****
--- 281,288 ----
      unsigned long buttons;
      icon_boxes *IconBoxes;              /* zero or more iconboxes */
  
+     int layer;
+ 
  } FvwmWindow;
  
  /* Window mask for Circulate and Direction functions */
***************
*** 296,301 ****
--- 298,304 ----
    unsigned long onFlags;
    unsigned long offFlags;
    char *name;
+   int layer;
  } WindowConditionMask;
  
  /***************************************************************************
***************
*** 304,310 ****
  /* The first 13 items are mapped directly from the style structure's
   * flag value, so they MUST correspond to the first 13 entries in misc.h */
  #define STARTICONIC             (1<<0)
- #define ONTOP                   (1<<1) /* does window stay on top */
  #define STICKY                  (1<<2) /* Does window stick to glass? */
  #define WINDOWLISTSKIP          (1<<3)
  #define SUPPRESSICON            (1<<4)
--- 307,312 ----
***************
*** 316,322 ****
  #define ClickToFocus            (1<<10)
  #define SloppyFocus             (1<<11)
  #define SHOW_ON_MAP    (1<<12) /* switch to desk when it gets mapped? */
! #define ALL_COMMON_FLAGS (STARTICONIC|ONTOP|STICKY|WINDOWLISTSKIP| \
  			  SUPPRESSICON|NOICON_TITLE|Lenience|StickyIcon| \
  			  CirculateSkipIcon|CirculateSkip|ClickToFocus| \
  			  SloppyFocus|SHOW_ON_MAP)
--- 318,324 ----
  #define ClickToFocus            (1<<10)
  #define SloppyFocus             (1<<11)
  #define SHOW_ON_MAP    (1<<12) /* switch to desk when it gets mapped? */
! #define ALL_COMMON_FLAGS (STARTICONIC|STICKY|WINDOWLISTSKIP| \
  			  SUPPRESSICON|NOICON_TITLE|Lenience|StickyIcon| \
  			  CirculateSkipIcon|CirculateSkip|ClickToFocus| \
  			  SloppyFocus|SHOW_ON_MAP)
***************
*** 326,332 ****
  #define MAPPED         (1<<15) /* is it mapped? */
  #define ICONIFIED      (1<<16) /* is it an icon now? */
  #define TRANSIENT      (1<<17) /* is it a transient window? */
- #define RAISED         (1<<18) /* if its a sticky window, needs raising? */
  #define VISIBLE        (1<<19) /* is the window fully visible */
  #define ICON_OURS      (1<<20) /* is the icon window supplied by the app? */
  #define PIXMAP_OURS    (1<<21)/* is the icon pixmap ours to free? */
--- 328,333 ----
diff -c fvwm+session/icons.c fvwm/icons.c
*** fvwm+session/icons.c	Tue Feb 23 21:11:06 1999
--- fvwm/icons.c	Tue Feb 23 20:00:50 1999
***************
*** 893,900 ****
    if(tmp_win->flags & ClickToFocus)
      FocusOn(tmp_win,TRUE);
  
-   KeepOnTop();
- 
    return;
  }
  
--- 893,898 ----
***************
*** 1009,1015 ****
  
        if(tmp_win->icon_pixmap_w != None)
  	XMapWindow(dpy, tmp_win->icon_pixmap_w);
-       KeepOnTop();
      }
    if((tmp_win->flags & ClickToFocus)||(tmp_win->flags & SloppyFocus))
      {
--- 1007,1012 ----
diff -c fvwm+session/misc.c fvwm/misc.c
*** fvwm+session/misc.c	Tue Feb 23 21:10:45 1999
--- fvwm/misc.c	Tue Feb 23 20:06:33 1999
***************
*** 811,843 ****
  }

- 
- /****************************************************************************
-  *
-  * Keeps the "StaysOnTop" windows on the top of the pile.
-  * This is achieved by clearing a flag for OnTop windows here, and waiting
-  * for a visibility notify on the windows. Exeption: OnTop windows which are
-  * obscured by other OnTop windows, which need to be raised here.
-  *
-  ****************************************************************************/
- void KeepOnTop()
- {
-   FvwmWindow *t;
- 
-   /* flag that on-top windows should be re-raised */
-   for (t = Scr.FvwmRoot.next; t != NULL; t = t->next)
-     {
-       if((t->flags & ONTOP)&&!(t->flags & VISIBLE))
- 	{
- 	  RaiseWindow(t);
- 	  t->flags &= ~RAISED;
- 	}
-       else
- 	t->flags |= RAISED;
-     }
- }
- 
- 
  /**************************************************************************
   *
   * Unmaps a window on transition to a new desktop
--- 811,816 ----
***************
*** 891,1085 ****
  }

- 
- 
  void RaiseWindow(FvwmWindow *t)
  {
!   FvwmWindow *t2;
!   int count, i;
!   Window *wins;
    XWindowChanges changes;
!   FvwmWindow *t1;
!   FvwmWindow **FvwmTopwins = NULL;
!   int j, count2;
! 
  
!   memset((void *) &changes, '\0', sizeof(changes));
!   /* raise the target, at least */
!   count = 1;
!   BroadcastPacket(M_RAISE_WINDOW, 3, t->w, t->frame, (unsigned long)t);
  
!   for (t2 = Scr.FvwmRoot.stack_next; t2 != &Scr.FvwmRoot; t2 = t2->stack_next)
!     {
!       if(t2->flags & ONTOP)
! 	count++;
!       if((t2->flags & TRANSIENT) &&(t2->transientfor == t->w)&&
! 	 (t2 != t))
! 	{
! 	  count++;
! 	  BroadcastPacket(M_RAISE_WINDOW, 3,
!                           t2->w, t2->frame,(unsigned long) t2);
! 	  if ((t2->flags & ICONIFIED)&&(!(t2->flags & SUPPRESSICON)))
! 	    {
! 	      count += 2;
! 	    }
! 	}
!     }
!   if ((t->flags & ICONIFIED)&&(!(t->flags & SUPPRESSICON)))
      {
!       count += 2;
!     }
  
!   wins = (Window *)safemalloc(count*sizeof(Window));
!   FvwmTopwins = (FvwmWindow **)safemalloc(count*sizeof(FvwmWindow));
  
!   i=0;
!   j = 0;
!   count2 = 0;
  
!   /* ONTOP windows on top */
!   for (t2 = Scr.FvwmRoot.stack_next; t2 != &Scr.FvwmRoot; t2 = t2->stack_next)
      {
!       if(t2->flags & ONTOP)
! 	{
! 	  BroadcastPacket(M_RAISE_WINDOW, 3,
!                           t2->w, t2->frame, (unsigned long) t2);
! 	  wins[i++] = t2->frame;
!           FvwmTopwins[j++] = t2;
! 	}
      }
  
!   /* now raise transients */
! #ifndef DONT_RAISE_TRANSIENTS
!   for (t2 = Scr.FvwmRoot.stack_next; t2 != &Scr.FvwmRoot; t2 = t2->stack_next)
    {
!     if((t2->flags & TRANSIENT) &&
!        (t2->transientfor == t->w) &&
!        (t2 != t) &&
!        (!(t2->flags & ONTOP)))
!     {
!       wins[i++] = t2->frame;
!       FvwmTopwins[j++] = t2;
!       if ((t2->flags & ICONIFIED)&&(!(t2->flags & SUPPRESSICON)))
!       {
          if(!(t2->flags & NOICON_TITLE))
            wins[i++] = t2->icon_w;
          if(!(t2->icon_pixmap_w))
            wins[i++] = t2->icon_pixmap_w;
!       }
!     }
!   }
! #endif
!   if ((t->flags & ICONIFIED)&&(!(t->flags & SUPPRESSICON)))
!   {
!     if(!(t->flags & NOICON_TITLE))
!       wins[i++] = t->icon_w;
!     if (t->icon_pixmap_w)
!       wins[i++] = t->icon_pixmap_w;
!   }
!   if(!(t->flags & ONTOP))
!     {
!       wins[i++] = t->frame;
!       FvwmTopwins[j++] = t;
!       Scr.LastWindowRaised = t;
!     }
!   count2 = j;
! 
!   if(i > 0)
!     {
! /*      XRaiseWindow(dpy,wins[0]);  */
!       /*
!            [email-masked] - 01/01/1999 -
!          simply calling XRaiseWindow(dpy,wins[0]); here will put StaysOnTop
!          windows over override_redirect windows like FvwmPager ballon_win or
!          Motif menus. Instead raise wins[0] only above the topmost window
! 	 which is managed by us.
!       */
!      if (wins[0] != Scr.FvwmRoot.stack_next->frame && wins[0] != Scr.FvwmRoot.stack_next->icon_w && wins[0] != Scr.FvwmRoot.stack_next->icon_pixmap_w) 
!       { 
!         if (Scr.FvwmRoot.stack_next->flags & ICONIFIED)
!           {
!             /*
!                 RBW - use the icon window or pixmap if there is one; but
!                 there may not be (NoIconTitle or NoIcon) --
!             */
!             if (Scr.FvwmRoot.stack_next->icon_w)
!               {
!                 changes.sibling = Scr.FvwmRoot.stack_next->icon_w;
!               }
!             else if (Scr.FvwmRoot.stack_next->icon_pixmap_w)
!               {
!                 changes.sibling = Scr.FvwmRoot.stack_next->icon_pixmap_w;
!               }
!             else
!               {
!                 changes.sibling = Scr.FvwmRoot.stack_next->frame;
!               }
!           }
!         else
!           {
!             changes.sibling = Scr.FvwmRoot.stack_next->frame;
!           }
!         changes.stack_mode = Above;
!         XConfigureWindow(dpy, wins[0], (CWSibling|CWStackMode), &changes);
!       }
! 
! 
!       /*
!           RBW  - 01/05/1998 - move all raised windows to front of stacking
!           order chain.
!       */
!       j = 0;
!       t2 = &Scr.FvwmRoot;
!       while (j < count2)
!         {
!           t1 = FvwmTopwins[j];
! 	  if (t1 != t2->stack_next)
!             {
!               t1->stack_prev->stack_next = t1->stack_next;  /* Pluck from chain.       */
!               t1->stack_next->stack_prev = t1->stack_prev;
!               t1->stack_next = t2->stack_next;    /* Set new pointers.       */
!               t1->stack_prev = t2->stack_next->stack_prev;
!               t2->stack_next->stack_prev = t1;    /* Insert in new position in chain. */
!               t2->stack_next = t1;
!             }
!           j++;
!           if (t2->stack_next != &Scr.FvwmRoot)
!           {
! 	    t2 = t2->stack_next;
!           }
!         }
  
      }
  
!   XRestackWindows(dpy,wins,i);
!   free(wins);
!   if (FvwmTopwins) free(FvwmTopwins);
    raisePanFrames();
  }
  
! 
  void LowerWindow(FvwmWindow *t)
  {
!   XLowerWindow(dpy,t->frame);
  
    BroadcastPacket(M_LOWER_WINDOW, 3, t->w, t->frame, (unsigned long)t);
  
    if((t->flags & ICONIFIED)&&(!(t->flags & SUPPRESSICON)))
      {
!       XLowerWindow(dpy, t->icon_w);
!       XLowerWindow(dpy, t->icon_pixmap_w);
      }
!   Scr.LastWindowRaised = (FvwmWindow *)0;
!   /*
!       RBW - 11/13/1998 - new: maintain the stacking order chain.
!   */
!   t->stack_prev->stack_next = t->stack_next;            /* Pluck from chain.       */
    t->stack_next->stack_prev = t->stack_prev;
!   t->stack_next = Scr.FvwmRoot.stack_prev->stack_next;  /* Set new pointers.       */
!   t->stack_prev = Scr.FvwmRoot.stack_prev;
!   Scr.FvwmRoot.stack_prev->stack_next = t;              /* Insert at end of chain. */
!   Scr.FvwmRoot.stack_prev = t;
  }

--- 864,1048 ----
  }

  void RaiseWindow(FvwmWindow *t)
  {
!   FvwmWindow *s, *r, *t2, *next;
!   unsigned int flags;
!   int i, count;
    XWindowChanges changes;
!   Window *wins;
  
!   Scr.LastWindowRaised = t;
  
!   for (s = Scr.FvwmRoot.stack_next; s != &Scr.FvwmRoot; s = s->stack_next)
      {
!        if ((t != s) && (t->layer >= s->layer))
!         {
!              break;
!         }
!      }
  
!   if (s == t->stack_next)
!      {
!       return;
!      }
! 
!   r = s->stack_prev;
!   /* We have to insert t and all its transients between r and s. */
! 
!   count = 0;
! 
! #ifndef DONT_RAISE_TRANSIENTS
!   for (t2 = Scr.FvwmRoot.stack_next; t2 != &Scr.FvwmRoot; t2 = next)
!      {
!       next = t2->stack_next;
!       if ((t2->flags & TRANSIENT) &&
!           (t2->transientfor == t->w) &&
!           (t2 != t) &&
!           (t2->layer == t->layer))
!         {
!           if (t2 == s)
!             {
!               /* t2 is a transient that is already in place.
!                  This should only happen if we haven't yet
!                  inserted anything between r and s. */
!               if (r != s->stack_prev)
!                 fprintf(stderr, "OOPS 1 in RaiseWindow\n");
!               s = s->stack_next;
!               r = s->stack_prev;
!             }
!           else
!             {
!           /* t2 is a transient to raise */
!           count++;
!           if ((t2->flags & ICONIFIED) && (!(t2->flags & SUPPRESSICON)))
!             {
!                count += 2;
!             }
  
!           BroadcastPacket(M_RAISE_WINDOW, 3,
!                           t2->w, t2->frame, (unsigned long)t2);
!           /* unplug it */
!           t2->stack_next->stack_prev = t2->stack_prev;
!           t2->stack_prev->stack_next = t2->stack_next;
!           /* put it above s */
!           t2->stack_next = s;
!           t2->stack_prev = s->stack_prev;
!           t2->stack_next->stack_prev = t2;
!           t2->stack_prev->stack_next = t2;
!           }
!         }
!      }
! #endif
  
!   /* don't forget t itself */
!   count++;
!   if ((t->flags & ICONIFIED) && (!(t->flags & SUPPRESSICON)))
      {
!        count += 2;
      }
  
!   BroadcastPacket(M_RAISE_WINDOW, 3, t->w, t->frame, (unsigned long)t);
! 
!   t->stack_prev->stack_next = t->stack_next;
!   t->stack_next->stack_prev = t->stack_prev;
! 
!   t->stack_next = s;
!   t->stack_prev = s->stack_prev;
!   t->stack_prev->stack_next = t;
!   t->stack_next->stack_prev = t;
! 
!   wins = (Window *)safemalloc(count*sizeof(Window));
! 
!   i = 0;
!   for (t2 = r->stack_next; t2 != s; t2 = t2->stack_next)
    {
!      if (i >= count)
!        fprintf(stderr, "OOPS2 in RaiseWindow i==%d\n", i);
!      wins[i++] = t2->frame;
!      if ((t2->flags & ICONIFIED) && (!(t2->flags & SUPPRESSICON)))
!      {
          if(!(t2->flags & NOICON_TITLE))
            wins[i++] = t2->icon_w;
          if(!(t2->icon_pixmap_w))
            wins[i++] = t2->icon_pixmap_w;
!      }
!    }
  
+   changes.sibling = s->frame;
+   if (changes.sibling != None)
+     {
+       changes.stack_mode = Above;
+       flags = CWSibling|CWStackMode;
      }
+   else
+     {
+       changes.stack_mode = Below;
+       flags = CWStackMode;
+     }
+   
+   XConfigureWindow (dpy, r->stack_next->frame, flags, &changes);
+   XRestackWindows (dpy, wins, count);
+ 
+   free (wins);
  
!   /* This should be unnecessary, since we never
!      do an unguarded XRaiseWindow */
    raisePanFrames();
  }
  
!   
  void LowerWindow(FvwmWindow *t)
  {
!   FvwmWindow *s;
!   XWindowChanges changes;
!   int i;
!   unsigned int flags;
! 
!   Scr.LastWindowRaised = NULL;
! 
!   for (s = Scr.FvwmRoot.stack_next; s != &Scr.FvwmRoot; s = s->stack_next)
!     {
!       if (t->layer > s->layer)
!         {
!            break;
!         }
!     }
! 
!   if (s == t->stack_next)
!     {
!       return;
!     }
  
    BroadcastPacket(M_LOWER_WINDOW, 3, t->w, t->frame, (unsigned long)t);
  
+   changes.sibling = s->frame;
+   if (changes.sibling != None)
+     {
+       changes.stack_mode = Above;
+       flags = CWSibling|CWStackMode;
+     }
+   else
+     {
+       changes.stack_mode = Below;
+       flags = CWStackMode;
+     }
+ 
+   XConfigureWindow(dpy, t->frame, flags, &changes);
+ 
    if((t->flags & ICONIFIED)&&(!(t->flags & SUPPRESSICON)))
      {
!       XConfigureWindow(dpy, t->icon_w, flags, &changes);
!       XConfigureWindow(dpy, t->icon_pixmap_w, flags, &changes);
      }
! 
!   t->stack_prev->stack_next = t->stack_next;
    t->stack_next->stack_prev = t->stack_prev;
! 
!   t->stack_next = s;
!   t->stack_prev = s->stack_prev;
!   t->stack_prev->stack_next = t;
!   t->stack_next->stack_prev = t;
  }

diff -c fvwm+session/misc.h fvwm/misc.h
*** fvwm+session/misc.h	Tue Feb 23 21:11:06 1999
--- fvwm/misc.h	Tue Feb 23 20:00:40 1999
***************
*** 45,51 ****
    icon_boxes *IconBoxes;                /* pointer to iconbox(s) */
    unsigned long on_buttons;
    unsigned long off_buttons;
! 
  } name_list;
  
  /* used for parsing configuration */
--- 45,51 ----
    icon_boxes *IconBoxes;                /* pointer to iconbox(s) */
    unsigned long on_buttons;
    unsigned long off_buttons;
!   int layer;
  } name_list;
  
  /* used for parsing configuration */
***************
*** 78,84 ****
  /* The first 13 items are mapped directly into the FvwmWindow structures
   * flag value, so they MUST correspond to the first 13 entries in fvwm.h */
  #define START_ICONIC_FLAG    (1<<0)
- #define STAYSONTOP_FLAG      (1<<1)
  #define STICKY_FLAG          (1<<2)
  #define LISTSKIP_FLAG        (1<<3)
  #define SUPPRESSICON_FLAG    (1<<4)
--- 78,83 ----
***************
*** 298,304 ****
  void SetClientProp(FvwmWindow *);
  void Iconify(FvwmWindow *, int, int);
  void DeIconify(FvwmWindow *);
- void KeepOnTop(void);
  void show_panner(void);
  void WaitForButtonsUp(void);
  void FocusOn(FvwmWindow *t,Bool FocusByMouse);
--- 297,302 ----
***************
*** 474,479 ****
--- 472,479 ----
  void SetEnv(F_CMD_ARGS);
  
  void CoerceEnterNotifyOnCurrentWindow();
+ 
+ void change_layer(F_CMD_ARGS);
  
  /*
  ** message levels for fvwm_msg:
diff -c fvwm+session/parse.h fvwm/parse.h
*** fvwm+session/parse.h	Tue Feb 23 21:10:45 1999
--- fvwm/parse.h	Tue Feb 23 19:35:27 1999
***************
*** 86,92 ****
      F_DFLT_COLORS,
      F_GLOBAL_OPTS,
      F_EMULATE,
! 
      F_RESIZE = 100,
      F_RAISE,
      F_LOWER,
--- 86,92 ----
      F_DFLT_COLORS,
      F_GLOBAL_OPTS,
      F_EMULATE,
!     F_LAYER,
      F_RESIZE = 100,
      F_RAISE,
      F_LOWER,
diff -c fvwm+session/placement.c fvwm/placement.c
*** fvwm+session/placement.c	Tue Feb 23 21:10:45 1999
--- fvwm/placement.c	Tue Feb 23 20:00:50 1999
***************
*** 35,40 ****
--- 35,43 ----
  void CleverPlacement(FvwmWindow *t, int *x, int *y, int pdeltax, int pdeltay);
  /**/
  
+ /* With the advent of layers, the meaning of ONTOP in the following 
+    explanation has changed to mean any window in a higher layer. */
+ 
  /* The following factors represent the amount of area that these types of
   * windows are counted as.  For example, by default the area of ONTOP windows
   * is counted 5 times as much as normal windows.  So CleverPlacement will
***************
*** 393,399 ****
        anew = (xr - xl) * (yb - yt);
        if(testw->flags & ICONIFIED)
          avoidance_factor = AVOIDICON;
!       else if(testw->flags & ONTOP)
          avoidance_factor = AVOIDONTOP;
        else if(testw->flags & STICKY)
          avoidance_factor = AVOIDSTICKY;
--- 396,402 ----
        anew = (xr - xl) * (yb - yt);
        if(testw->flags & ICONIFIED)
          avoidance_factor = AVOIDICON;
!       else if(testw->layer > t->layer)
          avoidance_factor = AVOIDONTOP;
        else if(testw->flags & STICKY)
          avoidance_factor = AVOIDSTICKY;
diff -c fvwm+session/session.c fvwm/session.c
*** fvwm+session/session.c	Tue Feb 23 23:25:36 1999
--- fvwm/session.c	Tue Feb 23 23:28:14 1999
***************
*** 33,38 ****
--- 33,39 ----
      int                 x, y, w, h, icon_x, icon_y;
      int                 x_max, y_max, w_max, h_max; 
      int                 desktop; 
+     int                 layer; 
      int                 used;
      int                 shaded, name_changed;
      unsigned long       flags;
***************
*** 320,325 ****
--- 321,327 ----
  	      Scr.Vx + ewin->icon_x_loc,
  	      Scr.Vy + ewin->icon_y_loc);
        fprintf(f, "  [DESK] %i\n", ewin->Desk);
+       fprintf(f, "  [LAYER] %i\n", ewin->layer);
        fprintf(f, "  [FLAGS] %lu %i %i\n", ewin->flags, 
  	      !!(ewin->buttons & WSHADE), ewin->tmpflags.NameChanged); 
      }
***************
*** 361,366 ****
--- 363,369 ----
                 matches[num_match - 1].icon_x = 0;
                 matches[num_match - 1].icon_y = 0;
                 matches[num_match - 1].desktop = 0;
+                matches[num_match - 1].layer = 0;
                 matches[num_match - 1].shaded = 0;
                 matches[num_match - 1].name_changed = 0;
                 matches[num_match - 1].flags = 0;
***************
*** 385,390 ****
--- 388,398 ----
                 sscanf(s, "%*s %i",
  		      &(matches[num_match - 1].desktop));
               }
+            else if (!strcmp(s1, "[LAYER]"))
+              { 
+                sscanf(s, "%*s %i",
+ 		      &(matches[num_match - 1].layer));
+              }
             else if (!strcmp(s1, "[FLAGS]"))
               {
                 sscanf(s, "%*s %lu %i %i",
***************
*** 553,559 ****
                   ewin->flags &= ~STARTICONIC;
                 }
  #define FLAG(x) if(matches[i].flags&x) ewin->flags|=x; else ewin->flags&=~x
-                FLAG(ONTOP);
                 FLAG(WINDOWLISTSKIP);
                 FLAG(SUPPRESSICON);
                 FLAG(NOICON_TITLE);
--- 561,566 ----
***************
*** 586,591 ****
--- 593,600 ----
                   ewin->flags &= ~STICKY;
  		 ewin->Desk = matches[i].desktop;
                 }
+               
+                ewin->layer = matches[i].layer;
    
                 ewin->attr.width = matches[i].w;
                 ewin->attr.height = matches[i].h;
diff -c fvwm+session/style.c fvwm/style.c
*** fvwm+session/style.c	Tue Feb 23 21:10:45 1999
--- fvwm/style.c	Tue Feb 23 20:00:39 1999
***************
*** 488,493 ****
--- 488,503 ----
            SKIP("LENIENCE");
            tname.off_flags |= LENIENCE_FLAG;
          }
+ 	else if (ITIS("LAYER"))
+ 	  {
+ 	    SKIP("LAYER");
+ 	    sscanf(restofline,"%d",&tname.layer);
+ 	    if(tname.layer < 0)
+ 	      fvwm_msg(ERR,"ProcessNewStyle", "Layer must be non-negative." );
+ 	    GETWORD;
+ 	    restofline = tmp;
+ 	    SKIPSPACE;
+ 	  }
          break;
        case 'm':
          if(ITIS("MWMBUTTONS"))
***************
*** 662,677 ****
          {
            SKIP("StartNormal");
            tname.on_flags |= START_ICONIC_FLAG;
-         }
-         else if(ITIS("StaysOnTop"))
-         {
-           SKIP("StaysOnTop");
-           tname.off_flags |= STAYSONTOP_FLAG;
-         }
-         else if(ITIS("StaysPut"))
-         {
-           SKIP("StaysPut");
-           tname.on_flags |= STAYSONTOP_FLAG;
          }
          else if(ITIS("Sticky"))
          {
--- 672,677 ----
diff -c fvwm+session/windows.c fvwm/windows.c
*** fvwm+session/windows.c	Tue Feb 23 21:10:45 1999
--- fvwm/windows.c	Tue Feb 23 20:00:50 1999
***************
*** 31,41 ****
  #define SHOW_NORMAL   (1<<2)
  #define SHOW_ICONIC   (1<<3)
  #define SHOW_STICKY   (1<<4)
- #define SHOW_ONTOP    (1<<5)
  #define NO_DESK_SORT  (1<<6)
  #define SHOW_ICONNAME (1<<7)
  #define SHOW_ALPHABETIC (1<<8)
! #define SHOW_EVERYTHING (SHOW_GEOMETRY | SHOW_ALLDESKS | SHOW_NORMAL | SHOW_ICONIC | SHOW_STICKY | SHOW_ONTOP)
  
  /* Function to compare window title names
   */
--- 31,40 ----
  #define SHOW_NORMAL   (1<<2)
  #define SHOW_ICONIC   (1<<3)
  #define SHOW_STICKY   (1<<4)
  #define NO_DESK_SORT  (1<<6)
  #define SHOW_ICONNAME (1<<7)
  #define SHOW_ALPHABETIC (1<<8)
! #define SHOW_EVERYTHING (SHOW_GEOMETRY | SHOW_ALLDESKS | SHOW_NORMAL | SHOW_ICONIC | SHOW_STICKY)
  
  /* Function to compare window title names
   */
***************
*** 81,86 ****
--- 80,87 ----
    MenuStatus menu_retval;
    XEvent *teventp;
    MenuOptions mops;
+   int low_layer = 0;  /* show all layers by default */ 
+   int high_layer = INT_MAX;
  
    mops.flags.allflags = 0;
    if (action && *action)
***************
*** 144,155 ****
          flags |= SHOW_STICKY;
        else if (StrEquals(tok,"OnlySticky"))
          flags = SHOW_STICKY;
!       else if (StrEquals(tok,"NoOnTop"))
!         flags &= ~SHOW_ONTOP;
!       else if (StrEquals(tok,"OnTop"))
!         flags |= SHOW_ONTOP;
!       else if (StrEquals(tok,"OnlyOnTop"))
!         flags = SHOW_ONTOP;
        else if (!line || !*line)
  	default_action = strdup(tok);
        else
--- 145,165 ----
          flags |= SHOW_STICKY;
        else if (StrEquals(tok,"OnlySticky"))
          flags = SHOW_STICKY;
!       else if (StrEquals(tok, "Layers")) 
!       {
! 	free(tok);
!         line = GetNextOption(line, &tok);
! 	if (tok) 
!         {
!           low_layer = high_layer = atoi(tok);
! 	  free(tok);
!           line = GetNextOption(line, &tok);
! 	  if (tok) 
!           {
!             high_layer = atoi(tok);
!           }
!         }
!       }
        else if (!line || !*line)
  	default_action = strdup(tok);
        else
***************
*** 235,247 ****
            continue; /* don't want icons - skip */
          if (!(flags & SHOW_STICKY) && (t->flags & STICKY))
            continue; /* don't want sticky ones - skip */
-         if (!(flags & SHOW_ONTOP) && (t->flags & ONTOP))
-           continue; /* don't want ontop ones - skip */
          if (!(flags & SHOW_NORMAL) &&
!             !((t->flags & ICONIFIED) ||
!               (t->flags & STICKY) ||
!               (t->flags & ONTOP)))
            continue; /* don't want "normal" ones - skip */
  
          /* put a seperator between desks, but not at the top */
          if (t->Desk != last_desk_displayed)
--- 245,255 ----
            continue; /* don't want icons - skip */
          if (!(flags & SHOW_STICKY) && (t->flags & STICKY))
            continue; /* don't want sticky ones - skip */
          if (!(flags & SHOW_NORMAL) &&
!             !((t->flags & ICONIFIED) || (t->flags & STICKY))) 
            continue; /* don't want "normal" ones - skip */
+         if ((t->layer < low_layer) || (t->layer > high_layer))
+           continue;  /* don't want this layer */
  
          /* put a seperator between desks, but not at the top */
          if (t->Desk != last_desk_displayed)
***************
*** 293,300 ****
  
            if (t->flags & STICKY)
              strcat(tname, " S");
-           if (t->flags & ONTOP)
-             strcat(tname, " T");
            if (t->flags & ICONIFIED)
              strcat(tname, ")");
            strcat(t_hot,"\t");
--- 301,306 ----

-- 
Matthias Clasen, 
Tel. 0761/203-5606
Email: [email-masked]
Mathematisches Institut, Albert-Ludwigs-Universitaet Freiburg
--
Visit the official FVWM web page at <URL:http://www.fvwm.org/>.
To unsubscribe from the list, send "unsubscribe fvwm-workers" in the
body of a message to [email-masked].
To report problems, send mail to [email-masked]

---

From: Dan Espen <[email-masked]>
Date: Thu, 25 Feb 1999 09:00:29 -0500

> each other with layer 0 being the bottommost. The default layer is 0.

I'm a little concerned that this  change breaks existing config files.
When I   check  my  config files, I    find 2  things  with  styles of
StaysOnTop.   Both have comments about it  not really working.  (I was
trying to force icons to stay above a window.)

I haven't examined your patch closely, or applied it.

I see the  bottom layer  is  zero, I guess  there  is no limit  to the
number of the top layer.  If there was, perhaps the current StaysOnTop
command could be shorthand for Layer 9, or 99?

Perhaps there should  be an upper limit  on the  layer number, besides
the maximum  value  of an  int, or  at least a  convention.  I've been
trying to reduce the   amount  of configuration tweaking  that   fvwm2
needs.  I would  expect that some of our  components,  for example the
Pager, will have built in default layer numbers.

Did you put the Pan Frames in a layer?  Which one?

--
Dan Espen
444 Hoes Lane  Room RRC 1C-214           E-mail: [email-masked]
Piscataway, NJ 08854                     Phone: (732) 699-5570
--
Visit the official FVWM web page at <URL:http://www.fvwm.org/>.
To unsubscribe from the list, send "unsubscribe fvwm-workers" in the
body of a message to [email-masked].
To report problems, send mail to [email-masked]

---

From: Matthias Clasen <[email-masked]>
Date: Thu, 25 Feb 1999 16:10:23 +0100

> command could be shorthand for Layer 9, or 99?

Well, I sort of assumed everybody would use one of the preprocessors
do eg define(`StaysOnTop',`Layer 99') or somesuch. 

> Pager, will have built in default layer numbers.

I think the Gnome hints have some predefined layers which go up to
10 or 12 or so. Having 2^32 layers is perhaps as useful as having
2^32 desks...

> Did you put the Pan Frames in a layer?  Which one?

No, the pan frames are floating above the whole layer hierarchy. This
is achieved by issuing raisePanFrames() at key points (That shouldn't 
strictly be necessary, since I tried to remove any direct calls to 
XRaiseWindow from fvwm - it should only rearrange the windows it is
managing, leaving panFrames and override-redirect windows floating above.)

---

From: Dan Espen <[email-masked]>
Date: Thu, 25 Feb 1999 09:38:37 -0500

> do eg define(`StaysOnTop',`Layer 99') or somesuch. 

I've avoided using any of the preprocessors up until now.

> 10 or 12 or so. Having 2^32 layers is perhaps as useful as having

2^31? but why quibble over a couple of billion desks or layers.
I think the number of desks is completely out of control,
which is what  led me to suggest a limit.

---

From: Patrick Rogan <[email-masked]>
Date: Thu, 25 Feb 1999 10:44:17 -0500

> I've avoided using any of the preprocessors up until now.

Don't force people to use preprocessors.  I don't use one, no one should
have to.

Please, "alias" StaysOnTop & StaysOnBottom to "Layer 99" & "Layer 0",
respectively.    This makes it user friendly and backward compatible. 
Don't devalue StaysOnTop, keep it as a convenient shortcut.

Also, Yes, limit the number of layers just so people can easily
conceptualize how they work & how they can be used.  

Make sure in the docs that you clearly say what layer new windows go
into by default  (Layer 1 ??, Layer 50 ??).  

If new windows go into Layer 0, you couldn't have an automatic
StaysOnBottom command.  You'd have to screw around with it instead of
just saying "StaysOnBottom" and getting on with your work.

~p@