From b4aac530862f5ce6b8293f75a9e576b48f44c013 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 8 Aug 2022 09:05:06 -0700 Subject: [PATCH] Simplify and improve focus handling - No need for two different timers, roll them into one. - Debounce focus updates, waiting until the focus has stabilized for a full focus interval. - Ignore windows in "no focus" frames. * exwm-input.el (exwm-input--update-focus-defer-timer): Remove the duplicate timer. (exwm-input--update-focus-lock): Bind `exwm-input--update-focus-window` instead. (exwm-input--on-buffer-list-update): Move focus logic to `exwm-input--update-focus-defer`. (exwm-input--update-focus-defer): Use a single `exwm-input--update-focus-timer` and don't record the window we're focusing, we pass that into `exwm-input--update-focus-commit` anyways. (exwm-input--update-focus-commit): Debounce focus updates and only commit them if the currently selected window matches the selected window when `exwm-input--update-focus-defer` was called. (exwm-input--update-focus-commit): Let-bind the window we're attempting to focus instead of setting a "locked" flag. This lets us deduplicate recursive attempts to re-focus the current window. (exwm-input--update-focus): Avoid focusing windows in frames with the `no-accept-focus` frame property. (exwm-input--exit): Remove references to `exwm-input--update-focus-defer-timer`. --- exwm-input.el | 56 ++++++++++++++++++++++++--------------------------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 835705f..e40849d 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -135,16 +135,11 @@ defined in `exwm-mode-map' here." (defvar exwm-input--timestamp-window nil) -(defvar exwm-input--update-focus-defer-timer nil "Timer for polling the lock.") - -(defvar exwm-input--update-focus-lock nil - "Lock for solving input focus update contention.") - (defvar exwm-input--update-focus-timer nil "Timer for deferring the update of input focus.") -(defvar exwm-input--update-focus-window nil "The (Emacs) window to be focused. -This value should always be overwritten.") +(defvar exwm-input--update-focus-window nil + "The window we're currently attempting to focus.") (defvar exwm-input--echo-area-timer nil "Timer for detecting echo area dirty.") @@ -305,37 +300,40 @@ ARGS are additional arguments to CALLBACK." (exwm--terminal-p)) ; skip other terminals, e.g. TTY client frames (exwm--log "current-buffer=%S selected-window=%S" (current-buffer) (selected-window)) - (redirect-frame-focus (selected-frame) nil) - (setq exwm-input--update-focus-window (selected-window)) (exwm-input--update-focus-defer))) (defun exwm-input--update-focus-defer () "Defer updating input focus." - (when exwm-input--update-focus-defer-timer - (cancel-timer exwm-input--update-focus-defer-timer)) - (if exwm-input--update-focus-lock - (setq exwm-input--update-focus-defer-timer - (exwm--defer 0 #'exwm-input--update-focus-defer)) - (setq exwm-input--update-focus-defer-timer nil) - (when exwm-input--update-focus-timer - (cancel-timer exwm-input--update-focus-timer)) - (setq exwm-input--update-focus-timer - ;; Attempt to accumulate successive events close enough. - (run-with-timer exwm-input--update-focus-interval - nil - #'exwm-input--update-focus-commit - exwm-input--update-focus-window)))) + (redirect-frame-focus (selected-frame) nil) + (when exwm-input--update-focus-timer + (cancel-timer exwm-input--update-focus-timer)) + (setq exwm-input--update-focus-timer + ;; Attempt to accumulate successive events close enough. + (run-with-timer exwm-input--update-focus-interval + nil + #'exwm-input--update-focus-commit + (selected-window)))) (defun exwm-input--update-focus-commit (window) "Commit updating input focus." - (setq exwm-input--update-focus-lock t) - (unwind-protect - (exwm-input--update-focus window) - (setq exwm-input--update-focus-lock nil))) + (let ((cwin (selected-window))) + (if exwm-input--update-focus-window + ;; If we're currently focusing another window, enqueue a task to + ;; update the focus again after we're done. Otherwise, if we're + ;; working on focusing this window, discard the duplicate event. + (unless (eq exwm-input--update-focus-window cwin) + (exwm-input--update-focus-defer)) + ;; Debounce focus updates. If we've switched windows since we scheduled + ;; the focus commit, wait again until we've stabilized on a window. + (if (and cwin window (eq cwin window)) + (let ((exwm-input--update-focus-window cwin)) + (exwm-input--update-focus window)) + (exwm-input--update-focus-defer))))) (defun exwm-input--update-focus (window) "Update input focus." - (when (window-live-p window) + (when (and (window-live-p window) + (not (frame-parameter (window-frame window) 'no-accept-focus))) (exwm--log "focus-window=%s focus-buffer=%s" window (window-buffer window)) (with-current-buffer (window-buffer window) (if (derived-mode-p 'exwm-mode) @@ -1234,8 +1232,6 @@ One use is to access the keymap bound to KEYS (as prefix keys) in `char-mode'." (setq exwm-input--echo-area-timer nil)) (remove-hook 'echo-area-clear-hook #'exwm-input--on-echo-area-clear) (remove-hook 'buffer-list-update-hook #'exwm-input--on-buffer-list-update) - (when exwm-input--update-focus-defer-timer - (cancel-timer exwm-input--update-focus-defer-timer)) (when exwm-input--update-focus-timer (cancel-timer exwm-input--update-focus-timer)) ;; Make input focus working even without a WM.