Subject: More ICCCM 2 things

From: Matthias Clasen <[email-masked]>
Date: Mon, 4 Jan 1999 02:09:58 +0100

Hi again, another day, another patch...

I have expanded my urgencyhint patch to cover all
(I think) missing pieces from ICCCM 2.0. Apart from
UrgencyHint these are a method for client-side 
colormap installation and the "Manager Selection".

While the urgency and colormap stuff is almost trivial,
the selection thing was a bit more difficult and may 
not be 100% correct. It is the first selection owner
code I have written, and I didn't find any examples
to copy (especially for the MULTIPLE target). Right
now the selection is not *very* useful, since you
can only get the icccm compliance level from it. 
But one nice thing which it allows is a secure method
for wm's to take over control from a running wm:
With the patch below, I can simply type 

fvwm2 -replace 

in an xterm and the previous incarnation of fvwm2
goes away to make room for the new one (assuming that
in your X setup, killing the wm doesn't end the session).

Again, the features added by this patch may not be
used by many clients... 

Matthias

--- fvwm/fvwm.c.orig	Fri Jan  1 21:50:52 1999
+++ fvwm/fvwm.c	Mon Jan  4 01:33:21 1999
@@ -56,6 +56,208 @@
 Window BlackoutWin=None;        /* window to hide window captures */
 Bool fFvwmInStartup = True;     /* Set to False when startup has finished */
 
+#ifdef ICCCM2
+
+/* [email-masked] - 03/01/1999 - new
+   Everything between #ifdef ICCCM2 #endif in this file and
+   in events.c implements handling of a "Manager Selection"
+   as described in the ICCCM 2.0
+   There is also some code to deal with client-side 
+   InstallColormap according to ICCCM 2.0 and with 
+   the UrgencyHint flag in WM_HINTS.
+*/
+
+extern XEvent Event;
+
+Window manager_win = None;
+Time managing_since;
+
+Atom _XA_WIN_SX;
+Atom _XA_MANAGER;
+Atom _XA_ATOM_PAIR;
+Atom _XA_WM_COLORMAP_NOTIFY;
+#define _XA_TARGETS   conversion_targets[0]
+#define _XA_MULTIPLE  conversion_targets[1]
+#define _XA_TIMESTAMP conversion_targets[2]
+#define _XA_VERSION   conversion_targets[3]
+#define MAX_TARGETS                      4
+
+Atom conversion_targets[MAX_TARGETS];
+
+long icccm_version[] = { 2, 0 };
+
+void 
+SetupICCCM2 (Bool replace_running_wm)
+{
+  Window running_wm_win;
+  XSetWindowAttributes attr;
+  XEvent xev;
+  XClientMessageEvent ev;
+  char win_sx[20];
+
+  sprintf (win_sx, "WIN_S%d", Scr.screen);
+  _XA_WIN_SX =    XInternAtom (dpy, win_sx, False);
+  _XA_MANAGER =   XInternAtom (dpy, "MANAGER", False);
+  _XA_ATOM_PAIR = XInternAtom (dpy, "ATOM_PAIR", False);
+  _XA_TARGETS =   XInternAtom (dpy, "TARGETS", False);
+  _XA_MULTIPLE =  XInternAtom (dpy, "MULTIPLE", False);
+  _XA_TIMESTAMP = XInternAtom (dpy, "TIMESTAMP", False);
+  _XA_VERSION =   XInternAtom (dpy, "VERSION", False);
+  _XA_WM_COLORMAP_NOTIFY = XInternAtom (dpy, "WM_COLORMAP_NOTIFY", False);
+
+  /* Check for a running ICCCM 2.0 compliant WM */
+  running_wm_win = XGetSelectionOwner (dpy, _XA_WIN_SX);
+  if (running_wm_win != None) {
+    fvwm_msg (INFO, "SetupICCCM2", 
+	      "another ICCCM 2.0 compliant WM is running");
+    if (!replace_running_wm) {
+      exit(1);
+    }
+    
+    /* We need to know when the old manager is gone.
+       Thus we wait until it destroys running_wm_win. */
+    attr.event_mask = StructureNotifyMask;
+    XChangeWindowAttributes (dpy, running_wm_win, CWEventMask, &attr);
+  } else {
+    fvwm_msg (INFO, "SetupICCCM2", "no ICCCM 2.0 compliant WM found");
+  }
+
+  attr.event_mask = PropertyChangeMask;
+  manager_win = XCreateWindow (dpy, Scr.Root, 0, 0, 1, 1, 0,
+			       CopyFromParent, CopyFromParent, 
+			       CopyFromParent, CWEventMask, &attr);
+
+  /* We are not yet in the event loop, thus lastTimestamp will not
+     be ready. Have to get a timestamp manually by provoking a 
+     PropertyNotify. */ 
+  XChangeProperty (dpy, manager_win, XA_WM_CLASS, XA_STRING, 8,
+		   PropModeAppend, NULL, 0);
+  XWindowEvent (dpy, manager_win, PropertyChangeMask, &xev);
+  attr.event_mask = 0L;
+  XChangeWindowAttributes (dpy, manager_win, CWEventMask, &attr);
+
+  managing_since = xev.xproperty.time;
+
+  XSetSelectionOwner (dpy, _XA_WIN_SX, manager_win, managing_since);
+  if (XGetSelectionOwner (dpy, _XA_WIN_SX) != manager_win) {
+    fvwm_msg (INFO, "SetupICCCM2", "failed to acquire selection ownership");
+  } 
+ 
+  /* Announce ourself as the new wm */
+  ev.type = ClientMessage;
+  ev.window = Scr.Root;
+  ev.message_type = _XA_MANAGER;
+  ev.format = 32;
+  ev.data.l[0] = managing_since;
+  ev.data.l[1] = _XA_WIN_SX;
+  XSendEvent (dpy, Scr.Root, False, StructureNotifyMask, (XEvent*)&ev);
+ 
+  if (running_wm_win != None) {
+    /* Wait for the old wm to finish. */
+    /* FIXME: need a timeout here. */
+    fvwm_msg (INFO, "SetupICCCM2", "waiting for WM to give up");
+    do {
+      XWindowEvent (dpy, running_wm_win, StructureNotifyMask, &xev);
+    } while (xev.type != DestroyNotify);
+  }
+}
+
+/* We must make sure that we have released SubstructureRedirect
+   before we destroy manager_win, so that another wm can start
+   successfully. */
+void 
+CloseICCCM2 ()
+{
+  fvwm_msg(INFO, "CloseICCCM2", "good luck, new wm");
+  XSelectInput(dpy, Scr.Root, 0 );
+  XSync(dpy, 0);
+  XDestroyWindow (dpy, manager_win);
+}
+
+/* FIXME: property change actually succeeded */
+static Bool
+convertProperty (Window w, Atom target, Atom property)
+{
+  if (target == _XA_TARGETS)
+    XChangeProperty (dpy, w, property, 
+		     XA_ATOM, 32, PropModeReplace, 
+		     (unsigned char *)conversion_targets, MAX_TARGETS); 
+  else if (target == _XA_TIMESTAMP)
+    XChangeProperty (dpy, w, property, 
+		     XA_INTEGER, 32, PropModeReplace, 
+		     (unsigned char *)&managing_since, 1); 
+  else if (target == _XA_VERSION)
+    XChangeProperty (dpy, w, property, 
+		     XA_INTEGER, 32, PropModeReplace, 
+		     (unsigned char *)icccm_version, 2); 
+  else return False;
+
+  /* FIXME: This is ugly. We should rather select for
+     PropertyNotify on the window, return to the main loop,
+     and send the SelectionNotify once we are sure the property 
+     has arrived. Problem: this needs a list of pending 
+     SelectionNotifys. */
+  XSync (dpy, 0);
+  return True;
+}
+
+void 
+HandleSelectionRequest ()
+{
+  Atom type, *adata;
+  int i, format;
+  unsigned long num, rest;
+  unsigned char *data;
+  XSelectionRequestEvent ev = Event.xselectionrequest;
+  XSelectionEvent reply;
+
+  reply.type = SelectionNotify;
+  reply.display = dpy;
+  reply.requestor = ev.requestor;
+  reply.selection = ev.selection;
+  reply.target = ev.target;
+  reply.property = None;
+  reply.time = ev.time;
+
+  if (ev.target == _XA_MULTIPLE) {
+    if (ev.property != None) {
+      XGetWindowProperty (dpy, ev.requestor, ev.property, 0, 256, False,
+			  _XA_ATOM_PAIR, &type, &format, &num, &rest, &data);
+      /* FIXME: to be 100% correct, should deal with rest > 0,
+	 but since we have 4 possible targets, we will hardly ever
+	 meet multiple requests with a length > 8 */
+      adata = (Atom*)data;
+      for (i = 0; i < num; i += 2) {
+	if (!convertProperty(ev.requestor, adata[i], adata[i+1])) 
+	  adata[i+1] = None;
+      }
+      XChangeProperty (dpy, ev.requestor, ev.property, _XA_ATOM_PAIR, 
+		       32, PropModeReplace, data, num);
+      XFree (data);
+    }
+  } else {
+    if (ev.property == None) ev.property = ev.target;
+    if (convertProperty (ev.requestor, ev.target, ev.property)) 
+      reply.property = ev.property;
+  }
+
+  XSync (dpy, 0);
+  XSendEvent (dpy, ev.requestor, False, 0L, (XEvent*)&reply);    
+}
+
+/* If another wm is requesting ownership of the selection,
+   we receive a SelectionClear event. In that case, we have to 
+   release all resources and destroy manager_win. Done() calls
+   CloseICCCM2() after undecorating all windows. */
+void
+HandleSelectionClear ()
+{
+  fvwm_msg(INFO, "HandleSelectionClear", "I lost my selection!");
+  Done(0, NULL);
+}
+
+#endif /* ICCCM2 */
+
 char *default_config_command = "Read "FVWMRC;
 
 #define MAX_CFG_CMDS 10
@@ -143,6 +345,9 @@
   char message[255];
   Bool single = False;
   Bool option_error = FALSE;
+#ifdef ICCCM2
+  Bool replace_wm = False;
+#endif
   int x, y;
 
   g_argv = argv;
@@ -219,6 +424,12 @@
     {
       Blackout = True;
     }
+#ifdef ICCCM2
+    else if (strncasecmp(argv[i], "-replace", 8) == 0)
+      {
+	replace_wm = True;
+      }
+#endif
     else if (strncasecmp(argv[i], "-version", 8) == 0)
     {
       fvwm_msg(INFO,"main", "Fvwm Version %s compiled on %s at %s\n",
@@ -388,6 +599,9 @@
   XChangeProperty (dpy, Scr.Root, _XA_MIT_PRIORITY_COLORS,
                    XA_CARDINAL, 32, PropModeReplace, NULL, 0);
 
+#ifdef ICCCM2
+  SetupICCCM2 (replace_wm);
+#endif
 
   XSetErrorHandler(CatchRedirectError);
   XSetIOErrorHandler(CatchFatal);
@@ -395,6 +609,9 @@
                LeaveWindowMask| EnterWindowMask | PropertyChangeMask |
                SubstructureRedirectMask | KeyPressMask |
                SubstructureNotifyMask|
+#ifdef ICCCM2
+	       ColormapChangeMask| 
+#endif
                ButtonPressMask | ButtonReleaseMask );
   XSync(dpy, 0);
 
@@ -709,6 +926,13 @@
     "+ \"I\" WindowId $0 FlipFocus",
     "+ \"I\" WindowId $0 Raise",
     "+ \"I\" WindowId $0 WarpToWindow 5p 5p",
+#ifdef ICCCM2
+    "AddToFunc UrgencyFunc \"I\" Iconify -1",
+    "+ \"I\" FlipFocus",
+    "+ \"I\" Raise",
+    "+ \"I\" WarpToWindow 5p 5p",
+    "AddToFunc UrgencyDoneFunc \"I\" Nop",
+#endif
     NULL
   };
   int i=0;
@@ -1445,6 +1669,10 @@
   ClosePipes();
 
   Reborder ();
+
+#ifdef ICCCM2
+  CloseICCCM2();
+#endif
 
   if(restart)
   {
--- fvwm/events.c.orig	Fri Jan  1 21:50:52 1999
+++ fvwm/events.c	Mon Jan  4 01:32:43 1999
@@ -86,6 +86,11 @@
 
 Window PressedW;
 
+#ifdef ICCCM2
+void HandleSelectionClear(void);
+void HandleSelectionRequest(void);
+#endif
+
 /*
 ** LASTEvent is the number of X events defined - it should be defined
 ** in X.h (to be like 35), but since extension (eg SHAPE) events are
@@ -130,6 +135,10 @@
   if (ShapesSupported)
     EventHandlerJumpTable[ShapeEventBase+ShapeNotify] = HandleShapeNotify;
 #endif /* SHAPE */
+#ifdef ICCCM2
+  EventHandlerJumpTable[SelectionClear]   = HandleSelectionClear;
+  EventHandlerJumpTable[SelectionRequest] = HandleSelectionRequest;  
+#endif
 }
 
 /***********************************************************************
@@ -403,6 +412,9 @@
 {
   XTextProperty text_prop;
   Boolean       OnThisPage    =  False;
+#ifdef ICCCM2
+  int was_urgent;
+#endif
 
   DBUG("HandlePropertyNotify","Routine Entered");
 
@@ -484,25 +496,34 @@
       break;
 
     case XA_WM_HINTS:
-      if (Tmp_win->wmhints)
+#ifdef ICCCM2
+      /* [email-masked] - 02/01/1998 - new - 
+	 the urgency flag is an ICCCM 2.0 addition to the WM_HINTS. */
+      was_urgent = 0;
+#endif
+      if (Tmp_win->wmhints) {
+#ifdef ICCCM2
+	was_urgent = Tmp_win->wmhints->flags & XUrgencyHint;
+#endif
 	XFree ((char *) Tmp_win->wmhints);
+      }
       Tmp_win->wmhints = XGetWMHints(dpy, Event.xany.window);
-
+      
       if(Tmp_win->wmhints == NULL)
 	return;
-
+      
       if((Tmp_win->wmhints->flags & IconPixmapHint)||
 	 (Tmp_win->wmhints->flags & IconWindowHint))
 	if(Tmp_win->icon_bitmap_file == Scr.DefaultIcon)
 	  Tmp_win->icon_bitmap_file = (char *)0;
-
+      
       if((Tmp_win->wmhints->flags & IconPixmapHint)||
-         (Tmp_win->wmhints->flags & IconWindowHint))
+	 (Tmp_win->wmhints->flags & IconWindowHint))
 	{
 	  if (!(Tmp_win->flags & SUPPRESSICON))
 	    {
-              if (Tmp_win->icon_w)
-                XDestroyWindow(dpy,Tmp_win->icon_w);
+	      if (Tmp_win->icon_w)
+		XDestroyWindow(dpy,Tmp_win->icon_w);
 	      XDeleteContext(dpy, Tmp_win->icon_w, FvwmContext);
 	      if(Tmp_win->flags & ICON_OURS)
 		{
@@ -515,8 +536,8 @@
 	      else
 		XUnmapWindow(dpy,Tmp_win->icon_pixmap_w);
 	    }
-          Tmp_win->icon_w = None;
-          Tmp_win->icon_pixmap_w = None;
+	  Tmp_win->icon_w = None;
+	  Tmp_win->icon_pixmap_w = None;
 	  Tmp_win->iconPixmap = (Window)NULL;
 	  if(Tmp_win->flags & ICONIFIED)
 	    {
@@ -525,20 +546,20 @@
 	      CreateIconWindow(Tmp_win,
 			       Tmp_win->icon_x_loc,Tmp_win->icon_y_loc);
 	      BroadcastPacket(M_ICONIFY, 7,
-                              Tmp_win->w, Tmp_win->frame,
-                              (unsigned long)Tmp_win,
-                              Tmp_win->icon_x_loc, Tmp_win->icon_y_loc,
-                              Tmp_win->icon_w_width, Tmp_win->icon_w_height);
+			      Tmp_win->w, Tmp_win->frame,
+			      (unsigned long)Tmp_win,
+			      Tmp_win->icon_x_loc, Tmp_win->icon_y_loc,
+			      Tmp_win->icon_w_width, Tmp_win->icon_w_height);
 	      BroadcastConfig(M_CONFIGURE_WINDOW, Tmp_win);
-
+	      
 	      if (!(Tmp_win->flags & SUPPRESSICON))
 		{
 		  LowerWindow(Tmp_win);
 		  AutoPlace(Tmp_win);
 		  if(Tmp_win->Desk == Scr.CurrentDesk)
 		    {
-                      if(Tmp_win->icon_w)
-                        XMapWindow(dpy, Tmp_win->icon_w);
+		       if(Tmp_win->icon_w)
+			 XMapWindow(dpy, Tmp_win->icon_w);
 		      if(Tmp_win->icon_pixmap_w != None)
 			XMapWindow(dpy, Tmp_win->icon_pixmap_w);
 		    }
@@ -547,8 +568,26 @@
 	      DrawIconWindow(Tmp_win);
 	    }
 	}
-      break;
 
+#ifdef ICCCM2      
+      /* [email-masked] - 02/01/1998 - new - 
+	 the urgency flag is an ICCCM 2.0 addition to the WM_HINTS. 
+	 Treat urgency changes by calling user-settable functions.
+	 These could e.g. deiconify and raise the window or temporarily
+	 change the decor. */
+      if (!was_urgent && (Tmp_win->wmhints->flags & XUrgencyHint))
+	{
+	  ExecuteFunction("Function UrgencyFunc",
+			  Tmp_win,&Event,C_WINDOW,-1);
+	}
+      
+      if (was_urgent && !(Tmp_win->wmhints->flags & XUrgencyHint))
+	{
+	  ExecuteFunction("Function UrgencyDoneFunc",
+			  Tmp_win,&Event,C_WINDOW,-1);
+	}
+#endif /* ICCCM2 */      
+      break;
     case XA_WM_NORMAL_HINTS:
 DB(("HandlePropertyNotify: XA_WM_NORMAL_HINTS calling GetWindowSizeHints"));
 	GetWindowSizeHints (Tmp_win);
@@ -623,6 +662,20 @@
     return;
   }
 
