From 9c95c03e18f6d5cf78bcd54bf00f8055a3863336 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Wed, 3 Feb 2016 12:12:24 +0800 Subject: [PATCH 1/2] Rework the X windows hierarchy model This commit add workspace and X window containers support to avoid using Emacs frames as the parents of X windows. This should make it easier to set input focus. * exwm-core.el (exwm--container, exwm--floating-frame-position): New file local variables. (exwm--floating-frame-geometry): Removed file local variable. * exwm-floating.el (exwm-floating--set-floating) (exwm-floating--unset-floating, exwm-floating--do-moveresize) (exwm-floating-move): Use container. (exwm-floating--fit-frame-to-window): No longer adjust stacking order. (exwm-floating--fit-frame-to-window): The first member is changed to buffer. (exwm-floating--start-moveresize): Use container. Correctly set input focus. * exwm-input.el (exwm-input--redirected, exwm-input--on-focus-in): Removed. (exwm-input--on-buffer-list-update): Remove the restriction on floating frames which is no longer valid. (exwm-input--update-focus): Adjust stacking order. (exwm-input--on-minibuffer-setup): New function for setting focus on the Emacs frame when entering minibuffer. (exwm-input--on-KeyPress-line-mode): No longer compensate FocusOut event. (exwm-input--grab-keyboard, exwm-input--release-keyboard): Local keys are now grabbed on the X window container. (exwm-input--init): Add `exwm-input--on-minibuffer-setup' to `minibuffer-setup-hook'. * exwm-layout.el (exwm-layout--resize-container): New function to resize/reposition both the X window and its container. (exwm-layout--show, exwm-layout--hide): Use container. (exwm-layout-set-fullscreen): Use container. No longer save width and height. (exwm-layout-unset-fullscreen, exwm-layout--set-frame-fullscreen): Use container. (exwm-layout--refresh): Update a frame parameter. Remove dead code. * exwm-manage.el (exwm-manage--manage-window): Reparent unmanaged X windows to the workspace. Create X window container as the parent of the X window. (exwm-manage--unmanage-window): Unmap/destroy container when appropriate. Use the position of container. (exwm-manage--unmanage-window): Destroy the container. * exwm-randr.el (exwm-randr--refresh): Resize workspace using container. * exwm-workspace.el (exwm-workspace-switch): Raise workspace. Correctly set input focus. (exwm-workspace--on-focus-in): Removed. (exwm-workspace-move-window): Reparent to workspace container. (exwm-workspace--init): Create workspace frames as visible. Create workspace containers. Remove exwm-workspace--on-focus-in from focus-in-hook. Update _NET_VIRTUAL_ROOTS. * exwm.el (exwm-init): No longer disable hourglass window. Initialize workspace module before input. * exwm-core.el (exwm--debug): New macro for setting debug forms. * exwm-floating.el (exwm-floating--set-floating): No longer do `exwm--lock' and `exwm--unlock' since `make-frame' is already adviced to take care of everything. Correctly set input focus to the newly created floating X window. * exwm-core.el (exwm--floating-edges): Removed file local variable. * exwm-floating.el (exwm-floating--set-floating) (exwm-floating--unset-floating): * exwm-layout.el (exwm-layout--show, exwm-layout-enlarge-window): * exwm-manage.el (exwm-manage--on-ConfigureRequest): No longer use floating geometry. * exwm-input.el (exwm-input--update-global-prefix-keys): Grab global keys on workspaces containers instead of the root window (or input focus would transfer to the workspace containing the pointer when the grab is active). * exwm-workspace.el (exwm-workspace-switch): No longer move mouse. --- exwm-core.el | 7 ++- exwm-floating.el | 154 ++++++++++++++++++++------------------------- exwm-input.el | 140 +++++++++++++++++++++-------------------- exwm-layout.el | 157 ++++++++++++++++++++++------------------------ exwm-manage.el | 140 +++++++++++++++++++++++++---------------- exwm-randr.el | 14 ++--- exwm-workspace.el | 93 +++++++++++++-------------- exwm.el | 5 +- 8 files changed, 356 insertions(+), 354 deletions(-) diff --git a/exwm-core.el b/exwm-core.el index aaa98e3..55801a9 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -38,6 +38,9 @@ (when exwm-debug-on `(message (concat "[EXWM] " ,format-string) ,@args))) +(defmacro exwm--debug (&rest forms) + (when exwm-debug-on `(progn ,@forms))) + (defvar exwm--connection nil "X connection.") (defvar exwm--root nil "Root window.") (defvar exwm--id-buffer-alist nil "Alist of ( . ).") @@ -77,12 +80,12 @@ ;; 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--floating-edges nil) ;four edges (defvar-local exwm--floating-mode-line-format nil) ;save mode-line-format (defvar-local exwm--fullscreen nil) ;used in fullscreen -(defvar-local exwm--floating-frame-geometry nil) ;in fullscreen +(defvar-local exwm--floating-frame-position nil) ;used in fullscreen (defvar-local exwm--fixed-size nil) ;fixed size (defvar-local exwm--on-KeyPress ;KeyPress event handler #'exwm-input--on-KeyPress-line-mode) diff --git a/exwm-floating.el b/exwm-floating.el index 8fbb2bc..251856c 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -65,7 +65,6 @@ exwm--frame) ;; Fallback to current workspace exwm-workspace--current))) - (original-id (frame-parameter original-frame 'exwm-window-id)) ;; Create new frame (frame (with-current-buffer (or (get-buffer "*scratch*") @@ -73,18 +72,16 @@ (set-buffer-major-mode (get-buffer-create "*scratch*")) (get-buffer "*scratch*"))) - (prog2 - (exwm--lock) - (make-frame - `((minibuffer . nil) ;use the one on workspace - (background-color . ,exwm-floating-border-color) - (internal-border-width . ,exwm-floating-border-width) - (left . 10000) - (top . 10000) - (unsplittable . t))) ;and fix the size later - (exwm--unlock)))) - (frame-id (string-to-number (frame-parameter frame 'window-id))) + (make-frame + `((minibuffer . nil) ;use the one on workspace + (background-color . ,exwm-floating-border-color) + (internal-border-width . ,exwm-floating-border-width) + (left . 10000) + (top . 10000) + (unsplittable . t))))) ;and fix the size later (outer-id (string-to-number (frame-parameter frame 'outer-window-id))) + (container (with-current-buffer (exwm--id->buffer id) + exwm--container)) (window (frame-first-window frame)) ;and it's the only window (x (slot-value exwm--geometry 'x)) (y (slot-value exwm--geometry 'y)) @@ -99,7 +96,6 @@ (exwm--log "Floating geometry (original, relative): %dx%d%+d%+d" width height x y) ;; Save window IDs - (set-frame-parameter frame 'exwm-window-id frame-id) (set-frame-parameter frame 'exwm-outer-id outer-id) ;; Set urgency flag if it's not appear in the active workspace (let ((idx (cl-position original-frame exwm-workspace--list))) @@ -152,36 +148,19 @@ (setq x (/ (- display-width width) 2) y (/ (- display-height height) 2)))))) (exwm--log "Floating geometry (corrected): %dx%d%+d%+d" width height x y) - ;; Set event mask - (xcb:+request exwm--connection - (make-instance 'xcb:ChangeWindowAttributes - :window frame-id :value-mask xcb:CW:EventMask - :event-mask xcb:EventMask:SubstructureRedirect)) - ;; Save the geometry - ;; Rationale: the frame will not be ready for some time, thus we cannot - ;; infer the correct window size from its geometry. - (with-current-buffer (exwm--id->buffer id) - (setq exwm--floating-edges (vector x y (+ width x) (+ height y)))) ;; Fit frame to client (exwm-floating--fit-frame-to-window outer-id width height) - ;; Reparent window to this frame - (xcb:+request exwm--connection - (make-instance 'xcb:ChangeWindowAttributes - :window id :value-mask xcb:CW:EventMask - :event-mask xcb:EventMask:NoEvent)) + ;; Reparent this frame to the container (xcb:+request exwm--connection (make-instance 'xcb:ReparentWindow - :window id :parent frame-id - :x exwm-floating-border-width - :y exwm-floating-border-width)) + :window outer-id :parent container :x 0 :y 0)) + ;; Place the container (xcb:+request exwm--connection - (make-instance 'xcb:ChangeWindowAttributes - :window id :value-mask xcb:CW:EventMask - :event-mask exwm--client-event-mask)) - ;; Reparent this frame to the original one - (xcb:+request exwm--connection - (make-instance 'xcb:ReparentWindow - :window outer-id :parent original-id + (make-instance 'xcb:ConfigureWindow + :window container + :value-mask (eval-when-compile + (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y)) :x (- x exwm-floating-border-width) :y (- y exwm-floating-border-width))) (xcb:flush exwm--connection) @@ -192,7 +171,7 @@ exwm--floating-frame frame) (set-window-buffer window (current-buffer)) ;this changes current buffer (set-window-dedicated-p window t)) - (select-window window)) + (select-frame-set-input-focus frame)) (run-hooks 'exwm-floating-setup-hook)) ;;;###autoload @@ -200,25 +179,16 @@ "Make window ID non-floating." (interactive) (let ((buffer (exwm--id->buffer id))) - ;; Reparent to workspace frame - (xcb:+request exwm--connection - (make-instance 'xcb:ChangeWindowAttributes - :window id :value-mask xcb:CW:EventMask - :event-mask xcb:EventMask:NoEvent)) - (xcb:+request exwm--connection - (make-instance 'xcb:ReparentWindow - :window id - :parent (frame-parameter exwm-workspace--current - 'exwm-window-id) - :x 0 :y 0)) ;temporary position - (xcb:+request exwm--connection - (make-instance 'xcb:ChangeWindowAttributes - :window id :value-mask xcb:CW:EventMask - :event-mask exwm--client-event-mask)) + (with-current-buffer buffer + (xcb:+request exwm--connection + (make-instance 'xcb:ReparentWindow + :window exwm--container + :parent (frame-parameter exwm-workspace--current + 'exwm-workspace) + :x 0 :y 0))) ;temporary position (xcb:flush exwm--connection) (with-current-buffer buffer (when exwm--floating-frame ;from floating to non-floating - (setq exwm--floating-edges nil) ;invalid by now (set-window-dedicated-p (frame-first-window exwm--floating-frame) nil) (delete-frame exwm--floating-frame))) ;remove the floating frame (with-current-buffer buffer @@ -260,13 +230,11 @@ are provided. You should call `xcb:flush' and restore the value of 'exwm-outer-id)) :value-mask (eval-when-compile (logior xcb:ConfigWindow:Width - xcb:ConfigWindow:Height - xcb:ConfigWindow:StackMode)) + xcb:ConfigWindow:Height)) :width (+ width (* 2 exwm-floating-border-width)) :height (+ height (* 2 exwm-floating-border-width) (window-mode-line-height) - (window-header-line-height)) - :stack-mode xcb:StackMode:Above))) ;top-most + (window-header-line-height))))) (defun exwm-floating-hide-mode-line () "Hide mode-line of a floating frame." @@ -294,22 +262,23 @@ are provided. You should call `xcb:flush' and restore the value of (setq window-size-fixed exwm--fixed-size))) (defvar exwm-floating--moveresize-calculate nil - "Calculate move/resize parameters [frame-id event-mask x y width height].") + "Calculate move/resize parameters [buffer event-mask x y width height].") ;;;###autoload (defun exwm-floating--start-moveresize (id &optional type) "Start move/resize." (let ((buffer (exwm--id->buffer id)) - frame frame-id x y width height cursor) + frame container x y width height cursor) (when (and buffer - (setq frame (with-current-buffer buffer exwm--floating-frame)) - (setq frame-id (frame-parameter frame 'exwm-outer-id)) + (with-current-buffer buffer + (setq frame exwm--floating-frame + container exwm--container)) ;; Test if the pointer can be grabbed (= xcb:GrabStatus:Success (slot-value (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:GrabPointer - :owner-events 0 :grab-window frame-id + :owner-events 0 :grab-window container :event-mask xcb:EventMask:NoEvent :pointer-mode xcb:GrabMode:Async :keyboard-mode xcb:GrabMode:Async @@ -317,11 +286,10 @@ are provided. You should call `xcb:flush' and restore the value of :cursor xcb:Cursor:None :time xcb:Time:CurrentTime)) 'status))) - (setq exwm--floating-edges nil) ;invalid by now (with-slots (root-x root-y win-x win-y) (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:QueryPointer :window id)) - (select-frame-set-input-focus frame) ;raise and focus it + (select-window (frame-first-window frame)) ;transfer input focus (setq width (frame-pixel-width frame) height (frame-pixel-height frame)) (unless type @@ -347,7 +315,7 @@ are provided. You should call `xcb:flush' and restore the value of (setq cursor exwm-floating--cursor-move exwm-floating--moveresize-calculate `(lambda (x y) - (vector ,frame-id + (vector ,buffer ,(eval-when-compile (logior xcb:ConfigWindow:X xcb:ConfigWindow:Y)) @@ -356,7 +324,7 @@ are provided. You should call `xcb:flush' and restore the value of (setq cursor exwm-floating--cursor-top-left exwm-floating--moveresize-calculate `(lambda (x y) - (vector ,frame-id + (vector ,buffer ,(eval-when-compile (logior xcb:ConfigWindow:X xcb:ConfigWindow:Y @@ -369,7 +337,7 @@ are provided. You should call `xcb:flush' and restore the value of (setq cursor exwm-floating--cursor-top exwm-floating--moveresize-calculate `(lambda (x y) - (vector ,frame-id + (vector ,buffer ,(eval-when-compile (logior xcb:ConfigWindow:Y xcb:ConfigWindow:Height)) @@ -378,7 +346,7 @@ are provided. You should call `xcb:flush' and restore the value of (setq cursor exwm-floating--cursor-top-right exwm-floating--moveresize-calculate `(lambda (x y) - (vector ,frame-id + (vector ,buffer ,(eval-when-compile (logior xcb:ConfigWindow:Y xcb:ConfigWindow:Width @@ -389,13 +357,13 @@ are provided. You should call `xcb:flush' and restore the value of (setq cursor exwm-floating--cursor-right exwm-floating--moveresize-calculate `(lambda (x y) - (vector ,frame-id ,xcb:ConfigWindow:Width + (vector ,buffer ,xcb:ConfigWindow:Width 0 0 (- x ,(- root-x width)) 0)))) ((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT) (setq cursor exwm-floating--cursor-bottom-right exwm-floating--moveresize-calculate `(lambda (x y) - (vector ,frame-id + (vector ,buffer ,(eval-when-compile (logior xcb:ConfigWindow:Width xcb:ConfigWindow:Height)) @@ -405,14 +373,14 @@ are provided. You should call `xcb:flush' and restore the value of (setq cursor exwm-floating--cursor-bottom exwm-floating--moveresize-calculate `(lambda (x y) - (vector ,frame-id + (vector ,buffer ,xcb:ConfigWindow:Height 0 0 0 (- y ,(- root-y height)))))) ((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT) (setq cursor exwm-floating--cursor-bottom-left exwm-floating--moveresize-calculate `(lambda (x y) - (vector ,frame-id + (vector ,buffer ,(eval-when-compile (logior xcb:ConfigWindow:X xcb:ConfigWindow:Width @@ -425,7 +393,7 @@ are provided. You should call `xcb:flush' and restore the value of (setq cursor exwm-floating--cursor-left exwm-floating--moveresize-calculate `(lambda (x y) - (vector ,frame-id + (vector ,buffer ,(eval-when-compile (logior xcb:ConfigWindow:X xcb:ConfigWindow:Width)) @@ -433,7 +401,7 @@ are provided. You should call `xcb:flush' and restore the value of ;; Select events and change cursor (should always succeed) (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:GrabPointer - :owner-events 0 :grab-window frame-id + :owner-events 0 :grab-window container :event-mask (eval-when-compile (logior xcb:EventMask:ButtonRelease xcb:EventMask:ButtonMotion)) @@ -488,12 +456,26 @@ are provided. You should call `xcb:flush' and restore the value of (xcb:unmarshal obj data) (setq result (funcall exwm-floating--moveresize-calculate (slot-value obj 'root-x) (slot-value obj 'root-y))) - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window (elt result 0) :value-mask (elt result 1) - :x (- (elt result 2) frame-x) - :y (- (elt result 3) frame-y) - :width (elt result 4) :height (elt result 5))) + (with-current-buffer (aref result 0) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window exwm--container + :value-mask (logand (aref result 1) + (eval-when-compile + (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y))) + :x (- (aref result 2) frame-x) + :y (- (aref result 3) frame-y))) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window (frame-parameter exwm--floating-frame + 'exwm-outer-id) + :value-mask + (logand (aref result 1) + (eval-when-compile + (logior xcb:ConfigWindow:Width + xcb:ConfigWindow:Height))) + :width (aref result 4) :height (aref result 5)))) (xcb:flush exwm--connection)))) (defun exwm-floating-move (&optional delta-x delta-y) @@ -505,13 +487,13 @@ 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* ((id (frame-parameter exwm--floating-frame 'exwm-outer-id)) - (geometry (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:GetGeometry :drawable id))) + (let* ((geometry (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:GetGeometry + :drawable exwm--container))) (edges (window-inside-absolute-pixel-edges))) (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow - :window id + :window exwm--container :value-mask (eval-when-compile (logior xcb:ConfigWindow:X xcb:ConfigWindow:Y)) diff --git a/exwm-input.el b/exwm-input.el index 99a4b8f..156399c 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -79,8 +79,6 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (xcb:flush exwm--connection)))) (defvar exwm-input--focus-window nil "The (Emacs) window to be focused.") -(defvar exwm-input--redirected nil - "Indicate next update on buffer list is actually a result of redirection.") (defvar exwm-input--timer nil "Currently running timer.") (defun exwm-input--on-buffer-list-update () @@ -89,25 +87,12 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (window (selected-window)) (buffer (current-buffer))) (when (and (not (minibufferp buffer)) - (frame-parameter frame 'exwm-window-id) ;e.g. emacsclient frame + (frame-parameter frame 'exwm-outer-id) ;e.g. emacsclient frame (eq buffer (window-buffer))) ;e.g. `with-temp-buffer' - (unless (and exwm-input--redirected - exwm-input--focus-window - (with-current-buffer (window-buffer - exwm-input--focus-window) - exwm--floating-frame)) - (setq exwm-input--focus-window window) - (when exwm-input--timer (cancel-timer exwm-input--timer)) - (setq exwm-input--timer - (run-with-idle-timer 0.01 nil #'exwm-input--update-focus))) - (setq exwm-input--redirected nil)))) - -(defun exwm-input--on-focus-in () - "Run in focus-in-hook to remove redirected focus on frame." - (let ((frame (selected-frame))) - (when (and (frame-parameter frame 'exwm-window-id) - (not (memq frame exwm-workspace--list))) - (setq exwm-input--redirected t)))) + (when exwm-input--timer (cancel-timer exwm-input--timer)) + (setq exwm-input--focus-window window + exwm-input--timer + (run-with-idle-timer 0.01 nil #'exwm-input--update-focus))))) (defun exwm-input--update-focus () "Update input focus." @@ -122,22 +107,42 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (force-mode-line-update) ;; The application may have changed its input focus (exwm-workspace-switch exwm-workspace-current-index t)) - (when exwm--floating-frame - (redirect-frame-focus exwm--floating-frame nil) - (select-frame-set-input-focus exwm--floating-frame t)) (exwm--log "Set focus on #x%x" exwm--id) - (exwm-input--set-focus exwm--id)) + (exwm-input--set-focus exwm--id) + ;; Adjust stacking orders + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window exwm--container + :value-mask xcb:ConfigWindow:StackMode + :stack-mode (if exwm--floating-frame + xcb:StackMode:Above + xcb:StackMode:Below))) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window (frame-parameter + (or exwm--floating-frame exwm--frame) + 'exwm-outer-id) + :value-mask xcb:ConfigWindow:StackMode + :stack-mode xcb:StackMode:Below)) + (xcb:flush exwm--connection)) (when (eq (selected-window) exwm-input--focus-window) (exwm--log "Focus on %s" exwm-input--focus-window) (select-frame-set-input-focus (window-frame exwm-input--focus-window) t) - (dolist (pair exwm--id-buffer-alist) - (with-current-buffer (cdr pair) - (when (and exwm--floating-frame - (eq exwm--frame exwm-workspace--current)) - (redirect-frame-focus exwm--floating-frame exwm--frame)))))) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window (frame-parameter + (window-frame exwm-input--focus-window) + 'exwm-outer-id) + :value-mask xcb:ConfigWindow:StackMode + :stack-mode xcb:StackMode:Below)) + (xcb:flush exwm--connection))) (setq exwm-input--focus-window nil)))) +(defun exwm-input--on-minibuffer-setup () + "Run in minibuffer-setup-hook to set input focus to the frame." + (x-focus-frame (selected-frame))) + (defvar exwm-input--during-key-sequence nil "Non-nil indicates Emacs is waiting for more keys to form a key sequence.") (defvar exwm-input--temp-line-mode nil @@ -221,32 +226,38 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") "Update `exwm-input--global-prefix-keys'." (when exwm--connection (let ((original exwm-input--global-prefix-keys) - keysym keycode) + keysym keycode ungrab-key grab-key workspace) (setq exwm-input--global-prefix-keys nil) (dolist (i exwm-input--global-keys) (cl-pushnew (elt i 0) exwm-input--global-prefix-keys)) (unless (equal original exwm-input--global-prefix-keys) - ;; Grab global keys on root window - (if (xcb:+request-checked+request-check exwm--connection - (make-instance 'xcb:UngrabKey - :key xcb:Grab:Any :grab-window exwm--root - :modifiers xcb:ModMask:Any)) - (exwm--log "Failed to ungrab keys") - (dolist (i exwm-input--global-prefix-keys) - (setq keysym (xcb:keysyms:event->keysym exwm--connection i)) - (when (or (not keysym) - (not (setq keycode (xcb:keysyms:keysym->keycode - exwm--connection (car keysym)))) - (xcb:+request-checked+request-check exwm--connection - (make-instance 'xcb:GrabKey - :owner-events 0 - :grab-window exwm--root - :modifiers (cadr keysym) - :key keycode - :pointer-mode xcb:GrabMode:Async - :keyboard-mode xcb:GrabMode:Async))) - (user-error "[EXWM] Failed to grab key: %s" - (single-key-description i))))))))) + (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 + :grab-window nil + :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") + (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) (cadr keysym) + (slot-value grab-key 'key) keycode) + (when (or (not keycode) + (xcb:+request-checked+request-check exwm--connection + grab-key)) + (user-error "[EXWM] Failed to grab key: %s" + (single-key-description k)))))))))) (defun exwm-input-set-key (key command) "Set a global key binding." @@ -289,21 +300,6 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (with-slots (detail state) key-press (let ((keysym (xcb:keysyms:keycode->keysym exwm--connection detail state)) event) - ;; Compensate FocusOut event (prevent cursor style change) - (unless (eq major-mode 'exwm-mode) - (let ((id (frame-parameter nil 'exwm-window-id))) - (xcb:+request exwm--connection - (make-instance 'xcb:SendEvent - :propagate 0 - :destination id - :event-mask xcb:EventMask:StructureNotify - :event - (xcb:marshal - (make-instance 'xcb:FocusIn - :detail xcb:NotifyDetail:Inferior - :event id - :mode xcb:NotifyMode:Normal) - exwm--connection))))) (when (and keysym (setq event (xcb:keysyms:keysym->event exwm--connection keysym state))) @@ -324,7 +320,10 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (when id (when (xcb:+request-checked+request-check exwm--connection (make-instance 'xcb:GrabKey - :owner-events 0 :grab-window id + :owner-events 0 + :grab-window + (with-current-buffer (exwm--id->buffer id) + exwm--container) :modifiers xcb:ModMask:Any :key xcb:Grab:Any :pointer-mode xcb:GrabMode:Async @@ -338,7 +337,10 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (when id (when (xcb:+request-checked+request-check exwm--connection (make-instance 'xcb:UngrabKey - :key xcb:Grab:Any :grab-window id + :key xcb:Grab:Any + :grab-window + (with-current-buffer (exwm--id->buffer id) + exwm--container) :modifiers xcb:ModMask:Any)) (exwm--log "Failed to release keyboard for #x%x" id)) (setq exwm--on-KeyPress #'exwm-input--on-KeyPress-char-mode))) @@ -487,7 +489,7 @@ SIMULATION-KEYS is a list of alist (key-sequence1 . key-sequence2)." (add-hook 'pre-command-hook #'exwm-input--finish-key-sequence) ;; Update focus when buffer list updates (add-hook 'buffer-list-update-hook #'exwm-input--on-buffer-list-update) - (add-hook 'focus-in-hook #'exwm-input--on-focus-in) + (add-hook 'minibuffer-setup-hook #'exwm-input--on-minibuffer-setup) ;; Update prefix keys for global keys (exwm-input--update-global-prefix-keys)) diff --git a/exwm-layout.el b/exwm-layout.el index ffe1f1d..c3bfcf8 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -30,41 +30,72 @@ (defvar exwm-floating-border-width) +(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)))) + ;;;###autoload (defun exwm-layout--show (id &optional window) "Show window ID exactly fit in the Emacs window WINDOW." (exwm--log "Show #x%x in %s" id window) (xcb:+request exwm--connection (make-instance 'xcb:MapWindow :window id)) + (with-current-buffer (exwm--id->buffer id) + (xcb:+request exwm--connection + (make-instance 'xcb:MapWindow :window exwm--container))) (xcb:+request exwm--connection (make-instance 'xcb:icccm:set-WM_STATE :window id :state xcb:icccm:WM_STATE:NormalState :icon xcb:Window:None)) - (let* ((buffer (exwm--id->buffer id)) - (edges (or (and buffer - (with-current-buffer buffer exwm--floating-edges)) - (window-inside-absolute-pixel-edges window))) + (let* ((edges (window-inside-absolute-pixel-edges window)) (width (- (elt edges 2) (elt edges 0))) - (height (- (elt edges 3) (elt edges 1))) - x y) - (if exwm--floating-edges - ;; The relative position of a floating X window is determinate - (setq x exwm-floating-border-width - y exwm-floating-border-width) - (let ((relative-edges (window-inside-pixel-edges window))) - (setq x (elt relative-edges 0) - y (elt relative-edges 1)))) - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window id - :value-mask (eval-when-compile - (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y - xcb:ConfigWindow:Width - xcb:ConfigWindow:Height - xcb:ConfigWindow:StackMode)) - :x x :y y :width width :height height - ;; In order to put non-floating window at bottom - :stack-mode xcb:StackMode:Below)) + (height (- (elt edges 3) (elt edges 1)))) + (with-current-buffer (exwm--id->buffer id) + (if exwm--floating-frame + ;; 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. + (progn + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window exwm--container + :value-mask (logior xcb:ConfigWindow:Width + xcb:ConfigWindow:Height) + :width (frame-pixel-width exwm--floating-frame) + :height (frame-pixel-height + exwm--floating-frame))) + (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 exwm-floating-border-width + :y exwm-floating-border-width + :width width + :height height))) + (let ((relative-edges (window-inside-pixel-edges window))) + (exwm-layout--resize-container id exwm--container + (elt relative-edges 0) + (elt relative-edges 1) + width height + (active-minibuffer-window))))) (xcb:+request exwm--connection (make-instance 'xcb:SendEvent :propagate 0 :destination id @@ -96,6 +127,9 @@ (make-instance 'xcb:ChangeWindowAttributes :window id :value-mask xcb:CW:EventMask :event-mask exwm--client-event-mask)) + (with-current-buffer (exwm--id->buffer id) + (xcb:+request exwm--connection + (make-instance 'xcb:UnmapWindow :window exwm--container))) (xcb:+request exwm--connection (make-instance 'xcb:icccm:set-WM_STATE :window id @@ -112,38 +146,16 @@ (user-error "Already in full-screen mode.")) ;; Set the floating frame fullscreen first when the client is floating (when exwm--floating-frame - (let* ((outer-id (frame-parameter exwm--floating-frame 'exwm-outer-id)) - (geometry (xcb:+request-unchecked+reply exwm--connection + (let* ((geometry (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:GetGeometry - :drawable outer-id)))) - (setq exwm--floating-frame-geometry - (vector (slot-value geometry 'x) (slot-value geometry 'y) - (slot-value geometry 'width) - (slot-value geometry 'height))) - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window outer-id - :value-mask (eval-when-compile - (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y - xcb:ConfigWindow:Width - xcb:ConfigWindow:Height)) - :x 0 :y 0 - :width (frame-pixel-width exwm-workspace--current) - :height (frame-pixel-height - exwm-workspace--current)))) + :drawable exwm--container)))) + (setq exwm--floating-frame-position + (vector (slot-value geometry 'x) (slot-value geometry 'y)))) (xcb:flush exwm--connection)) - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window exwm--id - :value-mask (eval-when-compile - (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y - xcb:ConfigWindow:Width - xcb:ConfigWindow:Height)) - :x 0 :y 0 - :width (frame-pixel-width exwm-workspace--current) - :height (frame-pixel-height exwm-workspace--current))) + (exwm-layout--resize-container exwm--id exwm--container 0 0 + (frame-pixel-width exwm-workspace--current) + (frame-pixel-height + exwm-workspace--current)) (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_WM_STATE :window exwm--id @@ -162,17 +174,12 @@ (when exwm--floating-frame (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow - :window (frame-parameter exwm--floating-frame - 'exwm-outer-id) + :window exwm--container :value-mask (eval-when-compile (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y - xcb:ConfigWindow:Width - xcb:ConfigWindow:Height)) - :x (elt exwm--floating-frame-geometry 0) - :y (elt exwm--floating-frame-geometry 1) - :width (elt exwm--floating-frame-geometry 2) - :height (elt exwm--floating-frame-geometry 3)))) + xcb:ConfigWindow:Y)) + :x (elt exwm--floating-frame-position 0) + :y (elt exwm--floating-frame-position 1)))) (exwm-layout--show exwm--id) (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_WM_STATE :window exwm--id :data [])) @@ -193,19 +200,10 @@ (make-instance 'xcb:RECTANGLE :x 0 :y 0 :width (x-display-pixel-width) :height (x-display-pixel-height)))) - (id (frame-parameter frame 'exwm-outer-id))) + (id (frame-parameter frame 'exwm-outer-id)) + (workspace (frame-parameter frame 'exwm-workspace))) (with-slots (x y width height) geometry - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window id - :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)) + (exwm-layout--resize-container id workspace x y width height) (xcb:flush exwm--connection)))) (defvar exwm-layout-show-all-buffers nil @@ -221,7 +219,7 @@ (get-buffer "*scratch*")))) windows) (if (not (memq frame exwm-workspace--list)) - (if (frame-parameter frame 'exwm-window-id) + (if (frame-parameter frame 'exwm-outer-id) ;; Refresh a floating frame (when (eq major-mode 'exwm-mode) (let ((window (frame-first-window frame))) @@ -230,9 +228,6 @@ (exwm-layout--show exwm--id window)))) ;; Other frames (e.g. terminal/graphical frame of emacsclient) ;; We shall bury all `exwm-mode' buffers in this case - (unless placeholder ;create the *scratch* buffer if it's killed - (setq placeholder (get-buffer-create "*scratch*")) - (set-buffer-major-mode placeholder)) (setq windows (window-list frame 0)) ;exclude minibuffer (dolist (window windows) (with-current-buffer (window-buffer window) @@ -329,7 +324,6 @@ windows." (setq width (max (+ exwm--normal-hints-min-width margin) (+ width delta)))))) (when width - (setq exwm--floating-edges nil) ;invalid from now on (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window (frame-parameter exwm--floating-frame @@ -356,7 +350,6 @@ windows." (setq height (max (+ exwm--normal-hints-min-height margin) (+ height delta)))))) (when height - (setq exwm--floating-edges nil) ;invalid from now on (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window (frame-parameter exwm--floating-frame diff --git a/exwm-manage.el b/exwm-manage.el index 5b562ed..d8f91f3 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -120,7 +120,7 @@ corresponding buffer.") (make-instance 'xcb:ReparentWindow :window id :parent (frame-parameter exwm-workspace--current - 'exwm-window-id) + 'exwm-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) @@ -145,10 +145,34 @@ 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:CopyFromParent + :visual 0 ;CopyFromParent + :value-mask (logior xcb:CW:OverrideRedirect + xcb:CW:EventMask) + :override-redirect 1 + :event-mask xcb:EventMask:SubstructureRedirect)) + (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 :border-width 0)) + ;; (xcb:+request exwm--connection ;map the window + ;; (make-instance 'xcb:MapWindow :window id)) (dolist (button ;grab buttons to set focus / move / resize (list xcb:ButtonIndex:1 xcb:ButtonIndex:2 xcb:ButtonIndex:3)) (xcb:+request-checked+request-check exwm--connection @@ -190,9 +214,8 @@ corresponding buffer.") (xcb:flush exwm--connection) (when (buffer-live-p buffer) (with-current-buffer buffer - (when exwm--floating-frame - (make-frame-invisible exwm--floating-frame) - (redisplay)) + (xcb:+request exwm--connection + (make-instance 'xcb:UnmapWindow :window exwm--container)) (setq exwm-workspace--switch-history-outdated t) ;; (when withdraw-only @@ -207,9 +230,7 @@ corresponding buffer.") (setq geometry-parent (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:GetGeometry - :drawable - (frame-parameter exwm--floating-frame - 'exwm-outer-id))) + :drawable exwm--container)) geometry (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:GetGeometry :drawable id))) @@ -227,6 +248,13 @@ corresponding buffer.") (make-instance 'xcb:DeleteProperty :window id :property xcb:Atom:WM_STATE)) (xcb:flush exwm--connection)) + ;; Destroy the container (it seems it has to be delayed). + (run-with-idle-timer 0 nil + `(lambda () + (xcb:+request exwm--connection + ,(make-instance 'xcb:DestroyWindow + :window exwm--container)) + (xcb:flush exwm--connection))) (let ((kill-buffer-query-functions nil) (floating exwm--floating-frame)) (kill-buffer) @@ -252,55 +280,62 @@ corresponding buffer.") ;;;###autoload (defun exwm-manage--close-window (id &optional buffer) "Close window ID in a proper way." - (catch 'return - (unless (exwm--id->buffer id) - (throw 'return t)) - (unless buffer (setq buffer (exwm--id->buffer id))) - ;; Destroy the client window if it does not support WM_DELETE_WINDOW - (unless (and (buffer-live-p buffer) - (with-current-buffer buffer - (memq xcb:Atom:WM_DELETE_WINDOW exwm--protocols))) - (xcb:+request exwm--connection - (make-instance 'xcb:DestroyWindow :window id)) - (xcb:flush exwm--connection) - (throw 'return nil)) - ;; Try to close the window with WM_DELETE_WINDOW client message - (xcb:+request exwm--connection - (make-instance 'xcb:icccm:SendEvent - :destination id - :event (xcb:marshal - (make-instance 'xcb:icccm:WM_DELETE_WINDOW - :window id) - exwm--connection))) - (xcb:flush exwm--connection) - ;; Try to determine if the client stop responding - (with-current-buffer buffer - (unless (memq xcb:Atom:_NET_WM_PING exwm--protocols) - ;; Ensure it's dead - (run-with-timer exwm-manage-ping-timeout nil - `(lambda () (exwm-manage--kill-client ,id))) + (let (container) + (catch 'return + (unless (exwm--id->buffer id) + (throw 'return t)) + (unless buffer (setq buffer (exwm--id->buffer id))) + (when (buffer-live-p buffer) + (setq container exwm--container)) + ;; Destroy the client window if it does not support WM_DELETE_WINDOW + (unless (and (buffer-live-p buffer) + (with-current-buffer buffer + (memq xcb:Atom:WM_DELETE_WINDOW exwm--protocols))) + (xcb:+request exwm--connection + (make-instance 'xcb:DestroyWindow :window id)) + (xcb:flush exwm--connection) (throw 'return nil)) - (setq exwm-manage--ping-lock t) + ;; Try to close the window with WM_DELETE_WINDOW client message (xcb:+request exwm--connection - (make-instance 'xcb:SendEvent - :propagate 0 :destination id - :event-mask xcb:EventMask:NoEvent + (make-instance 'xcb:icccm:SendEvent + :destination id :event (xcb:marshal - (make-instance 'xcb:ewmh:_NET_WM_PING - :window id :timestamp 0 - :client-window id) + (make-instance 'xcb:icccm:WM_DELETE_WINDOW + :window id) exwm--connection))) (xcb:flush exwm--connection) - (with-timeout (exwm-manage-ping-timeout - (if (yes-or-no-p (format "`%s' is not responding. \ + ;; Try to determine if the client stop responding + (with-current-buffer buffer + (unless (memq xcb:Atom:_NET_WM_PING exwm--protocols) + ;; Ensure it's dead + (run-with-timer exwm-manage-ping-timeout nil + `(lambda () (exwm-manage--kill-client ,id))) + (throw 'return nil)) + (setq exwm-manage--ping-lock t) + (xcb:+request exwm--connection + (make-instance 'xcb:SendEvent + :propagate 0 :destination id + :event-mask xcb:EventMask:NoEvent + :event (xcb:marshal + (make-instance 'xcb:ewmh:_NET_WM_PING + :window id :timestamp 0 + :client-window id) + exwm--connection))) + (xcb:flush exwm--connection) + (with-timeout (exwm-manage-ping-timeout + (if (yes-or-no-p (format "`%s' is not responding. \ Would you like to kill it? " - (buffer-name buffer))) - (progn (exwm-manage--kill-client id) - (throw 'return nil)) - (throw 'return nil))) - (while (and exwm-manage--ping-lock - (exwm--id->buffer id)) ;may have been destroyed - (accept-process-output nil 0.1)))))) + (buffer-name buffer))) + (progn (exwm-manage--kill-client id) + (throw 'return nil)) + (throw 'return nil))) + (while (and exwm-manage--ping-lock + (exwm--id->buffer id)) ;may have been destroyed + (accept-process-output nil 0.1))))) + ;; Finally destroy the container + (xcb:+request exwm--connection + (make-instance 'xcb:DestroyWindow :window container)) + (xcb:flush exwm--connection))) (defun exwm-manage--kill-client (&optional id) "Kill an X client." @@ -338,9 +373,8 @@ Would you like to kill it? " (list 0 0 (frame-pixel-width exwm-workspace--current) (frame-pixel-height exwm-workspace--current)) - (or exwm--floating-edges - (window-inside-absolute-pixel-edges - (get-buffer-window buffer t))))) + (window-inside-absolute-pixel-edges + (get-buffer-window buffer t)))) (exwm--log "Reply with ConfigureNotify (edges): %s" edges) (xcb:+request exwm--connection (make-instance 'xcb:SendEvent diff --git a/exwm-randr.el b/exwm-randr.el index 1ac8f29..7d3609f 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -48,6 +48,7 @@ (require 'xcb-randr) (require 'exwm-core) +(require 'exwm-layout) (eval-when-compile (require 'exwm-workspace)) (defvar exwm-randr-workspace-output-plist nil) @@ -93,15 +94,10 @@ (set-frame-parameter frame 'exwm-randr-output output) (set-frame-parameter frame 'exwm-geometry geometry) (with-slots (x y width height) geometry - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window (frame-parameter frame 'exwm-outer-id) - :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)) + (exwm-layout--resize-container (frame-parameter frame 'exwm-outer-id) + (frame-parameter frame + 'exwm-workspace) + x y width height) (setq workareas (nconc workareas (list x y width height)) viewports (nconc viewports (list x y)))))) ;; Update _NET_WORKAREA diff --git a/exwm-workspace.el b/exwm-workspace.el index d1fe6cf..eb7b246 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -122,22 +122,16 @@ The optional FORCE option is for internal use only." (unless (and (<= 0 index) (< index exwm-workspace-number)) (user-error "[EXWM] Workspace index out of range: %d" index)) (when (or force (/= exwm-workspace-current-index index)) - (let ((frame (elt exwm-workspace--list index))) + (let* ((frame (elt exwm-workspace--list index)) + (workspace (frame-parameter frame 'exwm-workspace))) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window workspace + :value-mask xcb:ConfigWindow:StackMode + :stack-mode xcb:StackMode:Above)) (setq exwm-workspace--current frame exwm-workspace-current-index index) - (select-frame-set-input-focus frame) - ;; Move mouse when necessary - (let ((position (mouse-pixel-position)) - x y w h) - (unless (eq frame (car position)) - (setq x (cadr position) - y (cddr position) - w (frame-pixel-width frame) - h (frame-pixel-height frame)) - (when (or (> x w) (> y h)) - (setq x (/ w 2) - y (/ h 2))) - (set-mouse-pixel-position frame x y))) + (select-window (frame-selected-window frame)) ;; Close the (possible) active minibuffer (when (active-minibuffer-window) (run-with-idle-timer 0 nil (lambda () (abort-recursive-edit)))) @@ -161,16 +155,6 @@ The optional FORCE option is for internal use only." :window exwm--root :data index)) (xcb:flush exwm--connection))))) -(defun exwm-workspace--on-focus-in () - "Fix unexpected frame switch." - ;; `focus-in-hook' is run by `handle-switch-frame' - (unless (eq this-command 'handle-switch-frame) - (let ((index (cl-position (selected-frame) exwm-workspace--list))) - (exwm--log "Focus on workspace %s" index) - (when (and index (/= index exwm-workspace-current-index)) - (exwm--log "Workspace was switched unexpectedly") - (exwm-workspace-switch index))))) - ;;;###autoload (defun exwm-workspace-move-window (index &optional id) "Move window ID to workspace INDEX." @@ -204,10 +188,9 @@ The optional FORCE option is for internal use only." (progn (xcb:+request exwm--connection (make-instance 'xcb:ReparentWindow - :window (frame-parameter exwm--floating-frame - 'exwm-outer-id) - :parent (frame-parameter frame - 'exwm-window-id) + :window exwm--container + :parent + (frame-parameter frame 'exwm-workspace) :x 0 :y 0)) (xcb:flush exwm--connection)) ;; Move the window itself @@ -222,8 +205,10 @@ The optional FORCE option is for internal use only." (exwm-layout--hide id) (xcb:+request exwm--connection (make-instance 'xcb:ReparentWindow - :window id - :parent (frame-parameter frame 'exwm-window-id) + ;; (current-buffer) is changed. + :window (with-current-buffer (exwm--id->buffer id) + exwm--container) + :parent (frame-parameter frame 'exwm-workspace) :x 0 :y 0)) (xcb:flush exwm--connection) (set-window-buffer (frame-selected-window frame) @@ -303,50 +288,58 @@ The optional FORCE option is for internal use only." (set-frame-parameter (car exwm-workspace--list) 'client nil)) ;; Create remaining frames (dotimes (_ (1- exwm-workspace-number)) - (nconc exwm-workspace--list - (list (make-frame '((window-system . x) - (visibility . nil)))))) + (nconc exwm-workspace--list (list (make-frame '((window-system . x)))))) ;; Configure workspaces (dolist (i exwm-workspace--list) - (let ((window-id (string-to-number (frame-parameter i 'window-id))) - (outer-id (string-to-number (frame-parameter i 'outer-window-id)))) + (let ((outer-id (string-to-number (frame-parameter i 'outer-window-id))) + (workspace (xcb:generate-id exwm--connection))) ;; Save window IDs - (set-frame-parameter i 'exwm-window-id window-id) (set-frame-parameter i 'exwm-outer-id outer-id) + (set-frame-parameter i 'exwm-workspace workspace) ;; Set OverrideRedirect on all frames (xcb:+request exwm--connection (make-instance 'xcb:ChangeWindowAttributes :window outer-id :value-mask xcb:CW:OverrideRedirect :override-redirect 1)) - ;; Select events on all virtual roots (xcb:+request exwm--connection - (make-instance 'xcb:ChangeWindowAttributes - :window window-id :value-mask xcb:CW:EventMask - :event-mask xcb:EventMask:SubstructureRedirect)))) + (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:CopyFromParent + :visual 0 ;CopyFromParent + :value-mask (logior xcb:CW:OverrideRedirect + xcb:CW:EventMask) + :override-redirect 1 + :event-mask xcb:EventMask:SubstructureRedirect)) + (exwm--debug + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_WM_NAME + :window workspace + :data + (format "EXWM workspace %d" + (cl-position i exwm-workspace--list))))) + (xcb:+request exwm--connection + (make-instance 'xcb:ReparentWindow + :window outer-id :parent workspace :x 0 :y 0)) + (xcb:+request exwm--connection + (make-instance 'xcb:MapWindow :window workspace)))) (xcb:flush exwm--connection) ;; We have to advice `x-create-frame' or every call to it would hang EXWM (advice-add 'x-create-frame :around #'exwm-workspace--x-create-frame) - ;; We have to delay making the frame visible until the - ;; override-redirect flag has been set. - (select-frame-set-input-focus (car exwm-workspace--list)) - (dolist (i exwm-workspace--list) - (set-frame-parameter i 'visibility t) - (lower-frame i)) ;; Delay making the workspaces fullscreen until Emacs becomes idle (run-with-idle-timer 0 nil (lambda () (dolist (i exwm-workspace--list) (set-frame-parameter i 'fullscreen 'fullboth)))) - (raise-frame (car exwm-workspace--list)) - ;; Handle unexpected frame switch - (add-hook 'focus-in-hook #'exwm-workspace--on-focus-in) ;; 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-window-id)) + (frame-parameter i 'exwm-workspace)) exwm-workspace--list)))) ;; Switch to the first workspace (exwm-workspace-switch 0 t)) diff --git a/exwm.el b/exwm.el index 96413c9..6af5446 100644 --- a/exwm.el +++ b/exwm.el @@ -501,8 +501,7 @@ (setq exwm--connection nil) (exwm--log "Other window manager detected")) ;; Disable some features not working well with EXWM - (setq use-dialog-box nil - display-hourglass nil) + (setq use-dialog-box nil) ;; Initialize ICCCM/EWMH support ;; (xcb:icccm:init exwm--connection) (xcb:ewmh:init exwm--connection) @@ -511,9 +510,9 @@ (exwm-layout--init) (exwm-floating--init) (exwm-manage--init) + (exwm-workspace--init) (exwm-input--init) (exwm--unlock) - (exwm-workspace--init) ;; Manage existing windows (exwm-manage--scan) (run-hooks 'exwm-init-hook))))) From d8281abca4bc5182040a7866560a1806c59176d4 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Wed, 3 Feb 2016 13:23:55 +0800 Subject: [PATCH 2/2] Avoid unnecessary changes of stacking order * exwm-input.el (exwm-input--update-focus): Only restack a tiling X window when it's not the last but one sibling. This should reduce flickering. --- exwm-input.el | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 156399c..f84b5fc 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -110,20 +110,34 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (exwm--log "Set focus on #x%x" exwm--id) (exwm-input--set-focus exwm--id) ;; Adjust stacking orders - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window exwm--container - :value-mask xcb:ConfigWindow:StackMode - :stack-mode (if exwm--floating-frame - xcb:StackMode:Above - xcb:StackMode:Below))) + (if exwm--floating-frame + ;; Put this floating X window at top. + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window exwm--container + :value-mask xcb:ConfigWindow:StackMode + :stack-mode xcb:StackMode:TopIf)) + ;; This should be the last X window but one in the siblings. + (with-slots (children) + (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:QueryTree + :window + (frame-parameter exwm--frame + 'exwm-workspace))) + (unless (eq (cadr children) exwm--container) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window exwm--container + :value-mask xcb:ConfigWindow:StackMode + :stack-mode xcb:StackMode:Below))))) + ;; Make sure Emacs frames are at bottom. (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window (frame-parameter (or exwm--floating-frame exwm--frame) 'exwm-outer-id) :value-mask xcb:ConfigWindow:StackMode - :stack-mode xcb:StackMode:Below)) + :stack-mode xcb:StackMode:BottomIf)) (xcb:flush exwm--connection)) (when (eq (selected-window) exwm-input--focus-window) (exwm--log "Focus on %s" exwm-input--focus-window)