about summary refs log tree commit diff
path: root/exwm-workspace.el
diff options
context:
space:
mode:
authorChris Feng <chris.w.feng@gmail.com>2018-02-17T17·04+0800
committerChris Feng <chris.w.feng@gmail.com>2018-02-17T17·04+0800
commit7823eb988c22f5dc804ef862d91a0fcf474ca718 (patch)
treec7a5428f6e195f5205750c9ad922b5f05bc6c407 /exwm-workspace.el
parent83c0a2db3448091e44cded075104a0b7df636431 (diff)
Make X windows container-less
; This is an attempt to make (managed) X windows container-less, i.e. direct children of the root window.  This is mainly to make EXWM compatible with third-party compositors.  Other issues like wrong absolute position should also get resolved by the way.  The workspace containers ("virtual roots") are also removed.  However Emacs frames are still wrapped in containers to avoid unexpected stack reordering.

* exwm-cm.el: Make this module obsolete as EXWM supports third-party compositors now.

* exwm-core.el (exwm--container):
* exwm-floating.el (exwm-floating--set-floating)
(exwm-floating--unset-floating, exwm-floating-hide)
(exwm-floating--start-moveresize, exwm-floating--stop-moveresize)
(exwm-floating--do-moveresize, exwm-floating-move):
* exwm-input.el (exwm-input--update-focus):
* exwm-layout.el (exwm-layout--show, exwm-layout--hide)
(exwm-layout-set-fullscreen, exwm-layout-unset-fullscreen):
* exwm-manage.el (exwm-manage--manage-window, exwm-manage--unmanage-window)
(exwm-manage--kill-buffer-query-function, exwm-manage--kill-client):
* exwm-workspace.el (exwm-workspace--set-fullscreen, exwm-workspace-switch)
(exwm-workspace-move-window, exwm-workspace--add-frame-as-workspace)
(exwm-workspace--remove-frame-as-workspace): Make adaptions for container-less X windows.

* exwm-workspace.el (exwm-workspace--update-ewmh-props):
* exwm.el (exwm--init-icccm-ewmh, exwm--exit-icccm-ewmh): No longer use virtual roots.

* exwm-input.el (exwm-input--on-workspace-list-change)
(exwm-input--update-global-prefix-keys, exwm-input--init, exwm-input--exit): From now on global key bindings are grabbed on the root window so it's no long required to re-grab them each time the workspace list changes.  As a result `exwm-input--on-workspace-list-change' and its corresponding references are discarded.  It remains to be seen if this change will raise input focus issues.

* exwm-manage.el (exwm-manage--manage-window): Explicitly set the workspace for newly managed X windows.
* exwm-floating.el (exwm-floating--set-floating): Avoid implicit reference to the current workspace.

