4 Concepts
dxtr edited this page 2026-10-18 16:16:25 +02:00

Concepts

A key press goes through several stages before it becomes text. This page names each stage and the cl-xkbcommon objects that handle it.

physical key ──► keycode ──► keymap + keyboard state ──► keysym ──► compose ──► text
                 (integer)   (layout, level,             (integer)  (dead keys,
                              modifiers)                             sequences)

Keycodes

A keycode identifies a physical key, regardless of what is printed on it. cl-xkbcommon takes xkb keycodes. On Linux an xkb keycode is the evdev code of the key plus 8:

Source What it gives What to pass
Wayland wl_keyboard.key evdev code (evdev-keycode code): code + 8
X11 key events xkb keycode as given
evdev (/dev/input) evdev code (evdev-keycode code): code + 8

Some useful xkb keycodes on a PC keyboard:

Keycode Key name Key
9 ESC Escape
21 AE12 left of Backspace (= on US, dead acute on Swedish)
24 AD01 Q
34 AD11 right of P ([ on US, å on Swedish)
36 RTRN Return
37 LCTL left Control
38 AC01 A
50 LFSH left Shift
64 LALT left Alt
66 CAPS Caps Lock
108 RALT right Alt (AltGr on many layouts)
133 LWIN left Super ("Windows" key)

Every key has a key name of up to four characters, such as AC01 (row C, column 1). Key names do not depend on the layout, so they are a stable way to refer to a key's position. key-name and find-key convert between keycodes and key names.

The type keycode is (unsigned-byte 32).

Keysyms

A keysym is the symbol a key produces: a, A, Return, F1, Shift_L, dead_acute. The same key produces different keysyms depending on the layout and the modifiers held: key 34 produces bracketleft on a US layout and aring (å) on a Swedish one.

Keysyms are integers. Most keysyms have names, and many stand for a character. See Keysyms.

The type keysym is (unsigned-byte 32).

Text

Keysyms and text are related but not the same:

  • Return is a keysym that types the character Return, but F1 and Shift_L type nothing.
  • A dead key such as dead_acute types nothing itself. It changes the next key, so dead acute followed by e types é. This is a compose sequence; see Compose and Dead Keys.

key-keysym gives the keysym, key-text the text without composing, and key-press-text the text a key press types, compose sequences included.

Keymaps

A keymap describes a whole keyboard: for every key, what it produces in each layout and at each level, and which keys are modifiers. It is compiled from names, following the XKB RMLVO model:

Name Meaning Examples
rules which rule set maps the other names to keymap parts "evdev"
model the keyboard model "pc105"
layout the layout, or several separated by commas "us", "se", "us,se"
variant a variant of each layout "dvorak", "nodeadkeys"
options extra behaviour "compose:ralt", "ctrl:nocaps"

These are the names setxkbmap and desktop keyboard settings use. A keymap can also be compiled from text, such as the text a Wayland compositor sends. See Contexts and Keymaps.

Layouts and levels

A keymap may hold several layouts (XKB calls them groups), such as US and Swedish. One is active at a time, and the keyboard state knows which. Layouts are numbered from 0, in the order keymap-layouts returns them.

Within a layout, each key has one or more levels. Level 1 is the plain key, level 2 is usually Shift, and level 3 is usually AltGr (the :level-3 modifier). On a Swedish layout, key 11 types 2, " with Shift, and @ with AltGr.

cl-xkbcommon does not expose levels directly. The keyboard state picks the level from the modifiers, and key-keysym and key-text return what that level produces.

Modifiers

A modifier is a state that changes what keys produce, or that an application reads for shortcuts: Shift, Control, Alt, Super, Caps Lock and so on. A modifier can be

  • depressed: held down, as Shift usually is;
  • latched: active for the next key only, as with sticky keys;
  • locked: active until it is pressed again, as Caps Lock is.

The modifier is active (in effect) if it is any of these.

cl-xkbcommon names the common modifiers with keywords:

Keyword Modifier
:shift Shift
:caps-lock Caps Lock
:control Control
:alt Alt
:num-lock Num Lock
:super Super, the "Windows" or "Command" key
:level-3 AltGr, which selects level 3
:level-5 selects level 5, on some layouts
:meta Meta
:hyper Hyper
:scroll-lock Scroll Lock

The type modifier-keyword is these keywords, and active-modifiers lists them in this order. A keymap may also have modifiers that no keyword names. Any modifier can be named by its name in the keymap, as a string, such as "Control" or "Mod3". Not every keymap has every modifier: keymap-modifier-p says whether it does, and a keyword the keymap lacks is simply never active.

Each modifier of a keymap has an index. Modifier n is bit n of a modifier mask, the integer Wayland and X11 use to send modifier state. keymap-modifiers lists the modifiers in index order.

Real and virtual modifiers

Underneath, X11 has eight real modifiers: Shift, Lock, Control and Mod1 to Mod5. Alt, Super, Meta and the rest are virtual modifiers, which a keymap maps onto real ones. Most keymaps map Alt and Meta both to Mod1, so while Alt is held, Meta is in effect too, and modifier-active-p says so for both.

active-modifiers lists each real mapping once: it leaves out a keyword that sits on the same real modifiers as one before it in the table above. Holding Alt gives (:alt), not (:alt :meta).

Consumed modifiers

When a modifier changes which keysym a key produces, the key consumes it. Shift+A produces A, so the A key consumes Shift. This matters for shortcuts: when Control and Shift are held and A is pressed, the keysym is A and the user pressed Control+A. Shift is part of the A, not a separate modifier of the shortcut. key-shortcut returns a key's keysym with the modifiers it did not consume, and modifier-consumed-p asks about one. See Recipes.

The objects

Object Made by What it is Shared between threads?
context make-context where keymaps and compose tables are compiled; collects libxkbcommon's log no
keymap make-keymap, make-keymap-from-string what every key produces yes, for reading
keyboard-state make-keyboard-state one keyboard's modifiers and layout no
compose-table make-compose-table, make-compose-table-from-string a locale's compose sequences no
compose-state make-compose-state where a compose sequence has got to no

A typical program has one context and one keymap per keyboard, which it replaces when the keymap changes. It has one keyboard state per keyboard, one compose table, and one compose state per keyboard.