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)
(set-window-buffer window (current-buffer)) ;this changes current buffer
(set-window-dedicated-p window t))
(with-current-buffer (exwm--id->buffer id)
;; 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)))))
(select-window window)))
(defun exwm-floating--unset-floating (id)
"Make window ID non-floating."
@ -212,11 +207,12 @@
(set-window-dedicated-p (frame-first-window exwm--floating-frame) nil)
(delete-frame exwm--floating-frame))) ;remove the floating frame
(with-current-buffer buffer
(setq exwm--floating-frame nil
(setq window-size-fixed nil
exwm--floating-frame nil
exwm--frame exwm-workspace--current))
(select-frame exwm-workspace--current t)
(set-window-buffer nil buffer)
(exwm-input--set-focus id)))
(let ((window (frame-selected-window exwm-workspace--current)))
(set-window-buffer window buffer)
(select-window window))))
(defun exwm-floating-toggle-floating ()
"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)
"Set input focus to window ID in a proper way."
(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)
(memq xcb:Atom:WM_TAKE_FOCUS exwm--protocols))
(progn
@ -79,45 +77,53 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.")
:time xcb:Time:CurrentTime)))
(xcb:flush exwm--connection)))
(defvar exwm-input--focus-id xcb:Window:None
"The window that is theoretically focused.")
(defvar exwm-input--focus-buffer nil "The buffer 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 ()
"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 ()
"Update input focus."
(when (and (frame-parameter nil 'exwm-window-id) ;e.g. emacsclient frame
(eq (current-buffer) (window-buffer))) ;e.g. `with-temp-buffer'
(when exwm-input--focus-buffer
(with-current-buffer exwm-input--focus-buffer
(exwm--log "Set focus on %s" exwm-input--focus-buffer)
(setq exwm-input--focus-buffer nil)
(if (eq major-mode 'exwm-mode)
(progn (exwm--log "Set focus ID to #x%x" exwm--id)
(setq exwm-input--focus-id exwm--id)
(when exwm--floating-frame
(if (eq (selected-frame) exwm--floating-frame)
;; Cancel the possible input focus redirection
(progn
(exwm--log "Cancel input focus redirection on %s"
exwm--floating-frame)
(redirect-frame-focus exwm--floating-frame nil))
;; Focus the floating frame
(exwm--log "Focus on floating frame %s"
exwm--floating-frame)
(x-focus-frame exwm--floating-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))))))))
(when exwm--floating-frame
(redirect-frame-focus exwm--floating-frame nil)
(select-frame-set-input-focus exwm--floating-frame t))
(exwm-input--set-focus exwm--id))
(select-frame-set-input-focus exwm-workspace--current 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))))))))
(defun exwm-input--finish-key-sequence ()
"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
(exwm-floating--start-moveresize event))
(t
;; Click to focus
(unless (and (boundp 'exwm--id) (= event exwm--id))
(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))))
(select-window (get-buffer-window (exwm--id->buffer event)
'visible))
;; The event should be replayed
(setq mode xcb:Allow:ReplayPointer))))
(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
;; (setq event (xcb:keysyms:keysym->event keysym state))
;; (or exwm-input--during-key-sequence
;; (= exwm-input--focus-id xcb:Window:None)
;; (setq window (active-minibuffer-window))
;; (eq event ?\C-c) ;mode-specific key
;; (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
(setq event (xcb:keysyms:keysym->event keysym state))
(or exwm-input--during-key-sequence
(= exwm-input--focus-id xcb:Window:None)
(setq minibuffer-window (active-minibuffer-window))
(eq event ?\C-c) ;mode-specific key
(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)
(add-hook 'pre-command-hook 'exwm-input--finish-key-sequence)
;; 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
(exwm-input--update-global-prefix-keys))

View file

@ -183,15 +183,8 @@ corresponding buffer.")
(let ((floating exwm--floating-frame))
(kill-buffer)
(when floating
(if (eq 'exwm-mode
(with-current-buffer
(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))))))))
(select-window
(frame-selected-window exwm-workspace--current))))))))
(defun exwm-manage--scan ()
"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
(bury-buffer)
(exwm-layout--hide id)
;; Force update input focus
(setq exwm-input--focus-id xcb:Window:None)
(exwm-input--update-focus)
(xcb:+request exwm--connection
(make-instance 'xcb:ReparentWindow
:window id

18
exwm.el
View file

@ -195,15 +195,9 @@
(defun exwm-reset ()
"Reset window to standard state: non-fullscreen, line-mode."
(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)
(when (eq major-mode 'exwm-mode)
(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
(exwm-layout--refresh)
(exwm-input-grab-keyboard))))
@ -709,12 +703,12 @@
(defun exwm--ido-buffer-window-other-frame (orig-fun buffer)
"Wrapper for `ido-buffer-window-other-frame' to exclude invisible windows."
(let* ((window (funcall orig-fun buffer))
(frame (window-frame window)))
;; Exclude windows on other workspaces
(unless (and (memq frame exwm-workspace--list)
(not (eq frame exwm-workspace--current)))
window)))
(with-current-buffer buffer
(if (eq major-mode 'exwm-mode)
;; `ido-mode' works well with `exwm-mode' buffers
(funcall orig-fun buffer)
;; Other buffers should be selected within the same workspace
(get-buffer-window buffer exwm-workspace--current))))
(defun exwm--fix-ido-buffer-window-other-frame ()
"Fix `ido-buffer-window-other-frame'."