Table of Contents
Keyboard
A key press says three separate things, and cl-windowing reports each one on its own:
| What | Where | Use it for |
|---|---|---|
| The physical key: where the key is | event-key |
Controls that depend on position, such as WASD movement. |
| The logical key: what the layout makes of it | event-symbol |
Shortcuts, such as Control+Z. |
| The text typed | :text events, event-text |
Text fields. |
Mixing them up is the usual source of keyboard bugs. On a French AZERTY
keyboard the key right of Caps Lock is physically :a and types q:
| Keyboard | Key pressed | event-key |
event-symbol |
:text |
|---|---|---|---|---|
| US | the key right of Caps Lock | :a |
:a |
"a" |
| French | the key right of Caps Lock | :a |
:q |
"q" |
| French | the key right of Tab | :q |
:a |
"a" |
A game that moves left on event-key :a uses the same physical key on
both keyboards. An editor that undoes on event-symbol :z uses the key
labelled Z on both.
Physical keys
event-key names the key for its place on a US keyboard, whatever the
layout: :a to :z, :digit-0 to :digit-9, :f1 to :f24, :return,
:escape, :space, :left-shift, :up, :keypad-enter and so on. Every
name is on Key Names. A key without a name is :unknown.
event-scancode is the platform's own code for the key. On Linux it is
the evdev key code, such as 30 for :a; on Windows, the set 1 scancode,
such as #x1E for :a, with #xE000 added for extended keys. Use it to
let players rebind keys that have no name, and store it with the binding:
it is stable on one machine but means nothing on another platform.
Logical keys
event-symbol is the key the layout makes of the key, in the same names.
It is nil when the layout makes none of them, as for letters a US
keyboard does not have, such as ö.
The symbol is computed with the modifiers held, so Shift changes it:
- Shift+A is still
:a, because upper and lower case letters share a name. - On a US keyboard Shift+1 types !, which has no name, so its symbol is
nil. For a shortcut such as Control+Shift+1, matchevent-key:digit-1and check the modifiers. - With Num Lock off, keypad keys are
:home,:up,:page-upand so on, and with it on,:keypad-0to:keypad-9and:keypad-decimal. - AltGr is
:right-alt.
Text
:text events carry what was typed, as a string, after the layout, dead
keys and compose sequences have done their work:
- A dead acute, then e, types
"é"in one:textevent. The dead key itself types nothing. - A compose sequence such as Compose, o, c types
"©". - AltGr combinations type their character, such as AltGr+E typing
"€"on many European layouts.
Dead keys and compose sequences use the compose table of the locale
($LC_ALL, $LC_CTYPE or $LANG). In a locale without one, they type
nothing.
Some keys do not send :text:
- No text is sent while Control, Alt or Super is held. Those combinations are shortcuts, so Control+S saves and does not type s.
- Keys that type control characters send none: Return, Tab, Backspace,
Delete and Escape. Handle them as
:key-pressevents, withevent-keyorevent-symbol.
The :text event comes right after the :key-press that typed it. Text
chosen in an input method comes as :text too; see
Input Methods.
Modifiers
(win:event-modifier-p event :control) => boolean
event-modifier-p says whether a modifier was in effect when a key,
pointer, button or scroll event happened. The modifiers are:
| Modifier | Meaning |
|---|---|
:shift |
Shift is held. |
:control |
Control is held. |
:alt |
Alt is held. AltGr is not Alt. |
:super |
Super (the Windows or Command key) is held. |
:caps-lock |
Caps Lock is on. |
:num-lock |
Num Lock is on. |
event-modifiers is the mask of them all, one bit each, the bit
modifier-bit returns: 1 for :shift, then 2, 4, 8, 16 and 32 in the
order of the table. It is for comparing sets of modifiers, and for
filling an event, as a backend or a test does:
(= (win:event-modifiers event) (win:modifier-bit :control)) ; Control alone
:text events carry no modifiers.
A press of a modifier key reports itself too, as a :key-press with
event-key :left-shift, :right-control and so on.
(when (and (eq (win:event-type event) :key-press)
(win:event-modifier-p event :control)
(not (win:event-modifier-p event :shift))
(eq (win:event-symbol event) :z))
(undo))
Key repeat
A held key repeats. Each repeat is a :key-press with event-repeat-p
true, followed by its :text if it types any. There is no :key-release
between repeats.
The delay before the first repeat and the rate afterwards are the platform's: the user's settings, as the compositor reports them, or 600 ms and 25 per second by default. A compositor that sets the rate to 0 turns repeating off. Modifier keys and others the keymap marks as not repeating do not repeat.
Repeats are made by process-events, which wakes up in time for them even
when it was given a longer timeout. They come only while it is called.
Ignore repeats where they do not make sense:
(when (and (eq (win:event-type event) :key-press)
(not (win:event-repeat-p event))
(eq (win:event-key event) :space))
(jump))
Focus
Keyboard events go to the window with the keyboard focus. It gains focus
with :focus-in and loses it with :focus-out. A key held when the
window loses focus stops repeating, and its :key-release is not reported
to that window. Treat :focus-out as releasing every key you track as
held.
Tracking held keys
cl-windowing reports changes, not state. To know which keys are down, as a game does every frame, keep a table:
(defvar *held* (make-hash-table))
(defun track (event)
(case (win:event-type event)
(:key-press (setf (gethash (win:event-key event) *held*) t))
(:key-release (remhash (win:event-key event) *held*))
(:focus-out (clrhash *held*))))
(defun held-p (key)
(gethash key *held*))
On Wayland
Input comes from the compositor's first seat. The keymap is the one the
compositor sends, compiled by libxkbcommon through
cl-xkbcommon. It changes when
the user switches layouts. If a keymap does not compile, cl-windowing
warns and keeps the previous one. Until there is a keymap, event-symbol
is nil and no text is typed.
Input methods, for typing Chinese, Japanese or Korean, speak text-input-v3 where the compositor offers it. See Input Methods.
On Windows
Keys are named by their scancodes, mapped to the same physical keys as on
Wayland, and the layout's virtual keys give event-symbol. Text comes as
Windows translates key presses, dead keys included. AltGr is not reported
as Control and Alt, though Windows sends it as both: it types characters,
and they arrive as :text. Input methods speak IMM32; see
Input Methods and Win32 Backend.