From: Iain Patterson <w...@iain.cx>

Use the same logic used by xfwm4, metacity et al to replace an existing
window manager on the screen and allow other window managers to replace
us, as defined by the ICCCM 2.0:

http://www.x.org/releases/X11R7.6/doc/xorg-docs/specs/ICCCM/icccm.html
  Communication with the Window Manager by Means of Selections

By convention those window managers try to become the selection owner of
the WM_Sn atom where n is the screen number.  If the atom is owned by
another window manager and the --replace argument was not given to wmaker
we fail to start.  If the argument was given we try to become the new
owner and wait for the existing window manger to exit.

After a successful startup we watch for SelectionClear events on the
atom and initiate a shutdown if one arrives, as that implies that
another window manager was started with --replace.
---
 src/WindowMaker.h |   1 +
 src/event.c       |  19 +++++++++
 src/main.c        |   3 ++
 src/screen.c      | 114 +++++++++++++++++++++++++++++++++++++++++++++++++++---
 src/screen.h      |   1 +
 5 files changed, 133 insertions(+), 5 deletions(-)

diff --git a/src/WindowMaker.h b/src/WindowMaker.h
index 3c2869c..606298d 100644
--- a/src/WindowMaker.h
+++ b/src/WindowMaker.h
@@ -463,6 +463,7 @@ extern struct WPreferences {
        char show_clip_title;
 
        struct {
+               unsigned int replace:1;               /* replace existing 
window manager */
                unsigned int nodock:1;                /* don't display the dock 
*/
                unsigned int noclip:1;                /* don't display the clip 
*/
                unsigned int clip_merged_in_dock:1;   /* disable clip, switch 
workspaces with dock */
diff --git a/src/event.c b/src/event.c
index dbb927b..1e100f7 100644
--- a/src/event.c
+++ b/src/event.c
@@ -103,6 +103,7 @@ static void handleFocusIn(XEvent *event);
 static void handleMotionNotify(XEvent *event);
 static void handleVisibilityNotify(XEvent *event);
 static void handle_inotify_events(void);
+static void handle_selection_clear(XSelectionClearEvent *event);
 static void wdelete_death_handler(WMagicNumber id);
 
 
@@ -274,6 +275,10 @@ void DispatchEvent(XEvent * event)
 #endif
                break;
 
+       case SelectionClear:
+               handle_selection_clear(&event->xselectionclear);
+               break;
+
        default:
                handleExtensions(event);
                break;
@@ -1886,3 +1891,17 @@ static void handleVisibilityNotify(XEvent * event)
                return;
        wwin->flags.obscured = (event->xvisibility.state == 
VisibilityFullyObscured);
 }
+
+static void handle_selection_clear(XSelectionClearEvent *event)
+{
+       WScreen *scr = wScreenForWindow(event->window);
+
+       if (!scr)
+               return;
+
+       if (event->selection != scr->sn_atom)
+               return;
+
+       wmessage(_("another window manager is replacing us!"));
+       Shutdown(WSExitMode);
+}
diff --git a/src/main.c b/src/main.c
index 297f3ab..b68c5b6 100644
--- a/src/main.c
+++ b/src/main.c
@@ -435,6 +435,7 @@ static void print_help(void)
        puts(_("The Window Maker window manager for the X window system"));
        puts("");
        puts(_(" -display host:dpy      display to use"));
+       puts(_(" --replace              replace running window manager"));
        puts(_(" --no-dock              do not open the application Dock"));
        puts(_(" --no-clip              do not open the workspace Clip"));
        puts(_(" --no-autolaunch        do not autolaunch applications"));
@@ -651,6 +652,8 @@ static int real_main(int argc, char **argv)
                                wPreferences.flags.noclip = 1;
                        } else if (strcmp(argv[i], "-nodrawer") == 0 || 
strcmp(argv[i], "--no-drawer") == 0) {
                                wPreferences.flags.nodrawer = 1;
+                       } else if (strcmp(argv[i], "-replace") == 0 || 
strcmp(argv[i], "--replace") == 0) {
+                               wPreferences.flags.replace = 1;
                        } else if (strcmp(argv[i], "-version") == 0 || 
strcmp(argv[i], "--version") == 0) {
                                printf("Window Maker %s\n", VERSION);
                                exit(0);
diff --git a/src/screen.c b/src/screen.c
index 10a1fc9..1f246c4 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -66,6 +66,8 @@
     |SubstructureRedirectMask|ButtonPressMask|ButtonReleaseMask\
     |KeyPressMask|KeyReleaseMask)
 
+#define REPLACE_WM_TIMEOUT 15
+
 #define STIPPLE_WIDTH 2
 #define STIPPLE_HEIGHT 2
 static char STIPPLE_DATA[] = { 0x02, 0x01 };
