1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2025-12-05 22:20:24 -08:00

Go through normal keymaps

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.
This commit is contained in:
Cecilio Pardo 2024-12-02 17:30:42 +01:00 committed by Stefan Monnier
parent 67aed063f6
commit ba02efb85e
3 changed files with 341 additions and 322 deletions

View file

@ -2670,35 +2670,28 @@ size of the object beneath the gesture: image, window, etc.
@cindex @code{low-level-key} event
@item (low-level-key @var{is-key-press} @var{key} @var{modifier} @var{time} @var{frame})
This event is sent on the physical press or release of keys, only on GUI
systems, currently X, MS-Windows and PGTK, and only if the variable
@code{enable-low-level-key-events} has a non-@code{nil} value. See its
documentation for the values it can take, that allow to filter which
keys should generate this kind of event.
This event is sent on the physical press or release of keys, only on
systems where it is supported, currently X, MS-Windows and PGTK, and
only if the variable @code{enable-low-level-key-events} has a
non-@code{nil} value. See its documentation for the values to use, that
can activate the events for all or some keys.
@var{is-key-press} is @code{t} for a key press, @code{nil} for a key release.
@var{time} is the event's time in milliseconds, @var{frame} is the
frame receiving it. @var{modifier} is @code{nil} if the key is not a
@var{is-key-press} is @code{t} for a key press, @code{nil} for a key
release. @var{time} is the event's time in milliseconds, as reported by
the underlying platform, and should only be used to measure time
intervals between events of this same kind. @var{frame} is the frame
receiving the event. @var{modifier} is @code{nil} if the key is not a
modifier key, @code{t} if it is, but it is unknown which one, or one of
@code{shift}, @code{control}, @code{meta}, @code{alt}, @code{super},
@code{hyper}.
@var{key}, an integer, identifies the key pressed or released. This
number is platform dependent, but there are variables for most keys that
can be used in place of the numbers to identify them. For example, the
variable @code{xk-backspace} identifies the @key{backspace} key.
number is platform dependent.
The names are parallel to those for KeySyms on X, as defined in
@file{xkeysymdef.h}. For example, @code{XK_Shift_L} (the left shift
key), corresponds to @code{xk-shift-l}. The @file{xkeysymdef.h} file
defines different KeySyms for capital and small versions of letters.
For this event, only the capital version is used, with the variables
@code{xk-a}, @code{xk-b}, etc.
These variables are initialized by calling the function @code{llk-init}.
This function also binds a handler to this event, which allows to detect
double taps on keys (normally modifier keys) and bind commands or
functions to be run. See the function @code{llk-bind}.
This is a special event (@pxref{Special Events}), ignored by
default. Loading @file{low-level-key.el} sets a handler
that can generate normal input events for key press, release and multi
tap. See the function @code{llk-bind}.
@cindex @code{preedit-text} event
@item (preedit-text @var{arg})

View file

