Improve input focus switch mechanism

This commit should fix most input focus bugs (especially those related to
floating windows). The actual settings of input focus are delayed to exclude
redundant event. Dead code since this commit is removed.

This commit also fixes a bug for non-floating windows converted form floating
state. The workaround for `ido-mode` is also improved to properly handle
`exwm-mode` buffers.
This commit is contained in:
Chris Feng 2015-08-12 18:09:35 +08:00
parent 1ce18afd05
commit 04e4269617
5 changed files with 62 additions and 81 deletions

View file

@ -179,12 +179,7 @@
exwm--floating-frame frame) exwm--floating-frame frame)
(set-window-buffer window (current-buffer)) ;this changes current buffer (set-window-buffer window (current-buffer)) ;this changes current buffer
(set-window-dedicated-p window t)) (set-window-dedicated-p window t))
(with-current-buffer (exwm--id->buffer id) (select-window window)))
;; Some window should not get input focus on creation
;; FIXME: other conditions?
(unless (memq xcb:Atom:_NET_WM_WINDOW_TYPE_UTILITY exwm-window-type)
(x-focus-frame exwm--floating-frame)
(exwm-input--set-focus id)))))
(defun exwm-floating--unset-floating (id) (defun exwm-floating--unset-floating (id)
"Make window ID non-floating." "Make window ID non-floating."
@ -212,11 +207,12 @@
(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 (delete-frame exwm--floating-frame))) ;remove the floating frame
(with-current-buffer buffer (with-current-buffer buffer
(setq exwm--floating-frame nil (setq window-size-fixed nil
exwm--floating-frame nil
exwm--frame exwm-workspace--current)) exwm--frame exwm-workspace--current))
(select-frame exwm-workspace--current t) (let ((window (frame-selected-window exwm-workspace--current)))
(set-window-buffer nil buffer) (set-window-buffer window buffer)
(exwm-input--set-focus id))) (select-window window))))
(defun exwm-floating-toggle-floating () (defun exwm-floating-toggle-floating ()
"Toggle the current window between floating and non-floating states." "Toggle the current window between floating and non-floating states."

View file

@ -57,8 +57,6 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.")
(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."
(with-current-buffer (exwm--id->buffer id) (with-current-buffer (exwm--id->buffer id)
(exwm--log "Set focus ID to #x%x" id)
(setq exwm-input--focus-id id)
(if (and (not exwm--hints-input) (if (and (not exwm--hints-input)
(memq xcb:Atom:WM_TAKE_FOCUS exwm--protocols)) (memq xcb:Atom:WM_TAKE_FOCUS exwm--protocols))
(progn (progn
@ -79,45 +77,53 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.")
:time xcb:Time:CurrentTime))) :time xcb:Time:CurrentTime)))
(xcb:flush exwm--connection))) (xcb:flush exwm--connection)))
(defvar exwm-input--focus-id xcb:Window:None (defvar exwm-input--focus-buffer nil "The buffer to be focused.")
"The window that is theoretically 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 ()
"Run in buffer-list-update-hook to track input focus."
(let ((frame (selected-frame))
(buffer (current-buffer)))
(when (and (not (minibufferp buffer))
(frame-parameter frame 'exwm-window-id) ;e.g. emacsclient frame
(eq buffer (window-buffer))) ;e.g. `with-temp-buffer'
(unless (and exwm-input--redirected
exwm-input--focus-buffer
(with-current-buffer exwm-input--focus-buffer
exwm--floating-frame))
(setq exwm-input--focus-buffer buffer)
(when exwm-input--timer (cancel-timer exwm-input--timer))
(setq exwm-input--timer
(run-with-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))))
(defun exwm-input--update-focus () (defun exwm-input--update-focus ()
"Update input focus." "Update input focus."
(when (and (frame-parameter nil 'exwm-window-id) ;e.g. emacsclient frame (when exwm-input--focus-buffer
(eq (current-buffer) (window-buffer))) ;e.g. `with-temp-buffer' (with-current-buffer exwm-input--focus-buffer
(if (eq major-mode 'exwm-mode) (exwm--log "Set focus on %s" exwm-input--focus-buffer)
(progn (exwm--log "Set focus ID to #x%x" exwm--id) (setq exwm-input--focus-buffer nil)
(setq exwm-input--focus-id exwm--id) (if (eq major-mode 'exwm-mode)
(when exwm--floating-frame (progn
(if (eq (selected-frame) exwm--floating-frame) (when exwm--floating-frame
;; Cancel the possible input focus redirection (redirect-frame-focus exwm--floating-frame nil)
(progn (select-frame-set-input-focus exwm--floating-frame t))
(exwm--log "Cancel input focus redirection on %s" (exwm-input--set-focus exwm--id))
exwm--floating-frame) (select-frame-set-input-focus exwm-workspace--current t)
(redirect-frame-focus exwm--floating-frame nil)) (dolist (pair exwm--id-buffer-alist)
;; Focus the floating frame (with-current-buffer (cdr pair)
(exwm--log "Focus on floating frame %s" (when (and exwm--floating-frame
exwm--floating-frame) (eq exwm--frame exwm-workspace--current))
(x-focus-frame exwm--floating-frame))) (redirect-frame-focus exwm--floating-frame exwm--frame))))))))
;; Finally focus the window
(when (exwm--id->buffer exwm-input--focus-id)
(exwm-input--set-focus exwm-input--focus-id)))
(let ((buffer (exwm--id->buffer exwm-input--focus-id)))
(when (and buffer (eq (selected-frame) exwm-workspace--current))
(with-current-buffer buffer
(exwm--log "Set focus ID to #x%x" xcb:Window:None)
(setq exwm-input--focus-id xcb:Window:None)
(if exwm--floating-frame
(unless (active-minibuffer-window)
;; Redirect input focus to the workspace frame
(exwm--log "Redirect input focus (%s => %s)"
exwm--floating-frame exwm-workspace--current)
(redirect-frame-focus exwm--floating-frame
exwm-workspace--current))
;; Focus the workspace frame
(exwm--log "Focus on workspace %s" exwm-workspace--current)
(x-focus-frame exwm-workspace--current))))))))
(defun exwm-input--finish-key-sequence () (defun exwm-input--finish-key-sequence ()
"Mark the end of a key sequence (with the aid of `pre-command-hook')." "Mark the end of a key sequence (with the aid of `pre-command-hook')."
@ -169,12 +175,8 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.")
;; Resize ;; Resize
(exwm-floating--start-moveresize event)) (exwm-floating--start-moveresize event))
(t (t
;; Click to focus (select-window (get-buffer-window (exwm--id->buffer event)
(unless (and (boundp 'exwm--id) (= event exwm--id)) 'visible))
(with-current-buffer (exwm--id->buffer event)
(select-frame-set-input-focus (or exwm--floating-frame
exwm--frame))
(select-window (get-buffer-window nil 'visible))))
;; The event should be replayed ;; The event should be replayed
(setq mode xcb:Allow:ReplayPointer)))) (setq mode xcb:Allow:ReplayPointer))))
(xcb:+request exwm--connection (xcb:+request exwm--connection
@ -246,7 +248,6 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.")
;; (when (and keysym ;; (when (and keysym
;; (setq event (xcb:keysyms:keysym->event keysym state)) ;; (setq event (xcb:keysyms:keysym->event keysym state))
;; (or exwm-input--during-key-sequence ;; (or exwm-input--during-key-sequence
;; (= exwm-input--focus-id xcb:Window:None)
;; (setq window (active-minibuffer-window)) ;; (setq window (active-minibuffer-window))
;; (eq event ?\C-c) ;mode-specific key ;; (eq event ?\C-c) ;mode-specific key
;; (memq event exwm-input--global-prefix-keys) ;; (memq event exwm-input--global-prefix-keys)
@ -273,7 +274,6 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.")
(if (and keysym (if (and keysym
(setq event (xcb:keysyms:keysym->event keysym state)) (setq event (xcb:keysyms:keysym->event keysym state))
(or exwm-input--during-key-sequence (or exwm-input--during-key-sequence
(= exwm-input--focus-id xcb:Window:None)
(setq minibuffer-window (active-minibuffer-window)) (setq minibuffer-window (active-minibuffer-window))
(eq event ?\C-c) ;mode-specific key (eq event ?\C-c) ;mode-specific key
(memq event exwm-input--global-prefix-keys) (memq event exwm-input--global-prefix-keys)
@ -466,7 +466,8 @@ SIMULATION-KEYS is a list of alist (key-sequence1 . key-sequence2)."
;; `pre-command-hook' marks the end of a key sequence (existing or not) ;; `pre-command-hook' marks the end of a key sequence (existing or not)
(add-hook 'pre-command-hook 'exwm-input--finish-key-sequence) (add-hook 'pre-command-hook 'exwm-input--finish-key-sequence)
;; Update focus when buffer list updates ;; Update focus when buffer list updates
(add-hook 'buffer-list-update-hook 'exwm-input--update-focus) (add-hook 'buffer-list-update-hook 'exwm-input--on-buffer-list-update)
(add-hook 'focus-in-hook 'exwm-input--on-focus-in)
;; Update prefix keys for global keys ;; Update prefix keys for global keys
(exwm-input--update-global-prefix-keys)) (exwm-input--update-global-prefix-keys))

View file

@ -183,15 +183,8 @@ corresponding buffer.")
(let ((floating exwm--floating-frame)) (let ((floating exwm--floating-frame))
(kill-buffer) (kill-buffer)
(when floating (when floating
(if (eq 'exwm-mode (select-window
(with-current-buffer (frame-selected-window exwm-workspace--current))))))))
(window-buffer
(frame-first-window exwm-workspace--current))
major-mode))
;; Input focus is to be set on a window
(x-focus-frame exwm-workspace--current)
;; Set input focus on a frame
(select-frame-set-input-focus exwm-workspace--current))))))))
(defun exwm-manage--scan () (defun exwm-manage--scan ()
"Search for existing windows and try to manage them." "Search for existing windows and try to manage them."

View file

@ -188,9 +188,6 @@ The optional FORCE option is for internal use only."
;; Move the window itself ;; Move the window itself
(bury-buffer) (bury-buffer)
(exwm-layout--hide id) (exwm-layout--hide id)
;; Force update input focus
(setq exwm-input--focus-id xcb:Window:None)
(exwm-input--update-focus)
(xcb:+request exwm--connection (xcb:+request exwm--connection
(make-instance 'xcb:ReparentWindow (make-instance 'xcb:ReparentWindow
:window id :window id

18
exwm.el
View file

@ -195,15 +195,9 @@
(defun exwm-reset () (defun exwm-reset ()
"Reset window to standard state: non-fullscreen, line-mode." "Reset window to standard state: non-fullscreen, line-mode."
(interactive) (interactive)
(unless (frame-parameter nil 'exwm-window-id)
;; Move focus away form a non-EXWM frame
(x-focus-frame exwm-workspace--current))
(with-current-buffer (window-buffer) (with-current-buffer (window-buffer)
(when (eq major-mode 'exwm-mode) (when (eq major-mode 'exwm-mode)
(when exwm--fullscreen (exwm-layout-unset-fullscreen)) (when exwm--fullscreen (exwm-layout-unset-fullscreen))
;; Force update input focus
(setq exwm-input--focus-id xcb:Window:None)
(exwm-input--update-focus)
;; Force refresh ;; Force refresh
(exwm-layout--refresh) (exwm-layout--refresh)
(exwm-input-grab-keyboard)))) (exwm-input-grab-keyboard))))
@ -709,12 +703,12 @@
(defun exwm--ido-buffer-window-other-frame (orig-fun buffer) (defun exwm--ido-buffer-window-other-frame (orig-fun buffer)
"Wrapper for `ido-buffer-window-other-frame' to exclude invisible windows." "Wrapper for `ido-buffer-window-other-frame' to exclude invisible windows."
(let* ((window (funcall orig-fun buffer)) (with-current-buffer buffer
(frame (window-frame window))) (if (eq major-mode 'exwm-mode)
;; Exclude windows on other workspaces ;; `ido-mode' works well with `exwm-mode' buffers
(unless (and (memq frame exwm-workspace--list) (funcall orig-fun buffer)
(not (eq frame exwm-workspace--current))) ;; Other buffers should be selected within the same workspace
window))) (get-buffer-window buffer exwm-workspace--current))))
(defun exwm--fix-ido-buffer-window-other-frame () (defun exwm--fix-ido-buffer-window-other-frame ()
"Fix `ido-buffer-window-other-frame'." "Fix `ido-buffer-window-other-frame'."