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
commit33a1a284762c80dd3751ed152c872e3612b21a47 (patch)
tree44c8b8b2b10c7bd70d4932167bb4cf71aead9fbb
parentd3be64e743b0e794b91743fa1fa31b721d1a3aca (diff)
Dissociate frame creation and deletion handlers from the actual configuration of frames as workspaces
* exwm-workspace.el (exwm-workspace--add-frame-as-workspace)
(exwm-workspace--remove-frame-as-workspace): Limit functionality
to the configuration of frames as workspaces.
(exwm-workspace--on-after-make-frame)
(exwm-workspace--on-delete-frame): Callbacks run on frame creation
and deletion that may use or stop them from being used as
workspaces.
-rw-r--r--exwm-workspace.el252
1 files changed, 139 insertions, 113 deletions
diff --git a/exwm-workspace.el b/exwm-workspace.el
index f1ca8d594ed7..323f5fc34b81 100644
--- a/exwm-workspace.el
+++ b/exwm-workspace.el
@@ -1257,6 +1257,141 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
 
 (defun exwm-workspace--add-frame-as-workspace (frame)
   "Configure frame FRAME to be treated as a workspace."
+  (setq exwm-workspace--list (nconc exwm-workspace--list (list frame)))
+  (let ((outer-id (string-to-number (frame-parameter frame
+                                                     'outer-window-id)))
+        (window-id (string-to-number (frame-parameter frame 'window-id)))
+        (container (xcb:generate-id exwm--connection)))
+    ;; Save window IDs
+    (set-frame-parameter frame 'exwm-outer-id outer-id)
+    (set-frame-parameter frame 'exwm-id window-id)
+    (set-frame-parameter frame 'exwm-container container)
+    ;; In case it's created by emacsclient.
+    (set-frame-parameter frame 'client nil)
+    ;; Copy RandR frame parameters from the first workspace to
+    ;; prevent potential problems.  The values do not matter here as
+    ;; they'll be updated by the RandR module later.
+    (let ((w (car exwm-workspace--list)))
+      (dolist (param '(exwm-randr-output
+                       exwm-geometry))
+        (set-frame-parameter frame param (frame-parameter w param))))
+    (xcb:+request exwm--connection
+        (make-instance 'xcb:CreateWindow
+                       :depth 0
+                       :wid container
+                       :parent exwm--root
+                       :x -1
+                       :y -1
+                       :width 1
+                       :height 1
+                       :border-width 0
+                       :class xcb:WindowClass:InputOutput
+                       :visual 0
+                       :value-mask (logior xcb:CW:BackPixmap
+                                           xcb:CW:OverrideRedirect)
+                       :background-pixmap xcb:BackPixmap:ParentRelative
+                       :override-redirect 1))
+    (xcb:+request exwm--connection
+        (make-instance 'xcb:ConfigureWindow
+                       :window container
+                       :value-mask xcb:ConfigWindow:StackMode
+                       :stack-mode xcb:StackMode:Below))
+    (exwm--debug
+     (xcb:+request exwm--connection
+         (make-instance 'xcb:ewmh:set-_NET_WM_NAME
+                        :window container
+                        :data
+                        (format "EXWM workspace %d frame container"
+                                (exwm-workspace--position frame)))))
+    (xcb:+request exwm--connection
+        (make-instance 'xcb:ReparentWindow
+                       :window outer-id :parent container :x 0 :y 0))
+    (xcb:+request exwm--connection
+        (make-instance 'xcb:MapWindow :window container)))
+  (xcb:flush exwm--connection)
+  ;; Delay making the workspace fullscreen until Emacs becomes idle
+  (exwm--defer 0 #'set-frame-parameter frame 'fullscreen 'fullboth)
+  ;; Update EWMH properties.
+  (exwm-workspace--update-ewmh-props)
+  (if exwm-workspace--create-silently
+      (setq exwm-workspace--switch-history-outdated t)
+    (exwm-workspace-switch frame t)
+    (run-hooks 'exwm-workspace-list-change-hook)))
+
+(defun exwm-workspace--remove-frame-as-workspace (frame)
+  "Stop treating frame FRAME as a workspace."
+  ;; TODO: restore all frame parameters (e.g. exwm-workspace, buffer-predicate,
+  ;; etc)
+  (exwm--log "Removing frame `%s' as workspace" frame)
+  (let* ((index (exwm-workspace--position frame))
+         (lastp (= index (1- (exwm-workspace--count))))
+         (nextw (elt exwm-workspace--list (+ index (if lastp -1 +1)))))
+    ;; Need to remove the workspace from the list in order for
+    ;; the correct calculation of indexes.
+    (setq exwm-workspace--list (delete frame exwm-workspace--list))
+    ;; Clients need to be moved to some other workspace before this is being
+    ;; removed.
+    (dolist (pair exwm--id-buffer-alist)
+      (with-current-buffer (cdr pair)
+        (when (eq exwm--frame frame)
+          (exwm-workspace-move-window nextw exwm--id))))
+    ;; Update the _NET_WM_DESKTOP property of each X window affected.
+    (dolist (pair exwm--id-buffer-alist)
+      (when (<= (1- index)
+                (exwm-workspace--position (buffer-local-value 'exwm--frame
+                                                              (cdr pair))))
+        (exwm-workspace--set-desktop (car pair))))
+    ;; If the current workspace is deleted, switch to next one.
+    (when (eq frame exwm-workspace--current)
+      (exwm-workspace-switch nextw)))
+  ;; Reparent out the frame.
+  (let ((outer-id (frame-parameter frame 'exwm-outer-id)))
+    (xcb:+request exwm--connection
+        (make-instance 'xcb:UnmapWindow
+                       :window outer-id))
+    (xcb:+request exwm--connection
+        (make-instance 'xcb:ReparentWindow
+                       :window outer-id
+                       :parent exwm--root
+                       :x 0
+                       :y 0))
+    ;; Reset the override-redirect.
+    (xcb:+request exwm--connection
+        (make-instance 'xcb:ChangeWindowAttributes
+                       :window outer-id
+                       :value-mask xcb:CW:OverrideRedirect
+                       :override-redirect 0))
+    (xcb:+request exwm--connection
+        (make-instance 'xcb:MapWindow
+                       :window outer-id)))
+  ;; Destroy the container.
+  (xcb:+request exwm--connection
+      (make-instance 'xcb:DestroyWindow
+                     :window (frame-parameter frame 'exwm-container)))
+  (xcb:flush exwm--connection)
+  ;; Update EWMH properties.
+  (exwm-workspace--update-ewmh-props)
+  ;; Update switch history.
+  (setq exwm-workspace--switch-history-outdated t)
+  (run-hooks 'exwm-workspace-list-change-hook))
+
+(defun exwm-workspace--on-delete-frame (frame)
+  "Hook run upon `delete-frame' that tears down FRAME's configuration as a workspace."
+  (cond
+   ((not (exwm-workspace--workspace-p frame))
+    (exwm--log "Frame `%s' is not a workspace" frame))
+   (t
+    (when (= 1 (exwm-workspace--count))
+      ;; The user managed to delete the last workspace, so create a new one.
+      (exwm--log "Last workspace deleted; create a new one")
+      ;; TODO: this makes sense in the hook.  But we need a function that takes
+      ;; care of converting a workspace into a regular unmanaged frame.
+      (let ((exwm-workspace--create-silently t))
+        (make-frame)))
+    (exwm-workspace--remove-frame-as-workspace frame))))
+
+(defun exwm-workspace--on-after-make-frame (frame)
+  "Hook run upon `delete-frame' that configures FRAME as a workspace."
   (cond
    ((exwm-workspace--workspace-p frame)
     (exwm--log "Frame `%s' is already a workspace" frame))
@@ -1278,116 +1413,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
     (exwm--log "Frame `%s' is floating" frame))
    (t
     (exwm--log "Adding frame `%s' as workspace" frame)
-    (setq exwm-workspace--list (nconc exwm-workspace--list (list frame)))
-    (let ((outer-id (string-to-number (frame-parameter frame
-                                                       'outer-window-id)))
-          (window-id (string-to-number (frame-parameter frame 'window-id)))
-          (container (xcb:generate-id exwm--connection)))
-      ;; Save window IDs
-      (set-frame-parameter frame 'exwm-outer-id outer-id)
-      (set-frame-parameter frame 'exwm-id window-id)
-      (set-frame-parameter frame 'exwm-container container)
-      ;; In case it's created by emacsclient.
-      (set-frame-parameter frame 'client nil)
-      ;; Copy RandR frame parameters from the first workspace to
-      ;; prevent potential problems.  The values do not matter here as
-      ;; they'll be updated by the RandR module later.
-      (let ((w (car exwm-workspace--list)))
-        (dolist (param '(exwm-randr-output
-                         exwm-geometry))
-          (set-frame-parameter frame param (frame-parameter w param))))
-      (xcb:+request exwm--connection
-          (make-instance 'xcb:CreateWindow
-                         :depth 0
-                         :wid container
-                         :parent exwm--root
-                         :x -1
-                         :y -1
-                         :width 1
-                         :height 1
-                         :border-width 0
-                         :class xcb:WindowClass:InputOutput
-                         :visual 0
-                         :value-mask (logior xcb:CW:BackPixmap
-                                             xcb:CW:OverrideRedirect)
-                         :background-pixmap xcb:BackPixmap:ParentRelative
-                         :override-redirect 1))
-      (xcb:+request exwm--connection
-          (make-instance 'xcb:ConfigureWindow
-                         :window container
-                         :value-mask xcb:ConfigWindow:StackMode
-                         :stack-mode xcb:StackMode:Below))
-      (exwm--debug
-       (xcb:+request exwm--connection
-           (make-instance 'xcb:ewmh:set-_NET_WM_NAME
-                          :window container
-                          :data
-                          (format "EXWM workspace %d frame container"
-                                  (exwm-workspace--position frame)))))
-      (xcb:+request exwm--connection
-          (make-instance 'xcb:ReparentWindow
-                         :window outer-id :parent container :x 0 :y 0))
-      (xcb:+request exwm--connection
-          (make-instance 'xcb:MapWindow :window container)))
-    (xcb:flush exwm--connection)
-    ;; Delay making the workspace fullscreen until Emacs becomes idle
-    (exwm--defer 0 #'set-frame-parameter frame 'fullscreen 'fullboth)
-    ;; Update EWMH properties.
-    (exwm-workspace--update-ewmh-props)
-    (if exwm-workspace--create-silently
-        (setq exwm-workspace--switch-history-outdated t)
-      (exwm-workspace-switch frame t)
-      (run-hooks 'exwm-workspace-list-change-hook)))))
-
-(defun exwm-workspace--remove-frame-as-workspace (frame)
-  "Stop treating frame FRAME as a workspace."
-  (cond
-   ((not (exwm-workspace--workspace-p frame))
-    (exwm--log "Frame `%s' is not a workspace" frame))
-   (t
-    (exwm--log "Removing frame `%s' as workspace" frame)
-    (when (= 1 (exwm-workspace--count))
-      ;; The user managed to delete the last workspace, so create a new one.
-      (exwm--log "Last workspace deleted; create a new one")
-      (let ((exwm-workspace--create-silently t))
-        (make-frame)))
-    (let* ((index (exwm-workspace--position frame))
-           (lastp (= index (1- (exwm-workspace--count))))
-           (nextw (elt exwm-workspace--list (+ index (if lastp -1 +1)))))
-      ;; Need to remove the workspace from the list in order for
-      ;; the correct calculation of indexes.
-      (setq exwm-workspace--list (delete frame exwm-workspace--list))
-      ;; Clients need to be moved to some other workspace before this is being
-      ;; removed.
-      (dolist (pair exwm--id-buffer-alist)
-        (with-current-buffer (cdr pair)
-          (when (eq exwm--frame frame)
-            (exwm-workspace-move-window nextw exwm--id))))
-      ;; Update the _NET_WM_DESKTOP property of each X window affected.
-      (dolist (pair exwm--id-buffer-alist)
-        (when (<= (1- index)
-                  (exwm-workspace--position (buffer-local-value 'exwm--frame
-                                                                (cdr pair))))
-          (exwm-workspace--set-desktop (car pair))))
-      ;; If the current workspace is deleted, switch to next one.
-      (when (eq frame exwm-workspace--current)
-        (exwm-workspace-switch nextw)))
-    ;; Reparent out the frame.
-    (xcb:+request exwm--connection
-        (make-instance 'xcb:ReparentWindow
-                       :window (frame-parameter frame 'exwm-outer-id)
-                       :parent exwm--root
-                       :x 0
-                       :y 0))
-    ;; Destroy the container.
-    (xcb:+request exwm--connection
-        (make-instance 'xcb:DestroyWindow
-                       :window (frame-parameter frame 'exwm-container)))
-    ;; Update EWMH properties.
-    (exwm-workspace--update-ewmh-props)
-    ;; Update switch history.
-    (setq exwm-workspace--switch-history-outdated t)
-    (run-hooks 'exwm-workspace-list-change-hook))))
+    (exwm-workspace--add-frame-as-workspace frame))))
 
 (defun exwm-workspace--update-ewmh-props ()
   "Update EWMH properties to match the workspace list."
@@ -1546,7 +1572,7 @@ applied to all subsequently created X frames."
   (advice-add 'handle-focus-out :around #'exwm-workspace--handle-focus-out)
   ;; Make new frames create new workspaces.
   (add-hook 'after-make-frame-functions
-            #'exwm-workspace--add-frame-as-workspace)
+            #'exwm-workspace--on-after-make-frame)
   (add-hook 'delete-frame-functions
             #'exwm-workspace--remove-frame-as-workspace)
   ;; Switch to the first workspace
@@ -1579,9 +1605,9 @@ applied to all subsequently created X frames."
   (advice-remove 'handle-focus-in #'exwm-workspace--handle-focus-in)
   (advice-remove 'handle-focus-out #'exwm-workspace--handle-focus-out)
   (remove-hook 'after-make-frame-functions
-               #'exwm-workspace--add-frame-as-workspace)
+               #'exwm-workspace--on-after-make-frame)
   (remove-hook 'delete-frame-functions
-               #'exwm-workspace--remove-frame-as-workspace))
+               #'exwm-workspace--on-delete-frame))
 
 (defun exwm-workspace--post-init ()
   "The second stage in the initialization of the workspace module."