@@ -91,6 +93,106 @@ static void make_keys(void)
 }
 
 /*
+ * Support for ICCCM 2.0: Window Manager Replacement protocol
+ * See: http://www.x.org/releases/X11R7.6/doc/xorg-docs/specs/ICCCM/icccm.html
+ *
+ * Basically, user should be able to dynamically change its window manager; 
this is done
+ * cooperatively through a special Selection ("WM_Sn" where 'n' is the X 
screen number)
+ *
+ * This function does 2 things:
+ *  - it checks if this selection is already owned, which means that another 
window
+ * manager is running. If it is the case and user specified '--replace' on the 
command
+ * line, then it asks the WM to shut down;
+ *  - when ok, it sets the selection ownership to ourself, so another window 
manager
+ * may ask us to shut down (this is handled in "event.c")
+ */
+static Bool replace_existing_wm(WScreen *scr)
+{
+       char atomName[16];
+       Window wm;
+       XSetWindowAttributes attribs;
+       XClientMessageEvent event;
+       unsigned long current_time;
+       int ret;
+
+       /* Try to acquire the atom named WM_S<screen> */
+       ret = snprintf(atomName, sizeof(atomName), "WM_S%d", scr->screen);
+       if (ret < 0 || ret == sizeof(atomName)) {
+               werror("out of memory trying to allocate window manager 
selection atom for screen %d", scr->screen);
+               return False;
+       }
+
+       scr->sn_atom = XInternAtom(dpy, atomName, False);
+       if (! scr->sn_atom)
+               return False;
+
+       /* Check if an existing window manager owns the selection */
+       wm = XGetSelectionOwner(dpy, scr->sn_atom);
+       if (wm) {
+               if (!wPreferences.flags.replace) {
+                       wmessage(_("another window manager is running"));
+                       wwarning(_("use the --replace flag to replace it"));
+                       return False;
+               }
+
+               attribs.event_mask = StructureNotifyMask;
+               if (!XChangeWindowAttributes(dpy, wm, CWEventMask, &attribs))
+                       wm = None;
+       }
+
+       /* for our window manager info notice board and the selection owner */
+       scr->info_window = XCreateSimpleWindow(dpy, scr->root_win, 0, 0, 10, 
10, 0, 0, 0);
+
+       /* Try to acquire the selection */
+       current_time = CurrentTime;
+       ret = XSetSelectionOwner(dpy, scr->sn_atom, scr->info_window, 
current_time);
+       if (ret == BadAtom || ret == BadWindow)
+               return False;
+
+       /* Wait for other window manager to exit */
+       if (wm) {
+               unsigned long wait = 0;
+               unsigned long timeout = REPLACE_WM_TIMEOUT * 1000000L;
+               XEvent event;
+
+               while (wait < timeout) {
+                       if (!(wait % 1000000))
+                               wmessage(_("waiting %lus for other window 
manager to exit"), (timeout - wait) / 1000000L);
+
+                       if (XCheckWindowEvent(dpy, wm, StructureNotifyMask, 
&event))
+                               if (event.type == DestroyNotify)
+                                       break;
+
+                       wusleep(100000);
+                       wait += 100000;
+               }
+
+               if (wait >= timeout) {
+                       wwarning(_("other window manager hasn't exited!"));
+                       return False;
+               }
+
+               wmessage(_("replacing the other window manager"));
+       }
+
+       if (XGetSelectionOwner(dpy, scr->sn_atom) != scr->info_window)
+               return False;
+
+       event.type = ClientMessage;
+       event.message_type = scr->sn_atom;
+       event.format = 32;
+       event.data.l[0] = (long) current_time;
+       event.data.l[1] = (long) scr->sn_atom;
+       event.data.l[2] = (long) scr->info_window;
+       event.data.l[3] = (long) 0L;
+       event.data.l[4] = (long) 0L;
+       event.window = scr->root_win;
+       XSendEvent(dpy, scr->root_win, False, StructureNotifyMask, (XEvent *) 
&event);
+
+       return True;
+}
+
+/*
  *----------------------------------------------------------------------
  * alreadyRunningError--
  *     X error handler used to catch errors when trying to do
@@ -523,6 +625,13 @@ WScreen *wScreenInit(int screen_number)
        CantManageScreen = 0;
        oldHandler = XSetErrorHandler(alreadyRunningError);
 
+       /* Do we need to replace an existing window manager? */
+       if (!replace_existing_wm(scr)) {
+               XDestroyWindow(dpy, scr->info_window);
+               wfree(scr);
+               return NULL;
+       }
+
        event_mask = EVENT_MASK;
        XSelectInput(dpy, scr->root_win, event_mask);
 
@@ -616,11 +725,6 @@ WScreen *wScreenInit(int screen_number)
        /* create GCs with default values */
        allocGCs(scr);
 
-       /* for our window manager info notice board. Need to
-        * create before reading the defaults, because it will be used there.
-        */
-       scr->info_window = XCreateSimpleWindow(dpy, scr->root_win, 0, 0, 10, 
10, 0, 0, 0);
-
        /* read defaults for this screen */
        wReadDefaults(scr, w_global.domain.wmaker->dictionary);
 
diff --git a/src/screen.h b/src/screen.h
index 8ffb6f9..f3fc6fb 100644
--- a/src/screen.h
+++ b/src/screen.h
@@ -69,6 +69,7 @@ typedef struct WDrawerChain {
 typedef struct _WScreen {
     int        screen;                        /* screen number */
     Window info_window;                       /* for our window manager info 
stuff */
+    Atom sn_atom;                     /* window manager selection */
 
     int scr_width;                    /* size of the screen */
     int scr_height;
-- 
2.1.4


-- 
To unsubscribe, send mail to wmaker-dev-unsubscr...@lists.windowmaker.org.

Reply via email to