From e3d33a4aad6b5748e9352501b2c6cb058379025f Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 20 Feb 2016 21:52:07 +0800 Subject: [PATCH] Prevent Emacs frames from restacking themselves Putting Emacs frames (workspace frames, floating frames) into dedicated containers greatly simplifies the stacking order management and totally fixes relevant issues. * exwm-floating.el (exwm-floating--set-floating): Create floating frame container. Remove redundant stacking order modification code. (exwm-floating--unset-floating): Destroy the floating frame container. No need to reparent the X window container. (exwm-floating--do-moveresize): Resize the floating frame container. * exwm-input.el (exwm-input--update-focus): No need to restack frames. * exwm-layout.el (exwm-layout--show, exwm-layout--set-frame-fullscreen) (exwm-layout-enlarge-window): Resize the floating frame container. * exwm-manage.el (exwm-manage--on-ConfigureRequest): Re-enable stacking order modification on ConfigureRequest. * exwm-workspace.el (exwm-workspace--confirm-kill-emacs): Reparent out all frames on exit. No need to remove selected events or created resources. (exwm-workspace--init): Create workspace frame containers. * exwm-layout.el (exwm-layout-set-fullscreen): * exwm-manage.el (exwm-manage--unmanage-window): Remove a redundant call to `xcb:flush'. * exwm-manage.el (exwm-manage--unmanage-window): Force unmap the X window. Unmap the floating frame before reparent it. --- exwm-floating.el | 109 +++++++++++++++++++++++++++++----------------- exwm-input.el | 14 ++---- exwm-layout.el | 88 +++++++++++++++++++++++-------------- exwm-manage.el | 42 ++++++++++-------- exwm-workspace.el | 60 +++++++++++++++++-------- 5 files changed, 192 insertions(+), 121 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index 2769488..1ec54d1 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -90,6 +90,7 @@ (outer-id (string-to-number (frame-parameter frame 'outer-window-id))) (container (with-current-buffer (exwm--id->buffer id) exwm--container)) + (frame-container (xcb:generate-id exwm--connection)) (window (frame-first-window frame)) ;and it's the only window (x (slot-value exwm--geometry 'x)) (y (slot-value exwm--geometry 'y)) @@ -103,8 +104,9 @@ y (- y (slot-value frame-geometry 'y)))) (exwm--log "Floating geometry (original, relative): %dx%d%+d%+d" width height x y) - ;; Save window IDs + ;; Save frame parameters. (set-frame-parameter frame 'exwm-outer-id outer-id) + (set-frame-parameter frame 'exwm-container frame-container) ;; Set urgency flag if it's not appear in the active workspace (let ((idx (cl-position original-frame exwm-workspace--list))) (when (/= idx exwm-workspace-current-index) @@ -163,18 +165,43 @@ ;; timely. ;; The frame will be made visible by `select-frame-set-input-focus'. (make-frame-invisible frame) - (let ((edges (window-inside-pixel-edges window))) - (set-frame-size frame - (+ width (- (frame-pixel-width frame) - (- (elt edges 2) (elt edges 0)))) - (+ height (- (frame-pixel-height frame) - (- (elt edges 3) (elt edges 1)))) - t)) - ;; Reparent this frame to the container + (let* ((edges (window-inside-pixel-edges window)) + (frame-width (+ width (- (frame-pixel-width frame) + (- (elt edges 2) (elt edges 0))))) + (frame-height (+ height (- (frame-pixel-height frame) + (- (elt edges 3) (elt edges 1)))))) + (set-frame-size frame frame-width frame-height t) + ;; Create the frame container as the parent of the frame and + ;; a child of the X window container. + (xcb:+request exwm--connection + (make-instance 'xcb:CreateWindow + :depth 0 :wid frame-container + :parent container + :x 0 :y 0 :width width :height height :border-width 0 + :class xcb:WindowClass:CopyFromParent + :visual 0 ;CopyFromParent + :value-mask xcb:CW:OverrideRedirect + :override-redirect 1)) + ;; Put it at bottom. + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window frame-container + :value-mask xcb:ConfigWindow:StackMode + :stack-mode xcb:StackMode:Below)) + ;; Map it. + (xcb:+request exwm--connection + (make-instance 'xcb:MapWindow :window frame-container)) + (exwm--debug + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_WM_NAME + :window frame-container + :data + (format "floating frame container for 0x%x" id))))) + ;; Reparent this frame to its container. (xcb:+request exwm--connection (make-instance 'xcb:ReparentWindow - :window outer-id :parent container :x 0 :y 0)) - ;; Place the container + :window outer-id :parent frame-container :x 0 :y 0)) + ;; Place the X window container. (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window container @@ -193,16 +220,9 @@ (remove-hook 'window-configuration-change-hook #'exwm-layout--refresh) (set-window-buffer window (current-buffer)) ;this changes current buffer (add-hook 'window-configuration-change-hook #'exwm-layout--refresh) - (set-window-dedicated-p window t)) - (select-frame-set-input-focus frame) - ;; `x_make_frame_visible' autoraises the frame. Force lowering it. - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window outer-id - :value-mask xcb:ConfigWindow:StackMode - :stack-mode xcb:StackMode:Below)) - ;; Show the X window with its container (and flush). - (exwm-layout--show id window)) + (set-window-dedicated-p window t) + (exwm-layout--show id window)) + (select-frame-set-input-focus frame)) (run-hooks 'exwm-floating-setup-hook) ;; Redraw the frame. (redisplay)) @@ -229,29 +249,28 @@ :window id :value-mask xcb:CW:EventMask :event-mask exwm--client-event-mask)) ;; Reparent the floating frame back to the root window. - (let ((frame-id (frame-parameter exwm--floating-frame 'exwm-outer-id))) + (let ((frame-id (frame-parameter exwm--floating-frame 'exwm-outer-id)) + (frame-container (frame-parameter exwm--floating-frame + 'exwm-container))) (xcb:+request exwm--connection (make-instance 'xcb:UnmapWindow :window frame-id)) (xcb:+request exwm--connection (make-instance 'xcb:ReparentWindow :window frame-id :parent exwm--root - :x 0 :y 0)))) - ;; Reparent the container to the workspace - (xcb:+request exwm--connection - (make-instance 'xcb:ReparentWindow - :window exwm--container - :parent (frame-parameter exwm-workspace--current - 'exwm-workspace) - :x 0 :y 0)) ;temporary position - ;; Put the container just above the Emacs frame + :x 0 :y 0)) + ;; Also destroy its container. + (xcb:+request exwm--connection + (make-instance 'xcb:DestroyWindow :window frame-container)))) + ;; Put the X window container just above the Emacs frame container + ;; (the stacking order won't change from now on). (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window exwm--container :value-mask (logior xcb:ConfigWindow:Sibling xcb:ConfigWindow:StackMode) :sibling (frame-parameter exwm-workspace--current - 'exwm-outer-id) + 'exwm-container) :stack-mode xcb:StackMode:Above))) (xcb:flush exwm--connection) (with-current-buffer buffer @@ -466,13 +485,19 @@ (geometry (frame-parameter exwm-workspace--current 'exwm-geometry)) (frame-x 0) (frame-y 0) - result) + result value-mask width height) (when geometry (setq frame-x (slot-value geometry 'x) frame-y (slot-value geometry 'y))) (xcb:unmarshal obj data) (setq result (funcall exwm-floating--moveresize-calculate - (slot-value obj 'root-x) (slot-value obj 'root-y))) + (slot-value obj 'root-x) (slot-value obj 'root-y)) + value-mask (logand (aref result 1) + (eval-when-compile + (logior xcb:ConfigWindow:Width + xcb:ConfigWindow:Height))) + width (aref result 4) + height (aref result 5)) (with-current-buffer (aref result 0) (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow @@ -483,16 +508,20 @@ xcb:ConfigWindow:Y))) :x (- (aref result 2) frame-x) :y (- (aref result 3) frame-y))) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window (frame-parameter exwm--floating-frame + 'exwm-container) + :value-mask value-mask + :width width + :height height)) (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window (frame-parameter exwm--floating-frame 'exwm-outer-id) - :value-mask - (logand (aref result 1) - (eval-when-compile - (logior xcb:ConfigWindow:Width - xcb:ConfigWindow:Height))) - :width (aref result 4) :height (aref result 5)))) + :value-mask value-mask + :width width + :height height))) (xcb:flush exwm--connection)))) (defun exwm-floating-move (&optional delta-x delta-y) diff --git a/exwm-input.el b/exwm-input.el index 5e07803..0a50bef 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -113,8 +113,8 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (exwm-workspace-switch exwm-workspace-current-index t)) (exwm--log "Set focus on #x%x" exwm--id) (exwm-input--set-focus exwm--id) - ;; Adjust stacking orders (when exwm--floating-frame + ;; Adjust stacking orders of the floating container. (if (exwm-workspace--minibuffer-own-frame-p) ;; Put this floating X window just below the minibuffer. (xcb:+request exwm--connection @@ -132,16 +132,8 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (make-instance 'xcb:ConfigureWindow :window exwm--container :value-mask xcb:ConfigWindow:StackMode - :stack-mode xcb:StackMode:Above)))) - ;; Make sure Emacs frames are at bottom. - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window (frame-parameter - (or exwm--floating-frame exwm--frame) - 'exwm-outer-id) - :value-mask xcb:ConfigWindow:StackMode - :stack-mode xcb:StackMode:BottomIf)) - (xcb:flush exwm--connection)) + :stack-mode xcb:StackMode:Above))) + (xcb:flush exwm--connection))) (when (eq (selected-window) exwm-input--focus-window) (exwm--log "Focus on %s" exwm-input--focus-window) (select-frame-set-input-focus (window-frame exwm-input--focus-window) diff --git a/exwm-layout.el b/exwm-layout.el index e3c1feb..c9146de 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -55,37 +55,46 @@ (exwm--log "Show #x%x in %s" id window) (let* ((edges (window-inside-absolute-pixel-edges window)) (width (- (elt edges 2) (elt edges 0))) - (height (- (elt edges 3) (elt edges 1)))) + (height (- (elt edges 3) (elt edges 1))) + frame-width frame-height) (with-current-buffer (exwm--id->buffer id) - (if exwm--floating-frame - ;; A floating X window is of the same size as the Emacs window, - ;; whereas its container is of the same size as the Emacs frame. - (progn - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window exwm--container - :value-mask (logior xcb:ConfigWindow:Width - xcb:ConfigWindow:Height) - :width (frame-pixel-width exwm--floating-frame) - :height (frame-pixel-height - exwm--floating-frame))) - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window exwm--id - :value-mask (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y - xcb:ConfigWindow:Width - xcb:ConfigWindow:Height) - :x exwm-floating-border-width - :y exwm-floating-border-width - :width width - :height height))) - (let ((relative-edges (window-inside-pixel-edges window))) - (exwm-layout--resize-container id exwm--container - (elt relative-edges 0) - (elt relative-edges 1) - width height - (active-minibuffer-window)))) + (if (not exwm--floating-frame) + (let ((relative-edges (window-inside-pixel-edges window))) + (exwm-layout--resize-container id exwm--container + (elt relative-edges 0) + (elt relative-edges 1) + width height + (active-minibuffer-window))) + ;; A floating X window is of the same size as the Emacs window, + ;; whereas its container is of the same size as the Emacs frame. + (setq frame-width (frame-pixel-width exwm--floating-frame) + frame-height (frame-pixel-height exwm--floating-frame)) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window exwm--container + :value-mask (logior xcb:ConfigWindow:Width + xcb:ConfigWindow:Height) + :width frame-width + :height frame-height)) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window (frame-parameter exwm--floating-frame + 'exwm-container) + :value-mask (logior xcb:ConfigWindow:Width + xcb:ConfigWindow:Height) + :width frame-width + :height frame-height)) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window exwm--id + :value-mask (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y + xcb:ConfigWindow:Width + xcb:ConfigWindow:Height) + :x exwm-floating-border-width + :y exwm-floating-border-width + :width width + :height height))) ;; Make the resizing take effect. (xcb:flush exwm--connection) (xcb:+request exwm--connection (make-instance 'xcb:MapWindow :window id)) @@ -151,8 +160,7 @@ (make-instance 'xcb:GetGeometry :drawable exwm--container)))) (setq exwm--floating-frame-position - (vector (slot-value geometry 'x) (slot-value geometry 'y)))) - (xcb:flush exwm--connection)) + (vector (slot-value geometry 'x) (slot-value geometry 'y))))) (exwm-layout--resize-container exwm--id exwm--container 0 0 (exwm-workspace--current-width) (exwm-workspace--current-height)) @@ -205,12 +213,14 @@ :width (x-display-pixel-width) :height (x-display-pixel-height)))) (id (frame-parameter frame 'exwm-outer-id)) + (container (frame-parameter frame 'exwm-container)) (workspace (frame-parameter frame 'exwm-workspace))) (with-slots (x y width height) geometry (when (and (eq frame exwm-workspace--current) (exwm-workspace--minibuffer-own-frame-p)) (exwm-workspace--resize-minibuffer-frame width height)) - (exwm-layout--resize-container id workspace x y width height) + (exwm-layout--resize-container id container 0 0 width height) + (exwm-layout--resize-container nil workspace x y width height t) (xcb:flush exwm--connection))) (cl-incf exwm-layout--fullscreen-frame-count)) @@ -349,6 +359,12 @@ windows." 'exwm-outer-id) :value-mask xcb:ConfigWindow:Width :width width)) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window (frame-parameter exwm--floating-frame + 'exwm-container) + :value-mask xcb:ConfigWindow:Width + :width width)) (xcb:flush exwm--connection)))) (t (let* ((height (frame-pixel-height)) @@ -375,6 +391,12 @@ windows." 'exwm-outer-id) :value-mask xcb:ConfigWindow:Height :height height)) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window (frame-parameter exwm--floating-frame + 'exwm-container) + :value-mask xcb:ConfigWindow:Height + :height height)) (xcb:flush exwm--connection)))))) ;;;###autoload diff --git a/exwm-manage.el b/exwm-manage.el index c9c6ff9..fd1d64b 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -224,6 +224,9 @@ corresponding buffer.") (xcb:+request exwm--connection (make-instance 'xcb:UnmapWindow :window exwm--container)) (xcb:flush exwm--connection) + ;; Unmap the X window. + (xcb:+request exwm--connection + (make-instance 'xcb:UnmapWindow :window id)) ;; (setq exwm-workspace--switch-history-outdated t) ;; @@ -256,18 +259,17 @@ corresponding buffer.") (xcb:+request exwm--connection (make-instance 'xcb:DeleteProperty :window id :property xcb:Atom:WM_STATE))) - ;; Destroy the container (it seems it has to be delayed). (when exwm--floating-frame - ;; Unmap the floating frame. + ;; Unmap the floating frame before destroying the containers. (let ((window (frame-parameter exwm--floating-frame 'exwm-outer-id))) (xcb:+request exwm--connection (make-instance 'xcb:UnmapWindow :window window)) (xcb:+request exwm--connection (make-instance 'xcb:ReparentWindow :window window :parent exwm--root :x 0 :y 0)))) + ;; Destroy the X window container (and the frame container if any). (xcb:+request exwm--connection (make-instance 'xcb:DestroyWindow :window exwm--container)) - (xcb:flush exwm--connection) (let ((kill-buffer-query-functions nil) (floating exwm--floating-frame)) (kill-buffer) @@ -310,12 +312,14 @@ corresponding buffer.") (make-instance 'xcb:UnmapWindow :window exwm--container)) (xcb:flush exwm--connection) (when exwm--floating-frame - (xcb:+request exwm--connection - (make-instance 'xcb:ReparentWindow - :window (frame-parameter exwm--floating-frame - 'exwm-outer-id) - :parent exwm--root - :x 0 :y 0))) + (let ((window (frame-parameter exwm--floating-frame 'exwm-outer-id))) + (xcb:+request exwm--connection + (make-instance 'xcb:UnmapWindow :window window)) + (xcb:+request exwm--connection + (make-instance 'xcb:ReparentWindow + :window window + :parent exwm--root + :x 0 :y 0)))) (xcb:+request exwm--connection (make-instance 'xcb:DestroyWindow :window exwm--container)) (xcb:flush exwm--connection) @@ -410,10 +414,13 @@ Would you like to kill it? " (let ((obj (make-instance 'xcb:ConfigureRequest)) buffer edges) (xcb:unmarshal obj data) - (with-slots (window x y width height border-width value-mask) + (with-slots (window x y width height + border-width sibling stack-mode value-mask) obj - (exwm--log "ConfigureRequest from #x%x (#x%x) @%dx%d%+d%+d, border: %d" - window value-mask width height x y border-width) + (exwm--log "ConfigureRequest from #x%x (#x%x) @%dx%d%+d%+d; \ +border-width: %d; sibling: #x%x; stack-mode: %d" + window value-mask width height x y + border-width sibling stack-mode) (if (setq buffer (exwm--id->buffer window)) ;; Send client message for managed windows (with-current-buffer buffer @@ -440,16 +447,15 @@ Would you like to kill it? " :border-width 0 :override-redirect 0) exwm--connection)))) (exwm--log "ConfigureWindow (preserve geometry)") - ;; Configure the unmanaged window without changing the stacking order. + ;; Configure the unmanaged window. (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window window - :value-mask - (logand value-mask - (lognot xcb:ConfigWindow:Sibling) - (lognot xcb:ConfigWindow:StackMode)) + :value-mask value-mask :x x :y y :width width :height height - :border-width border-width))))) + :border-width border-width + :sibling sibling + :stack-mode stack-mode))))) (xcb:flush exwm--connection)) (defun exwm-manage--on-MapRequest (data _synthetic) diff --git a/exwm-workspace.el b/exwm-workspace.el index 99e3b55..42c0818 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -252,7 +252,7 @@ The optional FORCE option is for internal use only." (concat " " name))))) (setq exwm--frame frame) (if exwm--floating-frame - ;; Move the floating frame is enough + ;; Move the floating container. (progn (xcb:+request exwm--connection (make-instance 'xcb:ReparentWindow @@ -261,7 +261,7 @@ The optional FORCE option is for internal use only." (frame-parameter frame 'exwm-workspace) :x 0 :y 0)) (xcb:flush exwm--connection)) - ;; Move the window itself + ;; Move the X window container. (if (/= index exwm-workspace-current-index) (bury-buffer) (set-window-buffer (get-buffer-window (current-buffer) t) @@ -483,28 +483,30 @@ This functions is modified from `display-buffer-reuse-window' and (0 (y-or-n-p prompt)) (x (yes-or-no-p (format "[EXWM] %d window%s currently alive. %s" x (if (= x 1) "" "s") prompt)))) - ;; Remove SubstructureRedirect event. - (xcb:+request exwm--connection - (make-instance 'xcb:ChangeWindowAttributes - :window exwm--root :value-mask xcb:CW:EventMask - :event-mask 0)) - ;; Remove the _NET_SUPPORTING_WM_CHECK X window. - (with-slots (value) - (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:ewmh:get-_NET_SUPPORTING_WM_CHECK - :window exwm--root)) - (xcb:+request exwm--connection - (make-instance 'xcb:DeleteProperty - :window exwm--root - :property xcb:Atom:_NET_SUPPORTING_WM_CHECK)) - (xcb:+request exwm--connection - (make-instance 'xcb:DestroyWindow :window value))) ;; Unmanage all X windows. (dolist (i exwm--id-buffer-alist) (exwm-manage--unmanage-window (car i) t) (xcb:+request exwm--connection (make-instance 'xcb:MapWindow :window (car i)))) + ;; Reparent out the minibuffer frame. + (when exwm-workspace-minibuffer-position + (xcb:+request exwm--connection + (make-instance 'xcb:ReparentWindow + :window (frame-parameter exwm-workspace--minibuffer + 'exwm-outer-id) + :parent exwm--root + :x 0 + :y 0))) + ;; Reparent out all workspace frames. + (dolist (f exwm-workspace--list) + (xcb:+request exwm--connection + (make-instance 'xcb:ReparentWindow + :window (frame-parameter f 'exwm-outer-id) + :parent exwm--root + :x 0 + :y 0))) (xcb:flush exwm--connection) + ;; Destroy all resources created by this connection. (xcb:disconnect exwm--connection) t)) @@ -589,9 +591,11 @@ This functions is modified from `display-buffer-reuse-window' and ;; Configure workspaces (dolist (i exwm-workspace--list) (let ((outer-id (string-to-number (frame-parameter i 'outer-window-id))) + (container (xcb:generate-id exwm--connection)) (workspace (xcb:generate-id exwm--connection))) ;; Save window IDs (set-frame-parameter i 'exwm-outer-id outer-id) + (set-frame-parameter i 'exwm-container container) (set-frame-parameter i 'exwm-workspace workspace) (xcb:+request exwm--connection (make-instance 'xcb:CreateWindow @@ -605,16 +609,34 @@ This functions is modified from `display-buffer-reuse-window' and xcb:CW:EventMask) :override-redirect 1 :event-mask xcb:EventMask:SubstructureRedirect)) + (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:CopyFromParent + :visual 0 ;CopyFromParent + :value-mask xcb:CW:OverrideRedirect + :override-redirect 1)) (exwm--debug (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_WM_NAME :window workspace :data (format "EXWM workspace %d" + (cl-position i exwm-workspace--list)))) + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_WM_NAME + :window container + :data + (format "EXWM workspace %d frame container" (cl-position i exwm-workspace--list))))) (xcb:+request exwm--connection (make-instance 'xcb:ReparentWindow - :window outer-id :parent workspace :x 0 :y 0)) + :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)))) (xcb:flush exwm--connection)