about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAdrián Medraño Calvo <adrian@medranocalvo.com>2018-03-06T00·00+0000
committerAdrián Medraño Calvo <adrian@medranocalvo.com>2018-03-06T00·00+0000
commit7aae6efdcd5d64c528315d08e49cdef2e956b540 (patch)
tree39d9111bc9c7bdc6bdce2a9f3b8956a33dca5cd1
parent350950abfce892a1f6f6fc5023ae576801a253ca (diff)
Support replacing and being replaced by other window managers
* exwm.el (exwm--on-SelectionClear, exwm--init-icccm-ewmh)
(exwm--exit-icccm-ewmh, exwm--wmsn-acquire, exwm--wmsn-release):
Get the window manager selection; die when it is cleared.
-rw-r--r--exwm-core.el9
-rw-r--r--exwm.el97
2 files changed, 103 insertions, 3 deletions
diff --git a/exwm-core.el b/exwm-core.el
index 7fd3a61211be..355b8b96c0ba 100644
--- a/exwm-core.el
+++ b/exwm-core.el
@@ -37,6 +37,15 @@
 
 (defvar exwm--connection nil "X connection.")
 
+(defvar exwm--wmsn-window nil
+  "An X window owning the WM_S0 selection.")
+
+(defvar exwm--wmsn-acquire-timeout 3
+  "Number of seconds to wait for other window managers to release the selection.")
+
+(defvar exwm--wmsn-replace 'ask
+  "Replace existing window manager.")
+
 (defvar exwm--guide-window nil
   "An X window separating workspaces and X windows.")
 
diff --git a/exwm.el b/exwm.el
index f2a023b91eec..90b1fe4e2a23 100644
--- a/exwm.el
+++ b/exwm.el
@@ -529,12 +529,26 @@
             (bury-buffer)))))
      (t (exwm--log "Unhandled client message: %s" obj)))))
 
+(defun exwm--on-SelectionClear (data _synthetic)
+  "Handle SelectionClear events."
+  (exwm--log "SelectionClear")
+  (let ((obj (make-instance 'xcb:SelectionClear))
+        owner selection)
+    (xcb:unmarshal obj data)
+    (setq owner (slot-value obj 'owner)
+          selection (slot-value obj 'selection))
+    (when (and (eq owner exwm--wmsn-window)
+               (eq selection xcb:Atom:WM_S0))
+      (exwm-exit))))
+
 (defun exwm--init-icccm-ewmh ()
   "Initialize ICCCM/EWMH support."
   ;; Handle PropertyNotify event
   (xcb:+event exwm--connection 'xcb:PropertyNotify #'exwm--on-PropertyNotify)
   ;; Handle relevant client messages
   (xcb:+event exwm--connection 'xcb:ClientMessage #'exwm--on-ClientMessage)
+  ;; Handle SelectionClear
+  (xcb:+event exwm--connection 'xcb:SelectionClear #'exwm--on-SelectionClear)
   ;; Set _NET_SUPPORTED
   (xcb:+request exwm--connection
       (make-instance 'xcb:ewmh:set-_NET_SUPPORTED
@@ -667,6 +681,81 @@
                      :data [0 0]))
   (xcb:flush exwm--connection))
 
+(defun exwm--wmsn-acquire (replace)
+  "Acquire the WM_Sn selection.
+
+REPLACE specifies what to do in case there already is a window
+manager.  If t, replace it, if nil, abort and if `ask'."
+  (with-slots (owner)
+      (xcb:+request-unchecked+reply exwm--connection
+          (make-instance 'xcb:GetSelectionOwner
+                         :selection xcb:Atom:WM_S0))
+    (when (/= owner xcb:Window:None)
+      (when (eq replace 'ask)
+        (setq replace (yes-or-no-p "Replace existing window manager?")))
+      (when (not replace)
+        (error "Other window manager detected")))
+    (let ((new-owner (xcb:generate-id exwm--connection)))
+      (xcb:+request exwm--connection
+          (make-instance 'xcb:CreateWindow
+                         :depth 0
+                         :wid new-owner
+                         :parent exwm--root
+                         :x -1
+                         :y -1
+                         :width 1
+                         :height 1
+                         :border-width 0
+                         :class xcb:WindowClass:CopyFromParent
+                         :visual 0
+                         :value-mask 0
+                         :override-redirect 0))
+      (xcb:+request exwm--connection
+          (make-instance 'xcb:ewmh:set-_NET_WM_NAME
+                         :window new-owner :data "EXWM selection owner"))
+      (xcb:+request-checked+request-check exwm--connection
+          (make-instance 'xcb:SetSelectionOwner
+                         :selection xcb:Atom:WM_S0
+                         :owner new-owner
+                         :time xcb:Time:CurrentTime))
+      (with-slots (owner)
+          (xcb:+request-unchecked+reply exwm--connection
+              (make-instance 'xcb:GetSelectionOwner
+                             :selection xcb:Atom:WM_S0))
+        (unless (eq owner new-owner)
+          (error "Could not acquire ownership of WM selection")))
+      ;; Wait for the other window manager to terminate.
+      (when (/= owner xcb:Window:None)
+        (let (reply)
+          (cl-dotimes (i 10) ;exwm--wmsn-acquire-timeout)
+            (setq reply (xcb:+request-unchecked+reply exwm--connection
+                            (make-instance 'xcb:GetGeometry :drawable owner)))
+            (when (not reply)
+              (cl-return))
+            (message "Waiting for other window manager to quit... %ds" i)
+            (sleep-for 1))
+          (when reply
+            (error "Other window manager did not release selection in time"))))
+      ;; announce
+      (let* ((cmd (make-instance 'xcb:ClientMessageData
+                                 :data32 (vector xcb:Time:CurrentTime
+                                                 xcb:Atom:WM_S0
+                                                 new-owner
+                                                 0
+                                                 0)))
+             (cm (make-instance 'xcb:ClientMessage
+                                               :window exwm--root
+                                               :format 32
+                                               :type xcb:Atom:MANAGER
+                                               :data cmd))
+             (se (make-instance 'xcb:SendEvent
+                         :propagate 0
+                         :destination exwm--root
+                         :event-mask xcb:EventMask:NoEvent
+                         :event (xcb:marshal cm exwm--connection))))
+        (xcb:+request exwm--connection se))
+      (setq exwm--wmsn-window new-owner))))
+
 ;;;###autoload
 (defun exwm-init (&optional frame)
   "Initialize EXWM."
@@ -689,6 +778,11 @@
               (slot-value (car (slot-value
                                 (xcb:get-setup exwm--connection) 'roots))
                           'root))
+        ;; Initialize ICCCM/EWMH support
+        (xcb:icccm:init exwm--connection t)
+        (xcb:ewmh:init exwm--connection t)
+        ;; Try to register window manager selection.
+        (exwm--wmsn-acquire 'ask)
         (when (xcb:+request-checked+request-check exwm--connection
                   (make-instance 'xcb:ChangeWindowAttributes
                                  :window exwm--root :value-mask xcb:CW:EventMask
@@ -697,9 +791,6 @@
         ;; Disable some features not working well with EXWM
         (setq use-dialog-box nil
               confirm-kill-emacs #'exwm--confirm-kill-emacs)
-        ;; Initialize ICCCM/EWMH support
-        (xcb:icccm:init exwm--connection t)
-        (xcb:ewmh:init exwm--connection t)
         (exwm--lock)
         (exwm--init-icccm-ewmh)
         (exwm-layout--init)