aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCecilio Pardo2024-12-02 17:30:42 +0100
committerStefan Monnier2025-06-30 11:34:54 -0400
commitba02efb85e403a55d321e3d213f1449246ad9484 (patch)
tree59689a9187907215c6f0ac3ecc1057f59f11f328
parent67aed063f6460e8add0fcfdebf562a3062cc26a2 (diff)
downloademacs-scratch/low-level-key.tar.gz
emacs-scratch/low-level-key.zip
Go through normal keymapsscratch/low-level-key
In this new version I changed the way events are handled. Now llk-handle generates input events to be used with normal keymaps, instead of running a command. The function llk-bind activates event generation for a key and a combination of events press, release, double, triple. I also made changes following your notes, such as: - Clarification on the event's time, documentation of keysyms variables moved, don't limit to GUI systems. - cl-defstruct for the event payload. - Format and naming conventions. - Default timeout to mouse double click timeout. You suggested to auto generate the keysym table, but we would still need a table to relate the Windows keys to the X equivalent. * doc/lispref/commands.texi (Misc Events): Adjust to new semantics. * src/keyboard.c (syms_of_keyboard): Adust docstring. FIXME: Old commit msg. * lisp/low-level-key.el (New file). (low-level-key): Struct with event data. (llk-bindings): User bindings for low level key events. (llk-tap-timeout): User option. (llk-keysyms): List of available keysyms. (llk--define-xk): Macro for defining keysyms. (llk--define-keysyms): Build llk-keysyms. (llk-bind): Function to create a binding. (llk--event-history-for-tap): Event history for tap detection. (llk--detect-n-tap): Function to detect taps. (describe-low-level-key): Command to get information about a key. (llk--describe): Show help buffer with information about an event. (llk-handle): Handler for key events. (llk--generate-event): Generate input event for key. (llk--generate-events): Generate input events for key.
-rw-r--r--doc/lispref/commands.texi41
-rw-r--r--lisp/low-level-key.el615
-rw-r--r--src/keyboard.c9
3 files changed, 342 insertions, 323 deletions
diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi
index 32b4df6b3bf..01565727e15 100644
--- a/doc/lispref/commands.texi
+++ b/doc/lispref/commands.texi
@@ -2670,35 +2670,28 @@ size of the object beneath the gesture: image, window, etc.
2670 2670
2671@cindex @code{low-level-key} event 2671@cindex @code{low-level-key} event
2672@item (low-level-key @var{is-key-press} @var{key} @var{modifier} @var{time} @var{frame}) 2672@item (low-level-key @var{is-key-press} @var{key} @var{modifier} @var{time} @var{frame})
2673This event is sent on the physical press or release of keys, only on GUI 2673This event is sent on the physical press or release of keys, only on
2674systems, currently X, MS-Windows and PGTK, and only if the variable 2674systems where it is supported, currently X, MS-Windows and PGTK, and
2675@code{enable-low-level-key-events} has a non-@code{nil} value. See its 2675only if the variable @code{enable-low-level-key-events} has a
2676documentation for the values it can take, that allow to filter which 2676non-@code{nil} value. See its documentation for the values to use, that
2677keys should generate this kind of event. 2677can activate the events for all or some keys.
2678 2678
2679@var{is-key-press} is @code{t} for a key press, @code{nil} for a key release. 2679@var{is-key-press} is @code{t} for a key press, @code{nil} for a key
2680@var{time} is the event's time in milliseconds, @var{frame} is the 2680release. @var{time} is the event's time in milliseconds, as reported by
2681frame receiving it. @var{modifier} is @code{nil} if the key is not a 2681the underlying platform, and should only be used to measure time
2682intervals between events of this same kind. @var{frame} is the frame
2683receiving the event. @var{modifier} is @code{nil} if the key is not a
2682modifier key, @code{t} if it is, but it is unknown which one, or one of 2684modifier key, @code{t} if it is, but it is unknown which one, or one of
2683@code{shift}, @code{control}, @code{meta}, @code{alt}, @code{super}, 2685@code{shift}, @code{control}, @code{meta}, @code{alt}, @code{super},
2684@code{hyper}. 2686@code{hyper}.
2685 2687
2686@var{key}, an integer, identifies the key pressed or released. This 2688@var{key}, an integer, identifies the key pressed or released. This
2687number is platform dependent, but there are variables for most keys that 2689number is platform dependent.
2688can be used in place of the numbers to identify them. For example, the 2690
2689variable @code{xk-backspace} identifies the @key{backspace} key. 2691This is a special event (@pxref{Special Events}), ignored by
2690 2692default. Loading @file{low-level-key.el} sets a handler
2691The names are parallel to those for KeySyms on X, as defined in 2693that can generate normal input events for key press, release and multi
2692@file{xkeysymdef.h}. For example, @code{XK_Shift_L} (the left shift 2694tap. See the function @code{llk-bind}.
2693key), corresponds to @code{xk-shift-l}. The @file{xkeysymdef.h} file
2694defines different KeySyms for capital and small versions of letters.
2695For this event, only the capital version is used, with the variables
2696@code{xk-a}, @code{xk-b}, etc.
2697
2698These variables are initialized by calling the function @code{llk-init}.
2699This function also binds a handler to this event, which allows to detect
2700double taps on keys (normally modifier keys) and bind commands or
2701functions to be run. See the function @code{llk-bind}.
2702 2695
2703@cindex @code{preedit-text} event 2696@cindex @code{preedit-text} event
2704@item (preedit-text @var{arg}) 2697@item (preedit-text @var{arg})
diff --git a/lisp/low-level-key.el b/lisp/low-level-key.el
index bc9311f82d0..8a8d912977d 100644
--- a/lisp/low-level-key.el
+++ b/lisp/low-level-key.el
@@ -1,26 +1,74 @@
1;;; -*- lexical-binding: t -*- 1;;; low-level-key.el --- Handling of key press/release events -*- lexical-binding: t -*-
2 2
3;; The physical-key event is like this: 3;; Copyright (C) 2025 Free Software Foundation, Inc.
4;; (low-level-key IS-KEY-PRESS KEY MODIFIER TIME FRAME) 4
5;; IS-KEY-PRESS is t if the key has been pressed, nil if it has been released. 5;; This file is part of GNU Emacs.
6;; KEY is the keysym number. 6
7;; MODIFIER is the modifier associated with this key. It is nil if the key is 7;; GNU Emacs is free software: you can redistribute it and/or modify
8;; not a modifier. It can be one of the following symbols: shift, control, meta, 8;; it under the terms of the GNU General Public License as published by
9;; super, hyper, alt. It can also be t if the key is a modifier but it can't be 9;; the Free Software Foundation, either version 3 of the License, or
10;; identified, as in the PGTK backend. 10;; (at your option) any later version.
11;; TIME is the timestamp in milliseconds of the event. 11
12;; FRAME is the frame where the event happened. 12;; GNU Emacs is distributed in the hope that it will be useful,
13;; 13;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14;; After calling 'llk-init' and setting a non-nil value for 14;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15;; 'enable-low-level-key-events', events begin to be handled by 'llk-handler', 15;; GNU General Public License for more details.
16;; which tries to detect n-taps and calls the corresponding function. 16
17;; You should have received a copy of the GNU General Public License
18;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
19
20;;; Commentary:
21
22;; Emacs can send low-level key events, that correspond to key presses
23;; and releases. These events are by default disabled.
24
25;; When enabled by setting `enable-low-level-key-events' to a non-nil
26;; value, emacs will begin sending them, and they will be ignored as
27;; low-level-key is bound to `ignore' on `special-event-map'.
28
29;; This file sets a handler for them (`llk-handle') which generates
30;; input events for key presses, key releases and double and triple
31;; taps. These events can be bound to commands on normal keymaps.
32
33;; Because generating these events for all keys would interfere with
34;; normal keyboard input, they must be activated individually by calling
35;; the function `llk-bind'.
36
37;; The low-level-key event payload is described by the 'low-level-key'
38;; struct. Use 'cl-describe-type' to get more information about it.
17;; 39;;
18;; To implement other functionalities, you can replace llk-handler with 40;; After loading this file and setting a non-nil value for
19;; your own function. 41;; 'enable-low-level-key-events', events begin to be handled by
42;; 'llk-handle', which tries to detect n-taps, and creates input
43;; events. See 'llk-bind'.
44
45;; Code:
20 46
21(require 'cl-lib) 47(require 'cl-lib)
22 48
23;; User options 49(defvar llk-tap-timeout (let ((time (mouse-double-click-time)))
50 (if (> time 0) time 800))
51 "Time in milliseconds between key presses/releases to consider a double tap.
52For triple taps, the time is twice this value.")
53
54(cl-defstruct (low-level-key (:type list))
55 "Structure for low level key events.
56Received as low-level-key on `special-event-map'."
57 (event-type nil :type symbol
58 :documentation "Type of event: low-level-key")
59 (is-key-press nil :type boolean
60 :documentation "t if the key has been pressed, nil if it has been released.")
61 (key nil :type integer
62 :documentation "The keysym number.")
63 (modifier nil :type symbol
64 :documentation "Modifier associated with this key. It is nil if the key is
65not a modifier. It can be one of the following symbols: shift, control, meta,
66super, hyper, alt. It can also be t if the key is a modifier but it can't be
67identified, as in the PGTK backend.")
68 (time nil :type integer
69 :documentation "Timestamp in milliseconds of the event.")
70 (frame nil :type frame
71 :documentation "Frame where the event happened."))
24 72
25(defvar llk-bindings nil 73(defvar llk-bindings nil
26 "List of bindings for low level key events (press/release/tap). 74 "List of bindings for low level key events (press/release/tap).
@@ -28,315 +76,298 @@
28Use the `llk-bind' function to add bindings. See its documentation for 76Use the `llk-bind' function to add bindings. See its documentation for
29a description of the binding information.") 77a description of the binding information.")
30 78
31(defvar llk-tap-count 2 79(defvar llk-keysyms nil
32 "Number or key press/releases to consider a tap.") 80 "List of keysyms and their names.
81Each element has the form (CODE . KEYSYM), where code is a NUMBER and
82KEYSYM a symbol, such as `xk-shift-l'")
33 83
34(defvar llk-tap-timeout 1000 84;; TODO docstring.
35 "Time (ms) between consecutive key presses/releases to consider a tap.") 85(defun llk-bind (key &rest events)
86 "Activates low level event generation for a key.
36 87
37(defvar llk-tap-keys 88KEY can be a number or a symbol. The symbols `shift', `control',
38 '(xk-shift-l xk-shift-r xk-control-l xk-control-r meta) 89`meta', `super', `hyper', `alt' activate events for the corresponding
39 "Keys that can generate taps.") 90modifier keys. A number activates events for the corresponding KeySym.
40 91
41(defvar llk-keysyms nil 92EVENTS are symbols that activate one event. Possible values are `press',
42 "List of keysym numbers and their corresponding symbols. 93`release', `double' and `triple'.
94
95See `llk-keysyms' for a list of known values for KEY and their names.
96For each of those, there is a corresponding variable. It is better to
97use the variables to specify keys, as numerical values are
98platform-dependent. The names are parallel to those for KeySyms on X,
99as defined in `xkeysymdef.h'. For example, `XK_Shift_L' (the left shift
100key), corresponds to `xk-shift-l'.
101
102The `xkeysymdef.h' file defines different KeySyms for capital and small
103versions of latin letters. For this event, only the capital version is
104used, with the variables `xk-a', `xk-b', etc.
105
106Low level key events must be enabled with the variable
107`enable-low-level-key-events'.
108
109Once a key is activated with this function, input events will be
110generated for them, and can be bound to commands using normal keymaps.
43 111
44Each element has the form (KEYSYM . SYMBOL). The variable value for 112For example, activating the double tap for the left shift key:
45each symbol is the keysym. This list is initialized by `llk-init'.")
46 113
47(defvar llk-describe-next-press nil 114 (llk-bind xk-shift-l \\='double)
48 "Internal variable to mark that next key press should be described.")
49 115
50(defmacro define-xk (name x-keysym w32-keysym) 116will generate the event `double-xk-shift-l', than can be bound to a
117command with:
118
119 (keymap-global-set [double-xk-shift-l] COMMAND)
120
121Prefixes for events are `press-key-', `release-key-', `double-' and
122`triple-'.
123
124If you use a KeySym number that is not on `llk-keysyms', the events will
125use its numerical value."
126 (setq llk-bindings
127 (cl-delete-if (lambda (x) (eq (car x) key)) llk-bindings))
128 (push (append (list key) events) llk-bindings))
129
130;; We store the last events (key/modifier is-press timestamp) here to
131;; test for multitap. This is not the use the low-level-key struct.
132(defvar llk--event-history-for-tap nil
133 "Internal variable for detecting taps.")
134
135(defun llk--detect-n-tap (n timeout)
136 "Internal function to detect n-tap keys."
137 ;; Only care about last 2xN events
138 (ntake (* 2 n) llk--event-history-for-tap)
139 ;; If we have:
140 ;; - Exactly 2 * n events.
141 ;; - down, up, down, up, ...
142 ;; - not two much time between first and last
143 (and (eq (* 2 n) (length llk--event-history-for-tap))
144 (cl-every #'eq
145 (ntake (* 2 n)
146 (list nil t nil t nil t nil t
147 nil t nil t nil t nil t))
148 (mapcar 'cl-second llk--event-history-for-tap))
149 (< (- (cl-third (cl-first llk--event-history-for-tap))
150 (cl-third (car (last llk--event-history-for-tap))))
151 timeout)
152 (progn (setq llk--event-history-for-tap nil) t)))
153
154(defun llk--generate-event (key event-type)
155 (when (numberp key)
156 (let ((sym (cdr (assoc key llk-keysyms))))
157 (when sym
158 (setq key sym))))
159 (push (intern (format "%s-%s" event-type key))
160 unread-command-events))
161
162(defun llk--generate-events (key is-press binding timestamp)
163 (if is-press
164 (when (member 'press binding)
165 (llk--generate-event key 'press-key))
166 (when (member 'release binding)
167 (llk--generate-event key 'release-key)))
168
169 (let ((double (member 'double binding))
170 (triple (member 'triple binding)))
171 ;; a non-tap key clears the event history.
172 (if (or double triple)
173 (progn
174 ;; Clear the event history if it has events from another key.
175 (unless (equal (car (car llk--event-history-for-tap)) key)
176 (setq llk--event-history-for-tap nil))
177 (push (list key is-press timestamp) llk--event-history-for-tap)
178 (and double
179 (llk--detect-n-tap 2 llk-tap-timeout)
180 (llk--generate-event key 'double-key))
181 (and triple
182 (llk--detect-n-tap 3 (* 2 llk-tap-timeout))
183 (llk--generate-event key 'triple-key)))
184 ;; A non-tap key clears the event history.
185 (setq llk--event-history-for-tap nil))))
186
187(defun llk-handle ()
188 (interactive)
189 (let* ((key (low-level-key-key last-input-event))
190 (modifier (low-level-key-modifier last-input-event))
191 (timestamp (low-level-key-time last-input-event))
192 (is-press (low-level-key-is-key-press last-input-event))
193 (binding (assoc key llk-bindings))
194 (binding-modifier (assoc modifier llk-bindings)))
195 (if binding
196 (llk--generate-events key is-press binding timestamp)
197 (when binding-modifier
198 (llk--generate-events modifier is-press binding-modifier timestamp)))))
199
200(defmacro llk--define-xk (name x-keysym w32-keysym)
51 "Internal macro to define keysyms." 201 "Internal macro to define keysyms."
52 `(let ((ksym (pcase (window-system) 202 `(let ((ksym (pcase (window-system)
53 ('pgtk ,x-keysym) 203 ((or 'pgtk 'x) ,x-keysym)
54 ('x ,x-keysym)
55 ('w32 ,w32-keysym)))) 204 ('w32 ,w32-keysym))))
56 (defconst ,name ksym "Constant for a keysym value.") 205 (defconst ,name ksym "Constant for a keysym value.")
57 (push (cons ksym ',name) llk-keysyms))) 206 (push (cons ksym ',name) llk-keysyms)))
58 207
59(defun llk-define-keysyms () 208(defun llk--define-keysyms ()
60 "Initialize the keysym list, `llk-keysyms'. 209 "Initialize the keysym list, `llk-keysyms'."
61
62Called from `llk-init'."
63 (setq llk-keysyms nil) 210 (setq llk-keysyms nil)
64 211
65 ;; tty keys 212 ;; tty keys
66 (define-xk xk-backspace #xff08 #x08) ;; XK_BackSpace VK_BACK 213 (llk--define-xk xk-backspace #xff08 #x08) ;; XK_BackSpace VK_BACK
67 (define-xk xk-tab #xff09 #x09) ;; XK_Tab VK_TAB 214 (llk--define-xk xk-tab #xff09 #x09) ;; XK_Tab VK_TAB
68 (define-xk xk-clear #xff0b #x0C) ;; XK_Clear VK_CLEAR 215 (llk--define-xk xk-clear #xff0b #x0C) ;; XK_Clear VK_CLEAR
69 (define-xk xk-return #xff0d #x0D) ;; XK_Return VK_RETURN 216 (llk--define-xk xk-return #xff0d #x0D) ;; XK_Return VK_RETURN
70 (define-xk xk-pause #xff13 #x13) ;; XK_Pause VK_PAUSE 217 (llk--define-xk xk-pause #xff13 #x13) ;; XK_Pause VK_PAUSE
71 (define-xk xk-scroll-lock #xff14 #x91) ;; XK_Scroll_Lock VK_SCROLL 218 (llk--define-xk xk-scroll-lock #xff14 #x91) ;; XK_Scroll_Lock VK_SCROLL
72 (define-xk xk-escape #xff1B #x1B) ;; XK_Escape VK_ESCAPE 219 (llk--define-xk xk-escape #xff1B #x1B) ;; XK_Escape VK_ESCAPE
73 (define-xk xk-delete #xffff #x2E) ;; XK_Delete VK_DELETE 220 (llk--define-xk xk-delete #xffff #x2E) ;; XK_Delete VK_DELETE
74 221
75 ;; Cursor control and motion 222 ;; Cursor control and motion
76 (define-xk xk-home #xff50 #x24) ;; XK_Home VK_HOME 223 (llk--define-xk xk-home #xff50 #x24) ;; XK_Home VK_HOME
77 (define-xk xk-left #xff51 #x25) ;; XK_Left VK_LEFT 224 (llk--define-xk xk-left #xff51 #x25) ;; XK_Left VK_LEFT
78 (define-xk xk-up #xff52 #x26) ;; XK_Up VK_UP 225 (llk--define-xk xk-up #xff52 #x26) ;; XK_Up VK_UP
79 (define-xk xk-right #xff53 #x27) ;; XK_Right VK_RIGHT 226 (llk--define-xk xk-right #xff53 #x27) ;; XK_Right VK_RIGHT
80 (define-xk xk-down #xff54 #x28) ;; XK_Down VK_DOWN 227 (llk--define-xk xk-down #xff54 #x28) ;; XK_Down VK_DOWN
81 (define-xk xk-page-up #xff55 #x21) ;; XK_Page_Up VK_PRIOR 228 (llk--define-xk xk-page-up #xff55 #x21) ;; XK_Page_Up VK_PRIOR
82 (define-xk xk-page-down #xff56 #x22) ;; XK_Page_Down VK_NEXT 229 (llk--define-xk xk-page-down #xff56 #x22) ;; XK_Page_Down VK_NEXT
83 (define-xk xk-end #xff57 #x23) ;; XK_End VK_END 230 (llk--define-xk xk-end #xff57 #x23) ;; XK_End VK_END
84 (define-xk xk-begin #xff58 #x24) ;; XK_Begin VK_HOME 231 (llk--define-xk xk-begin #xff58 #x24) ;; XK_Begin VK_HOME
85 232
86 ;; Special Windows keyboard keys 233 ;; Special Windows keyboard keys
87 (define-xk xk-win-l #xFF5B #x5B) ;; XK_Win_L VK_LWIN 234 (llk--define-xk xk-win-l #xFF5B #x5B) ;; XK_Win_L VK_LWIN
88 (define-xk xk-win-r #xFF5C #x5C) ;; XK_Win_R VK_RWIN 235 (llk--define-xk xk-win-r #xFF5C #x5C) ;; XK_Win_R VK_RWIN
89 (define-xk xk-app #xFF5D #x5D) ;; XK_App VK_APPS 236 (llk--define-xk xk-app #xFF5D #x5D) ;; XK_App VK_APPS
90 237
91 ;; Misc functions 238 ;; Misc functions
92 (define-xk xk-select #xff60 #x29) ;; XK_Select VK_SELECT 239 (llk--define-xk xk-select #xff60 #x29) ;; XK_Select VK_SELECT
93 (define-xk xk-print #xff61 #x2A) ;; XK_Print VK_PRINT 240 (llk--define-xk xk-print #xff61 #x2A) ;; XK_Print VK_PRINT
94 (define-xk xk-insert #xff64 #x2D) ;; XK_Insert VK_INSERT 241 (llk--define-xk xk-insert #xff64 #x2D) ;; XK_Insert VK_INSERT
95 (define-xk xk-num-lock #xff7f #x90) ;; XK_Num_Lock VK_NUMLOCK 242 (llk--define-xk xk-num-lock #xff7f #x90) ;; XK_Num_Lock VK_NUMLOCK
96 243
97 ;; Keypad 244 ;; Keypad
98 ;; TODO: Check values for MS-Windows 245 ;; TODO: Check values for MS-Windows
99 (define-xk xk-kp-enter #xff8d nil) ;; XK_KP_Enter ??? 246 (llk--define-xk xk-kp-enter #xff8d nil) ;; XK_KP_Enter ???
100 (define-xk xk-kp-multiply #xffaa nil) ;; XK_KP_Multiply ??? 247 (llk--define-xk xk-kp-multiply #xffaa nil) ;; XK_KP_Multiply ???
101 (define-xk xk-kp-add #xffab nil) ;; XK_KP_Add ??? 248 (llk--define-xk xk-kp-add #xffab nil) ;; XK_KP_Add ???
102 (define-xk xk-kp-subtract #xffad nil) ;; XK_KP_Subtract ??? 249 (llk--define-xk xk-kp-subtract #xffad nil) ;; XK_KP_Subtract ???
103 (define-xk xk-kp-decimal #xffae nil) ;; XK_KP_Decimal ??? 250 (llk--define-xk xk-kp-decimal #xffae nil) ;; XK_KP_Decimal ???
104 (define-xk xk-kp-divide #xffaf nil) ;; XK_KP_Divide ??? 251 (llk--define-xk xk-kp-divide #xffaf nil) ;; XK_KP_Divide ???
105 (define-xk xk-kp-0 #xffb0 #x60) ;; XK_KP_0 VK_NUMPAD0 252 (llk--define-xk xk-kp-0 #xffb0 #x60) ;; XK_KP_0 VK_NUMPAD0
106 (define-xk xk-kp-1 #xffb1 #x61) ;; XK_KP_1 VK_NUMPAD1 253 (llk--define-xk xk-kp-1 #xffb1 #x61) ;; XK_KP_1 VK_NUMPAD1
107 (define-xk xk-kp-2 #xffb2 #x62) ;; XK_KP_2 VK_NUMPAD2 254 (llk--define-xk xk-kp-2 #xffb2 #x62) ;; XK_KP_2 VK_NUMPAD2
108 (define-xk xk-kp-3 #xffb3 #x63) ;; XK_KP_3 VK_NUMPAD3 255 (llk--define-xk xk-kp-3 #xffb3 #x63) ;; XK_KP_3 VK_NUMPAD3
109 (define-xk xk-kp-4 #xffb4 #x64) ;; XK_KP_4 VK_NUMPAD4 256 (llk--define-xk xk-kp-4 #xffb4 #x64) ;; XK_KP_4 VK_NUMPAD4
110 (define-xk xk-kp-5 #xffb5 #x65) ;; XK_KP_5 VK_NUMPAD5 257 (llk--define-xk xk-kp-5 #xffb5 #x65) ;; XK_KP_5 VK_NUMPAD5
111 (define-xk xk-kp-6 #xffb6 #x66) ;; XK_KP_6 VK_NUMPAD6 258 (llk--define-xk xk-kp-6 #xffb6 #x66) ;; XK_KP_6 VK_NUMPAD6
112 (define-xk xk-kp-7 #xffb7 #x67) ;; XK_KP_7 VK_NUMPAD7 259 (llk--define-xk xk-kp-7 #xffb7 #x67) ;; XK_KP_7 VK_NUMPAD7
113 (define-xk xk-kp-8 #xffb8 #x68) ;; XK_KP_8 VK_NUMPAD8 260 (llk--define-xk xk-kp-8 #xffb8 #x68) ;; XK_KP_8 VK_NUMPAD8
114 (define-xk xk-kp-9 #xffb9 #x69) ;; XK_KP_9 VK_NUMPAD9 261 (llk--define-xk xk-kp-9 #xffb9 #x69) ;; XK_KP_9 VK_NUMPAD9
115 262
116 ;; Function keys 263 ;; Function keys
117 (define-xk xk-f1 #xffbe #x70) ;; XK_F1 VK_F1 264 (llk--define-xk xk-f1 #xffbe #x70) ;; XK_F1 VK_F1
118 (define-xk xk-f2 #xffbf #x71) ;; XK_F2 VK_F2 265 (llk--define-xk xk-f2 #xffbf #x71) ;; XK_F2 VK_F2
119 (define-xk xk-f3 #xffc0 #x72) ;; XK_F3 VK_F3 266 (llk--define-xk xk-f3 #xffc0 #x72) ;; XK_F3 VK_F3
120 (define-xk xk-f4 #xffc1 #x73) ;; XK_F4 VK_F4 267 (llk--define-xk xk-f4 #xffc1 #x73) ;; XK_F4 VK_F4
121 (define-xk xk-f5 #xffc2 #x74) ;; XK_F5 VK_F5 268 (llk--define-xk xk-f5 #xffc2 #x74) ;; XK_F5 VK_F5
122 (define-xk xk-f6 #xffc3 #x75) ;; XK_F6 VK_F6 269 (llk--define-xk xk-f6 #xffc3 #x75) ;; XK_F6 VK_F6
123 (define-xk xk-f7 #xffc4 #x76) ;; XK_F7 VK_F7 270 (llk--define-xk xk-f7 #xffc4 #x76) ;; XK_F7 VK_F7
124 (define-xk xk-f8 #xffc5 #x77) ;; XK_F8 VK_F8 271 (llk--define-xk xk-f8 #xffc5 #x77) ;; XK_F8 VK_F8
125 (define-xk xk-f9 #xffc6 #x78) ;; XK_F9 VK_F9 272 (llk--define-xk xk-f9 #xffc6 #x78) ;; XK_F9 VK_F9
126 (define-xk xk-f10 #xffc7 #x79) ;; XK_F10 VK_F10 273 (llk--define-xk xk-f10 #xffc7 #x79) ;; XK_F10 VK_F10
127 (define-xk xk-f11 #xffc8 #x7A) ;; XK_F11 VK_F11 274 (llk--define-xk xk-f11 #xffc8 #x7A) ;; XK_F11 VK_F11
128 (define-xk xk-f12 #xffc9 #x7B) ;; XK_F12 VK_F12 275 (llk--define-xk xk-f12 #xffc9 #x7B) ;; XK_F12 VK_F12
129 (define-xk xk-f13 #xffca #x7C) ;; XK_F13 VK_F13 276 (llk--define-xk xk-f13 #xffca #x7C) ;; XK_F13 VK_F13
130 (define-xk xk-f14 #xffcb #x7D) ;; XK_F14 VK_F14 277 (llk--define-xk xk-f14 #xffcb #x7D) ;; XK_F14 VK_F14
131 (define-xk xk-f15 #xffcc #x7E) ;; XK_F15 VK_F15 278 (llk--define-xk xk-f15 #xffcc #x7E) ;; XK_F15 VK_F15
132 (define-xk xk-f16 #xffcd #x7F) ;; XK_F16 VK_F16 279 (llk--define-xk xk-f16 #xffcd #x7F) ;; XK_F16 VK_F16
133 (define-xk xk-f17 #xffce #x80) ;; XK_F17 VK_F17 280 (llk--define-xk xk-f17 #xffce #x80) ;; XK_F17 VK_F17
134 (define-xk xk-f18 #xffcf #x81) ;; XK_F18 VK_F18 281 (llk--define-xk xk-f18 #xffcf #x81) ;; XK_F18 VK_F18
135 (define-xk xk-f19 #xffd0 #x82) ;; XK_F19 VK_F19 282 (llk--define-xk xk-f19 #xffd0 #x82) ;; XK_F19 VK_F19
136 (define-xk xk-f20 #xffd1 #x83) ;; XK_F20 VK_F20 283 (llk--define-xk xk-f20 #xffd1 #x83) ;; XK_F20 VK_F20
137 (define-xk xk-f21 #xffd2 #x84) ;; XK_F21 VK_F21 284 (llk--define-xk xk-f21 #xffd2 #x84) ;; XK_F21 VK_F21
138 (define-xk xk-f22 #xffd3 #x85) ;; XK_F22 VK_F22 285 (llk--define-xk xk-f22 #xffd3 #x85) ;; XK_F22 VK_F22
139 (define-xk xk-f23 #xffd4 #x86) ;; XK_F23 VK_F23 286 (llk--define-xk xk-f23 #xffd4 #x86) ;; XK_F23 VK_F23
140 (define-xk xk-f24 #xffd5 #x87) ;; XK_F24 VK_F24 287 (llk--define-xk xk-f24 #xffd5 #x87) ;; XK_F24 VK_F24
141 288
142 ;; Modifier keys 289 ;; Modifier keys
143 (define-xk xk-shift-l #xffe1 #xA0) ;; XK_Shift_L VK_LSHIFT 290 (llk--define-xk xk-shift-l #xffe1 #xA0) ;; XK_Shift_L VK_LSHIFT
144 (define-xk xk-shift-r #xffe2 #xA1) ;; XK_Shift_R VK_RSHIFT 291 (llk--define-xk xk-shift-r #xffe2 #xA1) ;; XK_Shift_R VK_RSHIFT
145 (define-xk xk-control-l #xffe3 #xA2) ;; XK_Control_L VK_LCONTROL 292 (llk--define-xk xk-control-l #xffe3 #xA2) ;; XK_Control_L VK_LCONTROL
146 (define-xk xk-control-r #xffe4 #xA3) ;; XK_Control_R VK_RCONTROL 293 (llk--define-xk xk-control-r #xffe4 #xA3) ;; XK_Control_R VK_RCONTROL
147 (define-xk xk-caps-lock #xffe5 #x14) ;; XK_Caps_Lock VK_CAPITAL 294 (llk--define-xk xk-caps-lock #xffe5 #x14) ;; XK_Caps_Lock VK_CAPITAL
148 (define-xk xk-metal-l #xffe7 nil) ;; XK_Meta_L 295 (llk--define-xk xk-meta-l #xffe7 nil) ;; XK_Meta_L
149 (define-xk xk-metal-t #xffee nil) ;; XK_Meta_R 296 (llk--define-xk xk-meta-r #xffee nil) ;; XK_Meta_R
150 (define-xk xk-alt-l #xffe9 #xA4) ;; XK_Alt_L VK_LMENU 297 (llk--define-xk xk-alt-l #xffe9 #xA4) ;; XK_Alt_L VK_LMENU
151 (define-xk xk-alt-r #xffea #xA5) ;; XK_Alt_R VK_RMENU 298 (llk--define-xk xk-alt-r #xffea #xA5) ;; XK_Alt_R VK_RMENU
152 (define-xk xk-super-l #xffeb nil) ;; XK_Super_L 299 (llk--define-xk xk-super-l #xffeb nil) ;; XK_Super_L
153 (define-xk xk-super-r #xffec nil) ;; XK_Super_R 300 (llk--define-xk xk-super-r #xffec nil) ;; XK_Super_R
154 (define-xk xk-hyper-l #xffed nil) ;; XK_Hyper_L 301 (llk--define-xk xk-hyper-l #xffed nil) ;; XK_Hyper_L
155 (define-xk xk-hyper-r #xffee nil) ;; XK_Hyper_R 302 (llk--define-xk xk-hyper-r #xffee nil) ;; XK_Hyper_R
156 303
157 ;; Latin 1 304 ;; Latin 1
158 ;; For numbers and letters, MS-Windows does not define constant names. 305 ;; For numbers and letters, MS-Windows does not define constant names.
159 ;; X11 defines distinct keysyms for lowercase and uppercase 306 ;; X11 defines distinct keysyms for lowercase and uppercase
160 ;; letters. We use only the uppercase ones. Events with lowercase 307 ;; letters. We use only the uppercase ones. Events with lowercase
161 ;; letters are converted to uppercase. 308 ;; letters are converted to uppercase.
162 (define-xk xk-space #x0020 #x20) ;; XK_space VK_SPACE 309 (llk--define-xk xk-space #x0020 #x20) ;; XK_space VK_SPACE
163 (define-xk xk-0 #x0030 #x30) ;; XK_0 310 (llk--define-xk xk-0 #x0030 #x30) ;; XK_0
164 (define-xk xk-1 #x0031 #x31) ;; XK_1 311 (llk--define-xk xk-1 #x0031 #x31) ;; XK_1
165 (define-xk xk-2 #x0032 #x32) ;; XK_2 312 (llk--define-xk xk-2 #x0032 #x32) ;; XK_2
166 (define-xk xk-3 #x0033 #x33) ;; XK_3 313 (llk--define-xk xk-3 #x0033 #x33) ;; XK_3
167 (define-xk xk-4 #x0034 #x34) ;; XK_4 314 (llk--define-xk xk-4 #x0034 #x34) ;; XK_4
168 (define-xk xk-5 #x0035 #x35) ;; XK_5 315 (llk--define-xk xk-5 #x0035 #x35) ;; XK_5
169 (define-xk xk-6 #x0036 #x36) ;; XK_6 316 (llk--define-xk xk-6 #x0036 #x36) ;; XK_6
170 (define-xk xk-7 #x0037 #x37) ;; XK_7 317 (llk--define-xk xk-7 #x0037 #x37) ;; XK_7
171 (define-xk xk-8 #x0038 #x38) ;; XK_8 318 (llk--define-xk xk-8 #x0038 #x38) ;; XK_8
172 (define-xk xk-9 #x0039 #x39) ;; XK_9 319 (llk--define-xk xk-9 #x0039 #x39) ;; XK_9
173 (define-xk xk-a #x0041 #x41) ;; XK_A 320 (llk--define-xk xk-a #x0041 #x41) ;; XK_A
174 (define-xk xk-b #x0042 #x42) ;; XK_B 321 (llk--define-xk xk-b #x0042 #x42) ;; XK_B
175 (define-xk xk-c #x0043 #x43) ;; XK_C 322 (llk--define-xk xk-c #x0043 #x43) ;; XK_C
176 (define-xk xk-d #x0044 #x44) ;; XK_D 323 (llk--define-xk xk-d #x0044 #x44) ;; XK_D
177 (define-xk xk-e #x0045 #x45) ;; XK_E 324 (llk--define-xk xk-e #x0045 #x45) ;; XK_E
178 (define-xk xk-f #x0046 #x46) ;; XK_F 325 (llk--define-xk xk-f #x0046 #x46) ;; XK_F
179 (define-xk xk-g #x0047 #x47) ;; XK_G 326 (llk--define-xk xk-g #x0047 #x47) ;; XK_G
180 (define-xk xk-h #x0048 #x48) ;; XK_H 327 (llk--define-xk xk-h #x0048 #x48) ;; XK_H
181 (define-xk xk-i #x0049 #x49) ;; XK_I 328 (llk--define-xk xk-i #x0049 #x49) ;; XK_I
182 (define-xk xk-j #x004A #x4A) ;; XK_J 329 (llk--define-xk xk-j #x004A #x4A) ;; XK_J
183 (define-xk xk-k #x004B #x4B) ;; XK_K 330 (llk--define-xk xk-k #x004B #x4B) ;; XK_K
184 (define-xk xk-l #x004C #x4C) ;; XK_L 331 (llk--define-xk xk-l #x004C #x4C) ;; XK_L
185 (define-xk xk-m #x004D #x4D) ;; XK_M 332 (llk--define-xk xk-m #x004D #x4D) ;; XK_M
186 (define-xk xk-n #x004E #x4E) ;; XK_N 333 (llk--define-xk xk-n #x004E #x4E) ;; XK_N
187 (define-xk xk-o #x004F #x4F) ;; XK_O 334 (llk--define-xk xk-o #x004F #x4F) ;; XK_O
188 (define-xk xk-p #x0050 #x50) ;; XK_P 335 (llk--define-xk xk-p #x0050 #x50) ;; XK_P
189 (define-xk xk-q #x0051 #x51) ;; XK_Q 336 (llk--define-xk xk-q #x0051 #x51) ;; XK_Q
190 (define-xk xk-r #x0052 #x52) ;; XK_R 337 (llk--define-xk xk-r #x0052 #x52) ;; XK_R
191 (define-xk xk-s #x0053 #x53) ;; XK_S 338 (llk--define-xk xk-s #x0053 #x53) ;; XK_S
192 (define-xk xk-t #x0054 #x54) ;; XK_T 339 (llk--define-xk xk-t #x0054 #x54) ;; XK_T
193 (define-xk xk-u #x0055 #x55) ;; XK_U 340 (llk--define-xk xk-u #x0055 #x55) ;; XK_U
194 (define-xk xk-v #x0056 #x56) ;; XK_V 341 (llk--define-xk xk-v #x0056 #x56) ;; XK_V
195 (define-xk xk-w #x0057 #x57) ;; XK_W 342 (llk--define-xk xk-w #x0057 #x57) ;; XK_W
196 (define-xk xk-x #x0058 #x58) ;; XK_X 343 (llk--define-xk xk-x #x0058 #x58) ;; XK_X
197 (define-xk xk-y #x0059 #x59) ;; XK_Y 344 (llk--define-xk xk-y #x0059 #x59) ;; XK_Y
198 (define-xk xk-z #x005A #x5A));; XK_Z 345 (llk--define-xk xk-z #x005A #x5A));; XK_Z
199
200(defun llk-init ()
201 "Initialize low level key events.
202
203Fills the `llk-keysyms' list, and binds the `low-level-key' event
204to the `llk-handle' function. Resets the `llk-bindings' list.
205Besides calling this function, you need to set `enable-low-level-key-events'
206to a non-nil value."
207 (interactive)
208 (llk-define-keysyms)
209 (define-key special-event-map [low-level-key] 'llk-handle)
210 (setq llk-bindings nil))
211
212(defsubst event-is-key-press (event)
213 "Return the value of the IS-KEY-PRESS field of the EVENT, a low level key event."
214 (declare (side-effect-free t))
215 (if (consp event) (nth 1 event)))
216
217(defsubst event-keysym (event)
218 "Return the value of the KEY field of the EVENT, a low level key event."
219 (declare (side-effect-free t))
220 (if (consp event) (nth 2 event)))
221
222(defsubst event-modifier (event)
223 "Return the value of the MODIFIER field of the EVENT, a low level key event."
224 (declare (side-effect-free t))
225 (if (consp event) (nth 3 event)))
226
227(defsubst event-time (event)
228 "Return the value of the TIME field of the EVENT, a low level key event."
229 (declare (side-effect-free t))
230 (if (consp event) (nth 4 event)))
231
232;; For example:
233;; Bind key tap to command
234;; (llk-bind 'tap 'xk-shift-l 'delete-other-windows)
235;; Bind modifier tap to command
236;; (llk-bind 'tap 'shift 'delete-other-windows)
237;; Bind tap to hyper modifier
238;; (llk-bind 'tap 'xk-shift-r (lambda ()
239;; (message "H-...")
240;; (setq unread-command-events
241;; (append (event-apply-hyper-modifier nil) nil))))
242;; Can bind to a command or function
243(defun llk-bind (action key function)
244 "Bind a command or function to a low level key event.
245
246The only action supported currently is `tap'. The key can be a keysym
247symbol, or a modifier symbol (shift, control, alt, meta, hyper, super).
248If there is no keysym symbol for a key, use the keysym number."
249 (push (list action key function) llk-bindings))
250
251;; We store the last events (key/modifier is-press timestamp) here to
252;; test for multitap.
253(defvar llk-events nil
254 "Internal variable for detecting taps.")
255
256;; If positive, return key (xk-shift-l, etc) else return nil.
257(defun llk-detect-n-tap (n timeout)
258 "Internal function to detect n-tap keys."
259 (let (key
260 (is-press (event-is-key-press last-input-event))
261 ;; convert number to keysym symbol
262 (keysym (cdr (assoc (event-keysym last-input-event) llk-keysyms)))
263 (timestamp (event-time last-input-event))
264 (modifier (event-modifier last-input-event)))
265
266 ;; if ehte is no symbol for this key, use its keysym number
267 (unless keysym (setq keysym (event-keysym last-input-event)))
268
269 ;; look in llk-tap-keys for the key, then the modifier
270 (if (member keysym llk-tap-keys)
271 (setq key keysym)
272 (if (member modifier llk-tap-keys)
273 (setq key modifier)))
274
275 (if (not key)
276 ;; Key not in tap list, clear history
277 (setq llk-events nil)
278 ;; Clear it also if the first element is from a different key
279 (and llk-events
280 (not (equal (car (car llk-events)) key))
281 (setq llk-events nil))
282 (push (list key is-press timestamp) llk-events)
283 ;; Only care about last 2xN events
284 (ntake (* 2 n) llk-events)
285 ;; If we have:
286 ;; - Exactly 2 * n events.
287 ;; - down, up, down, up, ...
288 ;; - not two much time between first and last
289 (and (eq (* 2 n) (length llk-events))
290 (cl-every 'eq
291 (ntake (* 2 n)
292 (list nil t nil t nil t nil t
293 nil t nil t nil t nil t))
294 (mapcar 'cl-second llk-events))
295 (< (- (cl-third (cl-first llk-events))
296 (cl-third (car (last llk-events))))
297 timeout)
298 (progn
299 (setq llk-events nil)
300 key)))))
301 346
302(defun describe-low-level-key () 347(defun describe-low-level-key ()
303 "Wait for key press and describe the low level key event it generates." 348 "Wait for key press and describe the low-level key event it generates."
304 (interactive) 349 (interactive)
305 (setq llk-describe-next-press t)) 350 (define-key special-event-map [low-level-key] 'llk--describe))
306
307(defun llk-show-event-description ()
308 "Show information about the last low level key event."
309 (setq llk-describe-next-press nil)
310 (with-help-window (help-buffer)
311 (insert "\n")
312 (let* ((xk (event-keysym last-input-event))
313 (sym (assoc xk llk-keysyms)))
314 (insert (format "Keysym number: %d (#x%X),\n" xk xk))
315 (if sym
316 (insert (format "which corresponds to named key %s.\n\n" (cdr sym)))
317 (insert "which does not correspond to any known named key.\n\n"))
318 (if (event-modifier last-input-event)
319 (insert (format "This key corresponds to the %s modifier.\n\n"
320 (event-modifier last-input-event)))
321 (insert "This key does not correspond to a modifier.\n\n"))
322 (insert "See the value of the `llk-keysyms' variable for a list of known keys.\n"))))
323 351
324(defun llk-handle () 352(defun llk--describe ()
325 "Internal function to handle low level key events." 353 "Internal function for `special-event-map' to describe low level key events."
326 (interactive) 354 (interactive)
327 (if (and (event-is-key-press last-input-event) 355 (when (low-level-key-is-key-press last-input-event)
328 llk-describe-next-press) 356 (define-key special-event-map [low-level-key] 'llk-handle)
329 (llk-show-event-description) 357 (with-help-window (help-buffer)
330 (let ((tap-key (llk-detect-n-tap 358 (insert "\n")
331 llk-tap-count 359 (let* ((xk (low-level-key-key last-input-event))
332 llk-tap-timeout))) 360 (sym (assoc xk llk-keysyms)))
333 (when tap-key 361 (insert (format "Keysym number: %d (#x%X),\n" xk xk))
334 (let ((func (cl-third 362 (if sym
335 (seq-find 363 (insert (format "which corresponds to named key %s.\n\n" (cdr sym)))
336 (lambda (b) 364 (insert "which does not correspond to any known named key.\n\n"))
337 (and (eq (cl-first b) 'tap) 365 (if (low-level-key-modifier last-input-event)
338 (eq (cl-second b) tap-key))) 366 (insert (format "This key corresponds to the %s modifier.\n\n"
339 llk-bindings)))) 367 (low-level-key-modifier last-input-event)))
340 (cond 368 (insert "This key does not correspond to a modifier.\n\n"))
341 ((commandp func) (call-interactively func)) 369 (insert "See the value of the `llk-keysyms' variable for a list of known keys.\n")))))
342 ((functionp func) (funcall func)))))))) 370
371(llk--define-keysyms)
372(define-key special-event-map [low-level-key] 'llk-handle)
373(setq llk-bindings nil)
diff --git a/src/keyboard.c b/src/keyboard.c
index 4bf02a06696..f5e9dc40a33 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -13158,13 +13158,8 @@ The value configures the set of keys that are handled:
13158 13158
13159If t, send events for all keys. 13159If t, send events for all keys.
13160 13160
13161If a number, send events for the corresponding keysym. When calling 13161If a number, send events for the corresponding keysym. This numbers are
13162`llk-init', a set of variables with the xk- prefix is initialized with 13162platform dependente. See `llk-keysyms'.
13163the numeric values for keysyms. Because this number are platform
13164dependent, only the variables should be used to refer to a key. For
13165example, the 'xk-backspace' variable refer to the backspace key, with
13166the numeric value 0xff08 on X, and the 0x08 on MS-Windows. You can see
13167all defined variables on the variable `llk-keysyms'.
13168 13163
13169If a symbol, a predefined set of keys is selected. The only currently 13164If a symbol, a predefined set of keys is selected. The only currently
13170valid symbol is 'modifiers. 13165valid symbol is 'modifiers.