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 + Dynamic workspace support
+ ICCCM/EWMH compliance + ICCCM/EWMH compliance
+ (Optional) RandR (multi-monitor) support + (Optional) RandR (multi-monitor) support
+ (Optional) Built-in compositing manager
+ (Optional) Built-in system tray + (Optional) Built-in system tray
Please check out the 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--connection nil "X connection.")
(defvar exwm--root nil "Root window.") (defvar exwm--root nil "Root window.")
(defvar exwm--id-buffer-alist nil "Alist of (<X window ID> . <Emacs buffer>).") (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) (defsubst exwm--id->buffer (id)
"X window ID => Emacs buffer." "X window ID => Emacs buffer."
@ -75,6 +77,20 @@
xcb:EventMask:StructureNotify)))) xcb:EventMask:StructureNotify))))
(xcb:flush exwm--connection)) (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) (defmacro exwm--defer (secs function &rest args)
"Defer the execution of FUNCTION. "Defer the execution of FUNCTION.
@ -103,11 +119,10 @@ least SECS seconds later."
;; Internal variables ;; Internal variables
(defvar-local exwm--id nil) ;window ID (defvar-local exwm--id nil) ;window ID
(defvar-local exwm--container nil) ;container
(defvar-local exwm--frame nil) ;workspace frame (defvar-local exwm--frame nil) ;workspace frame
(defvar-local exwm--floating-frame nil) ;floating frame (defvar-local exwm--floating-frame nil) ;floating frame
(defvar-local exwm--mode-line-format nil) ;save mode-line-format (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--fixed-size nil) ;fixed size
(defvar-local exwm--keyboard-grabbed nil) ;Keyboard grabbed. (defvar-local exwm--keyboard-grabbed nil) ;Keyboard grabbed.
(defvar-local exwm--on-KeyPress ;KeyPress event handler (defvar-local exwm--on-KeyPress ;KeyPress event handler
@ -271,6 +286,7 @@ least SECS seconds later."
(push `(executing-kbd-macro . ,exwm--kmacro-map) (push `(executing-kbd-macro . ,exwm--kmacro-map)
minor-mode-overriding-map-alist) minor-mode-overriding-map-alist)
(setq buffer-read-only t (setq buffer-read-only t
cursor-type nil
left-margin-width nil left-margin-width nil
right-margin-width nil right-margin-width nil
left-fringe-width 0 left-fringe-width 0

View file

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

View file

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

View file

@ -30,27 +30,6 @@
(defvar exwm-floating-border-width) (defvar exwm-floating-border-width)
(defvar exwm-workspace--id-struts-alist) (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) (defun exwm-layout--set-state (id state)
"Set WM_STATE." "Set WM_STATE."
(xcb:+request exwm--connection (xcb:+request exwm--connection
@ -73,72 +52,24 @@
(y (pop edges)) (y (pop edges))
(width (- (pop edges) x)) (width (- (pop edges) x))
(height (- (pop edges) y)) (height (- (pop edges) y))
(edges (window-inside-pixel-edges window)) frame-x frame-y frame-width frame-height)
(relative-x (pop edges))
(relative-y (pop edges))
frame-width frame-height)
(with-current-buffer (exwm--id->buffer id) (with-current-buffer (exwm--id->buffer id)
(if (not exwm--floating-frame) (when 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.
(setq frame-width (frame-pixel-width exwm--floating-frame) (setq frame-width (frame-pixel-width exwm--floating-frame)
frame-height (frame-pixel-height exwm--floating-frame)) frame-height (frame-pixel-height exwm--floating-frame))
(xcb:+request exwm--connection (when exwm--floating-frame-position
(make-instance 'xcb:ConfigureWindow (setq frame-x (elt exwm--floating-frame-position 0)
:window exwm--container frame-y (elt exwm--floating-frame-position 1)
:value-mask (logior xcb:ConfigWindow:Width ;; The frame was placed at (-1, -1).
xcb:ConfigWindow:Height) x (+ x frame-x 1)
:width frame-width y (+ y frame-y 1))
:height frame-height)) (setq exwm--floating-frame-position nil))
(xcb:+request exwm--connection (exwm--set-geometry (frame-parameter exwm--floating-frame
(make-instance 'xcb:ConfigureWindow 'exwm-container)
:window (frame-parameter exwm--floating-frame frame-x frame-y frame-width frame-height))
'exwm-container) (exwm--set-geometry id x y width height)
: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)
(xcb:+request exwm--connection (make-instance 'xcb:MapWindow :window id)) (xcb:+request exwm--connection (make-instance 'xcb:MapWindow :window id))
(xcb:+request exwm--connection (exwm-layout--set-state id xcb:icccm:WM_STATE:NormalState)))
(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))))
(xcb:flush exwm--connection)) (xcb:flush exwm--connection))
(defun exwm-layout--hide (id) (defun exwm-layout--hide (id)
@ -146,6 +77,15 @@
(with-current-buffer (exwm--id->buffer id) (with-current-buffer (exwm--id->buffer id)
(unless (exwm-layout--iconic-state-p) ;already hidden (unless (exwm-layout--iconic-state-p) ;already hidden
(exwm--log "Hide #x%x" id) (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 (xcb:+request exwm--connection
(make-instance 'xcb:ChangeWindowAttributes (make-instance 'xcb:ChangeWindowAttributes
:window id :value-mask xcb:CW:EventMask :window id :value-mask xcb:CW:EventMask
@ -156,8 +96,6 @@
(make-instance 'xcb:ChangeWindowAttributes (make-instance 'xcb:ChangeWindowAttributes
:window id :value-mask xcb:CW:EventMask :window id :value-mask xcb:CW:EventMask
:event-mask exwm--client-event-mask)) :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) (exwm-layout--set-state id xcb:icccm:WM_STATE:IconicState)
(xcb:flush exwm--connection)))) (xcb:flush exwm--connection))))
@ -167,9 +105,7 @@
(declare-function exwm-input-release-keyboard "exwm-input.el") (declare-function exwm-input-release-keyboard "exwm-input.el")
(declare-function exwm-workspace--current-height "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--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--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" (declare-function exwm-workspace-move-window "exwm-workspace.el"
(frame-or-index &optional id)) (frame-or-index &optional id))
@ -180,41 +116,16 @@
(with-current-buffer (if id (exwm--id->buffer id) (window-buffer)) (with-current-buffer (if id (exwm--id->buffer id) (window-buffer))
(when (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state) (when (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)
(user-error "Already in full-screen mode")) (user-error "Already in full-screen mode"))
;; Save the position of floating frame. ;; Expand the X window to fill the whole screen.
(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.
;; Rationale: Floating X windows may not be positioned at (0, 0) ;; Rationale: Floating X windows may not be positioned at (0, 0)
;; due to the extra border. ;; due to the extra border.
(exwm-layout--resize-container nil exwm--container 0 0 (exwm--set-geometry exwm--id 0 0
(exwm-workspace--current-width) (exwm-workspace--current-width)
(exwm-workspace--current-height) (exwm-workspace--current-height))
t)
(exwm-layout--resize-container nil exwm--id 0 0
(exwm-workspace--current-width)
(exwm-workspace--current-height)
t)
;; Raise the X window. ;; Raise the X window.
(xcb:+request exwm--connection (xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow (make-instance 'xcb:ConfigureWindow
:window exwm--container :window exwm--id
:value-mask (logior xcb:ConfigWindow:BorderWidth :value-mask (logior xcb:ConfigWindow:BorderWidth
xcb:ConfigWindow:StackMode) xcb:ConfigWindow:StackMode)
:border-width 0 :border-width 0
@ -234,39 +145,20 @@
(with-current-buffer (if id (exwm--id->buffer id) (window-buffer)) (with-current-buffer (if id (exwm--id->buffer id) (window-buffer))
(unless (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state) (unless (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)
(user-error "Not in full-screen mode")) (user-error "Not in full-screen mode"))
;; Restore the size of this workspace.
(exwm-workspace--set-fullscreen exwm--frame)
(if exwm--floating-frame (if exwm--floating-frame
;; Restore the floating frame. (exwm-layout--show exwm--id (frame-root-window exwm--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.
(xcb:+request exwm--connection (xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow (make-instance 'xcb:ConfigureWindow
:window exwm--container :window exwm--id
:value-mask (logior xcb:ConfigWindow:Sibling :value-mask (logior xcb:ConfigWindow:Sibling
xcb:ConfigWindow:StackMode) xcb:ConfigWindow:StackMode)
:sibling (frame-parameter exwm-workspace--current :sibling exwm--guide-window
'exwm-container) :stack-mode xcb:StackMode:Above))
:stack-mode xcb:StackMode:Above))) (let ((window (get-buffer-window nil t)))
(exwm-layout--show exwm--id) (when window
(exwm-layout--show exwm--id window))))
(xcb:+request exwm--connection (xcb:+request exwm--connection
(make-instance 'xcb:ewmh:set-_NET_WM_STATE :window exwm--id :data [])) (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) (xcb:flush exwm--connection)
(setq exwm--ewmh-state (setq exwm--ewmh-state
(delq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)) (delq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state))

View file

@ -92,8 +92,6 @@ corresponding buffer.")
:window exwm--root :window exwm--root
:data (vconcat (mapcar #'car exwm--id-buffer-alist))))) :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--current)
(defvar exwm-workspace--switch-history-outdated) (defvar exwm-workspace--switch-history-outdated)
(defvar exwm-workspace-current-index) (defvar exwm-workspace-current-index)
@ -137,7 +135,8 @@ corresponding buffer.")
(setq exwm--id-buffer-alist (setq exwm--id-buffer-alist
(nconc exwm--id-buffer-alist `((,id . ,(current-buffer))))) (nconc exwm--id-buffer-alist `((,id . ,(current-buffer)))))
(exwm-mode) (exwm-mode)
(setq exwm--id id) (setq exwm--id id
exwm--frame exwm-workspace--current)
(exwm--update-window-type id) (exwm--update-window-type id)
(exwm--update-class id) (exwm--update-class id)
(exwm--update-transient-for id) (exwm--update-transient-for id)
@ -180,38 +179,13 @@ corresponding buffer.")
(xcb:+request exwm--connection (xcb:+request exwm--connection
(make-instance 'xcb:MapWindow :window id)) (make-instance 'xcb:MapWindow :window id))
(with-slots (x y width height) exwm--geometry (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 ;; Center window of type _NET_WM_WINDOW_TYPE_SPLASH
(when (memq xcb:Atom:_NET_WM_WINDOW_TYPE_SPLASH exwm-window-type) (when (memq xcb:Atom:_NET_WM_WINDOW_TYPE_SPLASH exwm-window-type)
(xcb:+request exwm--connection (exwm--set-geometry id
(make-instance 'xcb:ConfigureWindow (/ (- (exwm-workspace--current-width) width) 2)
:window id (/ (- (exwm-workspace--current-height) height)
:value-mask (eval-when-compile 2)
(logior xcb:ConfigWindow:X nil nil)))
xcb:ConfigWindow:Y))
:x (/ (- (exwm-workspace--current-width) width)
2)
:y (/ (- (exwm-workspace--current-height)
height)
2)))))
;; Check for desktop. ;; Check for desktop.
(when (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DESKTOP exwm-window-type) (when (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DESKTOP exwm-window-type)
;; There should be only one desktop X window. ;; There should be only one desktop X window.
@ -229,41 +203,6 @@ corresponding buffer.")
(throw 'return 'ignored)) (throw 'return 'ignored))
;; Manage the window ;; Manage the window
(exwm--log "Manage #x%x" id) (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 (xcb:+request exwm--connection ;remove border
(make-instance 'xcb:ConfigureWindow (make-instance 'xcb:ConfigureWindow
:window id :value-mask xcb:ConfigWindow:BorderWidth :window id :value-mask xcb:ConfigWindow:BorderWidth
@ -340,12 +279,6 @@ manager is shutting down."
(exwm-workspace--set-fullscreen f))) (exwm-workspace--set-fullscreen f)))
(when (buffer-live-p buffer) (when (buffer-live-p buffer)
(with-current-buffer 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. ;; Unmap the X window.
(xcb:+request exwm--connection (xcb:+request exwm--connection
(make-instance 'xcb:UnmapWindow :window id)) (make-instance 'xcb:UnmapWindow :window id))
@ -353,30 +286,10 @@ manager is shutting down."
(setq exwm-workspace--switch-history-outdated t) (setq exwm-workspace--switch-history-outdated t)
;; ;;
(when withdraw-only (when withdraw-only
;; Reparent back to root
(xcb:+request exwm--connection (xcb:+request exwm--connection
(make-instance 'xcb:ChangeWindowAttributes (make-instance 'xcb:ChangeWindowAttributes
:window id :value-mask xcb:CW:EventMask :window id :value-mask xcb:CW:EventMask
:event-mask xcb:EventMask:NoEvent)) :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 ;; Delete WM_STATE property
(xcb:+request exwm--connection (xcb:+request exwm--connection
(make-instance 'xcb:DeleteProperty (make-instance 'xcb:DeleteProperty
@ -388,19 +301,20 @@ manager is shutting down."
:window id :window id
:property xcb:Atom:_NET_WM_DESKTOP)))) :property xcb:Atom:_NET_WM_DESKTOP))))
(when exwm--floating-frame (when exwm--floating-frame
;; Unmap the floating frame before destroying the containers. ;; Unmap the floating frame before destroying its container.
(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 (xcb:+request exwm--connection
(make-instance 'xcb:UnmapWindow :window window)) (make-instance 'xcb:UnmapWindow :window window))
(xcb:+request exwm--connection (xcb:+request exwm--connection
(make-instance 'xcb:ReparentWindow (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. ;; Restore the workspace if this X window is currently fullscreen.
(when (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state) (when (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)
(exwm-workspace--set-fullscreen exwm--frame)) (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) (exwm-manage--set-client-list)
(xcb:flush exwm--connection)) (xcb:flush exwm--connection))
(let ((kill-buffer-func (let ((kill-buffer-func
@ -444,38 +358,28 @@ manager is shutting down."
"Run in `kill-buffer-query-functions'." "Run in `kill-buffer-query-functions'."
(catch 'return (catch 'return
(when (or (not exwm--id) (when (or (not exwm--id)
(not exwm--container)
(xcb:+request-checked+request-check exwm--connection (xcb:+request-checked+request-check exwm--connection
(make-instance 'xcb:MapWindow (make-instance 'xcb:MapWindow
:window exwm--id))) :window exwm--id)))
;; The X window is no longer alive so just close the buffer. ;; 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 (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 (xcb:+request exwm--connection
(make-instance 'xcb:UnmapWindow :window window)) (make-instance 'xcb:UnmapWindow :window window))
(xcb:+request exwm--connection (xcb:+request exwm--connection
(make-instance 'xcb:ReparentWindow (make-instance 'xcb:ReparentWindow
:window window :window window
:parent exwm--root :parent exwm--root
:x 0 :y 0)))) :x 0 :y 0))
(when exwm--container (xcb:+request exwm--connection
(xcb:+request exwm--connection (make-instance 'xcb:DestroyWindow
(make-instance 'xcb:DestroyWindow :window container))))
:window exwm--container)))
(xcb:flush exwm--connection) (xcb:flush exwm--connection)
(throw 'return t)) (throw 'return t))
(unless (memq xcb:Atom:WM_DELETE_WINDOW exwm--protocols) (unless (memq xcb:Atom:WM_DELETE_WINDOW exwm--protocols)
;; The X window does not support WM_DELETE_WINDOW; destroy it. ;; 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 (xcb:+request exwm--connection
(make-instance 'xcb:DestroyWindow :window exwm--id)) (make-instance 'xcb:DestroyWindow :window exwm--id))
(xcb:flush exwm--connection) (xcb:flush exwm--connection)
@ -529,13 +433,6 @@ Would you like to kill it? "
(defun exwm-manage--kill-client (&optional id) (defun exwm-manage--kill-client (&optional id)
"Kill an X client." "Kill an X client."
(unless id (setq id (exwm--buffer->id (current-buffer)))) (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 (let* ((response (xcb:+request-unchecked+reply exwm--connection
(make-instance 'xcb:ewmh:get-_NET_WM_PID :window id))) (make-instance 'xcb:ewmh:get-_NET_WM_PID :window id)))
(pid (and response (slot-value response 'value))) (pid (and response (slot-value response 'value)))

View file

@ -164,7 +164,7 @@
(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 exwm-geometry)) (dolist (i '(exwm-randr-output))
(push (cons i :never) frameset-filter-alist))) (push (cons i :never) frameset-filter-alist)))
(defun exwm-randr--exit () (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 (defvar exwm-workspace--fullscreen-frame-count 0
"Count the fullscreen workspace frames.") "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) (defun exwm-workspace--set-fullscreen (frame)
"Make frame FRAME fullscreen according to `exwm-workspace--workareas'." "Make frame FRAME fullscreen according to `exwm-workspace--workareas'."
(let ((workarea (elt exwm-workspace--workareas (let ((workarea (elt exwm-workspace--workareas
(exwm-workspace--position frame))) (exwm-workspace--position frame)))
(id (frame-parameter frame 'exwm-outer-id)) (id (frame-parameter frame 'exwm-outer-id))
(container (frame-parameter frame 'exwm-container)) (container (frame-parameter frame 'exwm-container))
(workspace (frame-parameter frame 'exwm-workspace))
x y width height) x y width height)
(setq x (aref workarea 0) (setq x (aref workarea 0)
y (aref workarea 1) 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) (when (and (eq frame exwm-workspace--current)
(exwm-workspace--minibuffer-own-frame-p)) (exwm-workspace--minibuffer-own-frame-p))
(exwm-workspace--resize-minibuffer-frame)) (exwm-workspace--resize-minibuffer-frame))
(exwm-layout--resize-container id container 0 0 width height) (exwm--set-geometry container x y width height)
(exwm-layout--resize-container nil workspace x y width height t) (exwm--set-geometry id nil nil width height)
(xcb:flush exwm--connection)) (xcb:flush exwm--connection))
;; This is only used for workspace initialization. ;; This is only used for workspace initialization.
(when exwm-workspace--fullscreen-frame-count (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)) (let* ((frame (exwm-workspace--workspace-from-frame-or-index frame-or-index))
(old-frame exwm-workspace--current) (old-frame exwm-workspace--current)
(index (exwm-workspace--position frame)) (index (exwm-workspace--position frame))
(workspace (frame-parameter frame 'exwm-workspace))
(window (frame-parameter frame 'exwm-selected-window))) (window (frame-parameter frame 'exwm-selected-window)))
(when (or force (not (eq frame exwm-workspace--current))) (when (or force (not (eq frame exwm-workspace--current)))
(unless (window-live-p window) (unless (window-live-p window)
(setq window (frame-selected-window frame))) (setq window (frame-selected-window frame)))
;; Raise the workspace container. ;; Raise this frame.
(xcb:+request exwm--connection (xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow (make-instance 'xcb:ConfigureWindow
:window workspace :window (frame-parameter frame 'exwm-container)
:value-mask xcb:ConfigWindow:StackMode :value-mask (logior xcb:ConfigWindow:Sibling
:stack-mode xcb:StackMode:Above)) xcb:ConfigWindow:StackMode)
;; Raise X windows with struts set if there's no fullscreen X window. :sibling exwm--guide-window
(unless (with-current-buffer (window-buffer window) :stack-mode xcb:StackMode:Below))
(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))))
(setq exwm-workspace--current frame (setq exwm-workspace--current frame
exwm-workspace-current-index index) exwm-workspace-current-index index)
(unless (exwm-workspace--workspace-p (selected-frame)) (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) (exwm-workspace--resize-minibuffer-frame)
;; Set a default minibuffer frame. ;; Set a default minibuffer frame.
(setq default-minibuffer-frame 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 ;; Hide windows in other workspaces by preprending a space
(unless exwm-workspace-show-all-buffers (unless exwm-workspace-show-all-buffers
(dolist (i exwm--id-buffer-alist) (dolist (i exwm--id-buffer-alist)
@ -538,7 +535,7 @@ each time.")
(exwm-workspace--count))))) (exwm-workspace--count)))))
(make-frame)) (make-frame))
(run-hooks 'exwm-workspace-list-change-hook)) (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 (defvar exwm-workspace-list-change-hook nil
"Normal hook run when the workspace list is changed (workspace added, "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) (let ((exwm-workspace--prompt-add-allowed t)
(exwm-workspace--prompt-delete-allowed t)) (exwm-workspace--prompt-delete-allowed t))
(exwm-workspace--prompt-for-workspace "Move to [+/-]: ")))) (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)))) (unless id (setq id (exwm--buffer->id (window-buffer))))
(with-current-buffer (exwm--id->buffer id) (with-current-buffer (exwm--id->buffer id)
(unless (eq exwm--frame frame) (unless (eq exwm--frame frame)
@ -672,112 +670,111 @@ INDEX must not exceed the current number of workspaces."
(if (eq frame exwm-workspace--current) (if (eq frame exwm-workspace--current)
name name
(concat " " name))))) (concat " " name)))))
(setq exwm--frame frame) (setq old-frame exwm--frame
(if exwm--floating-frame exwm--frame frame)
;; Move the floating container. (if (not exwm--floating-frame)
(with-slots (x y) ;; Tiling.
(xcb:+request-unchecked+reply exwm--connection (progn
(make-instance 'xcb:GetGeometry :drawable exwm--container)) (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 (xcb:+request exwm--connection
(make-instance 'xcb:ReparentWindow (make-instance 'xcb:ReparentWindow
:window exwm--container :window outer-id
:parent :parent container
(frame-parameter frame 'exwm-workspace) :x 0 :y 0))
:x x :y y))
(xcb:flush exwm--connection) (xcb:flush exwm--connection)
(if (exwm-workspace--minibuffer-own-frame-p) (with-current-buffer (exwm--id->buffer id)
(when (eq frame exwm-workspace--current) (setq window-size-fixed nil
(select-frame-set-input-focus exwm--floating-frame) exwm--floating-frame new-frame)
(exwm-layout--refresh)) (set-window-dedicated-p (frame-root-window old-frame) nil)
;; The frame needs to be recreated since it won't use the (remove-hook 'window-configuration-change-hook
;; minibuffer on the new workspace. #'exwm-layout--refresh)
(let* ((old-frame exwm--floating-frame) (set-window-buffer window (current-buffer))
(new-frame (add-hook 'window-configuration-change-hook
(with-current-buffer #'exwm-layout--refresh)
(or (get-buffer "*scratch*") (set-window-dedicated-p window t))
(progn ;; Select a tiling window and delete the old frame.
(set-buffer-major-mode (select-window (frame-selected-window exwm-workspace--current))
(get-buffer-create "*scratch*")) (delete-frame old-frame)
(get-buffer "*scratch*"))) ;; The rest is the same.
(make-frame (make-frame-visible new-frame)
`((minibuffer . ,(minibuffer-window frame)) (exwm--set-geometry outer-id 0 0 nil nil)
(left . 10000) (xcb:flush exwm--connection)
(top . 10000) (redisplay)
(width . ,window-min-width) (if (eq frame exwm-workspace--current)
(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) (with-current-buffer (exwm--id->buffer id)
(setq window-size-fixed nil (select-window (frame-root-window exwm--floating-frame)))
exwm--frame frame (exwm-layout--hide id))))
exwm--floating-frame new-frame) ;; Update the 'exwm-selected-window' frame parameter.
(set-window-dedicated-p (frame-root-window old-frame) nil) (when (not (eq frame exwm-workspace--current))
(remove-hook 'window-configuration-change-hook (with-current-buffer (exwm--id->buffer id)
#'exwm-layout--refresh) (set-frame-parameter frame 'exwm-selected-window
(set-window-buffer window (current-buffer)) (frame-root-window
(add-hook 'window-configuration-change-hook exwm--floating-frame)))))
#'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)))
;; Set _NET_WM_DESKTOP. ;; Set _NET_WM_DESKTOP.
(exwm-workspace--set-desktop id) (exwm-workspace--set-desktop id)
(xcb:flush exwm--connection))) (xcb:flush exwm--connection)))
@ -1005,16 +1002,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
'exwm-container) 'exwm-container)
:value-mask xcb:ConfigWindow:StackMode :value-mask xcb:ConfigWindow:StackMode
:stack-mode xcb:StackMode:Above)) :stack-mode xcb:StackMode:Above))
(xcb:flush exwm--connection) (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)))
)
(defun exwm-workspace--hide-minibuffer () (defun exwm-workspace--hide-minibuffer ()
"Hide the minibuffer frame." "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 (let ((outer-id (string-to-number (frame-parameter frame
'outer-window-id))) 'outer-window-id)))
(window-id (string-to-number (frame-parameter frame 'window-id))) (window-id (string-to-number (frame-parameter frame 'window-id)))
(container (xcb:generate-id exwm--connection)) (container (xcb:generate-id exwm--connection)))
(workspace (xcb:generate-id exwm--connection)))
;; Save window IDs ;; Save window IDs
(set-frame-parameter frame 'exwm-outer-id outer-id) (set-frame-parameter frame 'exwm-outer-id outer-id)
(set-frame-parameter frame 'exwm-id window-id) (set-frame-parameter frame 'exwm-id window-id)
(set-frame-parameter frame 'exwm-container container) (set-frame-parameter frame 'exwm-container container)
(set-frame-parameter frame 'exwm-workspace workspace)
;; In case it's created by emacsclient. ;; In case it's created by emacsclient.
(set-frame-parameter frame 'client nil) (set-frame-parameter frame 'client nil)
;; Copy RandR frame parameters from the first workspace to ;; 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 (dolist (param '(exwm-randr-output
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
(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 (xcb:+request exwm--connection
(make-instance 'xcb:CreateWindow (make-instance 'xcb:CreateWindow
:depth 0 :depth 0
:wid container :wid container
:parent workspace :parent exwm--root
:x 0 :x -1
:y 0 :y -1
:width (x-display-pixel-width) :width 1
:height (x-display-pixel-height) :height 1
:border-width 0 :border-width 0
:class xcb:WindowClass:InputOutput :class xcb:WindowClass:InputOutput
:visual 0 :visual 0
@ -1253,13 +1216,12 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
xcb:CW:OverrideRedirect) xcb:CW:OverrideRedirect)
:background-pixmap xcb:BackPixmap:ParentRelative :background-pixmap xcb:BackPixmap:ParentRelative
:override-redirect 1)) :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 (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 (xcb:+request exwm--connection
(make-instance 'xcb:ewmh:set-_NET_WM_NAME (make-instance 'xcb:ewmh:set-_NET_WM_NAME
:window container :window container
@ -1270,9 +1232,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
(make-instance 'xcb:ReparentWindow (make-instance 'xcb:ReparentWindow
:window outer-id :parent container :x 0 :y 0)) :window outer-id :parent container :x 0 :y 0))
(xcb:+request exwm--connection (xcb:+request exwm--connection
(make-instance 'xcb:MapWindow :window container)) (make-instance 'xcb:MapWindow :window container)))
(xcb:+request exwm--connection
(make-instance 'xcb:MapWindow :window workspace)))
(xcb:flush exwm--connection) (xcb:flush exwm--connection)
;; Delay making the workspace fullscreen until Emacs becomes idle ;; Delay making the workspace fullscreen until Emacs becomes idle
(exwm--defer 0 #'set-frame-parameter frame 'fullscreen 'fullboth) (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 :parent exwm--root
:x 0 :x 0
:y 0)) :y 0))
;; Destroy the containers. ;; Destroy the container.
(xcb:+request exwm--connection (xcb:+request exwm--connection
(make-instance 'xcb:DestroyWindow (make-instance 'xcb:DestroyWindow
:window (frame-parameter frame 'exwm-workspace))) :window (frame-parameter frame 'exwm-container)))
;; Update EWMH properties. ;; Update EWMH properties.
(exwm-workspace--update-ewmh-props) (exwm-workspace--update-ewmh-props)
;; Update switch history. ;; Update switch history.
@ -1343,15 +1303,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
;; Set _NET_DESKTOP_GEOMETRY. ;; Set _NET_DESKTOP_GEOMETRY.
(exwm-workspace--set-desktop-geometry) (exwm-workspace--set-desktop-geometry)
;; Update workareas. ;; Update workareas.
(exwm-workspace--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)))))
(xcb:flush exwm--connection)) (xcb:flush exwm--connection))
(defun exwm-workspace--modify-all-x-frames-parameters (new-x-parameters) (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) (exwm-workspace-switch 0 t)
;; 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-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)) fullscreen exwm-selected-window exwm-urgency))
(push (cons i :never) frameset-filter-alist))) (push (cons i :never) frameset-filter-alist)))

10
exwm.el
View file

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