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`.
This commit is contained in:
Steven Allen 2022-08-08 09:05:06 -07:00
parent 089e0c8683
commit b4aac53086

View file

@ -135,16 +135,11 @@ defined in `exwm-mode-map' here."
(defvar exwm-input--timestamp-window nil) (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 (defvar exwm-input--update-focus-timer nil
"Timer for deferring the update of input focus.") "Timer for deferring the update of input focus.")
(defvar exwm-input--update-focus-window nil "The (Emacs) window to be focused. (defvar exwm-input--update-focus-window nil
This value should always be overwritten.") "The window we're currently attempting to focus.")
(defvar exwm-input--echo-area-timer nil "Timer for detecting echo area dirty.") (defvar exwm-input--echo-area-timer nil "Timer for detecting echo area dirty.")
@ -305,18 +300,11 @@ ARGS are additional arguments to CALLBACK."
(exwm--terminal-p)) ; skip other terminals, e.g. TTY client frames (exwm--terminal-p)) ; skip other terminals, e.g. TTY client frames
(exwm--log "current-buffer=%S selected-window=%S" (exwm--log "current-buffer=%S selected-window=%S"
(current-buffer) (selected-window)) (current-buffer) (selected-window))
(redirect-frame-focus (selected-frame) nil)
(setq exwm-input--update-focus-window (selected-window))
(exwm-input--update-focus-defer))) (exwm-input--update-focus-defer)))
(defun exwm-input--update-focus-defer () (defun exwm-input--update-focus-defer ()
"Defer updating input focus." "Defer updating input focus."
(when exwm-input--update-focus-defer-timer (redirect-frame-focus (selected-frame) nil)
(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 (when exwm-input--update-focus-timer
(cancel-timer exwm-input--update-focus-timer)) (cancel-timer exwm-input--update-focus-timer))
(setq exwm-input--update-focus-timer (setq exwm-input--update-focus-timer
@ -324,18 +312,28 @@ ARGS are additional arguments to CALLBACK."
(run-with-timer exwm-input--update-focus-interval (run-with-timer exwm-input--update-focus-interval
nil nil
#'exwm-input--update-focus-commit #'exwm-input--update-focus-commit
exwm-input--update-focus-window)))) (selected-window))))
(defun exwm-input--update-focus-commit (window) (defun exwm-input--update-focus-commit (window)
"Commit updating input focus." "Commit updating input focus."
(setq exwm-input--update-focus-lock t) (let ((cwin (selected-window)))
(unwind-protect (if exwm-input--update-focus-window
(exwm-input--update-focus window) ;; If we're currently focusing another window, enqueue a task to
(setq exwm-input--update-focus-lock nil))) ;; 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) (defun exwm-input--update-focus (window)
"Update input focus." "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)) (exwm--log "focus-window=%s focus-buffer=%s" window (window-buffer window))
(with-current-buffer (window-buffer window) (with-current-buffer (window-buffer window)
(if (derived-mode-p 'exwm-mode) (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)) (setq exwm-input--echo-area-timer nil))
(remove-hook 'echo-area-clear-hook #'exwm-input--on-echo-area-clear) (remove-hook 'echo-area-clear-hook #'exwm-input--on-echo-area-clear)
(remove-hook 'buffer-list-update-hook #'exwm-input--on-buffer-list-update) (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 (when exwm-input--update-focus-timer
(cancel-timer exwm-input--update-focus-timer)) (cancel-timer exwm-input--update-focus-timer))
;; Make input focus working even without a WM. ;; Make input focus working even without a WM.