@ -1,26 +1,74 @@
;;; -*- lexical-binding: t -*-
;;; low-level-key.el --- Handling of key press/release events -*- lexical-binding: t -*-
;; The physical-key event is like this:
;; (low-level-key IS-KEY-PRESS KEY MODIFIER TIME FRAME)
;; IS-KEY-PRESS is t if the key has been pressed, nil if it has been released.
;; KEY is the keysym number.
;; MODIFIER is the modifier associated with this key. It is nil if the key is
;; not a modifier. It can be one of the following symbols: shift, control, meta,
;; super, hyper, alt. It can also be t if the key is a modifier but it can't be
;; identified, as in the PGTK backend.
;; TIME is the timestamp in milliseconds of the event.
;; FRAME is the frame where the event happened.
;; Copyright (C) 2025 Free Software Foundation, Inc.
;; This file is part of GNU Emacs.
;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; Emacs can send low-level key events, that correspond to key presses
;; and releases. These events are by default disabled.
;; When enabled by setting `enable-low-level-key-events' to a non-nil
;; value, emacs will begin sending them, and they will be ignored as
;; low-level-key is bound to `ignore' on `special-event-map'.
;; This file sets a handler for them (`llk-handle') which generates
;; input events for key presses, key releases and double and triple
;; taps. These events can be bound to commands on normal keymaps.
;; Because generating these events for all keys would interfere with
;; normal keyboard input, they must be activated individually by calling
;; the function `llk-bind'.
;; The low-level-key event payload is described by the 'low-level-key'
;; struct. Use 'cl-describe-type' to get more information about it.
;;
;; After calling 'llk-init' and setting a non-nil value for
;; 'enable-low-level-key-events', events begin to be handled by 'llk-handler',
;; which tries to detect n-taps and calls the corresponding function.
;;
;; To implement other functionalities, you can replace llk-handler with
;; your own function.
;; After loading this file and setting a non-nil value for
;; 'enable-low-level-key-events', events begin to be handled by
;; 'llk-handle', which tries to detect n-taps, and creates input
;; events. See 'llk-bind'.
;; Code:
(require 'cl-lib)
;; User options
(defvar llk-tap-timeout (let ((time (mouse-double-click-time)))
(if (> time 0) time 800))
"Time in milliseconds between key presses/releases to consider a double tap.
For triple taps, the time is twice this value.")
(cl-defstruct (low-level-key (:type list))
"Structure for low level key events.
Received as low-level-key on `special-event-map'."
(event-type nil :type symbol
:documentation "Type of event: low-level-key")
(is-key-press nil :type boolean
:documentation "t if the key has been pressed, nil if it has been released.")
(key nil :type integer
:documentation "The keysym number.")
(modifier nil :type symbol
:documentation "Modifier associated with this key. It is nil if the key is
not a modifier. It can be one of the following symbols: shift, control, meta,
super, hyper, alt. It can also be t if the key is a modifier but it can't be
identified, as in the PGTK backend.")
(time nil :type integer
:documentation "Timestamp in milliseconds of the event.")
(frame nil :type frame
:documentation "Frame where the event happened."))
(defvar llk-bindings nil
"List of bindings for low level key events (press/release/tap).
@ -28,315 +76,298 @@
Use the `llk-bind' function to add bindings. See its documentation for
a description of the binding information.")
(defvar llk-tap-count 2
"Number or key press/releases to consider a tap.")
(defvar llk-tap-timeout 1000
"Time (ms) between consecutive key presses/releases to consider a tap.")
(defvar llk-tap-keys
'(xk-shift-l xk-shift-r xk-control-l xk-control-r meta)
"Keys that can generate taps.")
(defvar llk-keysyms nil
"List of keysym numbers and their corresponding symbols.
"List of keysyms and their names.
Each element has the form (CODE . KEYSYM), where code is a NUMBER and
KEYSYM a symbol, such as `xk-shift-l'")
Each element has the form (KEYSYM . SYMBOL). The variable value for
each symbol is the keysym. This list is initialized by `llk-init'.")
;; TODO docstring.
(defun llk-bind (key &rest events)
"Activates low level event generation for a key.
(defvar llk-describe-next-press nil
"Internal variable to mark that next key press should be described.")
KEY can be a number or a symbol. The symbols `shift', `control',
`meta', `super', `hyper', `alt' activate events for the corresponding
modifier keys. A number activates events for the corresponding KeySym.
(defmacro define-xk (name x-keysym w32-keysym)
EVENTS are symbols that activate one event. Possible values are `press',
`release', `double' and `triple'.
See `llk-keysyms' for a list of known values for KEY and their names.
For each of those, there is a corresponding variable. It is better to
use the variables to specify keys, as numerical values are
platform-dependent. The names are parallel to those for KeySyms on X,
as defined in `xkeysymdef.h'. For example, `XK_Shift_L' (the left shift
key), corresponds to `xk-shift-l'.
The `xkeysymdef.h' file defines different KeySyms for capital and small
versions of latin letters. For this event, only the capital version is
used, with the variables `xk-a', `xk-b', etc.
Low level key events must be enabled with the variable
`enable-low-level-key-events'.
Once a key is activated with this function, input events will be
generated for them, and can be bound to commands using normal keymaps.
For example, activating the double tap for the left shift key:
(llk-bind xk-shift-l \\='double)
will generate the event `double-xk-shift-l', than can be bound to a
command with:
(keymap-global-set [double-xk-shift-l] COMMAND)
Prefixes for events are `press-key-', `release-key-', `double-' and
`triple-'.
If you use a KeySym number that is not on `llk-keysyms', the events will
use its numerical value."
(setq llk-bindings
(cl-delete-if (lambda (x) (eq (car x) key)) llk-bindings))
(push (append (list key) events) llk-bindings))
;; We store the last events (key/modifier is-press timestamp) here to
;; test for multitap. This is not the use the low-level-key struct.
(defvar llk--event-history-for-tap nil
"Internal variable for detecting taps.")
(defun llk--detect-n-tap (n timeout)
"Internal function to detect n-tap keys."
;; Only care about last 2xN events
(ntake (* 2 n) llk--event-history-for-tap)
;; If we have:
;; - Exactly 2 * n events.
;; - down, up, down, up, ...
;; - not two much time between first and last
(and (eq (* 2 n) (length llk--event-history-for-tap))
(cl-every #'eq
(ntake (* 2 n)
(list nil t nil t nil t nil t
nil t nil t nil t nil t))
(mapcar 'cl-second llk--event-history-for-tap))
(< (- (cl-third (cl-first llk--event-history-for-tap))
(cl-third (car (last llk--event-history-for-tap))))
timeout)
(progn (setq llk--event-history-for-tap nil) t)))
(defun llk--generate-event (key event-type)
(when (numberp key)
(let ((sym (cdr (assoc key llk-keysyms))))
(when sym
(setq key sym))))
(push (intern (format "%s-%s" event-type key))
unread-command-events))
(defun llk--generate-events (key is-press binding timestamp)
(if is-press
(when (member 'press binding)
(llk--generate-event key 'press-key))
(when (member 'release binding)
(llk--generate-event key 'release-key)))
(let ((double (member 'double binding))
(triple (member 'triple binding)))
;; a non-tap key clears the event history.
(if (or double triple)
(progn
;; Clear the event history if it has events from another key.
(unless (equal (car (car llk--event-history-for-tap)) key)
(setq llk--event-history-for-tap nil))
(push (list key is-press timestamp) llk--event-history-for-tap)
(and double
(llk--detect-n-tap 2 llk-tap-timeout)
(llk--generate-event key 'double-key))
(and triple
(llk--detect-n-tap 3 (* 2 llk-tap-timeout))
(llk--generate-event key 'triple-key)))
;; A non-tap key clears the event history.
(setq llk--event-history-for-tap nil))))
(defun llk-handle ()
(interactive)
(let* ((key (low-level-key-key last-input-event))
(modifier (low-level-key-modifier last-input-event))
(timestamp (low-level-key-time last-input-event))
(is-press (low-level-key-is-key-press last-input-event))
(binding (assoc key llk-bindings))
(binding-modifier (assoc modifier llk-bindings)))
(if binding
(llk--generate-events key is-press binding timestamp)
(when binding-modifier
(llk--generate-events modifier is-press binding-modifier timestamp)))))
(defmacro llk--define-xk (name x-keysym w32-keysym)
"Internal macro to define keysyms."
`(let ((ksym (pcase (window-system)
('pgtk ,x-keysym)
('x ,x-keysym)
((or 'pgtk 'x) ,x-keysym)
('w32 ,w32-keysym))))
(defconst ,name ksym "Constant for a keysym value.")
(push (cons ksym ',name) llk-keysyms)))
(defun llk-define-keysyms ()
"Initialize the keysym list, `llk-keysyms'.
Called from `llk-init'."
(defun llk--define-keysyms ()
"Initialize the keysym list, `llk-keysyms'."
(setq llk-keysyms nil)
;; tty keys
(define-xk xk-backspace #xff08 #x08) ;; XK_BackSpace VK_BACK
(define-xk xk-tab #xff09 #x09) ;; XK_Tab VK_TAB
(define-xk xk-clear #xff0b #x0C) ;; XK_Clear VK_CLEAR
(define-xk xk-return #xff0d #x0D) ;; XK_Return VK_RETURN
(define-xk xk-pause #xff13 #x13) ;; XK_Pause VK_PAUSE
(define-xk xk-scroll-lock #xff14 #x91) ;; XK_Scroll_Lock VK_SCROLL
(define-xk xk-escape #xff1B #x1B) ;; XK_Escape VK_ESCAPE
(define-xk xk-delete #xffff #x2E) ;; XK_Delete VK_DELETE
(llk--define-xk xk-backspace #xff08 #x08) ;; XK_BackSpace VK_BACK
(llk--define-xk xk-tab #xff09 #x09) ;; XK_Tab VK_TAB
(llk--define-xk xk-clear #xff0b #x0C) ;; XK_Clear VK_CLEAR
(llk--define-xk xk-return #xff0d #x0D) ;; XK_Return VK_RETURN
(llk--define-xk xk-pause #xff13 #x13) ;; XK_Pause VK_PAUSE
(llk--define-xk xk-scroll-lock #xff14 #x91) ;; XK_Scroll_Lock VK_SCROLL
(llk--define-xk xk-escape #xff1B #x1B) ;; XK_Escape VK_ESCAPE
(llk--define-xk xk-delete #xffff #x2E) ;; XK_Delete VK_DELETE
;; Cursor control and motion
(define-xk xk-home #xff50 #x24) ;; XK_Home VK_HOME
(define-xk xk-left #xff51 #x25) ;; XK_Left VK_LEFT
(define-xk xk-up #xff52 #x26) ;; XK_Up VK_UP
(define-xk xk-right #xff53 #x27) ;; XK_Right VK_RIGHT
(define-xk xk-down #xff54 #x28) ;; XK_Down VK_DOWN
(define-xk xk-page-up #xff55 #x21) ;; XK_Page_Up VK_PRIOR
(define-xk xk-page-down #xff56 #x22) ;; XK_Page_Down VK_NEXT
(define-xk xk-end #xff57 #x23) ;; XK_End VK_END
(define-xk xk-begin #xff58 #x24) ;; XK_Begin VK_HOME
(llk--define-xk xk-home #xff50 #x24) ;; XK_Home VK_HOME
(llk--define-xk xk-left #xff51 #x25) ;; XK_Left VK_LEFT
(llk--define-xk xk-up #xff52 #x26) ;; XK_Up VK_UP
(llk--define-xk xk-right #xff53 #x27) ;; XK_Right VK_RIGHT
(llk--define-xk xk-down #xff54 #x28) ;; XK_Down VK_DOWN
(llk--define-xk xk-page-up #xff55 #x21) ;; XK_Page_Up VK_PRIOR
(llk--define-xk xk-page-down #xff56 #x22) ;; XK_Page_Down VK_NEXT
(llk--define-xk xk-end #xff57 #x23) ;; XK_End VK_END
(llk--define-xk xk-begin #xff58 #x24) ;; XK_Begin VK_HOME
;; Special Windows keyboard keys
(define-xk xk-win-l #xFF5B #x5B) ;; XK_Win_L VK_LWIN
(define-xk xk-win-r #xFF5C #x5C) ;; XK_Win_R VK_RWIN
(define-xk xk-app #xFF5D #x5D) ;; XK_App VK_APPS
(llk--define-xk xk-win-l #xFF5B #x5B) ;; XK_Win_L VK_LWIN
(llk--define-xk xk-win-r #xFF5C #x5C) ;; XK_Win_R VK_RWIN
(llk--define-xk xk-app #xFF5D #x5D) ;; XK_App VK_APPS
;; Misc functions
(define-xk xk-select #xff60 #x29) ;; XK_Select VK_SELECT
(define-xk xk-print #xff61 #x2A) ;; XK_Print VK_PRINT
(define-xk xk-insert #xff64 #x2D) ;; XK_Insert VK_INSERT
(define-xk xk-num-lock #xff7f #x90) ;; XK_Num_Lock VK_NUMLOCK
(llk--define-xk xk-select #xff60 #x29) ;; XK_Select VK_SELECT
(llk--define-xk xk-print #xff61 #x2A) ;; XK_Print VK_PRINT
(llk--define-xk xk-insert #xff64 #x2D) ;; XK_Insert VK_INSERT
(llk--define-xk xk-num-lock #xff7f #x90) ;; XK_Num_Lock VK_NUMLOCK
;; Keypad
;; TODO: Check values for MS-Windows
(define-xk xk-kp-enter #xff8d nil) ;; XK_KP_Enter ???
(define-xk xk-kp-multiply #xffaa nil) ;; XK_KP_Multiply ???
(define-xk xk-kp-add #xffab nil) ;; XK_KP_Add ???
(define-xk xk-kp-subtract #xffad nil) ;; XK_KP_Subtract ???
(define-xk xk-kp-decimal #xffae nil) ;; XK_KP_Decimal ???
(define-xk xk-kp-divide #xffaf nil) ;; XK_KP_Divide ???
(define-xk xk-kp-0 #xffb0 #x60) ;; XK_KP_0 VK_NUMPAD0
(define-xk xk-kp-1 #xffb1 #x61) ;; XK_KP_1 VK_NUMPAD1
(define-xk xk-kp-2 #xffb2 #x62) ;; XK_KP_2 VK_NUMPAD2
(define-xk xk-kp-3 #xffb3 #x63) ;; XK_KP_3 VK_NUMPAD3
(define-xk xk-kp-4 #xffb4 #x64) ;; XK_KP_4 VK_NUMPAD4
(define-xk xk-kp-5 #xffb5 #x65) ;; XK_KP_5 VK_NUMPAD5
(define-xk xk-kp-6 #xffb6 #x66) ;; XK_KP_6 VK_NUMPAD6
(define-xk xk-kp-7 #xffb7 #x67) ;; XK_KP_7 VK_NUMPAD7
(define-xk xk-kp-8 #xffb8 #x68) ;; XK_KP_8 VK_NUMPAD8
(define-xk xk-kp-9 #xffb9 #x69) ;; XK_KP_9 VK_NUMPAD9
(llk--define-xk xk-kp-enter #xff8d nil) ;; XK_KP_Enter ???
(llk--define-xk xk-kp-multiply #xffaa nil) ;; XK_KP_Multiply ???
(llk--define-xk xk-kp-add #xffab nil) ;; XK_KP_Add ???
(llk--define-xk xk-kp-subtract #xffad nil) ;; XK_KP_Subtract ???
(llk--define-xk xk-kp-decimal #xffae nil) ;; XK_KP_Decimal ???
(llk--define-xk xk-kp-divide #xffaf nil) ;; XK_KP_Divide ???
(llk--define-xk xk-kp-0 #xffb0 #x60) ;; XK_KP_0 VK_NUMPAD0
(llk--define-xk xk-kp-1 #xffb1 #x61) ;; XK_KP_1 VK_NUMPAD1
(llk--define-xk xk-kp-2 #xffb2 #x62) ;; XK_KP_2 VK_NUMPAD2
(llk--define-xk xk-kp-3 #xffb3 #x63) ;; XK_KP_3 VK_NUMPAD3
(llk--define-xk xk-kp-4 #xffb4 #x64) ;; XK_KP_4 VK_NUMPAD4
(llk--define-xk xk-kp-5 #xffb5 #x65) ;; XK_KP_5 VK_NUMPAD5
(llk--define-xk xk-kp-6 #xffb6 #x66) ;; XK_KP_6 VK_NUMPAD6
(llk--define-xk xk-kp-7 #xffb7 #x67) ;; XK_KP_7 VK_NUMPAD7
(llk--define-xk xk-kp-8 #xffb8 #x68) ;; XK_KP_8 VK_NUMPAD8
(llk--define-xk xk-kp-9 #xffb9 #x69) ;; XK_KP_9 VK_NUMPAD9
;; Function keys
(define-xk xk-f1 #xffbe #x70) ;; XK_F1 VK_F1
(define-xk xk-f2 #xffbf #x71) ;; XK_F2 VK_F2
(define-xk xk-f3 #xffc0 #x72) ;; XK_F3 VK_F3
(define-xk xk-f4 #xffc1 #x73) ;; XK_F4 VK_F4
(define-xk xk-f5 #xffc2 #x74) ;; XK_F5 VK_F5
(define-xk xk-f6 #xffc3 #x75) ;; XK_F6 VK_F6
(define-xk xk-f7 #xffc4 #x76) ;; XK_F7 VK_F7
(define-xk xk-f8 #xffc5 #x77) ;; XK_F8 VK_F8
(define-xk xk-f9 #xffc6 #x78) ;; XK_F9 VK_F9
(define-xk xk-f10 #xffc7 #x79) ;; XK_F10 VK_F10
(define-xk xk-f11 #xffc8 #x7A) ;; XK_F11 VK_F11
(define-xk xk-f12 #xffc9 #x7B) ;; XK_F12 VK_F12
(define-xk xk-f13 #xffca #x7C) ;; XK_F13 VK_F13
(define-xk xk-f14 #xffcb #x7D) ;; XK_F14 VK_F14
(define-xk xk-f15 #xffcc #x7E) ;; XK_F15 VK_F15
(define-xk xk-f16 #xffcd #x7F) ;; XK_F16 VK_F16
(define-xk xk-f17 #xffce #x80) ;; XK_F17 VK_F17
(define-xk xk-f18 #xffcf #x81) ;; XK_F18 VK_F18
(define-xk xk-f19 #xffd0 #x82) ;; XK_F19 VK_F19
(define-xk xk-f20 #xffd1 #x83) ;; XK_F20 VK_F20
(define-xk xk-f21 #xffd2 #x84) ;; XK_F21 VK_F21
(define-xk xk-f22 #xffd3 #x85) ;; XK_F22 VK_F22
(define-xk xk-f23 #xffd4 #x86) ;; XK_F23 VK_F23
(define-xk xk-f24 #xffd5 #x87) ;; XK_F24 VK_F24
(llk--define-xk xk-f1 #xffbe #x70) ;; XK_F1 VK_F1
(llk--define-xk xk-f2 #xffbf #x71) ;; XK_F2 VK_F2
(llk--define-xk xk-f3 #xffc0 #x72) ;; XK_F3 VK_F3
(llk--define-xk xk-f4 #xffc1 #x73) ;; XK_F4 VK_F4
(llk--define-xk xk-f5 #xffc2 #x74) ;; XK_F5 VK_F5
(llk--define-xk xk-f6 #xffc3 #x75) ;; XK_F6 VK_F6
(llk--define-xk xk-f7 #xffc4 #x76) ;; XK_F7 VK_F7
(llk--define-xk xk-f8 #xffc5 #x77) ;; XK_F8 VK_F8
(llk--define-xk xk-f9 #xffc6 #x78) ;; XK_F9 VK_F9
(llk--define-xk xk-f10 #xffc7 #x79) ;; XK_F10 VK_F10
(llk--define-xk xk-f11 #xffc8 #x7A) ;; XK_F11 VK_F11
(llk--define-xk xk-f12 #xffc9 #x7B) ;; XK_F12 VK_F12
(llk--define-xk xk-f13 #xffca #x7C) ;; XK_F13 VK_F13
(llk--define-xk xk-f14 #xffcb #x7D) ;; XK_F14 VK_F14
(llk--define-xk xk-f15 #xffcc #x7E) ;; XK_F15 VK_F15
(llk--define-xk xk-f16 #xffcd #x7F) ;; XK_F16 VK_F16
(llk--define-xk xk-f17 #xffce #x80) ;; XK_F17 VK_F17
(llk--define-xk xk-f18 #xffcf #x81) ;; XK_F18 VK_F18
(llk--define-xk xk-f19 #xffd0 #x82) ;; XK_F19 VK_F19
(llk--define-xk xk-f20 #xffd1 #x83) ;; XK_F20 VK_F20
(llk--define-xk xk-f21 #xffd2 #x84) ;; XK_F21 VK_F21
(llk--define-xk xk-f22 #xffd3 #x85) ;; XK_F22 VK_F22
(llk--define-xk xk-f23 #xffd4 #x86) ;; XK_F23 VK_F23
(llk--define-xk xk-f24 #xffd5 #x87) ;; XK_F24 VK_F24
;; Modifier keys
(define-xk xk-shift-l #xffe1 #xA0) ;; XK_Shift_L VK_LSHIFT
(define-xk xk-shift-r #xffe2 #xA1) ;; XK_Shift_R VK_RSHIFT
(define-xk xk-control-l #xffe3 #xA2) ;; XK_Control_L VK_LCONTROL
(define-xk xk-control-r #xffe4 #xA3) ;; XK_Control_R VK_RCONTROL
(define-xk xk-caps-lock #xffe5 #x14) ;; XK_Caps_Lock VK_CAPITAL
(define-xk xk-metal-l #xffe7 nil) ;; XK_Meta_L
(define-xk xk-metal-t #xffee nil) ;; XK_Meta_R
(define-xk xk-alt-l #xffe9 #xA4) ;; XK_Alt_L VK_LMENU
(define-xk xk-alt-r #xffea #xA5) ;; XK_Alt_R VK_RMENU
(define-xk xk-super-l #xffeb nil) ;; XK_Super_L
(define-xk xk-super-r #xffec nil) ;; XK_Super_R
(define-xk xk-hyper-l #xffed nil) ;; XK_Hyper_L
(define-xk xk-hyper-r #xffee nil) ;; XK_Hyper_R
(llk--define-xk xk-shift-l #xffe1 #xA0) ;; XK_Shift_L VK_LSHIFT
(llk--define-xk xk-shift-r #xffe2 #xA1) ;; XK_Shift_R VK_RSHIFT
(llk--define-xk xk-control-l #xffe3 #xA2) ;; XK_Control_L VK_LCONTROL
(llk--define-xk xk-control-r #xffe4 #xA3) ;; XK_Control_R VK_RCONTROL
(llk--define-xk xk-caps-lock #xffe5 #x14) ;; XK_Caps_Lock VK_CAPITAL
(llk--define-xk xk-meta-l #xffe7 nil) ;; XK_Meta_L
(llk--define-xk xk-meta-r #xffee nil) ;; XK_Meta_R
(llk--define-xk xk-alt-l #xffe9 #xA4) ;; XK_Alt_L VK_LMENU
(llk--define-xk xk-alt-r #xffea #xA5) ;; XK_Alt_R VK_RMENU
(llk--define-xk xk-super-l #xffeb nil) ;; XK_Super_L
(llk--define-xk xk-super-r #xffec nil) ;; XK_Super_R
(llk--define-xk xk-hyper-l #xffed nil) ;; XK_Hyper_L
(llk--define-xk xk-hyper-r #xffee nil) ;; XK_Hyper_R
;; Latin 1
;; For numbers and letters, MS-Windows does not define constant names.
;; X11 defines distinct keysyms for lowercase and uppercase
;; letters. We use only the uppercase ones. Events with lowercase
;; letters are converted to uppercase.
(define-xk xk-space #x0020 #x20) ;; XK_space VK_SPACE
(define-xk xk-0 #x0030 #x30) ;; XK_0
(define-xk xk-1 #x0031 #x31) ;; XK_1
(define-xk xk-2 #x0032 #x32) ;; XK_2
(define-xk xk-3 #x0033 #x33) ;; XK_3
(define-xk xk-4 #x0034 #x34) ;; XK_4
(define-xk xk-5 #x0035 #x35) ;; XK_5
(define-xk xk-6 #x0036 #x36) ;; XK_6
(define-xk xk-7 #x0037 #x37) ;; XK_7
(define-xk xk-8 #x0038 #x38) ;; XK_8
(define-xk xk-9 #x0039 #x39) ;; XK_9
(define-xk xk-a #x0041 #x41) ;; XK_A
(define-xk xk-b #x0042 #x42) ;; XK_B
(define-xk xk-c #x0043 #x43) ;; XK_C
(define-xk xk-d #x0044 #x44) ;; XK_D
(define-xk xk-e #x0045 #x45) ;; XK_E
(define-xk xk-f #x0046 #x46) ;; XK_F
(define-xk xk-g #x0047 #x47) ;; XK_G
(define-xk xk-h #x0048 #x48) ;; XK_H
(define-xk xk-i #x0049 #x49) ;; XK_I
(define-xk xk-j #x004A #x4A) ;; XK_J
(define-xk xk-k #x004B #x4B) ;; XK_K
(define-xk xk-l #x004C #x4C) ;; XK_L
(define-xk xk-m #x004D #x4D) ;; XK_M
(define-xk xk-n #x004E #x4E) ;; XK_N
(define-xk xk-o #x004F #x4F) ;; XK_O
(define-xk xk-p #x0050 #x50) ;; XK_P
(define-xk xk-q #x0051 #x51) ;; XK_Q
(define-xk xk-r #x0052 #x52) ;; XK_R
(define-xk xk-s #x0053 #x53) ;; XK_S
(define-xk xk-t #x0054 #x54) ;; XK_T
(define-xk xk-u #x0055 #x55) ;; XK_U
(define-xk xk-v #x0056 #x56) ;; XK_V
(define-xk xk-w #x0057 #x57) ;; XK_W
(define-xk xk-x #x0058 #x58) ;; XK_X
(define-xk xk-y #x0059 #x59) ;; XK_Y
(define-xk xk-z #x005A #x5A));; XK_Z
(defun llk-init ()
"Initialize low level key events.
Fills the `llk-keysyms' list, and binds the `low-level-key' event
to the `llk-handle' function. Resets the `llk-bindings' list.
Besides calling this function, you need to set `enable-low-level-key-events'
to a non-nil value."
(interactive)
(llk-define-keysyms)
(define-key special-event-map [low-level-key] 'llk-handle)
(setq llk-bindings nil))
(defsubst event-is-key-press (event)
"Return the value of the IS-KEY-PRESS field of the EVENT, a low level key event."
(declare (side-effect-free t))
(if (consp event) (nth 1 event)))
(defsubst event-keysym (event)
"Return the value of the KEY field of the EVENT, a low level key event."
(declare (side-effect-free t))
(if (consp event) (nth 2 event)))
(defsubst event-modifier (event)
"Return the value of the MODIFIER field of the EVENT, a low level key event."
(declare (side-effect-free t))
(if (consp event) (nth 3 event)))
(defsubst event-time (event)
"Return the value of the TIME field of the EVENT, a low level key event."
(declare (side-effect-free t))
(if (consp event) (nth 4 event)))
;; For example:
;; Bind key tap to command
;; (llk-bind 'tap 'xk-shift-l 'delete-other-windows)
;; Bind modifier tap to command
;; (llk-bind 'tap 'shift 'delete-other-windows)
;; Bind tap to hyper modifier
;; (llk-bind 'tap 'xk-shift-r (lambda ()
;; (message "H-...")
;; (setq unread-command-events
;; (append (event-apply-hyper-modifier nil) nil))))
;; Can bind to a command or function
(defun llk-bind (action key function)
"Bind a command or function to a low level key event.
The only action supported currently is `tap'. The key can be a keysym
symbol, or a modifier symbol (shift, control, alt, meta, hyper, super).
If there is no keysym symbol for a key, use the keysym number."
(push (list action key function) llk-bindings))
;; We store the last events (key/modifier is-press timestamp) here to
;; test for multitap.
(defvar llk-events nil
"Internal variable for detecting taps.")
;; If positive, return key (xk-shift-l, etc) else return nil.
(defun llk-detect-n-tap (n timeout)
"Internal function to detect n-tap keys."
(let (key
(is-press (event-is-key-press last-input-event))
;; convert number to keysym symbol
(keysym (cdr (assoc (event-keysym last-input-event) llk-keysyms)))
(timestamp (event-time last-input-event))
(modifier (event-modifier last-input-event)))
;; if ehte is no symbol for this key, use its keysym number
(unless keysym (setq keysym (event-keysym last-input-event)))
;; look in llk-tap-keys for the key, then the modifier
(if (member keysym llk-tap-keys)
(setq key keysym)
(if (member modifier llk-tap-keys)
(setq key modifier)))
(if (not key)
;; Key not in tap list, clear history
(setq llk-events nil)
;; Clear it also if the first element is from a different key
(and llk-events
(not (equal (car (car llk-events)) key))
(setq llk-events nil))
(push (list key is-press timestamp) llk-events)
;; Only care about last 2xN events
(ntake (* 2 n) llk-events)
;; If we have:
;; - Exactly 2 * n events.
;; - down, up, down, up, ...
;; - not two much time between first and last
(and (eq (* 2 n) (length llk-events))
(cl-every 'eq
(ntake (* 2 n)
(list nil t nil t nil t nil t
nil t nil t nil t nil t))
(mapcar 'cl-second llk-events))
(< (- (cl-third (cl-first llk-events))
(cl-third (car (last llk-events))))
timeout)
(progn
(setq llk-events nil)
key)))))
(llk--define-xk xk-space #x0020 #x20) ;; XK_space VK_SPACE
(llk--define-xk xk-0 #x0030 #x30) ;; XK_0
(llk--define-xk xk-1 #x0031 #x31) ;; XK_1
(llk--define-xk xk-2 #x0032 #x32) ;; XK_2
(llk--define-xk xk-3 #x0033 #x33) ;; XK_3
(llk--define-xk xk-4 #x0034 #x34) ;; XK_4
(llk--define-xk xk-5 #x0035 #x35) ;; XK_5
(llk--define-xk xk-6 #x0036 #x36) ;; XK_6
(llk--define-xk xk-7 #x0037 #x37) ;; XK_7
(llk--define-xk xk-8 #x0038 #x38) ;; XK_8
(llk--define-xk xk-9 #x0039 #x39) ;; XK_9
(llk--define-xk xk-a #x0041 #x41) ;; XK_A
(llk--define-xk xk-b #x0042 #x42) ;; XK_B
(llk--define-xk xk-c #x0043 #x43) ;; XK_C
(llk--define-xk xk-d #x0044 #x44) ;; XK_D
(llk--define-xk xk-e #x0045 #x45) ;; XK_E
(llk--define-xk xk-f #x0046 #x46) ;; XK_F
(llk--define-xk xk-g #x0047 #x47) ;; XK_G
(llk--define-xk xk-h #x0048 #x48) ;; XK_H
(llk--define-xk xk-i #x0049 #x49) ;; XK_I
(llk--define-xk xk-j #x004A #x4A) ;; XK_J
(llk--define-xk xk-k #x004B #x4B) ;; XK_K
(llk--define-xk xk-l #x004C #x4C) ;; XK_L
(llk--define-xk xk-m #x004D #x4D) ;; XK_M
(llk--define-xk xk-n #x004E #x4E) ;; XK_N
(llk--define-xk xk-o #x004F #x4F) ;; XK_O
(llk--define-xk xk-p #x0050 #x50) ;; XK_P
(llk--define-xk xk-q #x0051 #x51) ;; XK_Q
(llk--define-xk xk-r #x0052 #x52) ;; XK_R
(llk--define-xk xk-s #x0053 #x53) ;; XK_S
(llk--define-xk xk-t #x0054 #x54) ;; XK_T
(llk--define-xk xk-u #x0055 #x55) ;; XK_U
(llk--define-xk xk-v #x0056 #x56) ;; XK_V
(llk--define-xk xk-w #x0057 #x57) ;; XK_W
(llk--define-xk xk-x #x0058 #x58) ;; XK_X
(llk--define-xk xk-y #x0059 #x59) ;; XK_Y
(llk--define-xk xk-z #x005A #x5A));; XK_Z
(defun describe-low-level-key ()
"Wait for key press and describe the low level key event it generates."
"Wait for key press and describe the low-level key event it generates."
(interactive)
(setq llk-describe-next-press t))
(define-key special-event-map [low-level-key] 'llk--describe))
(defun llk-show-event-description ()
"Show information about the last low level key event."
(setq llk-describe-next-press nil)
(defun llk--describe ()
"Internal function for `special-event-map' to describe low level key events."
(interactive)
(when (low-level-key-is-key-press last-input-event)
(define-key special-event-map [low-level-key] 'llk-handle)
(with-help-window (help-buffer)
(insert "\n")
(let* ((xk (event-keysym last-input-event))
(let* ((xk (low-level-key-key last-input-event))
(sym (assoc xk llk-keysyms)))
(insert (format "Keysym number: %d (#x%X),\n" xk xk))
(if sym
(insert (format "which corresponds to named key %s.\n\n" (cdr sym)))
(insert "which does not correspond to any known named key.\n\n"))
(if (event-modifier last-input-event)
(if (low-level-key-modifier last-input-event)
(insert (format "This key corresponds to the %s modifier.\n\n"
(event-modifier last-input-event)))
(low-level-key-modifier last-input-event)))
(insert "This key does not correspond to a modifier.\n\n"))
(insert "See the value of the `llk-keysyms' variable for a list of known keys.\n"))))
(insert "See the value of the `llk-keysyms' variable for a list of known keys.\n")))))
(defun llk-handle ()
"Internal function to handle low level key events."
(interactive)
(if (and (event-is-key-press last-input-event)
llk-describe-next-press)
(llk-show-event-description)
(let ((tap-key (llk-detect-n-tap
llk-tap-count
llk-tap-timeout)))
(when tap-key
(let ((func (cl-third
(seq-find
(lambda (b)
(and (eq (cl-first b) 'tap)
(eq (cl-second b) tap-key)))
llk-bindings))))
(cond
((commandp func) (call-interactively func))
((functionp func) (funcall func))))))))
(llk--define-keysyms)
(define-key special-event-map [low-level-key] 'llk-handle)
(setq llk-bindings nil)

View file

@ -13158,13 +13158,8 @@ The value configures the set of keys that are handled:
If t, send events for all keys.
If a number, send events for the corresponding keysym. When calling
`llk-init', a set of variables with the xk- prefix is initialized with
the numeric values for keysyms. Because this number are platform
dependent, only the variables should be used to refer to a key. For
example, the 'xk-backspace' variable refer to the backspace key, with
the numeric value 0xff08 on X, and the 0x08 on MS-Windows. You can see
all defined variables on the variable `llk-keysyms'.
If a number, send events for the corresponding keysym. This numbers are
platform dependente. See `llk-keysyms'.
If a symbol, a predefined set of keys is selected. The only currently
valid symbol is 'modifiers.