Add support for RandR 1.5 monitor

* exwm-randr.el (exwm-randr-workspace-monitor-plist): New user option
for specifying which monitor each workspace should be displayed on.
(exwm-randr-workspace-monitor-plist): Made obsolete.
(exwm-randr--get-monitors): New function for fetching active monitors.
(exwm-randr--refresh): Adapted to use monitor.
(exwm-randr--init): Now requires RandR 1.5.

* exwm-randr.el:
* exwm-workspace.el: Rename `output' to `monitor'.
This commit is contained in:
Chris Feng 2018-11-04 00:00:00 +00:00
parent fec85bb72a
commit e597ab4f78
2 changed files with 81 additions and 82 deletions

View file

@ -27,11 +27,11 @@
;; that. ;; that.
;; To use this module, load, enable it and configure ;; To use this module, load, enable it and configure
;; `exwm-randr-workspace-output-plist' and `exwm-randr-screen-change-hook' ;; `exwm-randr-workspace-monitor-plist' and `exwm-randr-screen-change-hook'
;; as follows: ;; as follows:
;; ;;
;; (require 'exwm-randr) ;; (require 'exwm-randr)
;; (setq exwm-randr-workspace-output-plist '(0 "VGA1")) ;; (setq exwm-randr-workspace-monitor-plist '(0 "VGA1"))
;; (add-hook 'exwm-randr-screen-change-hook ;; (add-hook 'exwm-randr-screen-change-hook
;; (lambda () ;; (lambda ()
;; (start-process-shell-command ;; (start-process-shell-command
@ -63,22 +63,34 @@
"Normal hook run when screen changes." "Normal hook run when screen changes."
:type 'hook) :type 'hook)
(defcustom exwm-randr-workspace-output-plist nil (defcustom exwm-randr-workspace-monitor-plist nil
"Plist mapping workspace to output. "Plist mapping workspaces to monitors.
If an output is not available, the workspaces mapped to it are displayed on In RandR 1.5 a monitor is a rectangle region decoupled from the physical
the primary output until it becomes available. Unspecified workspaces are size of screens, and can be identified with `xrandr --listmonitors' (name of
all mapped to the primary output. For example, with the following value the primary monitor is prefixed with an `*'). When no monitor is created it
workspace other than 1 and 3 would always be displayed on the primary output automatically fallback to RandR 1.2 output which represents the physical
where workspace 1 and 3 would be displayed on their corresponding output screen size. RandR 1.5 monitors can be created with `xrandr --setmonitor'.
whenever the outputs are available. For example, to split an output (`LVDS-1') of size 1280x800 into two
side-by-side monitors one could invoke (the digits after `/' are size in mm)
'(1 \"HDMI-1\" 3 \"DP-1\") xrandr --setmonitor *LVDS-1-L 640/135x800/163+0+0 LVDS-1
xrandr --setmonitor LVDS-1-R 640/135x800/163+640+0 none
The outputs available can be identified by running the 'xrandr' utility with If a monitor is not active, the workspaces mapped to it are displayed on the
the first one in result being the primary output." primary monitor until it becomes active (if ever). Unspecified workspaces
are all mapped to the primary monitor. For example, with the following
setting workspace other than 1 and 3 would always be displayed on the
primary monitor where workspace 1 and 3 would be displayed on their
corresponding monitors whenever the monitors are active.
\\='(1 \"HDMI-1\" 3 \"DP-1\")"
:type '(plist :key-type integer :value-type string)) :type '(plist :key-type integer :value-type string))
(with-no-warnings
(define-obsolete-variable-alias 'exwm-randr-workspace-output-plist
'exwm-randr-workspace-monitor-plist "27.1"))
(defvar exwm-workspace--fullscreen-frame-count) (defvar exwm-workspace--fullscreen-frame-count)
(defvar exwm-workspace--list) (defvar exwm-workspace--list)
(declare-function exwm-workspace--count "exwm-workspace.el") (declare-function exwm-workspace--count "exwm-workspace.el")
@ -89,57 +101,54 @@ the first one in result being the primary output."
(declare-function exwm-workspace--show-minibuffer "exwm-workspace.el" ()) (declare-function exwm-workspace--show-minibuffer "exwm-workspace.el" ())
(declare-function exwm-workspace--update-workareas "exwm-workspace.el" ()) (declare-function exwm-workspace--update-workareas "exwm-workspace.el" ())
(defun exwm-randr--get-monitors ()
"Get RandR monitors."
(let (monitor-name geometry monitor-plist primary-monitor)
(with-slots (monitors)
(xcb:+request-unchecked+reply exwm--connection
(make-instance 'xcb:randr:GetMonitors
:window exwm--root
:get-active 1))
(dolist (monitor monitors)
(with-slots (name primary x y width height) monitor
(setq monitor-name (x-get-atom-name name)
geometry (make-instance 'xcb:RECTANGLE
:x x
:y y
:width width
:height height)
monitor-plist (plist-put monitor-plist monitor-name geometry))
;; Save primary monitor when available.
(when (/= 0 primary)
(setq primary-monitor monitor-name)))))
(exwm--log "Primary monitor: %s" primary-monitor)
(exwm--log "Monitors: %s" monitor-plist)
(list primary-monitor monitor-plist)))
(defun exwm-randr--refresh () (defun exwm-randr--refresh ()
"Refresh workspaces according to the updated RandR info." "Refresh workspaces according to the updated RandR info."
(let (output-name geometry output-plist primary-output default-geometry (let* ((result (exwm-randr--get-monitors))
container-output-alist container-frame-alist) (primary-monitor (elt result 0))
;; Query all outputs (monitor-plist (elt result 1))
(with-slots (config-timestamp outputs) container-monitor-alist container-frame-alist)
(xcb:+request-unchecked+reply exwm--connection (when (and primary-monitor monitor-plist)
(make-instance 'xcb:randr:GetScreenResourcesCurrent
:window exwm--root))
(dolist (output outputs)
(with-slots (crtc connection name)
(xcb:+request-unchecked+reply exwm--connection
(make-instance 'xcb:randr:GetOutputInfo
:output output
:config-timestamp config-timestamp))
(setf output-name ;UTF-8 encoded
(decode-coding-string (apply #'unibyte-string name) 'utf-8))
(if (or (/= connection xcb:randr:Connection:Connected)
(= 0 crtc)) ;FIXME
(plist-put output-plist output-name nil)
(with-slots (x y width height)
(xcb:+request-unchecked+reply exwm--connection
(make-instance 'xcb:randr:GetCrtcInfo
:crtc crtc
:config-timestamp config-timestamp))
(setq geometry (make-instance 'xcb:RECTANGLE
:x x :y y
:width width :height height)
output-plist (plist-put output-plist output-name geometry))
(unless primary-output
(setq primary-output output-name
default-geometry geometry)))))))
(exwm--log "(randr) outputs: %s" output-plist)
(when output-plist
(when exwm-workspace--fullscreen-frame-count (when exwm-workspace--fullscreen-frame-count
;; Not all workspaces are fullscreen; reset this counter. ;; Not all workspaces are fullscreen; reset this counter.
(setq exwm-workspace--fullscreen-frame-count 0)) (setq exwm-workspace--fullscreen-frame-count 0))
(dotimes (i (exwm-workspace--count)) (dotimes (i (exwm-workspace--count))
(let* ((output (plist-get exwm-randr-workspace-output-plist i)) (let* ((monitor (plist-get exwm-randr-workspace-monitor-plist i))
(geometry (lax-plist-get output-plist output)) (geometry (lax-plist-get monitor-plist monitor))
(frame (elt exwm-workspace--list i)) (frame (elt exwm-workspace--list i))
(container (frame-parameter frame 'exwm-container))) (container (frame-parameter frame 'exwm-container)))
(unless geometry (unless geometry
(setq geometry default-geometry (setq monitor primary-monitor
output primary-output)) geometry (lax-plist-get monitor-plist primary-monitor)))
(setq container-output-alist (nconc (setq container-monitor-alist (nconc
`((,container . ,(intern output))) `((,container . ,(intern monitor)))
container-output-alist) container-monitor-alist)
container-frame-alist (nconc `((,container . ,frame)) container-frame-alist (nconc `((,container . ,frame))
container-frame-alist)) container-frame-alist))
(set-frame-parameter frame 'exwm-randr-output output) (set-frame-parameter frame 'exwm-randr-monitor monitor)
(set-frame-parameter frame 'exwm-geometry geometry))) (set-frame-parameter frame 'exwm-geometry geometry)))
;; Update workareas. ;; Update workareas.
(exwm-workspace--update-workareas) (exwm-workspace--update-workareas)
@ -156,23 +165,24 @@ the first one in result being the primary output."
;; Update active/inactive workspaces. ;; Update active/inactive workspaces.
(dolist (w exwm-workspace--list) (dolist (w exwm-workspace--list)
(exwm-workspace--set-active w nil)) (exwm-workspace--set-active w nil))
;; Mark the workspace on the top of each monitor as active.
(dolist (xwin (dolist (xwin
(reverse (reverse
(slot-value (xcb:+request-unchecked+reply exwm--connection (slot-value (xcb:+request-unchecked+reply exwm--connection
(make-instance 'xcb:QueryTree (make-instance 'xcb:QueryTree
:window exwm--root)) :window exwm--root))
'children))) 'children)))
(let ((output (cdr (assq xwin container-output-alist)))) (let ((monitor (cdr (assq xwin container-monitor-alist))))
(when output (when monitor
(setq container-output-alist (setq container-monitor-alist
(rassq-delete-all output container-output-alist)) (rassq-delete-all monitor container-monitor-alist))
(exwm-workspace--set-active (cdr (assq xwin container-frame-alist)) (exwm-workspace--set-active (cdr (assq xwin container-frame-alist))
t)))) t))))
(xcb:flush exwm--connection) (xcb:flush exwm--connection)
(run-hooks 'exwm-randr-refresh-hook)))) (run-hooks 'exwm-randr-refresh-hook))))
(defun exwm-randr--on-ScreenChangeNotify (_data _synthetic) (defun exwm-randr--on-ScreenChangeNotify (_data _synthetic)
(exwm--log "(RandR) ScreenChangeNotify") (exwm--log)
(run-hooks 'exwm-randr-screen-change-hook) (run-hooks 'exwm-randr-screen-change-hook)
(exwm-randr--refresh)) (exwm-randr--refresh))
@ -184,8 +194,8 @@ the first one in result being the primary output."
(with-slots (major-version minor-version) (with-slots (major-version minor-version)
(xcb:+request-unchecked+reply exwm--connection (xcb:+request-unchecked+reply exwm--connection
(make-instance 'xcb:randr:QueryVersion (make-instance 'xcb:randr:QueryVersion
:major-version 1 :minor-version 3)) :major-version 1 :minor-version 5))
(if (or (/= major-version 1) (< minor-version 3)) (if (or (/= major-version 1) (< minor-version 5))
(error "[EXWM] The server only support RandR version up to %d.%d" (error "[EXWM] The server only support RandR version up to %d.%d"
major-version minor-version) major-version minor-version)
;; External monitor(s) may already be connected. ;; External monitor(s) may already be connected.
@ -193,26 +203,15 @@ the first one in result being the primary output."
(exwm-randr--refresh) (exwm-randr--refresh)
(xcb:+event exwm--connection 'xcb:randr:ScreenChangeNotify (xcb:+event exwm--connection 'xcb:randr:ScreenChangeNotify
#'exwm-randr--on-ScreenChangeNotify) #'exwm-randr--on-ScreenChangeNotify)
;; (xcb:+event exwm--connection 'xcb:randr:Notify
;; (lambda (_data _synthetic)
;; (exwm--log "(RandR) Notify")
;; (exwm-randr--refresh)))
(xcb:+request exwm--connection (xcb:+request exwm--connection
(make-instance 'xcb:randr:SelectInput (make-instance 'xcb:randr:SelectInput
:window exwm--root :window exwm--root
:enable xcb:randr:NotifyMask:ScreenChange :enable xcb:randr:NotifyMask:ScreenChange))
;; :enable (eval-when-compile
;; (logior
;; xcb:randr:NotifyMask:ScreenChange
;; xcb:randr:NotifyMask:OutputChange
;; xcb:randr:NotifyMask:OutputProperty
;; xcb:randr:NotifyMask:CrtcChange))
))
(xcb:flush exwm--connection) (xcb:flush exwm--connection)
(add-hook 'exwm-workspace-list-change-hook #'exwm-randr--refresh)))) (add-hook 'exwm-workspace-list-change-hook #'exwm-randr--refresh))))
;; Prevent frame parameters introduced by this module from being ;; Prevent frame parameters introduced by this module from being
;; saved/restored. ;; saved/restored.
(dolist (i '(exwm-randr-output)) (dolist (i '(exwm-randr-monitor))
(unless (assq i frameset-filter-alist) (unless (assq i frameset-filter-alist)
(push (cons i :never) frameset-filter-alist)))) (push (cons i :never) frameset-filter-alist))))

View file

@ -374,7 +374,7 @@ NIL if FRAME is not a workspace"
(run-hooks 'exwm-workspace--update-workareas-hook)) (run-hooks 'exwm-workspace--update-workareas-hook))
(defun exwm-workspace--set-active (frame active) (defun exwm-workspace--set-active (frame active)
"Make frame FRAME active on its output." "Make frame FRAME active on its monitor."
(exwm--log "active=%s; frame=%s" frame active) (exwm--log "active=%s; frame=%s" frame active)
(set-frame-parameter frame 'exwm-active active) (set-frame-parameter frame 'exwm-active active)
(if active (if active
@ -543,15 +543,15 @@ for internal use only."
(set-frame-parameter (buffer-local-value 'exwm--frame (window-buffer)) (set-frame-parameter (buffer-local-value 'exwm--frame (window-buffer))
'exwm-selected-window (selected-window))) 'exwm-selected-window (selected-window)))
;; Show/Hide X windows. ;; Show/Hide X windows.
(let ((output-old (frame-parameter old-frame 'exwm-randr-output)) (let ((monitor-old (frame-parameter old-frame 'exwm-randr-monitor))
(output-new (frame-parameter frame 'exwm-randr-output)) (monitor-new (frame-parameter frame 'exwm-randr-monitor))
(active-old (exwm-workspace--active-p old-frame)) (active-old (exwm-workspace--active-p old-frame))
(active-new (exwm-workspace--active-p frame)) (active-new (exwm-workspace--active-p frame))
workspaces-to-hide) workspaces-to-hide)
(cond (cond
((not active-old) ((not active-old)
(exwm-workspace--set-active frame t)) (exwm-workspace--set-active frame t))
((equal output-old output-new) ((equal monitor-old monitor-new)
(exwm-workspace--set-active frame t) (exwm-workspace--set-active frame t)
(unless (eq frame old-frame) (unless (eq frame old-frame)
(exwm-workspace--set-active old-frame nil) (exwm-workspace--set-active old-frame nil)
@ -560,8 +560,8 @@ for internal use only."
(t (t
(dolist (w exwm-workspace--list) (dolist (w exwm-workspace--list)
(when (and (exwm-workspace--active-p w) (when (and (exwm-workspace--active-p w)
(equal output-new (equal monitor-new
(frame-parameter w 'exwm-randr-output))) (frame-parameter w 'exwm-randr-monitor)))
(exwm-workspace--set-active w nil) (exwm-workspace--set-active w nil)
(setq workspaces-to-hide (append workspaces-to-hide (list w))))) (setq workspaces-to-hide (append workspaces-to-hide (list w)))))
(exwm-workspace--set-active frame t))) (exwm-workspace--set-active frame t)))
@ -818,8 +818,8 @@ INDEX must not exceed the current number of workspaces."
;; Floating. ;; Floating.
(setq container (frame-parameter exwm--floating-frame (setq container (frame-parameter exwm--floating-frame
'exwm-container)) 'exwm-container))
(unless (equal (frame-parameter old-frame 'exwm-randr-output) (unless (equal (frame-parameter old-frame 'exwm-randr-monitor)
(frame-parameter frame 'exwm-randr-output)) (frame-parameter frame 'exwm-randr-monitor))
(with-slots (x y) (with-slots (x y)
(xcb:+request-unchecked+reply exwm--connection (xcb:+request-unchecked+reply exwm--connection
(make-instance 'xcb:GetGeometry (make-instance 'xcb:GetGeometry
@ -1240,7 +1240,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
;; prevent potential problems. The values do not matter here as ;; prevent potential problems. The values do not matter here as
;; they'll be updated by the RandR module later. ;; they'll be updated by the RandR module later.
(let ((w (car exwm-workspace--list))) (let ((w (car exwm-workspace--list)))
(dolist (param '(exwm-randr-output (dolist (param '(exwm-randr-monitor
exwm-geometry)) exwm-geometry))
(set-frame-parameter frame param (frame-parameter w param)))) (set-frame-parameter frame param (frame-parameter w param))))
(xcb:+request exwm--connection (xcb:+request exwm--connection