From 5c5729c0d4832a870a928d6d7cc2f990a7e8f649 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Mon, 19 Feb 2018 22:40:27 +0800 Subject: Fix various issues with multi-monitor support * exwm-workspace.el (exwm-workspace-switch): Do not hide X windows when switching to a workspace on another output; update the timestamp (last switched to) of a workspace frame. (exwm-workspace-move-window): Do not hide an X window when moving it to an active workspace on another output. * exwm-floating.el (exwm-floating--set-floating): * exwm-layout.el (exwm-layout-set-fullscreen): * exwm-manage.el (exwm-manage--manage-window) (exwm-manage--on-ConfigureRequest): * exwm-systemtray.el (exwm-systemtray--refresh) (exwm-systemtray--init): Correct coordinate calculations. * exwm-workspace.el (exwm-workspace--current-width): Removed since no longer used. --- exwm-floating.el | 74 ++++++++++++++++--------------------- exwm-layout.el | 9 +---- exwm-manage.el | 26 ++++++++----- exwm-systemtray.el | 26 +++++++------ exwm-workspace.el | 105 ++++++++++++++++++++++++++++++++++------------------- 5 files changed, 131 insertions(+), 109 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index 8cd0491beae8..5a2c61e878d1 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -129,52 +129,22 @@ This is also used by X window containers.") (y (slot-value exwm--geometry 'y)) (width (slot-value exwm--geometry 'width)) (height (slot-value exwm--geometry 'height))) - (exwm--log "Floating geometry (original, absolute): %dx%d%+d%+d" - width height x y) - (when (and (/= x 0) - (/= y 0)) - (let ((workarea (elt exwm-workspace--workareas - (exwm-workspace--position original-frame)))) - (setq x (- x (aref workarea 0)) - y (- y (aref workarea 1))))) - (exwm--log "Floating geometry (original, relative): %dx%d%+d%+d" - width height x y) + (exwm--log "Floating geometry (original): %dx%d%+d%+d" width height x y) ;; Save frame parameters. (set-frame-parameter frame 'exwm-outer-id outer-id) (set-frame-parameter frame 'exwm-id window-id) (set-frame-parameter frame 'exwm-container frame-container) ;; Fix illegal parameters ;; FIXME: check normal hints restrictions - (let* ((display-width (frame-pixel-width original-frame)) - (display-height (- (frame-pixel-height original-frame) - (if (exwm-workspace--minibuffer-own-frame-p) - 0 - (window-pixel-height (minibuffer-window - original-frame))) - (* 2 (window-mode-line-height)) - (window-header-line-height window))) - (display-height (* 2 (/ display-height 2)))) ;round to even - (if (> width display-width) - ;; Too wide - (progn (setq x 0 - width display-width)) - ;; Invalid width - (when (= 0 width) (setq width (/ display-width 2))) - ;; Make sure at least half of the window is visible - (when (or (> (+ x (/ width 2)) display-width) (> 0 (+ x (/ width 2)))) - (setq x (/ (- display-width width) 2)))) - (if (> height display-height) - ;; Too tall - (setq y 0 - height display-height) - ;; Invalid height - (when (= 0 height) (setq height (/ display-height 2))) - ;; Make sure at least half of the window is visible - (when (or (> (+ y (/ height 2)) display-height) - (> 0 (+ y (/ height 2)))) - (setq y (/ (- display-height height) 2)))) + (let* ((workarea (elt exwm-workspace--workareas + (exwm-workspace--position original-frame))) + (x* (aref workarea 0)) + (y* (aref workarea 1)) + (width* (aref workarea 2)) + (height* (aref workarea 3))) ;; Center floating windows - (when (and (= x 0) (= y 0)) + (when (and (or (= x 0) (= x x*)) + (or (= y 0) (= y y*))) (let ((buffer (exwm--id->buffer exwm-transient-for)) window edges) (when (and buffer (setq window (get-buffer-window buffer))) @@ -184,11 +154,29 @@ This is also used by X window containers.") (setq edges nil))) (if edges ;; Put at the center of leading window - (setq x (/ (- (elt edges 2) (elt edges 0) width) 2) - y (/ (- (elt edges 3) (elt edges 1) height) 2)) + (setq x (+ x* (/ (- (elt edges 2) (elt edges 0) width) 2)) + y (+ y* (/ (- (elt edges 3) (elt edges 1) height) 2))) ;; Put at the center of screen - (setq x (/ (- display-width width) 2) - y (/ (- display-height height) 2)))))) + (setq x (/ (- width* width) 2) + y (/ (- height* height) 2))))) + (if (> width width*) + ;; Too wide + (progn (setq x x* + width width*)) + ;; Invalid width + (when (= 0 width) (setq width (/ width* 2))) + ;; Make sure at least half of the window is visible + (unless (< x* (+ x (/ width 2)) (+ x* width*)) + (setq x (+ x* (/ (- width* width) 2))))) + (if (> height height*) + ;; Too tall + (setq y y* + height height*) + ;; Invalid height + (when (= 0 height) (setq height (/ height* 2))) + ;; Make sure at least half of the window is visible + (unless (< y* (+ y (/ height 2)) (+ y* height*)) + (setq y (+ y* (/ (- height* height) 2)))))) (exwm--set-geometry id x y nil nil) (xcb:flush exwm--connection) (exwm--log "Floating geometry (corrected): %dx%d%+d%+d" width height x y) diff --git a/exwm-layout.el b/exwm-layout.el index 3f624ad7fe4d..5a01f900323d 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -49,8 +49,6 @@ (declare-function exwm-input-release-keyboard "exwm-input.el") (declare-function exwm-workspace--client-p "exwm-workspace.el" (&optional frame)) -(declare-function exwm-workspace--current-height "exwm-workspace.el") -(declare-function exwm-workspace--current-width "exwm-workspace.el") (declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el") (declare-function exwm-workspace--workspace-p "exwm-workspace.el" (workspace)) @@ -137,11 +135,8 @@ (cl-return-from 'exwm-layout-set-fullscreen)) (with-current-buffer (if id (exwm--id->buffer id) (window-buffer)) ;; Expand the X window to fill the whole screen. - ;; Rationale: Floating X windows may not be positioned at (0, 0) - ;; due to the extra border. - (exwm--set-geometry exwm--id 0 0 - (exwm-workspace--current-width) - (exwm-workspace--current-height)) + (with-slots (x y width height) (exwm-workspace--get-geometry exwm--frame) + (exwm--set-geometry exwm--id x y width height)) ;; Raise the X window. (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow diff --git a/exwm-manage.el b/exwm-manage.el index 790b6f147904..dd7c2e3953c9 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -65,6 +65,7 @@ You can still make the X windows floating afterwards." (defvar exwm-workspace--id-struts-alist) (defvar exwm-workspace--list) (defvar exwm-workspace--switch-history-outdated) +(defvar exwm-workspace--workareas) (defvar exwm-workspace-current-index) (declare-function exwm--update-class "exwm.el" (id &optional force)) (declare-function exwm--update-hints "exwm.el" (id &optional force)) @@ -80,8 +81,7 @@ You can still make the X windows floating afterwards." (declare-function exwm-input-grab-keyboard "exwm-input.el") (declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id)) (declare-function exwm-workspace--count "exwm-workspace.el" ()) -(declare-function exwm-workspace--current-height "exwm-workspace.el") -(declare-function exwm-workspace--current-width "exwm-workspace.el") +(declare-function exwm-workspace--position "exwm-workspace.el" (frame)) (declare-function exwm-workspace--set-desktop "exwm-workspace.el" (id)) (declare-function exwm-workspace--set-fullscreen "exwm-workspace.el" (frame)) (declare-function exwm-workspace--update-struts "exwm-workspace.el" ()) @@ -204,11 +204,17 @@ You can still make the X windows floating afterwards." (with-slots (x y width height) exwm--geometry ;; Center window of type _NET_WM_WINDOW_TYPE_SPLASH (when (memq xcb:Atom:_NET_WM_WINDOW_TYPE_SPLASH exwm-window-type) - (exwm--set-geometry id - (/ (- (exwm-workspace--current-width) width) 2) - (/ (- (exwm-workspace--current-height) height) - 2) - nil nil))) + (let* ((workarea (elt exwm-workspace--workareas + (exwm-workspace--position exwm--frame))) + (x* (aref workarea 0)) + (y* (aref workarea 1)) + (width* (aref workarea 2)) + (height* (aref workarea 3))) + (exwm--set-geometry id + (+ x* (/ (- width* width) 2)) + (+ y* (/ (- height* height) 2)) + nil + nil)))) ;; Check for desktop. (when (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DESKTOP exwm-window-type) ;; There should be only one desktop X window. @@ -501,9 +507,9 @@ border-width: %d; sibling: #x%x; stack-mode: %d" (with-current-buffer buffer (setq edges (if (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state) - (list 0 0 - (exwm-workspace--current-width) - (exwm-workspace--current-height)) + (with-slots (x y width height) + (exwm-workspace--get-geometry exwm--frame) + (list x y width height)) (window-inside-absolute-pixel-edges (get-buffer-window buffer t)))) (exwm--log "Reply with ConfigureNotify (edges): %s" edges) diff --git a/exwm-systemtray.el b/exwm-systemtray.el index 5377ef8ed617..d932032e5905 100644 --- a/exwm-systemtray.el +++ b/exwm-systemtray.el @@ -82,8 +82,6 @@ You shall use the default value if using auto-hide minibuffer." (defvar exwm-workspace--workareas) (defvar exwm-workspace-current-index) (defvar xcb:Atom:_NET_SYSTEM_TRAY_S0) -(declare-function exwm-workspace--current-height "exwm-workspace.el") -(declare-function exwm-workspace--current-width "exwm-workspace.el") (declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el") (defun exwm-systemtray--embed (icon) @@ -205,13 +203,15 @@ You shall use the default value if using auto-hide minibuffer." (setq x (+ x (slot-value (cdr pair) 'width) exwm-systemtray-icon-gap)) (setq map t))) - (xcb:+request exwm-systemtray--connection - (make-instance 'xcb:ConfigureWindow - :window exwm-systemtray--embedder - :value-mask (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Width) - :x (- (exwm-workspace--current-width) x) - :width x)) + (let ((workarea (elt exwm-workspace--workareas + exwm-workspace-current-index))) + (xcb:+request exwm-systemtray--connection + (make-instance 'xcb:ConfigureWindow + :window exwm-systemtray--embedder + :value-mask (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Width) + :x (- (aref workarea 2) x) + :width x))) (when map (xcb:+request exwm-systemtray--connection (make-instance 'xcb:MapWindow :window exwm-systemtray--embedder)))) @@ -434,9 +434,11 @@ You shall use the default value if using auto-hide minibuffer." (- (line-pixel-height) exwm-systemtray-height) ;; Vertically centered. (/ (- (line-pixel-height) exwm-systemtray-height) 2))) - (setq frame exwm-workspace--current - ;; Bottom aligned. - y (- (exwm-workspace--current-height) exwm-systemtray-height))) + (let ((workarea (elt exwm-workspace--workareas + exwm-workspace-current-index))) + (setq frame exwm-workspace--current + ;; Bottom aligned. + y (- (aref workarea 3) exwm-systemtray-height)))) (setq parent (string-to-number (frame-parameter frame 'window-id)) depth (slot-value (xcb:+request-unchecked+reply exwm-systemtray--connection diff --git a/exwm-workspace.el b/exwm-workspace.el index e2dfa232f8b6..9b0bfeb3387d 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -272,14 +272,6 @@ NIL if FRAME is not a workspace" :width (x-display-pixel-width) :height (x-display-pixel-height)))) -;;;###autoload -(defun exwm-workspace--current-width () - "Return the width of current workspace." - (let ((geometry (frame-parameter exwm-workspace--current 'exwm-geometry))) - (if geometry - (slot-value geometry 'width) - (x-display-pixel-width)))) - ;;;###autoload (defun exwm-workspace--current-height () "Return the height of current workspace." @@ -536,14 +528,23 @@ for internal use only." ;; 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))))))) + (let ((hide-x-windows-on-old-frame + (with-slots ((x1 x) + (y1 y)) + (exwm-workspace--get-geometry frame) + (with-slots ((x2 x) + (y2 y)) + (exwm-workspace--get-geometry old-frame) + (and (= x1 x2) (= y1 y2)))))) + (dolist (i exwm--id-buffer-alist) + (with-current-buffer (cdr i) + (if (eq old-frame exwm--frame) + (when hide-x-windows-on-old-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) @@ -553,6 +554,8 @@ for internal use only." (exwm-workspace-rename-buffer (if (eq frame exwm--frame) name (concat " " name))))))) + ;; Update frame's timestamp. + (set-frame-parameter frame 'exwm-timestamp (float-time)) ;; Update demands attention flag (set-frame-parameter frame 'exwm-urgency nil) ;; Update switch workspace history @@ -700,7 +703,7 @@ INDEX must not exceed the current number of workspaces." (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)) - old-frame container) + x-old y-old x-new y-new should-hide old-frame container) (unless id (setq id (exwm--buffer->id (window-buffer)))) (with-current-buffer (exwm--id->buffer id) (unless (eq exwm--frame frame) @@ -712,6 +715,36 @@ INDEX must not exceed the current number of workspaces." (concat " " name))))) (setq old-frame exwm--frame exwm--frame frame) + ;; Save the positions of new & old frames. + (with-slots ((x1 x) + (y1 y)) + (exwm-workspace--get-geometry old-frame) + (with-slots ((x2 x) + (y2 y)) + (exwm-workspace--get-geometry frame) + (setq x-old x1 + y-old y1 + x-new x2 + y-new y2))) + (if (and (= x-old x-new) + (= y-old y-new)) + ;; Switch to a workspace on the same output. + (setq should-hide t) + ;; Check if this frame has the largest timestamp of that output. + (let ((timestamp (frame-parameter frame 'exwm-timestamp)) + (timestamp-active + (apply #'max + (mapcar (lambda (w) + (with-slots (x y) + (exwm-workspace--get-geometry w) + (if (and (= x x-new) + (= y y-new)) + (frame-parameter w 'exwm-timestamp) + -1))) + exwm-workspace--list)))) + (when (< timestamp timestamp-active) + ;; Switch to a workspace not active on another output. + (setq should-hide t)))) (if (not exwm--floating-frame) ;; Tiling. (progn @@ -724,31 +757,27 @@ INDEX must not exceed the current number of workspaces." (exwm--id->buffer id)) (if (eq frame exwm-workspace--current) (select-window (frame-selected-window frame)) - (exwm-layout--hide id))) + (when should-hide + (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))))) + (unless (and (= x-old x-new) + (= y-old y-new)) + (with-slots (x y) + (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:GetGeometry + :drawable container)) + (setq x (+ x (- x-new x-old)) + y (+ y (- y-new y-old))) + (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)) + (when should-hide + (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'. @@ -808,7 +837,8 @@ INDEX must not exceed the current number of workspaces." (if (eq frame exwm-workspace--current) (with-current-buffer (exwm--id->buffer id) (select-window (frame-root-window exwm--floating-frame))) - (exwm-layout--hide id)))) + (when should-hide + (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) @@ -1486,7 +1516,8 @@ applied to all subsequently created X frames." ;; Prevent frame parameters introduced by this module from being ;; saved/restored. (dolist (i '(exwm-outer-id exwm-id exwm-container exwm-geometry - fullscreen exwm-selected-window exwm-urgency)) + exwm-selected-window exwm-timestamp exwm-urgency + fullscreen)) (push (cons i :never) frameset-filter-alist))) (defun exwm-workspace--exit () -- cgit 1.4.1