Make X windows container-less

; This is an attempt to make (managed) X windows container-less, i.e. direct children of the root window.  This is mainly to make EXWM compatible with third-party compositors.  Other issues like wrong absolute position should also get resolved by the way.  The workspace containers ("virtual roots") are also removed.  However Emacs frames are still wrapped in containers to avoid unexpected stack reordering.

* exwm-cm.el: Make this module obsolete as EXWM supports third-party compositors now.

* exwm-core.el (exwm--container):
* exwm-floating.el (exwm-floating--set-floating)
(exwm-floating--unset-floating, exwm-floating-hide)
(exwm-floating--start-moveresize, exwm-floating--stop-moveresize)
(exwm-floating--do-moveresize, exwm-floating-move):
* exwm-input.el (exwm-input--update-focus):
* exwm-layout.el (exwm-layout--show, exwm-layout--hide)
(exwm-layout-set-fullscreen, exwm-layout-unset-fullscreen):
* exwm-manage.el (exwm-manage--manage-window, exwm-manage--unmanage-window)
(exwm-manage--kill-buffer-query-function, exwm-manage--kill-client):
* exwm-workspace.el (exwm-workspace--set-fullscreen, exwm-workspace-switch)
(exwm-workspace-move-window, exwm-workspace--add-frame-as-workspace)
(exwm-workspace--remove-frame-as-workspace): Make adaptions for container-less X windows.

* exwm-workspace.el (exwm-workspace--update-ewmh-props):
* exwm.el (exwm--init-icccm-ewmh, exwm--exit-icccm-ewmh): No longer use virtual roots.

* exwm-input.el (exwm-input--on-workspace-list-change)
(exwm-input--update-global-prefix-keys, exwm-input--init, exwm-input--exit): From now on global key bindings are grabbed on the root window so it's no long required to re-grab them each time the workspace list changes.  As a result `exwm-input--on-workspace-list-change' and its corresponding references are discarded.  It remains to be seen if this change will raise input focus issues.

* exwm-manage.el (exwm-manage--manage-window): Explicitly set the workspace for newly managed X windows.
* exwm-floating.el (exwm-floating--set-floating): Avoid implicit reference to the current workspace.

