This patch allows wmaker to replace existing window managers with the --replace flag, like xfwm4 and metacity can. It also allows those window managers (or another wmaker) to replace wmaker in the same way.

According to my notes I opened a bug in kix's BTS for this functionality. Alas the BTS doesn't appear to be working at the moment so I can't reference it.
From 1e8f636da413612e057d35cab889a66b8d5784c6 Mon Sep 17 00:00:00 2001
From: Iain Patterson <w...@iain.cx>
Date: Mon, 10 Jun 2013 14:57:04 +0100
Subject: [PATCH] Replace and be replaced.

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.

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       |  18 ++++++++++
 src/main.c        |   3 ++
 src/screen.c      | 101 +++++++++++++++++++++++++++++++++++++++++++++++++++---
 src/screen.h      |   1 +
 5 files changed, 119 insertions(+), 5 deletions(-)

diff --git a/src/WindowMaker.h b/src/WindowMaker.h
index 100f75b..b6448be 100644
--- a/src/WindowMaker.h
+++ b/src/WindowMaker.h
@@ -433,6 +433,7 @@ typedef struct WPreferences {
     int 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, dock gets its 
workspace switching functionality */
diff --git a/src/event.c b/src/event.c
index c6951bb..c341074 100644
--- a/src/event.c
+++ b/src/event.c
@@ -131,6 +131,7 @@ static void handleKeyPress(XEvent *event);
 static void handleFocusIn(XEvent *event);
 static void handleMotionNotify(XEvent *event);
 static void handleVisibilityNotify(XEvent *event);
+static void handleSelectionClear(XEvent *event);
 static void handle_inotify_events(int fd, int wd);
 static void wdelete_death_handler(WMagicNumber id);
 
@@ -306,6 +307,10 @@ void DispatchEvent(XEvent * event)
 #endif
                break;
 
+       case SelectionClear:
+               handleSelectionClear(event);
+               break;
+
        default:
                handleExtensions(event);
                break;
@@ -1822,3 +1827,16 @@ static void handleVisibilityNotify(XEvent * event)
                return;
        wwin->flags.obscured = (event->xvisibility.state == 
VisibilityFullyObscured);
 }
+
+static void handleSelectionClear(XEvent * event)
+{
+       WScreen *scr = wScreenForWindow(event->xselection.selection);
+       if (!scr)
+               return;
+
+       if (event->xselection.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 5497e53..6dba03d 100644
--- a/src/main.c
+++ b/src/main.c
@@ -487,6 +487,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"));
@@ -691,6 +692,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 90b5445..fbdbe4b 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -69,6 +69,8 @@
     |SubstructureRedirectMask|ButtonPressMask|ButtonReleaseMask\
     |KeyPressMask|KeyReleaseMask)
 
+#define REPLACE_WM_TIMEOUT 15
+
 /**** Global variables ****/
 extern Cursor wCursor[WCUR_LAST];
 extern WPreferences wPreferences;
@@ -113,6 +115,93 @@ static void make_keys(void)
        dDrawers = WMCreatePLString("Drawers");
 }
 
+/* Replace existing window manager on the screen */
+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");
+               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--
@@ -548,6 +637,13 @@ WScreen *wScreenInit(int screen_number)
                event_mask &= ~(ButtonPressMask | ButtonReleaseMask);
        }
 
+       /* Do we need to replace an existing window manager? */
+       if (!replace_existing_wm(scr)) {
+               XDestroyWindow(dpy, scr->info_window);
+               wfree(scr);
+               return NULL;
+       }
+
        XSelectInput(dpy, scr->root_win, event_mask);
 
 #ifdef KEEP_XKB_LOCK_STATUS
@@ -646,11 +742,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, WDWindowMaker->dictionary);
 
diff --git a/src/screen.h b/src/screen.h
index be27cdb..9c354c1 100644
--- a/src/screen.h
+++ b/src/screen.h
@@ -74,6 +74,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;
-- 
1.8.1.4

Reply via email to