Add Customize interface for simulation keys

* exwm-input.el (exwm-input-simulation-keys): New user option for
setting simulation keys.
(exwm-input--init): Initialize simulation keys from this variable.

* exwm-input.el (exwm-input--simulation-prefix-keys)
(exwm-input--on-KeyPress-line-mode)
(exwm-input--update-simulation-prefix-keys)
(exwm-input-set-simulation-keys, exwm-input-set-local-simulation-keys)
(exwm-input-send-simulation-key): `exwm-input--simulation-keys' is now
a hash table storing both simulation keys and prefix keys.
(exwm-input--unset-simulation-keys): Clear the hash table.
(exwm-input--exit): Use `exwm-input--unset-simulation-keys'.
* exwm-core.el (exwm--kmacro-map, exwm-mode-menu): Adapt to the
change of `exwm-input--simulation-keys'.

* exwm-config.el (exwm-config-default): Update the example.
This commit is contained in:
Chris Feng 2018-02-20 21:57:49 +08:00
parent 5c5729c0d4
commit 7d4c57a6ab
3 changed files with 118 additions and 55 deletions

View file

@ -52,17 +52,17 @@
(interactive (list (read-shell-command "$ "))) (interactive (list (read-shell-command "$ ")))
(start-process-shell-command command nil command))) (start-process-shell-command command nil command)))
;; Line-editing shortcuts ;; Line-editing shortcuts
(exwm-input-set-simulation-keys (setq exwm-input-simulation-keys
'(([?\C-b] . left) '(([?\C-b] . [left])
([?\C-f] . right) ([?\C-f] . [right])
([?\C-p] . up) ([?\C-p] . [up])
([?\C-n] . down) ([?\C-n] . [down])
([?\C-a] . home) ([?\C-a] . [home])
([?\C-e] . end) ([?\C-e] . [end])
([?\M-v] . prior) ([?\M-v] . [prior])
([?\C-v] . next) ([?\C-v] . [next])
([?\C-d] . delete) ([?\C-d] . [delete])
([?\C-k] . (S-end delete)))) ([?\C-k] . [S-end delete])))
;; Enable EXWM ;; Enable EXWM
(exwm-enable) (exwm-enable)
;; Configure Ido ;; Configure Ido

View file

@ -45,7 +45,7 @@
(defvar exwm--root nil "Root window.") (defvar exwm--root nil "Root window.")
(defvar exwm-input--global-prefix-keys) (defvar exwm-input--global-prefix-keys)
(defvar exwm-input--simulation-prefix-keys) (defvar exwm-input--simulation-keys)
(defvar exwm-input-line-mode-passthrough) (defvar exwm-input-line-mode-passthrough)
(defvar exwm-input-prefix-keys) (defvar exwm-input-prefix-keys)
(declare-function exwm-input--fake-key "exwm-input.el" (event)) (declare-function exwm-input--fake-key "exwm-input.el" (event))
@ -189,7 +189,7 @@ least SECS seconds later."
(active-minibuffer-window) (active-minibuffer-window)
(memq last-input-event exwm-input--global-prefix-keys) (memq last-input-event exwm-input--global-prefix-keys)
(memq last-input-event exwm-input-prefix-keys) (memq last-input-event exwm-input-prefix-keys)
(memq last-input-event exwm-input--simulation-prefix-keys)) (gethash last-input-event exwm-input--simulation-keys))
(set-transient-map (make-composed-keymap (list exwm-mode-map (set-transient-map (make-composed-keymap (list exwm-mode-map
global-map))) global-map)))
(push last-input-event unread-command-events)) (push last-input-event unread-command-events))
@ -229,19 +229,20 @@ least SECS seconds later."
;; This is merely a reference. ;; This is merely a reference.
("Send simulation key" :filter ("Send simulation key" :filter
(lambda (&rest _args) (lambda (&rest _args)
(mapcar (lambda (i) (let (result)
(let ((keys (cdr i))) (maphash
(if (vectorp keys) (lambda (key value)
(setq keys (append keys)) (when (sequencep key)
(unless (sequencep keys) (setq result (append result
(setq keys (list keys)))) `([
(vector (key-description keys) ,(key-description value)
`(lambda () (lambda ()
(interactive) (interactive)
(dolist (key ',keys) (dolist (i ',value)
(exwm-input--fake-key key))) (exwm-input--fake-key i)))
:keys (key-description (car i))))) :keys ,(key-description key)])))))
exwm-input--simulation-keys))) exwm-input--simulation-keys)
result)))
["Define global binding" exwm-input-set-key] ["Define global binding" exwm-input-set-key]

View file

@ -113,9 +113,6 @@
(defvar exwm-input--simulation-keys nil "Simulation keys in line-mode.") (defvar exwm-input--simulation-keys nil "Simulation keys in line-mode.")
(defvar exwm-input--simulation-prefix-keys nil
"List of prefix keys of simulation keys in line-mode.")
(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.")
@ -537,7 +534,7 @@ called interactively. Only invoke it non-interactively in configuration."
;; ;;
(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))) (gethash event exwm-input--simulation-keys)))
(setq mode xcb:Allow:AsyncKeyboard) (setq mode xcb:Allow:AsyncKeyboard)
(exwm-input--cache-event event)) (exwm-input--cache-event event))
(unless mode (unless mode
@ -733,50 +730,111 @@ multiple keys."
;; (unless (listp last-input-event) ;not a key event ;; (unless (listp last-input-event) ;not a key event
;; (exwm-input--fake-key last-input-event))) ;; (exwm-input--fake-key last-input-event)))
(defun exwm-input--update-simulation-prefix-keys ()
"Update the list of prefix keys of simulation keys."
(setq exwm-input--simulation-prefix-keys nil)
(dolist (i exwm-input--simulation-keys)
(if exwm-input--local-simulation-keys
(local-set-key (car i) #'exwm-input-send-simulation-key)
(define-key exwm-mode-map (car i) #'exwm-input-send-simulation-key))
(cl-pushnew (elt (car i) 0) exwm-input--simulation-prefix-keys)))
(defun exwm-input-set-simulation-keys (simulation-keys) (defun exwm-input-set-simulation-keys (simulation-keys)
"Set simulation keys. "Set simulation keys.
SIMULATION-KEYS is an alist of the form (original-key . simulated-key)." SIMULATION-KEYS is an alist of the form (original-key . simulated-key),
(setq exwm-input--simulation-keys nil) where both original-key and simulated-key are key sequences.
Simulation keys set this way take effect in real time. For configuration
it's recommended to customize or set `exwm-input-simulation-keys' instead."
;; Clear keymaps and the hash table.
(when (hash-table-p exwm-input--simulation-keys)
(maphash (lambda (key _value)
(when (sequencep key)
(if exwm-input--local-simulation-keys
(local-unset-key key)
(define-key exwm-mode-map key nil))))
exwm-input--simulation-keys)
(clrhash exwm-input--simulation-keys))
;; Update the hash table.
(setq exwm-input--simulation-keys (make-hash-table :test #'equal))
(dolist (i simulation-keys) (dolist (i simulation-keys)
(cl-pushnew `(,(vconcat (car i)) . ,(cdr i)) exwm-input--simulation-keys)) (let ((original (vconcat (car i)))
(exwm-input--update-simulation-prefix-keys)) (simulated (cdr i)))
(setq simulated (if (sequencep simulated)
(append simulated nil)
(list simulated)))
;; The key stored is a key sequence (vector).
;; The value stored is a list of key events.
(puthash original simulated exwm-input--simulation-keys)
;; Also mark the prefix key as used.
(puthash (aref original 0) t exwm-input--simulation-keys)))
;; Update keymaps.
(maphash (lambda (key _value)
(when (sequencep key)
(if exwm-input--local-simulation-keys
(local-set-key key #'exwm-input-send-simulation-key)
(define-key exwm-mode-map key
#'exwm-input-send-simulation-key))))
exwm-input--simulation-keys))
(defcustom exwm-input-simulation-keys nil
"Simulation keys.
It is an alist of the form (original-key . simulated-key), where both
original-key and simulated-key are key sequences. Original-key is what you
type to an X window in line-mode which then gets translated to simulated-key
by EXWM and forwarded to the X window.
Notes:
* Setting the value directly (rather than customizing it) after EXWM
finishes initialization has no effect.
* Original-keys consist of multiple key events are only supported in Emacs
27 and later.
* A minority of applications do not accept simulated keys by default. It's
required to customize them to accept events sent by SendEvent.
* The predefined examples in the Customize interface are not guaranteed to
work for all applications. This can be tweaked on a per application basis
with `exwm-input-set-local-simulation-keys'."
:type '(alist :key-type (choice (key-sequence :tag "Original"))
:value-type (choice (key-sequence :tag "Move left" [left])
(key-sequence :tag "Move right" [right])
(key-sequence :tag "Move up" [up])
(key-sequence :tag "Move down" [down])
(key-sequence :tag "Move to BOL" [home])
(key-sequence :tag "Move to EOL" [end])
(key-sequence :tag "Page up" [prior])
(key-sequence :tag "Page down" [next])
(key-sequence :tag "Copy" [C-c])
(key-sequence :tag "Paste" [C-v])
(key-sequence :tag "Delete" [delete])
(key-sequence :tag "Delete to EOL"
[S-end delete])
(key-sequence :tag "User-defined")))
:set (lambda (symbol value)
(set symbol value)
(exwm-input-set-simulation-keys value)))
(defun exwm-input--unset-simulation-keys ()
"Clear simulation keys and key bindings defined."
(when (hash-table-p exwm-input--simulation-keys)
(maphash (lambda (key _value)
(when (sequencep key)
(define-key exwm-mode-map key nil)))
exwm-input--simulation-keys)
(clrhash exwm-input--simulation-keys)))
(defun exwm-input-set-local-simulation-keys (simulation-keys) (defun exwm-input-set-local-simulation-keys (simulation-keys)
"Set buffer-local simulation keys. "Set buffer-local simulation keys.
Its usage is the same with `exwm-input-set-simulation-keys'." Its usage is the same with `exwm-input-set-simulation-keys'."
(make-local-variable 'exwm-input--simulation-keys) (make-local-variable 'exwm-input--simulation-keys)
(make-local-variable 'exwm-input--simulation-prefix-keys)
(use-local-map (copy-keymap exwm-mode-map)) (use-local-map (copy-keymap exwm-mode-map))
(let ((exwm-input--local-simulation-keys t)) (let ((exwm-input--local-simulation-keys t))
(exwm-input-set-simulation-keys simulation-keys))) (exwm-input-set-simulation-keys simulation-keys)))
;;;###autoload ;;;###autoload
(cl-defun exwm-input-send-simulation-key (times) (cl-defun exwm-input-send-simulation-key (times)
"Fake a key event according to the last input key sequence. "Fake a key event according to the last input key sequence."
Sending multiple fake keys at once is only supported by Emacs 27 and later."
(interactive "p") (interactive "p")
(unless (derived-mode-p 'exwm-mode) (unless (derived-mode-p 'exwm-mode)
(cl-return-from 'exwm-input-send-simulation-key)) (cl-return-from 'exwm-input-send-simulation-key))
(let ((pair (assoc (this-single-command-keys) exwm-input--simulation-keys))) (let ((keys (gethash (this-single-command-keys)
(when pair exwm-input--simulation-keys)))
(setq pair (cdr pair)) (dotimes (_ times)
(unless (listp pair) (dolist (key keys)
(setq pair (list pair))) (exwm-input--fake-key key)))))
(dotimes (_ times)
(dolist (j pair)
(exwm-input--fake-key j))))))
(defun exwm-input--on-pre-command () (defun exwm-input--on-pre-command ()
"Run in `pre-command-hook'." "Run in `pre-command-hook'."
@ -814,6 +872,9 @@ Sending multiple fake keys at once is only supported by Emacs 27 and later."
:name-len (length atom) :name-len (length atom)
:name atom)) :name atom))
'atom))) 'atom)))
;; Initialize simulation keys.
(when exwm-input-simulation-keys
(exwm-input-set-simulation-keys exwm-input-simulation-keys))
;; Attach event listeners ;; Attach event listeners
(xcb:+event exwm--connection 'xcb:PropertyNotify (xcb:+event exwm--connection 'xcb:PropertyNotify
#'exwm-input--on-PropertyNotify) #'exwm-input--on-PropertyNotify)
@ -841,6 +902,7 @@ Sending multiple fake keys at once is only supported by Emacs 27 and later."
(defun exwm-input--exit () (defun exwm-input--exit ()
"Exit the input module." "Exit the input module."
(exwm-input--unset-simulation-keys)
(remove-hook 'pre-command-hook #'exwm-input--on-pre-command) (remove-hook 'pre-command-hook #'exwm-input--on-pre-command)
(remove-hook 'post-command-hook #'exwm-input--on-post-command) (remove-hook 'post-command-hook #'exwm-input--on-post-command)
(remove-hook 'buffer-list-update-hook #'exwm-input--on-buffer-list-update) (remove-hook 'buffer-list-update-hook #'exwm-input--on-buffer-list-update)