+#ifdef ICCCM2
+  /* FIXME: Is this safe enough ? I guess if clients behave 
+     according to ICCCM and send these messages only if they
+     when grabbed the pointer, it is OK */
+  {
+    extern Bool client_controls_colormaps;
+    extern Atom _XA_WM_COLORMAP_NOTIFY;
+    if (Event.xclient.message_type == _XA_WM_COLORMAP_NOTIFY) {
+      client_controls_colormaps = Event.xclient.data.l[1];
+      return;
+    }  
+  }
+#endif
+  
   /*
   ** CKH - if we get here, it was an unknown client message, so send
   ** it to the client if it was in a window we know about.  I'm not so
--- fvwm/colormaps.c.orig	Fri Jan  1 21:50:51 1999
+++ fvwm/colormaps.c	Mon Jan  4 01:28:53 1999
@@ -20,6 +20,16 @@
 #include "screen.h"
 #include "module.h"
 
+#ifdef ICCCM2
+
+/* [email-masked] - 03/01/1999 - new
+   Everything between #ifdef ICCCM2 #endif in this file and
+   in events.c implements handling of client-side InstallColormap 
+   as described in the ICCCM 2.0 */
+
+Bool client_controls_colormaps = False;
+#endif /* ICCCM2 */
+
 FvwmWindow *colormap_win;
 Colormap last_cmap = None;
 extern FvwmWindow *Tmp_win;
@@ -90,7 +100,11 @@
   /* Reinstall the colormap that we think should be installed,
    * UNLESS and unrecognized window has the focus - it might be
    * an override-redirect window that has its own colormap. */
-  if((ReInstall)&&(Scr.UnknownWinFocused == None))
+  if((ReInstall)&&(Scr.UnknownWinFocused == None)
+#ifdef ICCCM2
+     && !client_controls_colormaps
+#endif
+     )
     {
       XInstallColormap(dpy,last_cmap);
     }
@@ -138,7 +152,11 @@
   /* Don't load any new colormap if root colormap(s) has been
    * force loaded.
    */
-  if (Scr.root_pushes)
+  if (Scr.root_pushes
+#ifdef ICCCM2
+      || client_controls_colormaps
+#endif
+      )
     {
       return;
     }

-- 
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.hpc.uh.edu/fvwm/>.
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].