From 9c95c03e18f6d5cf78bcd54bf00f8055a3863336 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Wed, 3 Feb 2016 12:12:24 +0800 Subject: 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-input.el | 140 +++++++++++++++++++++++++++++----------------------------- 1 file changed, 71 insertions(+), 69 deletions(-) (limited to 'exwm-input.el') diff --git a/exwm-input.el b/exwm-input.el index 99a4b8fa628e..156399c74486 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)) -- cgit 1.4.1