* exwm-core.el (exwm--set-geometry): New function for setting the geometry of an X window.
* exwm-layout.el (exwm-layout--resize-container): Replaced by `exwm-layout--resize-container'.

* exwm-core.el (exwm--guide-window): New global variable recording the guide X window.
* exwm.el (exwm--init-icccm-ewmh): Set it.

* exwm-input.el (exwm-input--post-init): New function containing staffs for initialization but should better get called after the event loop starts.
* exwm.el (exwm-init): Use it.
This commit is contained in:
Chris Feng 2018-02-18 01:04:04 +08:00
parent 83c0a2db34
commit 7823eb988c
10 changed files with 362 additions and 2444 deletions

View file

@ -8,7 +8,6 @@ It features:
+ Dynamic workspace support
+ ICCCM/EWMH compliance
+ (Optional) RandR (multi-monitor) support
+ (Optional) Built-in compositing manager
+ (Optional) Built-in system tray
Please check out the

1756
exwm-cm.el

File diff suppressed because it is too large Load diff

View file

@ -46,6 +46,8 @@
(defvar exwm--connection nil "X connection.")
(defvar exwm--root nil "Root window.")
(defvar exwm--id-buffer-alist nil "Alist of (<X window ID> . <Emacs buffer>).")
(defvar exwm--guide-window nil
"An X window separating workspaces and X windows.")
(defsubst exwm--id->buffer (id)
"X window ID => Emacs buffer."
@ -75,6 +77,20 @@
xcb:EventMask:StructureNotify))))
(xcb:flush exwm--connection))
(defun exwm--set-geometry (xwin x y width height)
"Set the geometry of X window XWIN to WIDTHxHEIGHT+X+Y.
Nil can be passed as placeholder."
(exwm--log "Setting #x%x to %sx%s+%s+%s" xwin width height x y)
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window xwin
:value-mask (logior (if x xcb:ConfigWindow:X 0)
(if y xcb:ConfigWindow:Y 0)
(if width xcb:ConfigWindow:Width 0)
(if height xcb:ConfigWindow:Height 0))
:x x :y y :width width :height height)))
(defmacro exwm--defer (secs function &rest args)
"Defer the execution of FUNCTION.
@ -103,11 +119,10 @@ least SECS seconds later."
;; Internal variables
(defvar-local exwm--id nil) ;window ID
(defvar-local exwm--container nil) ;container
(defvar-local exwm--frame nil) ;workspace frame
(defvar-local exwm--floating-frame nil) ;floating frame
(defvar-local exwm--mode-line-format nil) ;save mode-line-format
(defvar-local exwm--floating-frame-position nil) ;used in fullscreen
(defvar-local exwm--floating-frame-position nil) ;set when hidden.
(defvar-local exwm--fixed-size nil) ;fixed size
(defvar-local exwm--keyboard-grabbed nil) ;Keyboard grabbed.
(defvar-local exwm--on-KeyPress ;KeyPress event handler
@ -271,6 +286,7 @@ least SECS seconds later."
(push `(executing-kbd-macro . ,exwm--kmacro-map)
minor-mode-overriding-map-alist)
(setq buffer-read-only t
cursor-type nil
left-margin-width nil
right-margin-width nil
left-fringe-width 0

View file

@ -75,12 +75,11 @@ context of the corresponding buffer.")
xcb:Atom:_NET_WM_ACTION_CLOSE)))))
(defvar exwm-workspace--current)
(defvar exwm-workspace--struts)
(defvar exwm-workspace--workareas)
(defvar exwm-workspace-current-index)
(declare-function exwm-layout--refresh "exwm-layout.el" ())
(declare-function exwm-layout--show "exwm-layout.el" (id &optional window))
(declare-function exwm-layout--hide "exwm-layout.el" (id))
(declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id))
(declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el")
(declare-function exwm-workspace--position "exwm-workspace.el" (frame))
@ -91,7 +90,8 @@ context of the corresponding buffer.")
(when window
;; Hide the non-floating X window first.
(set-window-buffer window (other-buffer nil t))))
(let* ((original-frame exwm-workspace--current)
(let* ((original-frame (buffer-local-value 'exwm--frame
(exwm--id->buffer id)))
;; Create new frame
(frame (with-current-buffer
(or (get-buffer "*scratch*")
@ -100,16 +100,14 @@ context of the corresponding buffer.")
(get-buffer-create "*scratch*"))
(get-buffer "*scratch*")))
(make-frame
`((minibuffer . nil) ;use the default minibuffer.
(left . 10000)
(top . 10000)
`((minibuffer . ,(minibuffer-window exwm--frame))
(left . ,(* window-min-width -100))
(top . ,(* window-min-height -100))
(width . ,window-min-width)
(height . ,window-min-height)
(unsplittable . t))))) ;and fix the size later
(outer-id (string-to-number (frame-parameter frame 'outer-window-id)))
(window-id (string-to-number (frame-parameter frame 'window-id)))
(container (buffer-local-value 'exwm--container
(exwm--id->buffer id)))
(frame-container (xcb:generate-id exwm--connection))
(window (frame-first-window frame)) ;and it's the only window
(x (slot-value exwm--geometry 'x))
@ -176,6 +174,8 @@ context of the corresponding buffer.")
;; Put at the center of screen
(setq x (/ (- display-width width) 2)
y (/ (- display-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)
;; Fit frame to client
;; It seems we have to make the frame invisible in order to resize it
@ -194,61 +194,55 @@ context of the corresponding buffer.")
exwm--mode-line-format mode-line-format
mode-line-format nil))
(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.
;; Create the frame container as the parent of the frame.
(xcb:+request exwm--connection
(make-instance 'xcb:CreateWindow
:depth 0
:wid frame-container
:parent container
:x 0
:y 0
:parent exwm--root
:x (- x (elt edges 0))
:y (- y (elt edges 1))
:width width
:height height
:border-width 0
:border-width exwm-floating-border-width
:class xcb:WindowClass:InputOutput
:visual 0
:value-mask (logior xcb:CW:BackPixmap
xcb:CW:OverrideRedirect)
(if exwm-floating--border-pixel
xcb:CW:BorderPixel 0)
xcb:CW:OverrideRedirect
(if exwm-floating--border-colormap
xcb:CW:Colormap 0))
:background-pixmap xcb:BackPixmap:ParentRelative
: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))
:border-pixel exwm-floating--border-pixel
:override-redirect 1
:colormap exwm-floating--border-colormap))
(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)))))
(format "floating frame container for 0x%x" id))))
;; Map it.
(xcb:+request exwm--connection
(make-instance 'xcb:MapWindow :window frame-container))
;; Put the X window right above this frame container.
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window id
:value-mask (logior xcb:ConfigWindow:Sibling
xcb:ConfigWindow:StackMode)
:sibling frame-container
:stack-mode xcb:StackMode:Above)))
;; Reparent this frame to its container.
(xcb:+request exwm--connection
(make-instance 'xcb:ReparentWindow
:window outer-id :parent frame-container :x 0 :y 0))
;; Place the X window container.
;; Also show the floating border.
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window container
:value-mask (eval-when-compile
(logior xcb:ConfigWindow:X
xcb:ConfigWindow:Y
xcb:ConfigWindow:BorderWidth))
:x x
:y y
:border-width exwm-floating-border-width))
(exwm-floating--set-allowed-actions id nil)
(xcb:flush exwm--connection)
;; Set window/buffer
(with-current-buffer (exwm--id->buffer id)
(setq window-size-fixed exwm--fixed-size
exwm--frame original-frame
exwm--floating-frame frame)
;; Do the refresh manually.
(remove-hook 'window-configuration-change-hook #'exwm-layout--refresh)
@ -256,24 +250,19 @@ context of the corresponding buffer.")
(add-hook 'window-configuration-change-hook #'exwm-layout--refresh)
(set-window-dedicated-p window t)
(exwm-layout--show id window))
(if (exwm-layout--iconic-state-p id)
;; Hide iconic floating X windows.
(with-current-buffer (exwm--id->buffer id)
(exwm-floating-hide))
(with-selected-frame exwm-workspace--current
(exwm-layout--refresh))
(with-current-buffer (exwm--id->buffer id)
(if (exwm-layout--iconic-state-p id)
;; Hide iconic floating X windows.
(exwm-floating-hide)
(with-selected-frame exwm--frame
(exwm-layout--refresh)))
(select-frame-set-input-focus frame))
;; FIXME: Strangely, the Emacs frame can move itself at this point
;; when there are left/top struts set. Force resetting its
;; position seems working, but it'd better to figure out why.
;; FIXME: This also happens in another case (#220) where the cause is
;; still unclear.
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window outer-id
:value-mask (logior xcb:ConfigWindow:X
xcb:ConfigWindow:Y)
:x 0 :y 0))
(exwm--set-geometry outer-id 0 0 nil nil)
(xcb:flush exwm--connection))
(with-current-buffer (exwm--id->buffer id)
(run-hooks 'exwm-floating-setup-hook))
@ -286,10 +275,6 @@ context of the corresponding buffer.")
(with-current-buffer buffer
(when exwm--floating-frame
;; The X window is already mapped.
;; Unmap the container to prevent flickering.
(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:ChangeWindowAttributes
@ -315,29 +300,30 @@ context of the corresponding buffer.")
;; 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
;; Place the X window just above the reference X window.
;; (the stacking order won't change from now on).
;; Also hide the possible floating border.
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window exwm--container
:window id
:value-mask (logior xcb:ConfigWindow:BorderWidth
xcb:ConfigWindow:Sibling
xcb:ConfigWindow:StackMode)
:border-width 0
:sibling (frame-parameter exwm-workspace--current
'exwm-container)
:sibling exwm--guide-window
:stack-mode xcb:StackMode:Above)))
(exwm-floating--set-allowed-actions id t)
(xcb:flush exwm--connection)
(with-current-buffer buffer
(when exwm--floating-frame ;from floating to non-floating
(set-window-dedicated-p (frame-first-window exwm--floating-frame) nil)
(delete-frame exwm--floating-frame))) ;remove the floating frame
;; Select a tiling window and delete the old frame.
(select-window (frame-selected-window exwm-workspace--current))
(with-current-buffer buffer
(delete-frame exwm--floating-frame))))
(with-current-buffer buffer
(setq window-size-fixed nil
exwm--floating-frame nil
exwm--frame exwm-workspace--current))
exwm--floating-frame nil))
;; Only show X windows in normal state.
(unless (exwm-layout--iconic-state-p)
(pop-to-buffer-same-window buffer)))
@ -361,14 +347,7 @@ context of the corresponding buffer.")
(interactive)
(when (and (eq major-mode 'exwm-mode)
exwm--floating-frame)
;; Put this floating X window at bottom.
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window exwm--container
:value-mask xcb:ConfigWindow:StackMode
:stack-mode xcb:StackMode:Below))
(exwm-layout--set-state exwm--id xcb:icccm:WM_STATE:IconicState)
(xcb:flush exwm--connection)
(exwm-layout--hide exwm--id)
(select-frame-set-input-focus exwm-workspace--current)))
(define-obsolete-function-alias 'exwm-floating-hide-mode-line
@ -387,7 +366,8 @@ context of the corresponding buffer.")
;; Managed.
(with-current-buffer buffer-or-id
(setq frame exwm--floating-frame
container-or-id exwm--container))
container-or-id (frame-parameter exwm--floating-frame
'exwm-container)))
;; Unmanaged.
(setq container-or-id id))
(when (and container-or-id
@ -545,96 +525,58 @@ context of the corresponding buffer.")
"Stop move/resize."
(xcb:+request exwm--connection
(make-instance 'xcb:UngrabPointer :time xcb:Time:CurrentTime))
;; Inform the X window that its absolute position is changed
(when (and exwm-floating--moveresize-calculate
;; Unmanaged.
(eq major-mode 'exwm-mode))
(let ((edges (window-inside-absolute-pixel-edges (frame-selected-window)))
x y width height id)
(setq x (pop edges)
y (pop edges)
width (- (pop edges) x)
height (- (pop edges) y))
(with-current-buffer (window-buffer (frame-selected-window))
(setq id exwm--id)
(with-slots ((x* x)
(y* y)
(width* width)
(height* height))
exwm--geometry
(setf x* x
y* y
width* width
height* height)))
(xcb:+request exwm--connection
(make-instance 'xcb:SendEvent
:propagate 0
:destination id
:event-mask xcb:EventMask:StructureNotify
:event (xcb:marshal
(make-instance 'xcb:ConfigureNotify
:event id :window id
:above-sibling xcb:Window:None
:x x
:y y
:width width
:height height
:border-width 0
:override-redirect 0)
exwm--connection)))))
(xcb:flush exwm--connection)
(setq exwm-floating--moveresize-calculate nil))
(when exwm-floating--moveresize-calculate
(let (result buffer-or-id)
(setq result (funcall exwm-floating--moveresize-calculate 0 0)
buffer-or-id (aref result 0))
(when (bufferp buffer-or-id)
(with-current-buffer buffer-or-id
(exwm-layout--show exwm--id
(frame-root-window exwm--floating-frame)))))
(setq exwm-floating--moveresize-calculate nil)))
(defun exwm-floating--do-moveresize (data _synthetic)
"Perform move/resize."
(when exwm-floating--moveresize-calculate
(let* ((obj (make-instance 'xcb:MotionNotify))
(workarea (elt exwm-workspace--workareas
exwm-workspace-current-index))
(frame-x (aref workarea 0))
(frame-y (aref workarea 1))
result value-mask width height buffer-or-id container-or-id)
result value-mask x y width height buffer-or-id container-or-id)
(xcb:unmarshal obj data)
(setq result (funcall exwm-floating--moveresize-calculate
(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)))
buffer-or-id (aref result 0)
value-mask (aref result 1)
x (aref result 2)
y (aref result 3)
width (max 1 (aref result 4))
height (max 1 (aref result 5)))
(setq buffer-or-id (aref result 0))
(setq container-or-id
(if (bufferp buffer-or-id)
;; Managed.
(buffer-local-value 'exwm--container buffer-or-id)
(with-current-buffer buffer-or-id
(frame-parameter exwm--floating-frame 'exwm-container))
;; Unmanaged.
buffer-or-id))
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window container-or-id
:value-mask (aref result 1)
:x (- (aref result 2) frame-x)
:y (- (aref result 3) frame-y)
:x x
:y y
:width width
:height height))
(when (bufferp buffer-or-id)
;; Managed.
(with-current-buffer buffer-or-id
(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 value-mask
:width width
:height height))))
(setq value-mask (logand value-mask (logior xcb:ConfigWindow:Width
xcb:ConfigWindow:Height)))
(when (/= 0 value-mask)
(with-current-buffer buffer-or-id
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window (frame-parameter exwm--floating-frame
'exwm-outer-id)
:value-mask value-mask
:width width
:height height)))))
(xcb:flush exwm--connection))))
(defun exwm-floating-move (&optional delta-x delta-y)
@ -646,37 +588,19 @@ Both DELTA-X and DELTA-Y default to 1. This command should be bound locally."
(unless delta-x (setq delta-x 1))
(unless delta-y (setq delta-y 1))
(unless (and (= 0 delta-x) (= 0 delta-y))
(let* ((geometry (xcb:+request-unchecked+reply exwm--connection
(let* ((floating-container (frame-parameter exwm--floating-frame
'exwm-container))
(geometry (xcb:+request-unchecked+reply exwm--connection
(make-instance 'xcb:GetGeometry
:drawable exwm--container)))
:drawable floating-container)))
(edges (window-inside-absolute-pixel-edges)))
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window exwm--container
:value-mask (eval-when-compile
(logior xcb:ConfigWindow:X
xcb:ConfigWindow:Y))
:x (+ (slot-value geometry 'x) delta-x)
:y (+ (slot-value geometry 'y) delta-y)))
;; Inform the X window that its absolute position is changed
(xcb:+request exwm--connection
(make-instance 'xcb:SendEvent
:propagate 0 :destination exwm--id
:event-mask xcb:EventMask:StructureNotify
:event (xcb:marshal
(make-instance 'xcb:ConfigureNotify
:event exwm--id
:window exwm--id
:above-sibling xcb:Window:None
:x (+ (elt edges 0) delta-x)
:y (+ (elt edges 1) delta-y)
:width (- (elt edges 2)
(elt edges 0))
:height (- (elt edges 3)
(elt edges 1))
:border-width 0
:override-redirect 0)
exwm--connection))))
(with-slots (x y) geometry
(exwm--set-geometry floating-container
(+ x delta-x) (+ y delta-y) nil nil))
(exwm--set-geometry exwm--id
(+ (pop edges) delta-x)
(+ (pop edges) delta-y)
nil nil))
(xcb:flush exwm--connection)))
(defun exwm-floating--init ()

View file

@ -63,6 +63,8 @@
(defvar exwm-input--simulation-prefix-keys nil
"List of prefix keys of simulation keys in line-mode.")
(declare-function exwm-layout--show "exwm-layout.el" (id &optional window))
(defun exwm-input--set-focus (id)
"Set input focus to window ID in a proper way."
(when (exwm--id->buffer id)
@ -183,20 +185,6 @@ ARGS are additional arguments to CALLBACK."
(let ((exwm-input--global-prefix-keys nil))
(exwm-input--update-global-prefix-keys)))
(defun exwm-input--on-workspace-list-change ()
"Run in `exwm-input--update-global-prefix-keys'."
(dolist (f exwm-workspace--list)
;; Reuse the 'exwm-grabbed' frame parameter set in
;; `exwm-input--update-global-prefix-keys'.
(unless (frame-parameter f 'exwm-grabbed)
(xcb:+request exwm--connection
(make-instance 'xcb:ChangeWindowAttributes
:window (frame-parameter f 'exwm-workspace)
:value-mask xcb:CW:EventMask
:event-mask xcb:EventMask:FocusChange))))
(exwm-input--update-global-prefix-keys)
(xcb:flush exwm--connection))
(declare-function exwm-workspace--client-p "exwm-workspace.el"
(&optional frame))
@ -253,7 +241,6 @@ This value should always be overwritten.")
exwm-input--update-focus-window))))
(declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id))
(declare-function exwm-layout--set-state "exwm-layout.el" (id state))
(declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el")
(declare-function exwm-workspace-switch "exwm-workspace.el"
(frame-or-index &optional force))
@ -276,19 +263,27 @@ This value should always be overwritten.")
(set-frame-parameter exwm--frame 'exwm-selected-window window)
(exwm--defer 0 #'exwm-workspace-switch exwm--frame))
(exwm--log "Set focus on #x%x" exwm--id)
(exwm-input--set-focus exwm--id)
(when exwm--floating-frame
;; Adjust stacking orders of the floating container.
;; Adjust stacking orders of the floating X window.
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window exwm--container
:window exwm--id
:value-mask xcb:ConfigWindow:StackMode
:stack-mode xcb:StackMode:Above))
:stack-mode xcb:StackMode:TopIf))
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window (frame-parameter exwm--floating-frame
'exwm-container)
:value-mask (logior
xcb:ConfigWindow:Sibling
xcb:ConfigWindow:StackMode)
:sibling exwm--id
:stack-mode xcb:StackMode:Below))
;; This floating X window might be hide by `exwm-floating-hide'.
(when (exwm-layout--iconic-state-p)
(exwm-layout--set-state exwm--id
xcb:icccm:WM_STATE:NormalState))
(xcb:flush exwm--connection)))
(exwm-layout--show exwm--id window))
(xcb:flush exwm--connection))
(exwm-input--set-focus exwm--id))
(when (eq (selected-window) window)
(exwm--log "Focus on %s" window)
(if (and (exwm-workspace--workspace-p (selected-frame))
@ -389,51 +384,38 @@ This value should always be overwritten.")
"Update `exwm-input--global-prefix-keys'."
(when exwm--connection
(let ((original exwm-input--global-prefix-keys)
keysym keycode ungrab-key grab-key workspace)
keysym keycode grab-key)
(setq exwm-input--global-prefix-keys nil)
(dolist (i exwm-input--global-keys)
(cl-pushnew (elt i 0) exwm-input--global-prefix-keys))
;; Stop here if the global prefix keys are update-to-date and
;; there's no new workspace.
(unless (and (equal original exwm-input--global-prefix-keys)
(cl-every (lambda (w) (frame-parameter w 'exwm-grabbed))
exwm-workspace--list))
(setq ungrab-key (make-instance 'xcb:UngrabKey
:key xcb:Grab:Any :grab-window nil
:modifiers xcb:ModMask:Any)
grab-key (make-instance 'xcb:GrabKey
(unless (equal original exwm-input--global-prefix-keys)
(setq grab-key (make-instance 'xcb:GrabKey
:owner-events 0
:grab-window nil
:grab-window exwm--root
:modifiers nil
:key nil
:pointer-mode xcb:GrabMode:Async
:keyboard-mode xcb:GrabMode:Async))
(dolist (w exwm-workspace--list)
(setq workspace (frame-parameter w 'exwm-workspace))
(setf (slot-value ungrab-key 'grab-window) workspace)
(if (xcb:+request-checked+request-check exwm--connection ungrab-key)
(exwm--log "Failed to ungrab keys")
;; Label this frame.
(set-frame-parameter w 'exwm-grabbed t)
(dolist (k exwm-input--global-prefix-keys)
(setq keysym (xcb:keysyms:event->keysym exwm--connection k)
keycode (xcb:keysyms:keysym->keycode exwm--connection
(car keysym)))
(setf (slot-value grab-key 'grab-window) workspace
(slot-value grab-key 'modifiers) (cdr keysym)
(slot-value grab-key 'key) keycode)
(when (or (= 0 keycode)
(xcb:+request-checked+request-check exwm--connection
grab-key)
;; Also grab this key with num-lock mask set.
(when (/= 0 xcb:keysyms:num-lock-mask)
(setf (slot-value grab-key 'modifiers)
(logior (cdr keysym)
xcb:keysyms:num-lock-mask))
(xcb:+request-checked+request-check exwm--connection
grab-key)))
(user-error "[EXWM] Failed to grab key: %s"
(single-key-description k))))))))))
(dolist (k exwm-input--global-prefix-keys)
(setq keysym (xcb:keysyms:event->keysym exwm--connection k)
keycode (xcb:keysyms:keysym->keycode exwm--connection
(car keysym)))
(setf (slot-value grab-key 'modifiers) (cdr keysym)
(slot-value grab-key 'key) keycode)
(when (or (= 0 keycode)
(xcb:+request-checked+request-check exwm--connection
grab-key)
;; Also grab this key with num-lock mask set.
(when (/= 0 xcb:keysyms:num-lock-mask)
(setf (slot-value grab-key 'modifiers)
(logior (cdr keysym)
xcb:keysyms:num-lock-mask))
(xcb:+request-checked+request-check exwm--connection
grab-key)))
(user-error "[EXWM] Failed to grab key: %s"
(single-key-description k))))))))
;;;###autoload
(defun exwm-input-set-key (key command)
@ -808,23 +790,17 @@ Its usage is the same with `exwm-input-set-simulation-keys'."
(add-hook 'pre-command-hook #'exwm-input--on-pre-command)
(add-hook 'post-command-hook #'exwm-input--on-post-command)
;; Update focus when buffer list updates
(add-hook 'buffer-list-update-hook #'exwm-input--on-buffer-list-update)
;; Re-grab global keys.
(add-hook 'exwm-workspace-list-change-hook
#'exwm-input--on-workspace-list-change)
(exwm-input--on-workspace-list-change)
;; Prevent frame parameters introduced by this module from being
;; saved/restored.
(dolist (i '(exwm-grabbed))
(push (cons i :never) frameset-filter-alist)))
(add-hook 'buffer-list-update-hook #'exwm-input--on-buffer-list-update))
(defun exwm-input--post-init ()
"The second stage in the initialization of the input module."
(exwm-input--update-global-prefix-keys))
(defun exwm-input--exit ()
"Exit the input module."
(remove-hook 'pre-command-hook #'exwm-input--on-pre-command)
(remove-hook 'post-command-hook #'exwm-input--on-post-command)
(remove-hook 'buffer-list-update-hook #'exwm-input--on-buffer-list-update)
(remove-hook 'exwm-workspace-list-change-hook
#'exwm-input--on-workspace-list-change)
(when exwm-input--update-focus-defer-timer
(cancel-timer exwm-input--update-focus-defer-timer))
(when exwm-input--update-focus-timer

View file

@ -30,27 +30,6 @@
(defvar exwm-floating-border-width)
(defvar exwm-workspace--id-struts-alist)
(defun exwm-layout--resize-container (id container x y width height
&optional container-only)
"Resize a container (and its content unless CONTAINER-ONLY is non-nil)."
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window container
:value-mask (eval-when-compile
(logior xcb:ConfigWindow:X
xcb:ConfigWindow:Y
xcb:ConfigWindow:Width
xcb:ConfigWindow:Height))
:x x :y y :width width :height height))
(unless container-only
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window id
:value-mask (eval-when-compile
(logior xcb:ConfigWindow:Width
xcb:ConfigWindow:Height))
:width width :height height))))
(defun exwm-layout--set-state (id state)
"Set WM_STATE."
(xcb:+request exwm--connection
@ -73,72 +52,24 @@
(y (pop edges))
(width (- (pop edges) x))
(height (- (pop edges) y))
(edges (window-inside-pixel-edges window))
(relative-x (pop edges))
(relative-y (pop edges))
frame-width frame-height)
frame-x frame-y frame-width frame-height)
(with-current-buffer (exwm--id->buffer id)
(if (not exwm--floating-frame)
(exwm-layout--resize-container id exwm--container
relative-x relative-y width height
;; Keep the size of the X window if
;; it's the minibuffer that resized.
(and
(active-minibuffer-window)
(< 1 (window-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.
(when exwm--floating-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 relative-x
:y relative-y
:width width
:height height)))
;; Make the resizing take effect.
(xcb:flush exwm--connection)
(when exwm--floating-frame-position
(setq frame-x (elt exwm--floating-frame-position 0)
frame-y (elt exwm--floating-frame-position 1)
;; The frame was placed at (-1, -1).
x (+ x frame-x 1)
y (+ y frame-y 1))
(setq exwm--floating-frame-position nil))
(exwm--set-geometry (frame-parameter exwm--floating-frame
'exwm-container)
frame-x frame-y frame-width frame-height))
(exwm--set-geometry id x y width height)
(xcb:+request exwm--connection (make-instance 'xcb:MapWindow :window id))
(xcb:+request exwm--connection
(make-instance 'xcb:MapWindow :window exwm--container))
(exwm-layout--set-state id xcb:icccm:WM_STATE:NormalState))
(xcb:+request exwm--connection
(make-instance 'xcb:SendEvent
:propagate 0 :destination id
:event-mask xcb:EventMask:StructureNotify
:event (xcb:marshal
(make-instance 'xcb:ConfigureNotify
:event id
:window id
:above-sibling xcb:Window:None
:x x
:y y
:width width
:height height
:border-width 0
:override-redirect 0)
exwm--connection))))
(exwm-layout--set-state id xcb:icccm:WM_STATE:NormalState)))
(xcb:flush exwm--connection))
(defun exwm-layout--hide (id)
@ -146,6 +77,15 @@
(with-current-buffer (exwm--id->buffer id)
(unless (exwm-layout--iconic-state-p) ;already hidden
(exwm--log "Hide #x%x" id)
(when exwm--floating-frame
(let* ((container (frame-parameter exwm--floating-frame
'exwm-container))
(geometry (xcb:+request-unchecked+reply exwm--connection
(make-instance 'xcb:GetGeometry
:drawable container))))
(setq exwm--floating-frame-position
(vector (slot-value geometry 'x) (slot-value geometry 'y)))
(exwm--set-geometry container -1 -1 1 1)))
(xcb:+request exwm--connection
(make-instance 'xcb:ChangeWindowAttributes
:window id :value-mask xcb:CW:EventMask
@ -156,8 +96,6 @@
(make-instance 'xcb:ChangeWindowAttributes
:window id :value-mask xcb:CW:EventMask
:event-mask exwm--client-event-mask))
(xcb:+request exwm--connection
(make-instance 'xcb:UnmapWindow :window exwm--container))
(exwm-layout--set-state id xcb:icccm:WM_STATE:IconicState)
(xcb:flush exwm--connection))))
@ -167,9 +105,7 @@
(declare-function exwm-input-release-keyboard "exwm-input.el")
(declare-function exwm-workspace--current-height "exwm-workspace.el")
(declare-function exwm-workspace--current-width "exwm-workspace.el")
(declare-function exwm-workspace--get-geometry "exwm-workspace.el" (frame))
(declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el")
(declare-function exwm-workspace--set-fullscreen "exwm-workspace.el" (frame))
(declare-function exwm-workspace-move-window "exwm-workspace.el"
(frame-or-index &optional id))
@ -180,41 +116,16 @@
(with-current-buffer (if id (exwm--id->buffer id) (window-buffer))
(when (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)
(user-error "Already in full-screen mode"))
;; Save the position of floating frame.
(when exwm--floating-frame
(let* ((geometry (xcb:+request-unchecked+reply exwm--connection
(make-instance 'xcb:GetGeometry
:drawable exwm--container))))
(setq exwm--floating-frame-position
(vector (slot-value geometry 'x) (slot-value geometry 'y)))))
;; Expand the workspace to fill the whole screen.
(with-slots (x y width height) (exwm-workspace--get-geometry exwm--frame)
(exwm-layout--resize-container nil
(frame-parameter exwm--frame
'exwm-workspace)
x y width height
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.
;; 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-layout--resize-container nil exwm--container 0 0
(exwm-workspace--current-width)
(exwm-workspace--current-height)
t)
(exwm-layout--resize-container nil exwm--id 0 0
(exwm-workspace--current-width)
(exwm-workspace--current-height)
t)
(exwm--set-geometry exwm--id 0 0
(exwm-workspace--current-width)
(exwm-workspace--current-height))
;; Raise the X window.
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window exwm--container
:window exwm--id
:value-mask (logior xcb:ConfigWindow:BorderWidth
xcb:ConfigWindow:StackMode)
:border-width 0
@ -234,39 +145,20 @@
(with-current-buffer (if id (exwm--id->buffer id) (window-buffer))
(unless (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)
(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
(make-instance 'xcb:ConfigureWindow
:window exwm--container
:value-mask (eval-when-compile
(logior xcb:ConfigWindow:X
xcb:ConfigWindow:Y
xcb:ConfigWindow:BorderWidth))
:x (elt exwm--floating-frame-position 0)
:y (elt exwm--floating-frame-position 1)
:border-width exwm-floating-border-width))
;; Put the X window just above the Emacs frame.
(exwm-layout--show exwm--id (frame-root-window exwm--floating-frame))
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window exwm--container
:window exwm--id
:value-mask (logior xcb:ConfigWindow:Sibling
xcb:ConfigWindow:StackMode)
:sibling (frame-parameter exwm-workspace--current
'exwm-container)
:stack-mode xcb:StackMode:Above)))
(exwm-layout--show exwm--id)
:sibling exwm--guide-window
:stack-mode xcb:StackMode:Above))
(let ((window (get-buffer-window nil t)))
(when window
(exwm-layout--show exwm--id window))))
(xcb:+request exwm--connection
(make-instance 'xcb:ewmh:set-_NET_WM_STATE :window exwm--id :data []))
;; Raise X windows with struts set again.
(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)))
(xcb:flush exwm--connection)
(setq exwm--ewmh-state
(delq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state))

View file

@ -92,8 +92,6 @@ corresponding buffer.")
:window exwm--root
:data (vconcat (mapcar #'car exwm--id-buffer-alist)))))
(defvar exwm-floating--border-colormap)
(defvar exwm-floating--border-pixel)
(defvar exwm-workspace--current)
(defvar exwm-workspace--switch-history-outdated)
(defvar exwm-workspace-current-index)
@ -137,7 +135,8 @@ corresponding buffer.")
(setq exwm--id-buffer-alist
(nconc exwm--id-buffer-alist `((,id . ,(current-buffer)))))
(exwm-mode)
(setq exwm--id id)
(setq exwm--id id
exwm--frame exwm-workspace--current)
(exwm--update-window-type id)
(exwm--update-class id)
(exwm--update-transient-for id)
@ -180,38 +179,13 @@ corresponding buffer.")
(xcb:+request exwm--connection
(make-instance 'xcb:MapWindow :window id))
(with-slots (x y width height) exwm--geometry
;; Reparent to virtual root
(unless (or (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DESKTOP
exwm-window-type)
(memq xcb:Atom:_NET_WM_WINDOW_TYPE_DOCK
exwm-window-type))
(let ((workspace (frame-parameter exwm-workspace--current
'exwm-workspace))
workarea)
(when (and (/= x 0)
(/= y 0))
(setq workarea (elt exwm-workspace--workareas
exwm-workspace-current-index)
x (- x (aref workarea 0))
y (- y (aref workarea 1))))
(xcb:+request exwm--connection
(make-instance 'xcb:ReparentWindow
:window id
:parent workspace
:x x :y y))))
;; Center window of type _NET_WM_WINDOW_TYPE_SPLASH
(when (memq xcb:Atom:_NET_WM_WINDOW_TYPE_SPLASH exwm-window-type)
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window id
:value-mask (eval-when-compile
(logior xcb:ConfigWindow:X
xcb:ConfigWindow:Y))
:x (/ (- (exwm-workspace--current-width) width)
2)
:y (/ (- (exwm-workspace--current-height)
height)
2)))))
(exwm--set-geometry id
(/ (- (exwm-workspace--current-width) width) 2)
(/ (- (exwm-workspace--current-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.
@ -229,41 +203,6 @@ corresponding buffer.")
(throw 'return 'ignored))
;; Manage the window
(exwm--log "Manage #x%x" id)
;; Create a new container as the parent of this X window
(setq exwm--container (xcb:generate-id exwm--connection))
(xcb:+request exwm--connection
(make-instance 'xcb:CreateWindow
:depth 0
:wid exwm--container
:parent (frame-parameter exwm-workspace--current
'exwm-workspace)
:x 0
:y 0
:width 1
:height 1
:border-width 0
:class xcb:WindowClass:InputOutput
:visual 0
:value-mask (logior xcb:CW:BackPixmap
(if exwm-floating--border-pixel
xcb:CW:BorderPixel 0)
xcb:CW:OverrideRedirect
xcb:CW:EventMask
(if exwm-floating--border-colormap
xcb:CW:Colormap 0))
:background-pixmap xcb:BackPixmap:ParentRelative
:border-pixel exwm-floating--border-pixel
:override-redirect 1
:event-mask xcb:EventMask:SubstructureRedirect
:colormap exwm-floating--border-colormap))
(exwm--debug
(xcb:+request exwm--connection
(make-instance 'xcb:ewmh:set-_NET_WM_NAME
:window exwm--container
:data (format "EXWM container for 0x%x" id))))
(xcb:+request exwm--connection
(make-instance 'xcb:ReparentWindow
:window id :parent exwm--container :x 0 :y 0))
(xcb:+request exwm--connection ;remove border
(make-instance 'xcb:ConfigureWindow
:window id :value-mask xcb:ConfigWindow:BorderWidth
@ -340,12 +279,6 @@ manager is shutting down."
(exwm-workspace--set-fullscreen f)))
(when (buffer-live-p buffer)
(with-current-buffer buffer
;; Flickering seems unavoidable here if the DestroyWindow request is
;; not initiated by us.
;; What we can do is to hide the its container ASAP.
(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))
@ -353,30 +286,10 @@ manager is shutting down."
(setq exwm-workspace--switch-history-outdated t)
;;
(when withdraw-only
;; Reparent back to root
(xcb:+request exwm--connection
(make-instance 'xcb:ChangeWindowAttributes
:window id :value-mask xcb:CW:EventMask
:event-mask xcb:EventMask:NoEvent))
(let (x y geometry geometry-parent)
(if (not exwm--floating-frame)
(setq x 0 y 0) ;the position does not matter
(setq geometry-parent
(xcb:+request-unchecked+reply exwm--connection
(make-instance 'xcb:GetGeometry
:drawable exwm--container))
geometry (xcb:+request-unchecked+reply exwm--connection
(make-instance 'xcb:GetGeometry
:drawable id)))
(if (not (and geometry-parent geometry))
(setq x 0 y 0) ;e.g. have been destroyed
(setq x (+ (slot-value geometry-parent 'x)
(slot-value geometry 'x))
y (+ (slot-value geometry-parent 'y)
(slot-value geometry 'y)))))
(xcb:+request exwm--connection
(make-instance 'xcb:ReparentWindow
:window id :parent exwm--root :x x :y y)))
;; Delete WM_STATE property
(xcb:+request exwm--connection
(make-instance 'xcb:DeleteProperty
@ -388,19 +301,20 @@ manager is shutting down."
:window id
:property xcb:Atom:_NET_WM_DESKTOP))))
(when exwm--floating-frame
;; Unmap the floating frame before destroying the containers.
(let ((window (frame-parameter exwm--floating-frame 'exwm-outer-id)))
;; Unmap the floating frame before destroying its container.
(let ((window (frame-parameter exwm--floating-frame 'exwm-outer-id))
(container (frame-parameter exwm--floating-frame
'exwm-container)))
(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))))
:window window :parent exwm--root :x 0 :y 0))
(xcb:+request exwm--connection
(make-instance 'xcb:DestroyWindow :window container))))
;; Restore the workspace if this X window is currently fullscreen.
(when (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)
(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))
(exwm-manage--set-client-list)
(xcb:flush exwm--connection))
(let ((kill-buffer-func
@ -444,38 +358,28 @@ manager is shutting down."
"Run in `kill-buffer-query-functions'."
(catch 'return
(when (or (not exwm--id)
(not exwm--container)
(xcb:+request-checked+request-check exwm--connection
(make-instance 'xcb:MapWindow
:window exwm--id)))
;; The X window is no longer alive so just close the buffer.
;; Destroy the container.
;; Hide the container to prevent flickering.
(when exwm--container
(xcb:+request exwm--connection
(make-instance 'xcb:UnmapWindow
:window exwm--container))
(xcb:flush exwm--connection))
(when exwm--floating-frame
(let ((window (frame-parameter exwm--floating-frame 'exwm-outer-id)))
(let ((window (frame-parameter exwm--floating-frame 'exwm-outer-id))
(container (frame-parameter exwm--floating-frame
'exwm-container)))
(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))))
(when exwm--container
(xcb:+request exwm--connection
(make-instance 'xcb:DestroyWindow
:window exwm--container)))
:x 0 :y 0))
(xcb:+request exwm--connection
(make-instance 'xcb:DestroyWindow
:window container))))
(xcb:flush exwm--connection)
(throw 'return t))
(unless (memq xcb:Atom:WM_DELETE_WINDOW exwm--protocols)
;; The X window does not support WM_DELETE_WINDOW; destroy it.
;; Hide the container to prevent flickering.
(xcb:+request exwm--connection
(make-instance 'xcb:UnmapWindow :window exwm--container))
(xcb:+request exwm--connection
(make-instance 'xcb:DestroyWindow :window exwm--id))
(xcb:flush exwm--connection)
@ -529,13 +433,6 @@ Would you like to kill it? "
(defun exwm-manage--kill-client (&optional id)
"Kill an X client."
(unless id (setq id (exwm--buffer->id (current-buffer))))
;; Hide the container to prevent flickering.
(let ((buffer (exwm--id->buffer id)))
(when buffer
(with-current-buffer buffer
(xcb:+request exwm--connection
(make-instance 'xcb:UnmapWindow :window exwm--container))
(xcb:flush exwm--connection))))
(let* ((response (xcb:+request-unchecked+reply exwm--connection
(make-instance 'xcb:ewmh:get-_NET_WM_PID :window id)))
(pid (and response (slot-value response 'value)))

View file

@ -164,7 +164,7 @@
(add-hook 'exwm-workspace-list-change-hook #'exwm-randr--refresh))))
;; Prevent frame parameters introduced by this module from being
;; saved/restored.
(dolist (i '(exwm-randr-output exwm-geometry))
(dolist (i '(exwm-randr-output))
(push (cons i :never) frameset-filter-alist)))
(defun exwm-randr--exit ()

View file

@ -321,16 +321,12 @@ Value nil means to use the default position which is fixed at bottom, while
(defvar exwm-workspace--fullscreen-frame-count 0
"Count the fullscreen workspace frames.")
(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 according to `exwm-workspace--workareas'."
(let ((workarea (elt exwm-workspace--workareas
(exwm-workspace--position frame)))
(id (frame-parameter frame 'exwm-outer-id))
(container (frame-parameter frame 'exwm-container))
(workspace (frame-parameter frame 'exwm-workspace))
x y width height)
(setq x (aref workarea 0)
y (aref workarea 1)
@ -339,8 +335,8 @@ Value nil means to use the default position which is fixed at bottom, while
(when (and (eq frame exwm-workspace--current)
(exwm-workspace--minibuffer-own-frame-p))
(exwm-workspace--resize-minibuffer-frame))
(exwm-layout--resize-container id container 0 0 width height)
(exwm-layout--resize-container nil workspace x y width height t)
(exwm--set-geometry container x y width height)
(exwm--set-geometry id nil nil width height)
(xcb:flush exwm--connection))
;; This is only used for workspace initialization.
(when exwm-workspace--fullscreen-frame-count
@ -457,26 +453,18 @@ The optional FORCE option is for internal use only."
(let* ((frame (exwm-workspace--workspace-from-frame-or-index frame-or-index))
(old-frame exwm-workspace--current)
(index (exwm-workspace--position frame))
(workspace (frame-parameter frame 'exwm-workspace))
(window (frame-parameter frame 'exwm-selected-window)))
(when (or force (not (eq frame exwm-workspace--current)))
(unless (window-live-p window)
(setq window (frame-selected-window frame)))
;; Raise the workspace container.
;; Raise this frame.
(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 (with-current-buffer (window-buffer window)
(memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state))
(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))))
:window (frame-parameter frame 'exwm-container)
:value-mask (logior xcb:ConfigWindow:Sibling
xcb:ConfigWindow:StackMode)
:sibling exwm--guide-window
:stack-mode xcb:StackMode:Below))
(setq exwm-workspace--current frame
exwm-workspace-current-index index)
(unless (exwm-workspace--workspace-p (selected-frame))
@ -497,6 +485,15 @@ The optional FORCE option is for internal use only."
(exwm-workspace--resize-minibuffer-frame)
;; 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)))))))
;; Hide windows in other workspaces by preprending a space
(unless exwm-workspace-show-all-buffers
(dolist (i exwm--id-buffer-alist)
@ -538,7 +535,7 @@ each time.")
(exwm-workspace--count)))))
(make-frame))
(run-hooks 'exwm-workspace-list-change-hook))
(exwm-workspace-switch (car (last exwm-workspace--list)))))
(exwm-workspace-switch frame-or-index)))
(defvar exwm-workspace-list-change-hook nil
"Normal hook run when the workspace list is changed (workspace added,
@ -662,7 +659,8 @@ INDEX must not exceed the current number of workspaces."
(let ((exwm-workspace--prompt-add-allowed t)
(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)))
(let ((frame (exwm-workspace--workspace-from-frame-or-index frame-or-index))
old-frame container)
(unless id (setq id (exwm--buffer->id (window-buffer))))
(with-current-buffer (exwm--id->buffer id)
(unless (eq exwm--frame frame)
@ -672,112 +670,111 @@ INDEX must not exceed the current number of workspaces."
(if (eq frame exwm-workspace--current)
name
(concat " " name)))))
(setq exwm--frame frame)
(if exwm--floating-frame
;; Move the floating container.
(with-slots (x y)
(xcb:+request-unchecked+reply exwm--connection
(make-instance 'xcb:GetGeometry :drawable exwm--container))
(setq old-frame exwm--frame
exwm--frame frame)
(if (not exwm--floating-frame)
;; Tiling.
(progn
(set-window-buffer (get-buffer-window nil t)
(other-buffer nil t))
(unless (eq frame exwm-workspace--current)
;; Clear the 'exwm-selected-window' frame parameter.
(set-frame-parameter frame 'exwm-selected-window nil))
(set-window-buffer (frame-selected-window frame)
(exwm--id->buffer id))
(if (eq frame exwm-workspace--current)
(select-window (frame-selected-window frame))
(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)))))
(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))
;; 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'.
(let* ((old-frame exwm--floating-frame)
(new-frame
(with-current-buffer
(or (get-buffer "*scratch*")
(progn
(set-buffer-major-mode
(get-buffer-create "*scratch*"))
(get-buffer "*scratch*")))
(make-frame
`((minibuffer . ,(minibuffer-window frame))
(left . ,(* window-min-width -100))
(top . ,(* window-min-height -100))
(width . ,window-min-width)
(height . ,window-min-height)
(unsplittable . t)))))
(outer-id (string-to-number
(frame-parameter new-frame
'outer-window-id)))
(window-id (string-to-number
(frame-parameter new-frame 'window-id)))
(window (frame-root-window new-frame)))
(set-frame-parameter new-frame 'exwm-outer-id outer-id)
(set-frame-parameter new-frame 'exwm-id window-id)
(set-frame-parameter new-frame 'exwm-container container)
(make-frame-invisible new-frame)
(set-frame-size new-frame
(frame-pixel-width old-frame)
(frame-pixel-height old-frame)
t)
(xcb:+request exwm--connection
(make-instance 'xcb:ReparentWindow
:window exwm--container
:parent
(frame-parameter frame 'exwm-workspace)
:x x :y y))
:window outer-id
:parent container
:x 0 :y 0))
(xcb:flush exwm--connection)
(if (exwm-workspace--minibuffer-own-frame-p)
(when (eq frame exwm-workspace--current)
(select-frame-set-input-focus exwm--floating-frame)
(exwm-layout--refresh))
;; The frame needs to be recreated since it won't use the
;; minibuffer on the new workspace.
(let* ((old-frame exwm--floating-frame)
(new-frame
(with-current-buffer
(or (get-buffer "*scratch*")
(progn
(set-buffer-major-mode
(get-buffer-create "*scratch*"))
(get-buffer "*scratch*")))
(make-frame
`((minibuffer . ,(minibuffer-window frame))
(left . 10000)
(top . 10000)
(width . ,window-min-width)
(height . ,window-min-height)
(unsplittable . t)))))
(outer-id (string-to-number
(frame-parameter new-frame
'outer-window-id)))
(window-id (string-to-number
(frame-parameter new-frame 'window-id)))
(frame-container (frame-parameter old-frame
'exwm-container))
(window (frame-root-window new-frame)))
(set-frame-parameter new-frame 'exwm-outer-id outer-id)
(set-frame-parameter new-frame 'exwm-id window-id)
(set-frame-parameter new-frame 'exwm-container
frame-container)
(make-frame-invisible new-frame)
(set-frame-size new-frame
(frame-pixel-width old-frame)
(frame-pixel-height old-frame)
t)
(xcb:+request exwm--connection
(make-instance 'xcb:ReparentWindow
:window outer-id
:parent frame-container
:x 0 :y 0))
(xcb:flush exwm--connection)
(with-current-buffer (exwm--id->buffer id)
(setq window-size-fixed nil
exwm--floating-frame new-frame)
(set-window-dedicated-p (frame-root-window old-frame) nil)
(remove-hook 'window-configuration-change-hook
#'exwm-layout--refresh)
(set-window-buffer window (current-buffer))
(add-hook 'window-configuration-change-hook
#'exwm-layout--refresh)
(set-window-dedicated-p window t))
;; Select a tiling window and delete the old frame.
(select-window (frame-selected-window exwm-workspace--current))
(delete-frame old-frame)
;; The rest is the same.
(make-frame-visible new-frame)
(exwm--set-geometry outer-id 0 0 nil nil)
(xcb:flush exwm--connection)
(redisplay)
(if (eq frame exwm-workspace--current)
(with-current-buffer (exwm--id->buffer id)
(setq window-size-fixed nil
exwm--frame frame
exwm--floating-frame new-frame)
(set-window-dedicated-p (frame-root-window old-frame) nil)
(remove-hook 'window-configuration-change-hook
#'exwm-layout--refresh)
(set-window-buffer window (current-buffer))
(add-hook 'window-configuration-change-hook
#'exwm-layout--refresh)
(delete-frame old-frame)
(set-window-dedicated-p window t)
(exwm-layout--show id window))
(if (not (eq frame exwm-workspace--current))
(make-frame-visible new-frame)
(select-frame-set-input-focus new-frame)
(redisplay))))
;; Update the 'exwm-selected-window' frame parameter.
(when (not (eq frame exwm-workspace--current))
(with-current-buffer (exwm--id->buffer id)
(set-frame-parameter frame 'exwm-selected-window
(frame-root-window
exwm--floating-frame)))))
;; Move the X window container.
(set-window-buffer (get-buffer-window (current-buffer) t)
(other-buffer nil t))
(unless (eq frame exwm-workspace--current)
;; Clear the 'exwm-selected-window' frame parameter.
(set-frame-parameter frame 'exwm-selected-window nil))
(exwm-layout--hide id)
;; (current-buffer) is changed.
(with-current-buffer (exwm--id->buffer id)
;; Reparent to the destination workspace.
(xcb:+request exwm--connection
(make-instance 'xcb:ReparentWindow
:window exwm--container
:parent (frame-parameter frame 'exwm-workspace)
:x 0 :y 0))
;; Place it just above the destination frame container.
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window exwm--container
:value-mask (logior xcb:ConfigWindow:Sibling
xcb:ConfigWindow:StackMode)
:sibling (frame-parameter frame 'exwm-container)
:stack-mode xcb:StackMode:Above)))
(xcb:flush exwm--connection)
(set-window-buffer (frame-selected-window frame)
(exwm--id->buffer id)))
(select-window (frame-root-window exwm--floating-frame)))
(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)
(set-frame-parameter frame 'exwm-selected-window
(frame-root-window
exwm--floating-frame)))))
;; Set _NET_WM_DESKTOP.
(exwm-workspace--set-desktop id)
(xcb:flush exwm--connection)))
@ -1005,16 +1002,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
'exwm-container)
:value-mask xcb:ConfigWindow:StackMode
:stack-mode xcb:StackMode:Above))
(xcb:flush exwm--connection)
;; Unfortunately we need the following lines to workaround a cursor
;; flickering issue for line-mode floating X windows. They just make the
;; minibuffer appear to be focused.
;; (FIXED?)
;; (with-current-buffer (window-buffer (minibuffer-window
;; exwm-workspace--minibuffer))
;; (setq cursor-in-non-selected-windows
;; (frame-parameter exwm-workspace--minibuffer 'cursor-type)))
)
(xcb:flush exwm--connection))
(defun exwm-workspace--hide-minibuffer ()
"Hide the minibuffer frame."
@ -1198,13 +1186,11 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
(let ((outer-id (string-to-number (frame-parameter frame
'outer-window-id)))
(window-id (string-to-number (frame-parameter frame 'window-id)))
(container (xcb:generate-id exwm--connection))
(workspace (xcb:generate-id exwm--connection)))
(container (xcb:generate-id exwm--connection)))
;; Save window IDs
(set-frame-parameter frame 'exwm-outer-id outer-id)
(set-frame-parameter frame 'exwm-id window-id)
(set-frame-parameter frame 'exwm-container container)
(set-frame-parameter frame 'exwm-workspace workspace)
;; In case it's created by emacsclient.
(set-frame-parameter frame 'client nil)
;; Copy RandR frame parameters from the first workspace to
@ -1214,38 +1200,15 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
(dolist (param '(exwm-randr-output
exwm-geometry))
(set-frame-parameter frame param (frame-parameter w param))))
(xcb:+request exwm--connection
(make-instance 'xcb:CreateWindow
:depth 0
:wid workspace
:parent exwm--root
:x 0
:y 0
:width (x-display-pixel-width)
:height (x-display-pixel-height)
:border-width 0
:class xcb:WindowClass:InputOutput
:visual 0
:value-mask (logior xcb:CW:BackPixmap
xcb:CW:OverrideRedirect
xcb:CW:EventMask)
:background-pixmap xcb:BackPixmap:ParentRelative
:override-redirect 1
:event-mask xcb:EventMask:SubstructureRedirect))
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window workspace
:value-mask xcb:ConfigWindow:StackMode
:stack-mode xcb:StackMode:Below))
(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)
:parent exwm--root
:x -1
:y -1
:width 1
:height 1
:border-width 0
:class xcb:WindowClass:InputOutput
:visual 0
@ -1253,13 +1216,12 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
xcb:CW:OverrideRedirect)
:background-pixmap xcb:BackPixmap:ParentRelative
:override-redirect 1))
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window container
:value-mask xcb:ConfigWindow:StackMode
:stack-mode xcb:StackMode:Below))
(exwm--debug
(xcb:+request exwm--connection
(make-instance 'xcb:ewmh:set-_NET_WM_NAME
:window workspace
:data
(format "EXWM workspace %d"
(exwm-workspace--position frame))))
(xcb:+request exwm--connection
(make-instance 'xcb:ewmh:set-_NET_WM_NAME
:window container
@ -1270,9 +1232,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
(make-instance 'xcb:ReparentWindow
: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)))
(make-instance 'xcb:MapWindow :window container)))
(xcb:flush exwm--connection)
;; Delay making the workspace fullscreen until Emacs becomes idle
(exwm--defer 0 #'set-frame-parameter frame 'fullscreen 'fullboth)
@ -1323,10 +1283,10 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
:parent exwm--root
:x 0
:y 0))
;; Destroy the containers.
;; Destroy the container.
(xcb:+request exwm--connection
(make-instance 'xcb:DestroyWindow
:window (frame-parameter frame 'exwm-workspace)))
:window (frame-parameter frame 'exwm-container)))
;; Update EWMH properties.
(exwm-workspace--update-ewmh-props)
;; Update switch history.
@ -1343,15 +1303,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
;; Set _NET_DESKTOP_GEOMETRY.
(exwm-workspace--set-desktop-geometry)
;; Update workareas.
(exwm-workspace--update-workareas)
;; Set _NET_VIRTUAL_ROOTS.
(xcb:+request exwm--connection
(make-instance 'xcb:ewmh:set-_NET_VIRTUAL_ROOTS
:window exwm--root
:data (vconcat (mapcar
(lambda (i)
(frame-parameter i 'exwm-workspace))
exwm-workspace--list)))))
(exwm-workspace--update-workareas))
(xcb:flush exwm--connection))
(defun exwm-workspace--modify-all-x-frames-parameters (new-x-parameters)
@ -1505,7 +1457,7 @@ applied to all subsequently created X frames."
(exwm-workspace-switch 0 t)
;; Prevent frame parameters introduced by this module from being
;; saved/restored.
(dolist (i '(exwm-outer-id exwm-id exwm-container exwm-workspace
(dolist (i '(exwm-outer-id exwm-id exwm-container exwm-geometry
fullscreen exwm-selected-window exwm-urgency))
(push (cons i :never) frameset-filter-alist)))

10
exwm.el
View file

@ -36,7 +36,6 @@
;; + Dynamic workspace support
;; + ICCCM/EWMH compliance
;; + (Optional) RandR (multi-monitor) support
;; + (Optional) Built-in compositing manager
;; + (Optional) Built-in system tray
;; Installation & configuration
@ -509,7 +508,7 @@
xcb:Atom:_NET_ACTIVE_WINDOW
;; xcb:Atom:_NET_WORKAREA
xcb:Atom:_NET_SUPPORTING_WM_CHECK
xcb:Atom:_NET_VIRTUAL_ROOTS
;; xcb:Atom:_NET_VIRTUAL_ROOTS
;; xcb:Atom:_NET_DESKTOP_LAYOUT
;; xcb:Atom:_NET_SHOWING_DESKTOP
@ -593,13 +592,14 @@
xcb:Atom:_NET_WM_FULL_PLACEMENT)))
;; Create a child window for setting _NET_SUPPORTING_WM_CHECK
(let ((new-id (xcb:generate-id exwm--connection)))
(setq exwm--guide-window new-id)
(xcb:+request exwm--connection
(make-instance 'xcb:CreateWindow
:depth 0
:wid new-id
:parent exwm--root
:x 0
:y 0
:x -1
:y -1
:width 1
:height 1
:border-width 0
@ -636,7 +636,6 @@
xcb:Atom:_NET_CURRENT_DESKTOP
xcb:Atom:_NET_ACTIVE_WINDOW
xcb:Atom:_NET_SUPPORTING_WM_CHECK
xcb:Atom:_NET_VIRTUAL_ROOTS
;; TODO: Keep this list synchronized with that in
;; `exwm--init-icccm-ewmh'.
))
@ -688,6 +687,7 @@
(exwm-input--init)
(exwm--unlock)
(exwm-workspace--post-init)
(exwm-input--post-init)
;; Manage existing windows
(exwm-manage--scan)
(run-hooks 'exwm-init-hook)))))