Replay KeyPress events instead of fake them in line-mode

X windows in line-mode receive KeyPress events faked with SendEvent requests
previously. This causes many problems including:

* Some applications (e.g. xterm) ignore synthetic events completely
* KeyPress and KeyRelease evnets arrive disorderly

This commit makes EXWM exploiting AllowEvents requests (in ReplayKeyboard mode)
to forward KeyPress events to X windows instead.
This commit is contained in:
Chris Feng 2015-08-26 15:27:31 +08:00
parent 874c383672
commit 981293f06a

View file

@ -241,61 +241,27 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.")
(defvar exwm-input--temp-line-mode nil (defvar exwm-input--temp-line-mode nil
"Non-nil indicates it's in temporary line-mode for char-mode.") "Non-nil indicates it's in temporary line-mode for char-mode.")
;; ;; This implementation has a problem that it also releases queued keys after
;; ;; requesting AllowEvent. The client window will capture unexpected key events
;; ;; in this case.
;; ;; P.S.; to use this implementation, comment out the KeyRelease listener
;; ;; together with this one and make GrabKey in Sync mode.
;; (cl-defmethod exwm-input--on-KeyPress-line-mode ((obj xcb:KeyPress))
;; "Parse X KeyPress event to Emacs key event and then feed the command loop."
;; (with-slots (detail state) obj
;; (let ((keysym (xcb:keysyms:keycode->keysym exwm--connection detail state))
;; event window mode)
;; (when (and keysym
;; (setq event (xcb:keysyms:keysym->event keysym state))
;; (or exwm-input--during-key-sequence
;; (setq window (active-minibuffer-window))
;; (eq event ?\C-c) ;mode-specific key
;; (memq event exwm-input--global-prefix-keys)
;; (memq event exwm-input-prefix-keys)
;; (memq event
;; exwm-input--simulation-prefix-keys)))
;; (setq mode xcb:Allow:SyncKeyboard)
;; (unless window (setq exwm-input--during-key-sequence t))
;; (push event unread-command-events))
;; (xcb:+request exwm--connection
;; (make-instance 'xcb:AllowEvents
;; :mode (or mode xcb:Allow:ReplayKeyboard)
;; :time xcb:Time:CurrentTime))
;; (xcb:flush exwm--connection))))
;; This implementation has a drawback that some (legacy) applications
;; (e.g. xterm) ignore the synthetic key events, making it only viable for EXWM
;; to work in char-mode in such case.
(cl-defmethod exwm-input--on-KeyPress-line-mode ((obj xcb:KeyPress)) (cl-defmethod exwm-input--on-KeyPress-line-mode ((obj xcb:KeyPress))
"Parse X KeyPress event to Emacs key event and then feed the command loop." "Parse X KeyPress event to Emacs key event and then feed the command loop."
(with-slots (detail state) obj (with-slots (detail state) obj
(let ((keysym (xcb:keysyms:keycode->keysym exwm--connection detail state)) (let ((keysym (xcb:keysyms:keycode->keysym exwm--connection detail state))
event minibuffer-window) event minibuffer-window mode)
(if (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
(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)
(memq event exwm-input-prefix-keys) (memq event exwm-input-prefix-keys)
(memq event exwm-input--simulation-prefix-keys))) (memq event exwm-input--simulation-prefix-keys)))
;; Forward key to Emacs frame (setq mode xcb:Allow:AsyncKeyboard)
(progn (unless minibuffer-window (unless minibuffer-window (setq exwm-input--during-key-sequence t))
(setq exwm-input--during-key-sequence t)) (push event unread-command-events))
(push event unread-command-events)) (xcb:+request exwm--connection
;; Forward key to window (make-instance 'xcb:AllowEvents
(xcb:+request exwm--connection :mode (or mode xcb:Allow:ReplayKeyboard)
(make-instance 'xcb:SendEvent :time xcb:Time:CurrentTime))
:propagate 0 :destination (slot-value obj 'event) (xcb:flush exwm--connection))))
:event-mask xcb:EventMask:NoEvent
:event (xcb:marshal obj exwm--connection)))
(xcb:flush exwm--connection)))))
(cl-defmethod exwm-input--on-KeyPress-char-mode ((obj xcb:KeyPress)) (cl-defmethod exwm-input--on-KeyPress-char-mode ((obj xcb:KeyPress))
"Handle KeyPress event in char-mode." "Handle KeyPress event in char-mode."
@ -307,7 +273,12 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.")
(setq exwm-input--temp-line-mode t (setq exwm-input--temp-line-mode t
exwm-input--during-key-sequence t) exwm-input--during-key-sequence t)
(exwm-input--grab-keyboard)) ;grab keyboard temporarily (exwm-input--grab-keyboard)) ;grab keyboard temporarily
(push event unread-command-events))))) (push event unread-command-events))))
(xcb:+request exwm--connection
(make-instance 'xcb:AllowEvents
:mode xcb:Allow:AsyncKeyboard
:time xcb:Time:CurrentTime))
(xcb:flush exwm--connection))
(defun exwm-input--grab-keyboard (&optional id) (defun exwm-input--grab-keyboard (&optional id)
"Grab all key events on window ID." "Grab all key events on window ID."
@ -319,7 +290,7 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.")
:modifiers xcb:ModMask:Any :modifiers xcb:ModMask:Any
:key xcb:Grab:Any :key xcb:Grab:Any
:pointer-mode xcb:GrabMode:Async :pointer-mode xcb:GrabMode:Async
:keyboard-mode xcb:GrabMode:Async)) :keyboard-mode xcb:GrabMode:Sync))
(exwm--log "Failed to grab keyboard for #x%x" id)) (exwm--log "Failed to grab keyboard for #x%x" id))
(setq exwm--on-KeyPress 'exwm-input--on-KeyPress-line-mode))) (setq exwm--on-KeyPress 'exwm-input--on-KeyPress-line-mode)))