* exwm-core.el (exwm--set-geometry): New function for setting the geometry of an X window.
* exwm-layout.el (exwm-layout--resize-container): Replaced by `exwm-layout--resize-container'.

* exwm-core.el (exwm--guide-window): New global variable recording the guide X window.
* exwm.el (exwm--init-icccm-ewmh): Set it.

* exwm-input.el (exwm-input--post-init): New function containing staffs for initialization but should better get called after the event loop starts.
* exwm.el (exwm-init): Use it.
Diffstat (limited to 'exwm-workspace.el')
-rw-r--r--exwm-workspace.el320
1 files changed, 136 insertions, 184 deletions
diff --git a/exwm-workspace.el b/exwm-workspace.el
index 0aabbefcdda2..2917c6910b6a 100644
--- a/exwm-workspace.el
+++ b/exwm-workspace.el
@@ -321,16 +321,12 @@ Value nil means to use the default position which is fixed at bottom, while
 (defvar exwm-workspace--fullscreen-frame-count 0
   "Count the fullscreen workspace frames.")
 
-(declare-function exwm-layout--resize-container "exwm-layout.el"
-                  (id container x y width height &optional container-only))
-
 (defun exwm-workspace--set-fullscreen (frame)
   "Make frame FRAME fullscreen according to `exwm-workspace--workareas'."
   (let ((workarea (elt exwm-workspace--workareas
                        (exwm-workspace--position frame)))
         (id (frame-parameter frame 'exwm-outer-id))
         (container (frame-parameter frame 'exwm-container))
-        (workspace (frame-parameter frame 'exwm-workspace))
         x y width height)
     (setq x (aref workarea 0)
           y (aref workarea 1)
@@ -339,8 +335,8 @@ Value nil means to use the default position which is fixed at bottom, while
     (when (and (eq frame exwm-workspace--current)
                (exwm-workspace--minibuffer-own-frame-p))
       (exwm-workspace--resize-minibuffer-frame))
-    (exwm-layout--resize-container id container 0 0 width height)
-    (exwm-layout--resize-container nil workspace x y width height t)
+    (exwm--set-geometry container x y width height)
+    (exwm--set-geometry id nil nil width height)
     (xcb:flush exwm--connection))
   ;; This is only used for workspace initialization.
   (when exwm-workspace--fullscreen-frame-count
@@ -457,26 +453,18 @@ The optional FORCE option is for internal use only."
   (let* ((frame (exwm-workspace--workspace-from-frame-or-index frame-or-index))
          (old-frame exwm-workspace--current)
          (index (exwm-workspace--position frame))
-         (workspace (frame-parameter frame 'exwm-workspace))
          (window (frame-parameter frame 'exwm-selected-window)))
     (when (or force (not (eq frame exwm-workspace--current)))
       (unless (window-live-p window)
         (setq window (frame-selected-window frame)))
-      ;; Raise the workspace container.
+      ;; Raise this frame.
       (xcb:+request exwm--connection
           (make-instance 'xcb:ConfigureWindow
-                         :window workspace
-                         :value-mask xcb:ConfigWindow:StackMode
-                         :stack-mode xcb:StackMode:Above))
-      ;; Raise X windows with struts set if there's no fullscreen X window.
-      (unless (with-current-buffer (window-buffer window)
-                (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state))
-        (dolist (pair exwm-workspace--id-struts-alist)
-          (xcb:+request exwm--connection
-              (make-instance 'xcb:ConfigureWindow
-                             :window (car pair)
-                             :value-mask xcb:ConfigWindow:StackMode
-                             :stack-mode xcb:StackMode:Above))))
+                         :window (frame-parameter frame 'exwm-container)
+                         :value-mask (logior xcb:ConfigWindow:Sibling
+                                             xcb:ConfigWindow:StackMode)
+                         :sibling exwm--guide-window
+                         :stack-mode xcb:StackMode:Below))
       (setq exwm-workspace--current frame
             exwm-workspace-current-index index)
       (unless (exwm-workspace--workspace-p (selected-frame))
@@ -497,6 +485,15 @@ The optional FORCE option is for internal use only."
           (exwm-workspace--resize-minibuffer-frame)
         ;; Set a default minibuffer frame.
         (setq default-minibuffer-frame frame))
+      ;; Show/Hide X windows.
+      (dolist (i exwm--id-buffer-alist)
+        (with-current-buffer (cdr i)
+          (if (eq old-frame exwm--frame)
+              (exwm-layout--hide exwm--id)
+            (when (eq frame exwm--frame)
+              (let ((window (get-buffer-window nil t)))
+                (when window
+                  (exwm-layout--show exwm--id window)))))))
       ;; Hide windows in other workspaces by preprending a space
       (unless exwm-workspace-show-all-buffers
         (dolist (i exwm--id-buffer-alist)
@@ -538,7 +535,7 @@ each time.")
                               (exwm-workspace--count)))))
         (make-frame))
       (run-hooks 'exwm-workspace-list-change-hook))
-    (exwm-workspace-switch (car (last exwm-workspace--list)))))
+    (exwm-workspace-switch frame-or-index)))
 
 (defvar exwm-workspace-list-change-hook nil
   "Normal hook run when the workspace list is changed (workspace added,
@@ -662,7 +659,8 @@ INDEX must not exceed the current number of workspaces."
                 (let ((exwm-workspace--prompt-add-allowed t)
                       (exwm-workspace--prompt-delete-allowed t))
                   (exwm-workspace--prompt-for-workspace "Move to [+/-]: "))))
-  (let ((frame (exwm-workspace--workspace-from-frame-or-index frame-or-index)))
+  (let ((frame (exwm-workspace--workspace-from-frame-or-index frame-or-index))
+        old-frame container)
     (unless id (setq id (exwm--buffer->id (window-buffer))))
     (with-current-buffer (exwm--id->buffer id)
       (unless (eq exwm--frame frame)
@@ -672,112 +670,111 @@ INDEX must not exceed the current number of workspaces."
              (if (eq frame exwm-workspace--current)
                  name
                (concat " " name)))))
-        (setq exwm--frame frame)
-        (if exwm--floating-frame
-            ;; Move the floating container.
-            (with-slots (x y)
-                (xcb:+request-unchecked+reply exwm--connection
-                    (make-instance 'xcb:GetGeometry :drawable exwm--container))
+        (setq old-frame exwm--frame
+              exwm--frame frame)
+        (if (not exwm--floating-frame)
+            ;; Tiling.
+            (progn
+              (set-window-buffer (get-buffer-window nil t)
+                                 (other-buffer nil t))
+              (unless (eq frame exwm-workspace--current)
+                ;; Clear the 'exwm-selected-window' frame parameter.
+                (set-frame-parameter frame 'exwm-selected-window nil))
+              (set-window-buffer (frame-selected-window frame)
+                                 (exwm--id->buffer id))
+              (if (eq frame exwm-workspace--current)
+                  (select-window (frame-selected-window frame))
+                (exwm-layout--hide id)))
+          ;; Floating.
+          (setq container (frame-parameter exwm--floating-frame
+                                           'exwm-container))
+          (with-slots ((x1 x)
+                       (y1 y))
+              (exwm-workspace--get-geometry old-frame)
+            (with-slots ((x2 x)
+                         (y2 y))
+                (exwm-workspace--get-geometry frame)
+              (unless (and (= x1 x2)
+                           (= y1 y2))
+                (with-slots (x y)
+                    (xcb:+request-unchecked+reply exwm--connection
+                        (make-instance 'xcb:GetGeometry
+                                       :drawable container))
+                  (setq x (+ x (- x2 x1))
+                        y (+ y (- y2 y1)))
+                  (exwm--set-geometry id x y nil nil)
+                  (exwm--set-geometry container x y nil nil)))))
+          (if (exwm-workspace--minibuffer-own-frame-p)
+              (if (eq frame exwm-workspace--current)
+                  (select-window (frame-root-window exwm--floating-frame))
+                (select-window (frame-selected-window exwm-workspace--current))
+                (exwm-layout--hide id))
+            ;; The frame needs to be recreated since it won't use the
+            ;; minibuffer on the new workspace.
+            ;; The code is mostly copied from `exwm-floating--set-floating'.
+            (let* ((old-frame exwm--floating-frame)
+                   (new-frame
+                    (with-current-buffer
+                        (or (get-buffer "*scratch*")
+                            (progn
+                              (set-buffer-major-mode
+                               (get-buffer-create "*scratch*"))
+                              (get-buffer "*scratch*")))
+                      (make-frame
+                       `((minibuffer . ,(minibuffer-window frame))
+                         (left . ,(* window-min-width -100))
+                         (top . ,(* window-min-height -100))
+                         (width . ,window-min-width)
+                         (height . ,window-min-height)
+                         (unsplittable . t)))))
+                   (outer-id (string-to-number
+                              (frame-parameter new-frame
+                                               'outer-window-id)))
+                   (window-id (string-to-number
+                               (frame-parameter new-frame 'window-id)))
+                   (window (frame-root-window new-frame)))
+              (set-frame-parameter new-frame 'exwm-outer-id outer-id)
+              (set-frame-parameter new-frame 'exwm-id window-id)
+              (set-frame-parameter new-frame 'exwm-container container)
+              (make-frame-invisible new-frame)
+              (set-frame-size new-frame
+                              (frame-pixel-width old-frame)
+                              (frame-pixel-height old-frame)
+                              t)
               (xcb:+request exwm--connection
                   (make-instance 'xcb:ReparentWindow
-                                 :window exwm--container
-                                 :parent
-                                 (frame-parameter frame 'exwm-workspace)
-                                 :x x :y y))
+                                 :window outer-id
+                                 :parent container
+                                 :x 0 :y 0))
+              (xcb:flush exwm--connection)
+              (with-current-buffer (exwm--id->buffer id)
+                (setq window-size-fixed nil
+                      exwm--floating-frame new-frame)
+                (set-window-dedicated-p (frame-root-window old-frame) nil)
+                (remove-hook 'window-configuration-change-hook
+                             #'exwm-layout--refresh)
+                (set-window-buffer window (current-buffer))
+                (add-hook 'window-configuration-change-hook
+                          #'exwm-layout--refresh)
+                (set-window-dedicated-p window t))
+              ;; Select a tiling window and delete the old frame.
+              (select-window (frame-selected-window exwm-workspace--current))
+              (delete-frame old-frame)
+              ;; The rest is the same.
+              (make-frame-visible new-frame)
+              (exwm--set-geometry outer-id 0 0 nil nil)
               (xcb:flush exwm--connection)
-              (if (exwm-workspace--minibuffer-own-frame-p)
-                  (when (eq frame exwm-workspace--current)
-                    (select-frame-set-input-focus exwm--floating-frame)
-                    (exwm-layout--refresh))
-                ;; The frame needs to be recreated since it won't use the
-                ;; minibuffer on the new workspace.
-                (let* ((old-frame exwm--floating-frame)
-                       (new-frame
-                        (with-current-buffer
-                            (or (get-buffer "*scratch*")
-                                (progn
-                                  (set-buffer-major-mode
-                                   (get-buffer-create "*scratch*"))
-                                  (get-buffer "*scratch*")))
-                          (make-frame
-                           `((minibuffer . ,(minibuffer-window frame))
-                             (left . 10000)
-                             (top . 10000)
-                             (width . ,window-min-width)
-                             (height . ,window-min-height)
-                             (unsplittable . t)))))
-                       (outer-id (string-to-number
-                                  (frame-parameter new-frame
-                                                   'outer-window-id)))
-                       (window-id (string-to-number
-                                   (frame-parameter new-frame 'window-id)))
-                       (frame-container (frame-parameter old-frame
-                                                         'exwm-container))
-                       (window (frame-root-window new-frame)))
-                  (set-frame-parameter new-frame 'exwm-outer-id outer-id)
-                  (set-frame-parameter new-frame 'exwm-id window-id)
-                  (set-frame-parameter new-frame 'exwm-container
-                                       frame-container)
-                  (make-frame-invisible new-frame)
-                  (set-frame-size new-frame
-                                  (frame-pixel-width old-frame)
-                                  (frame-pixel-height old-frame)
-                                  t)
-                  (xcb:+request exwm--connection
-                      (make-instance 'xcb:ReparentWindow
-                                     :window outer-id
-                                     :parent frame-container
-                                     :x 0 :y 0))
-                  (xcb:flush exwm--connection)
+              (redisplay)
+              (if (eq frame exwm-workspace--current)
                   (with-current-buffer (exwm--id->buffer id)
-                    (setq window-size-fixed nil
-                          exwm--frame frame
-                          exwm--floating-frame new-frame)
-                    (set-window-dedicated-p (frame-root-window old-frame) nil)
-                    (remove-hook 'window-configuration-change-hook
-                                 #'exwm-layout--refresh)
-                    (set-window-buffer window (current-buffer))
-                    (add-hook 'window-configuration-change-hook
-                              #'exwm-layout--refresh)
-                    (delete-frame old-frame)
-                    (set-window-dedicated-p window t)
-                    (exwm-layout--show id window))
-                  (if (not (eq frame exwm-workspace--current))
-                      (make-frame-visible new-frame)
-                    (select-frame-set-input-focus new-frame)
-                    (redisplay))))
-              ;; Update the 'exwm-selected-window' frame parameter.
-              (when (not (eq frame exwm-workspace--current))
-                (with-current-buffer (exwm--id->buffer id)
-                  (set-frame-parameter frame 'exwm-selected-window
-                                       (frame-root-window
-                                        exwm--floating-frame)))))
-          ;; Move the X window container.
-          (set-window-buffer (get-buffer-window (current-buffer) t)
-                             (other-buffer nil t))
-          (unless (eq frame exwm-workspace--current)
-            ;; Clear the 'exwm-selected-window' frame parameter.
-            (set-frame-parameter frame 'exwm-selected-window nil))
-          (exwm-layout--hide id)
-          ;; (current-buffer) is changed.
-          (with-current-buffer (exwm--id->buffer id)
-            ;; Reparent to the destination workspace.
-            (xcb:+request exwm--connection
-                (make-instance 'xcb:ReparentWindow
-                               :window exwm--container
-                               :parent (frame-parameter frame 'exwm-workspace)
-                               :x 0 :y 0))
-            ;; Place it just above the destination frame container.
-            (xcb:+request exwm--connection
-                (make-instance 'xcb:ConfigureWindow
-                               :window exwm--container
-                               :value-mask (logior xcb:ConfigWindow:Sibling
-                                                   xcb:ConfigWindow:StackMode)
-                               :sibling (frame-parameter frame 'exwm-container)
-                               :stack-mode xcb:StackMode:Above)))
-          (xcb:flush exwm--connection)
-          (set-window-buffer (frame-selected-window frame)
-                             (exwm--id->buffer id)))
+                    (select-window (frame-root-window exwm--floating-frame)))
+                (exwm-layout--hide id))))
+          ;; Update the 'exwm-selected-window' frame parameter.
+          (when (not (eq frame exwm-workspace--current))
+            (with-current-buffer (exwm--id->buffer id)
+              (set-frame-parameter frame 'exwm-selected-window
+                                   (frame-root-window
+                                    exwm--floating-frame)))))
         ;; Set _NET_WM_DESKTOP.
         (exwm-workspace--set-desktop id)
         (xcb:flush exwm--connection)))
@@ -1005,16 +1002,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
                                               'exwm-container)
                      :value-mask xcb:ConfigWindow:StackMode
                      :stack-mode xcb:StackMode:Above))
-  (xcb:flush exwm--connection)
-  ;; Unfortunately we need the following lines to workaround a cursor
-  ;; flickering issue for line-mode floating X windows.  They just make the
-  ;; minibuffer appear to be focused.
-  ;; (FIXED?)
-  ;; (with-current-buffer (window-buffer (minibuffer-window
-  ;;                                      exwm-workspace--minibuffer))
-  ;;   (setq cursor-in-non-selected-windows
-  ;;         (frame-parameter exwm-workspace--minibuffer 'cursor-type)))
-  )
+  (xcb:flush exwm--connection))
 
 (defun exwm-workspace--hide-minibuffer ()
   "Hide the minibuffer frame."
@@ -1198,13 +1186,11 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
     (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))
-          (workspace (xcb:generate-id exwm--connection)))
+          (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)
-      (set-frame-parameter frame 'exwm-workspace workspace)
       ;; In case it's created by emacsclient.
       (set-frame-parameter frame 'client nil)
       ;; Copy RandR frame parameters from the first workspace to
@@ -1217,51 +1203,27 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
       (xcb:+request exwm--connection
           (make-instance 'xcb:CreateWindow
                          :depth 0
-                         :wid workspace
+                         :wid container
                          :parent exwm--root
-                         :x 0
-                         :y 0
-                         :width (x-display-pixel-width)
-                         :height (x-display-pixel-height)
+                         :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
-                                             xcb:CW:EventMask)
+                                             xcb:CW:OverrideRedirect)
                          :background-pixmap xcb:BackPixmap:ParentRelative
-                         :override-redirect 1
-                         :event-mask xcb:EventMask:SubstructureRedirect))
+                         :override-redirect 1))
       (xcb:+request exwm--connection
           (make-instance 'xcb:ConfigureWindow
-                         :window workspace
+                         :window container
                          :value-mask xcb:ConfigWindow:StackMode
                          :stack-mode xcb:StackMode:Below))
-      (xcb:+request exwm--connection
-          (make-instance 'xcb:CreateWindow
-                         :depth 0
-                         :wid container
-                         :parent workspace
-                         :x 0
-                         :y 0
-                         :width (x-display-pixel-width)
-                         :height (x-display-pixel-height)
-                         :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))
       (exwm--debug
        (xcb:+request exwm--connection
            (make-instance 'xcb:ewmh:set-_NET_WM_NAME
-                          :window workspace
-                          :data
-                          (format "EXWM workspace %d"
-                                  (exwm-workspace--position frame))))
-       (xcb:+request exwm--connection
-           (make-instance 'xcb:ewmh:set-_NET_WM_NAME
                           :window container
                           :data
                           (format "EXWM workspace %d frame container"
@@ -1270,9 +1232,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
           (make-instance 'xcb:ReparentWindow
                          :window outer-id :parent container :x 0 :y 0))
       (xcb:+request exwm--connection
-          (make-instance 'xcb:MapWindow :window container))
-      (xcb:+request exwm--connection
-          (make-instance 'xcb:MapWindow :window workspace)))
+          (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)
@@ -1323,10 +1283,10 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
                        :parent exwm--root
                        :x 0
                        :y 0))
-    ;; Destroy the containers.
+    ;; Destroy the container.
     (xcb:+request exwm--connection
         (make-instance 'xcb:DestroyWindow
-                       :window (frame-parameter frame 'exwm-workspace)))
+                       :window (frame-parameter frame 'exwm-container)))
     ;; Update EWMH properties.
     (exwm-workspace--update-ewmh-props)
     ;; Update switch history.
@@ -1343,15 +1303,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
     ;; Set _NET_DESKTOP_GEOMETRY.
     (exwm-workspace--set-desktop-geometry)
     ;; Update workareas.
-    (exwm-workspace--update-workareas)
-    ;; Set _NET_VIRTUAL_ROOTS.
-    (xcb:+request exwm--connection
-        (make-instance 'xcb:ewmh:set-_NET_VIRTUAL_ROOTS
-                       :window exwm--root
-                       :data (vconcat (mapcar
-                                       (lambda (i)
-                                         (frame-parameter i 'exwm-workspace))
-                                       exwm-workspace--list)))))
+    (exwm-workspace--update-workareas))
   (xcb:flush exwm--connection))
 
 (defun exwm-workspace--modify-all-x-frames-parameters (new-x-parameters)
@@ -1505,7 +1457,7 @@ applied to all subsequently created X frames."
   (exwm-workspace-switch 0 t)
   ;; Prevent frame parameters introduced by this module from being
   ;; saved/restored.
-  (dolist (i '(exwm-outer-id exwm-id exwm-container exwm-workspace
+  (dolist (i '(exwm-outer-id exwm-id exwm-container exwm-geometry
                              fullscreen exwm-selected-window exwm-urgency))
     (push (cons i :never) frameset-filter-alist)))