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.
This commit is contained in:
Chris Feng 2018-02-19 22:40:27 +08:00
parent b8ce20b4f3
commit 5c5729c0d4
5 changed files with 131 additions and 109 deletions

View file

@ -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)

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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 ()