diff --git a/exwm-layout.el b/exwm-layout.el index 259788f..b79e42e 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -155,6 +155,9 @@ (defvar exwm-workspace--current) (defvar exwm-workspace--list) +(declare-function exwm-workspace--set-fullscreen "exwm-workspace.el" + (frame &optional no-struts container-only)) + ;;;###autoload (defun exwm-layout-set-fullscreen (&optional id) "Make window ID fullscreen." @@ -169,9 +172,25 @@ :drawable exwm--container)))) (setq exwm--floating-frame-position (vector (slot-value geometry 'x) (slot-value geometry 'y))))) - (exwm-layout--resize-container exwm--id exwm--container 0 0 + ;; Expand the workspace frame & its container to fill the whole screen. + (exwm-workspace--set-fullscreen exwm--frame t t) + ;; Raise the workspace container (in case there are docks). + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window (frame-parameter exwm--frame 'exwm-workspace) + :value-mask xcb:ConfigWindow:StackMode + :stack-mode xcb:StackMode:Above)) + ;; Expand the X window and its container to fill the whole screen. + ;; Rationale: Floating X windows may not be positioned at (0, 0) + ;; due to the extra border. + (exwm-layout--resize-container nil exwm--container 0 0 (exwm-workspace--current-width) - (exwm-workspace--current-height)) + (exwm-workspace--current-height) + t) + (exwm-layout--resize-container nil exwm--id 0 0 + (exwm-workspace--current-width) + (exwm-workspace--current-height) + t) ;; Raise the X window. (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow @@ -193,6 +212,8 @@ (with-current-buffer (if id (exwm--id->buffer id) (window-buffer)) (unless exwm--fullscreen (user-error "Not in full-screen mode.")) + ;; Restore the size of this workspace. + (exwm-workspace--set-fullscreen exwm--frame) (if exwm--floating-frame ;; Restore the floating frame. (xcb:+request exwm--connection diff --git a/exwm-manage.el b/exwm-manage.el index 1394890..3597822 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -86,7 +86,7 @@ corresponding buffer.") (declare-function exwm--update-title "exwm.el" (id)) (declare-function exwm--update-hints "exwm.el" (id &optional force)) (declare-function exwm--update-protocols "exwm.el" (id &optional force)) -(declare-function exwm--update-strut "exwm.el" (id)) +(declare-function exwm--update-struts "exwm.el" (id)) (declare-function exwm-floating--set-floating "exwm-floating.el" (id)) (declare-function exwm-floating--unset-floating "exwm-floating.el" (id)) (declare-function exwm-workspace--set-desktop "exwm-workspace.el" (id)) @@ -133,7 +133,7 @@ corresponding buffer.") (exwm--log "No need to manage #x%x" id) ;; Update struts. (when (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DOCK exwm-window-type) - (exwm--update-strut id)) + (exwm--update-struts id)) ;; Remove all events (xcb:+request exwm--connection (make-instance 'xcb:ChangeWindowAttributes @@ -141,8 +141,9 @@ corresponding buffer.") :event-mask (if (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DOCK exwm-window-type) - ;; Listen for change of struts property of dock. - xcb:EventMask:PropertyChange + ;; Listen for PropertyChange (struts) and + ;; UnmapNotify/DestroyNotify event of the dock. + exwm--client-event-mask xcb:EventMask:NoEvent))) ;; The window needs to be mapped (xcb:+request exwm--connection @@ -247,6 +248,15 @@ corresponding buffer.") (with-current-buffer (exwm--id->buffer id) (run-hooks 'exwm-manage-finish-hook))))) +(defvar exwm-workspace--id-struts-alist) +(defvar exwm-workspace--list) + +(declare-function exwm-workspace--update-struts "exwm-workspace.el" ()) +(declare-function exwm-workspace--set-fullscreen "exwm-workspace.el" + (frame &optional no-struts container-only)) +(declare-function exwm-workspace--set-workareas "exwm-workspace.el" + (&optional workareas)) + (defun exwm-manage--unmanage-window (id &optional withdraw-only) "Unmanage window ID. @@ -257,6 +267,14 @@ manager is shutting down." (exwm--log "Unmanage #x%x (buffer: %s, widthdraw: %s)" id buffer withdraw-only) (setq exwm--id-buffer-alist (assq-delete-all id exwm--id-buffer-alist)) + ;; Update workspaces when a dock is destroyed. + (when (assq id exwm-workspace--id-struts-alist) + (setq exwm-workspace--id-struts-alist + (assq-delete-all id exwm-workspace--id-struts-alist)) + (exwm-workspace--update-struts) + (dolist (f exwm-workspace--list) + (exwm-workspace--set-fullscreen f)) + (exwm-workspace--set-workareas)) (when (buffer-live-p buffer) (with-current-buffer buffer ;; Flickering seems unavoidable here if the DestroyWindow request is @@ -314,6 +332,9 @@ manager is shutting down." (xcb:+request exwm--connection (make-instance 'xcb:ReparentWindow :window window :parent exwm--root :x 0 :y 0)))) + ;; Restore the workspace if this X window is currently fullscreen. + (when exwm--fullscreen + (exwm-workspace--set-fullscreen exwm--frame)) ;; Destroy the X window container (and the frame container if any). (xcb:+request exwm--connection (make-instance 'xcb:DestroyWindow :window exwm--container)) diff --git a/exwm-randr.el b/exwm-randr.el index f71120a..9f6be78 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -58,7 +58,8 @@ (defvar exwm-workspace-number) (defvar exwm-workspace--list) -(declare-function exwm-workspace--set-fullscreen "exwm-workspace.el" (frame)) +(declare-function exwm-workspace--set-fullscreen "exwm-workspace.el" + (frame &optional no-struts container-only)) (declare-function exwm-workspace--set-workareas "exwm-workspace.el" (&optional workareas)) (declare-function exwm-workspace--set-desktop-geometry "exwm-workspace.el" ()) diff --git a/exwm-workspace.el b/exwm-workspace.el index 6902151..cfbe02a 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -128,10 +128,30 @@ Value nil means to use the default position which is fixed at bottom, while "Reports whether the minibuffer is displayed in its own frame." (memq exwm-workspace-minibuffer-position '(top bottom))) -;; FIXME: RandR and multiple docks. -(defvar exwm-workspace--strut nil "Areas occupied by struts.") -(defvar exwm-workspace--strut-is-partial nil - "Whether the struts are from _NET_WM_STRUT_PARTIAL.") +(defvar exwm-workspace--id-struts-alist nil "Alist of X window and struts.") +(defvar exwm-workspace--struts nil "Areas occupied by struts.") + +(defun exwm-workspace--update-struts () + "Update `exwm-workspace--struts'." + (let ((left 0) + (right 0) + (top 0) + (bottom 0) + struts) + (dolist (pair exwm-workspace--id-struts-alist) + (setq struts (cdr pair)) + (when struts + (when (< left (aref struts 0)) + (setq left (aref struts 0))) + (when (< right (aref struts 1)) + (setq right (aref struts 1))) + (when (< top (aref struts 2)) + (setq top (aref struts 2))) + (when (< bottom (aref struts 3)) + (setq bottom (aref struts 3))))) + (setq exwm-workspace--struts (vector left right top bottom)) + (when (equal exwm-workspace--struts [0 0 0 0]) + (setq exwm-workspace--struts nil)))) (defvar exwm-workspace--fullscreen-frame-count 0 "Count the fullscreen workspace frames.") @@ -139,8 +159,12 @@ Value nil means to use the default position which is fixed at bottom, while (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, with regard to its RandR output if applicable." +(defun exwm-workspace--set-fullscreen (frame &optional no-struts + container-only) + "Make frame FRAME fullscreen, with regard to its RandR output if applicable. + +If NO-STRUTS is non-nil, struts are ignored. If CONTAINER-ONLY is non-nil, the +workspace frame and its container is not resized." (let ((geometry (or (frame-parameter frame 'exwm-geometry) (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:GetGeometry @@ -153,24 +177,27 @@ Value nil means to use the default position which is fixed at bottom, while (workspace (frame-parameter frame 'exwm-workspace)) x* y* width* height*) (with-slots (x y width height) geometry - (if exwm-workspace--strut - (setq x* (+ x (aref exwm-workspace--strut 0)) - y* (+ y (aref exwm-workspace--strut 2)) - width* (- width (aref exwm-workspace--strut 0) - (aref exwm-workspace--strut 1)) - height* (- height (aref exwm-workspace--strut 2) - (aref exwm-workspace--strut 3))) + (if (and exwm-workspace--struts (not no-struts)) + (setq x* (+ x (aref exwm-workspace--struts 0)) + y* (+ y (aref exwm-workspace--struts 2)) + width* (- width (aref exwm-workspace--struts 0) + (aref exwm-workspace--struts 1)) + height* (- height (aref exwm-workspace--struts 2) + (aref exwm-workspace--struts 3))) (setq x* x y* y width* width height* height)) (when (and (eq frame exwm-workspace--current) - (exwm-workspace--minibuffer-own-frame-p)) + (exwm-workspace--minibuffer-own-frame-p) + (not container-only)) (exwm-workspace--resize-minibuffer-frame width height)) - (exwm-layout--resize-container id container 0 0 width* height*) + (unless container-only + (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-workspace--fullscreen-frame-count)) + (unless container-only + (cl-incf exwm-workspace--fullscreen-frame-count))) ;;;###autoload (defun exwm-workspace--resize-minibuffer-frame (&optional width height) @@ -182,19 +209,19 @@ workspace frame." (let ((y (if (eq exwm-workspace-minibuffer-position 'top) 0 (- (or height (exwm-workspace--current-height)) - (if exwm-workspace--strut - (+ (aref exwm-workspace--strut 2) - (aref exwm-workspace--strut 3)) + (if exwm-workspace--struts + (+ (aref exwm-workspace--struts 2) + (aref exwm-workspace--struts 3)) 0) (frame-pixel-height exwm-workspace--minibuffer)))) (container (frame-parameter exwm-workspace--minibuffer 'exwm-container))) (unless width (setq width (exwm-workspace--current-width))) - (when exwm-workspace--strut + (when exwm-workspace--struts (setq width (- width - (aref exwm-workspace--strut 0) - (aref exwm-workspace--strut 1)))) + (aref exwm-workspace--struts 0) + (aref exwm-workspace--struts 1)))) (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window container @@ -233,11 +260,22 @@ The optional FORCE option is for internal use only." (let* ((frame (elt exwm-workspace--list index)) (workspace (frame-parameter frame 'exwm-workspace)) (window (frame-parameter frame 'exwm-selected-window))) + (unless (window-live-p window) + (setq window (frame-selected-window frame))) + ;; Raise the workspace container. (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 (buffer-local-value 'exwm--fullscreen (window-buffer window)) + (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)))) (setq exwm-workspace--current frame exwm-workspace-current-index index) (unless (memq (selected-frame) exwm-workspace--list) @@ -245,8 +283,7 @@ The optional FORCE option is for internal use only." (set-frame-parameter (with-current-buffer (window-buffer) exwm--frame) 'exwm-selected-window (selected-window))) - (select-window (or (when (window-live-p window) window) - (frame-selected-window frame))) + (select-window window) (set-frame-parameter frame 'exwm-selected-window nil) ;; Close the (possible) active minibuffer (when (active-minibuffer-window) @@ -556,9 +593,9 @@ The optional FORCE option is for internal use only." y 0) (setq value-mask (logior xcb:ConfigWindow:Y xcb:ConfigWindow:Height) y (- (exwm-workspace--current-height) - (if exwm-workspace--strut - (+ (aref exwm-workspace--strut 2) - (aref exwm-workspace--strut 3)) + (if exwm-workspace--struts + (+ (aref exwm-workspace--struts 2) + (aref exwm-workspace--struts 3)) 0) height))) (xcb:+request exwm--connection @@ -731,13 +768,13 @@ The optional FORCE option is for internal use only." (setq workareas (vconcat workareas workarea)))))) ;; Exclude areas occupied by struts. ;; FIXME: RandR. - (when exwm-workspace--strut - (let ((dx (aref exwm-workspace--strut 0)) - (dy (aref exwm-workspace--strut 2)) - (dw (- (+ (aref exwm-workspace--strut 0) - (aref exwm-workspace--strut 1)))) - (dh (- (+ (aref exwm-workspace--strut 2) - (aref exwm-workspace--strut 3))))) + (when exwm-workspace--struts + (let ((dx (aref exwm-workspace--struts 0)) + (dy (aref exwm-workspace--struts 2)) + (dw (- (+ (aref exwm-workspace--struts 0) + (aref exwm-workspace--struts 1)))) + (dh (- (+ (aref exwm-workspace--struts 2) + (aref exwm-workspace--struts 3))))) (dotimes (i exwm-workspace-number) (cl-incf (aref workareas (* i 4)) dx) (cl-incf (aref workareas (+ (* i 4))) dy) diff --git a/exwm.el b/exwm.el index 3d7edcf..59f4313 100644 --- a/exwm.el +++ b/exwm.el @@ -227,45 +227,49 @@ (when reply ;nil when destroyed (setq exwm--protocols (append (slot-value reply 'value) nil))))))) -(defun exwm--update-strut-legacy (id) +(defun exwm--update-struts-legacy (id) "Update _NET_WM_STRUT." - (unless exwm-workspace--strut-is-partial - (let ((reply (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:ewmh:get-_NET_WM_STRUT - :window id)))) - (setq exwm-workspace--strut (when reply (slot-value reply 'value))) + (let ((pair (assq id exwm-workspace--id-struts-alist)) + reply struts) + (unless (and pair (< 4 (length (cdr pair)))) + (setq reply (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:ewmh:get-_NET_WM_STRUT + :window id))) + (when reply + (setq struts (slot-value reply 'value)) + (if pair + (setcdr pair struts) + (push (cons id struts) exwm-workspace--id-struts-alist)) + (exwm-workspace--update-struts)) ;; Update workspaces. (dolist (f exwm-workspace--list) (exwm-workspace--set-fullscreen f)) - ;; Resize the minibuffer frame. - (when (exwm-workspace--minibuffer-own-frame-p) - (exwm-workspace--resize-minibuffer-frame)) ;; Update _NET_WORKAREA. (exwm-workspace--set-workareas)))) -(defun exwm--update-strut-partial (id) +(defun exwm--update-struts-partial (id) "Update _NET_WM_STRUT_PARTIAL." (let ((reply (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:ewmh:get-_NET_WM_STRUT_PARTIAL - :window id)))) - (setq exwm-workspace--strut (when reply (slot-value reply 'value))) - (if (not exwm-workspace--strut) - (setq exwm-workspace--strut-is-partial nil) - (setq exwm-workspace--strut (substring exwm-workspace--strut 0 4)) - (setq exwm-workspace--strut-is-partial t)) + :window id))) + struts pair) + (when reply + (setq struts (slot-value reply 'value) + pair (assq id exwm-workspace--id-struts-alist)) + (if pair + (setcdr pair struts) + (push (cons id struts) exwm-workspace--id-struts-alist)) + (exwm-workspace--update-struts)) ;; Update workspaces. (dolist (f exwm-workspace--list) (exwm-workspace--set-fullscreen f)) - ;; Resize the minibuffer frame. - (when (exwm-workspace--minibuffer-own-frame-p) - (exwm-workspace--resize-minibuffer-frame)) ;; Update _NET_WORKAREA. (exwm-workspace--set-workareas))) -(defun exwm--update-strut (id) +(defun exwm--update-struts (id) "Update _NET_WM_STRUT_PARTIAL or _NET_WM_STRUT." - (exwm--update-strut-partial id) - (exwm--update-strut-legacy id)) + (exwm--update-struts-partial id) + (exwm--update-struts-legacy id)) (defun exwm--on-PropertyNotify (data _synthetic) "Handle PropertyNotify event." @@ -279,9 +283,9 @@ (if (not (buffer-live-p buffer)) ;; Properties of unmanaged X windows. (cond ((= atom xcb:Atom:_NET_WM_STRUT) - (exwm--update-strut-legacy id)) + (exwm--update-struts-legacy id)) ((= atom xcb:Atom:_NET_WM_STRUT_PARTIAL) - (exwm--update-strut-partial id))) + (exwm--update-struts-partial id))) (with-current-buffer buffer (cond ((= atom xcb:Atom:_NET_WM_WINDOW_TYPE) (exwm--update-window-type id t))