about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--exwm-config.el22
-rw-r--r--exwm-core.el31
-rw-r--r--exwm-input.el120
3 files changed, 118 insertions, 55 deletions
diff --git a/exwm-config.el b/exwm-config.el
index a434fe8d90ff..5742cac18745 100644
--- a/exwm-config.el
+++ b/exwm-config.el
@@ -52,17 +52,17 @@
                         (interactive (list (read-shell-command "$ ")))
                         (start-process-shell-command command nil command)))
   ;; Line-editing shortcuts
-  (exwm-input-set-simulation-keys
-   '(([?\C-b] . left)
-     ([?\C-f] . right)
-     ([?\C-p] . up)
-     ([?\C-n] . down)
-     ([?\C-a] . home)
-     ([?\C-e] . end)
-     ([?\M-v] . prior)
-     ([?\C-v] . next)
-     ([?\C-d] . delete)
-     ([?\C-k] . (S-end delete))))
+  (setq exwm-input-simulation-keys
+        '(([?\C-b] . [left])
+          ([?\C-f] . [right])
+          ([?\C-p] . [up])
+          ([?\C-n] . [down])
+          ([?\C-a] . [home])
+          ([?\C-e] . [end])
+          ([?\M-v] . [prior])
+          ([?\C-v] . [next])
+          ([?\C-d] . [delete])
+          ([?\C-k] . [S-end delete])))
   ;; Enable EXWM
   (exwm-enable)
   ;; Configure Ido
diff --git a/exwm-core.el b/exwm-core.el
index 71498c9dc81f..41c3b5772495 100644
--- a/exwm-core.el
+++ b/exwm-core.el
@@ -45,7 +45,7 @@
 (defvar exwm--root nil "Root window.")
 
 (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-prefix-keys)
 (declare-function exwm-input--fake-key "exwm-input.el" (event))
@@ -189,7 +189,7 @@ least SECS seconds later."
               (active-minibuffer-window)
               (memq last-input-event exwm-input--global-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
                                                          global-map)))
           (push last-input-event unread-command-events))
@@ -229,19 +229,20 @@ least SECS seconds later."
     ;; This is merely a reference.
     ("Send simulation key" :filter
      (lambda (&rest _args)
-       (mapcar (lambda (i)
-                 (let ((keys (cdr i)))
-                   (if (vectorp keys)
-                       (setq keys (append keys))
-                     (unless (sequencep keys)
-                       (setq keys (list keys))))
-                   (vector (key-description keys)
-                           `(lambda ()
-                              (interactive)
-                              (dolist (key ',keys)
-                                (exwm-input--fake-key key)))
-                           :keys (key-description (car i)))))
-               exwm-input--simulation-keys)))
+       (let (result)
+         (maphash
+          (lambda (key value)
+            (when (sequencep key)
+              (setq result (append result
+                                   `([
+                                      ,(key-description value)
+                                      (lambda ()
+                                        (interactive)
+                                        (dolist (i ',value)
+                                          (exwm-input--fake-key i)))
+                                      :keys ,(key-description key)])))))
+          exwm-input--simulation-keys)
+         result)))
 
     ["Define global binding" exwm-input-set-key]
 
diff --git a/exwm-input.el b/exwm-input.el
index e536a14144b6..73a0dba6f22e 100644
--- a/exwm-input.el
+++ b/exwm-input.el
@@ -113,9 +113,6 @@
 
 (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
   "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-prefix-keys)
-                     (memq event exwm-input--simulation-prefix-keys)))
+                     (gethash event exwm-input--simulation-keys)))
         (setq mode xcb:Allow:AsyncKeyboard)
         (exwm-input--cache-event event))
       (unless mode
@@ -733,50 +730,111 @@ multiple keys."
 ;;   (unless (listp last-input-event)      ;not a key 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)
   "Set simulation keys.
 
-SIMULATION-KEYS is an alist of the form (original-key . simulated-key)."
-  (setq exwm-input--simulation-keys nil)
+SIMULATION-KEYS is an alist of the form (original-key . simulated-key),
+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)
-    (cl-pushnew `(,(vconcat (car i)) . ,(cdr i)) exwm-input--simulation-keys))
-  (exwm-input--update-simulation-prefix-keys))
+    (let ((original (vconcat (car i)))
+          (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)
   "Set buffer-local 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-prefix-keys)
   (use-local-map (copy-keymap exwm-mode-map))
   (let ((exwm-input--local-simulation-keys t))
     (exwm-input-set-simulation-keys simulation-keys)))
 
 ;;;###autoload
 (cl-defun exwm-input-send-simulation-key (times)
-  "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."
+  "Fake a key event according to the last input key sequence."
   (interactive "p")
   (unless (derived-mode-p 'exwm-mode)
     (cl-return-from 'exwm-input-send-simulation-key))
-  (let ((pair (assoc (this-single-command-keys) exwm-input--simulation-keys)))
-    (when pair
-      (setq pair (cdr pair))
-      (unless (listp pair)
-        (setq pair (list pair)))
-      (dotimes (_ times)
-        (dolist (j pair)
-          (exwm-input--fake-key j))))))
+  (let ((keys (gethash (this-single-command-keys)
+                       exwm-input--simulation-keys)))
+    (dotimes (_ times)
+      (dolist (key keys)
+        (exwm-input--fake-key key)))))
 
 (defun exwm-input--on-pre-command ()
   "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 atom))
                       'atom)))
+  ;; Initialize simulation keys.
+  (when exwm-input-simulation-keys
+    (exwm-input-set-simulation-keys exwm-input-simulation-keys))
   ;; Attach event listeners
   (xcb:+event exwm--connection 'xcb: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 ()
   "Exit the input module."
+  (exwm-input--unset-simulation-keys)
   (remove-hook 'pre-command-hook #'exwm-input--on-pre-command)
   (remove-hook 'post-command-hook #'exwm-input--on-post-command)
   (remove-hook 'buffer-list-update-hook #'exwm-input--on-buffer-list-update)