The X Keyboard Extension

Peter Hutterer

The X Keyboard Extension (XKB) is responsible for some of the keyboard behaviour and, more importantly, it defines the layout of the keyboard as seen by clients.

XKB as seen by the user is quite different to XKB as seen by the server, this chapter will explain the the differences between the interfaces and how they generate a keyboard layout that can be used by clients.

The most important thing about keyboard handling is that the value passed around is almost always the keycode---a context-free number that represents the key on the keyboard. Keycodes must be between 8 and 255 and the same physical key should generate the same keycode, every time. Beyond that, keycodes have no semantic meaning, they merely serve as index into various lookup tables.

RMLVO and Kccgst

The heart of keyboard configuration are the two visible interfaces of "Rules, Model, Layout, Variant, Options" (RMLVO) and "Keycode Compat Geometry Symbols Types" (Kccgst). Understanding the symbiotic relationship between these two interfaces is key to understanding keyboard configuration.

RMLVO

RMLVO keyboard configuration is what is usually exposed through various user interfaces. Users usually expect to set a keyboard layout as a combination of localised sets of symbols, for example the "us" or "de" layout, and a possible variant of that set, for example "dvorak" instead of the default "qwerty".

Further configuration is possible with additional options which usually enable or change a single key or action. Commonly used options are to enable server-actions such as zapping (terminating) the server or reassigning keys, for example changing Caps Lock to be a Compose key.

The definitions of what these options, layouts and variants actually mean are defined in the rules files. These days only two rules files matter: evdev and base, with the former being the one for most contemporary GNU/Linux systems, the latter being the rules file for other systems. Rules file are simply lookup tables in the form of "for this layout, load this symbol description". The curious reader can find them in /usr/share/X11/xkb/rules on most distributions. Rules come with an xml file that provide human-readable descriptions for each value. These xml files are used by GUIs.

Finally, models are the representation for which keys are actually present on a particular keyboard model. Because of the Linux kernel's abstraction, models have less weight than they used to have, with most keyboard simply using the "evdev" model.

Once the user has specified the desired combination of RMLVO, these descriptions are translated into an Kccgst description which is then loaded into the server. All files required for RMLVO lookups and translations are provided by the xkeyboard-config module and usually reside in /usr/share/X11/xkb.

Some more information about RMLVO is available here: http://who-t.blogspot.com.au/2008/09/rmlvo-keyboard-configuration.html

Kccgst

RMLVO is user-friendly but incomplete, it does not define what a "us" layout actually means. This is the job of the Kccgst files, and the rules to match one with the other.

When a user specifies the layout "us", the model is matched with that model's "Keycodes" section, that model's "Geometry" section, and the "us" "Symbols" section. "Types" and "Compat" are largely default sets, they will be described later.

The Geometry is easiest to explain: it describes the physical location and dimensions of each key. It has no other purpose than allowing clients to display how the keyboard looks. It is also the only part of Kccgst that is not mandatory. The geometry is largely based on the model, so for those keyboards without their own model and geometry, an abstracted, more generic geometry is used.

The Keycodes section simply assigns symbolic names to keys and their numerical codes. The keycodes are grouped in rows, the symbolic name for the "Q" key on qwerty layout is AD01 - first key in row D. Other symbolic names are FK03 (third function key), LCTL (left control key), etc. These symbolic names are only useful within a Kccgst description and serve as lookups.

The Symbols section is the one that actually describes what a key does. It matches up a keycode's symbolic name with the symbols that that key represents. On a qwerty layout, the "Q" key looks like this:

key { type= "ALPHABETIC", symbols[Group1]= [ q, Q ] };

On an azerty layout, the same key looks like this: key { type= "ALPHABETIC", symbols[Group1]= [ a, A ] };

Note that the symbolic name is the same, since the physical key generates the same keycode, regardless of the layout.

This key example shows a key with 2 "levels". The Types section defines the various types of keys and their behaviour when modifiers are pressed. Each key is assigned a type in the Symbols section. The ALPHABETIC type shown above produces the first level's symbol by default, and the second level's symbol if the Shift or CapsLock modifiers are logically down. Other types work on other modifiers like AltGr, Ctrl+Alt, etc. But in all cases, the modifier combination simply refers to a specific level of that key.

The Symbols section defines which keys map to which modifiers. The example below shows that both the left and the right shift key map to the Shift modifier and thus trigger the second level in the ALPHABETIC type.

modifier_map Shift { <LFSH> };
modifier_map Shift { <RTSH> };

The Compat section defines actions that happen on key combinations. These actions include terminating the server, moving the pointer around, switching between layouts, etc.

One final note: in the example above, the symbols are defined for Group1. Each key may have up to 4 groups, with one group being active at any time on that keyboard. This allows for several layouts to be loaded simultaneously, with quick switching between the layouts.

Converting from RMLVO to Kccgst

RMLVO is largely a user-specific interface only, the server deals with Kccgst. This process works in two steps: first, the RMLVO is converted into a simple Kccgst description which is largely done by matching the RMLVO without looking into the actual sections. Then, the simple Kccgst is converted to the full description. That description is then loaded into (or by) the server.

A simple Kccgst description might look like this:

xkb_keymap {
    xkb_keycodes  { include "evdev+aliases(qwerty)" };
    xkb_types     { include "complete"      };
    xkb_compat    { include "complete"      };
    xkb_symbols   { include "pc+us+inet(evdev)+compose(caps)+terminate(ctrl_alt_bksp)"     };
    xkb_geometry  { include "pc(pc104)"     };
};

The conversion from simple Kccgst to the full description is handled by the xkbcomp process, a lexical parser that reads all files and assembles the final description. xkbcomp can produce textual descriptions or a binary format called "XKM" that closely represents the C structs the X server uses internally.

On server startup, the server forks xkbcomp for each keyboard and loads the xkm file that xkbcomp produces. Efforts are underway to move the core of xkbcomp into a libxkbcommon library to avoid the fork.

Key processing

Once the keyboard layout is set up, the process of key handling is relatively simple. A keycode is submitted by the driver. The server checks the keycode and changes modifier state where applicable. It also checks for any actions to be performed on that key. Once that is done, the keycode is sent to the client as a key event.

The client then matches up the keycode and modifier state with the keyboard layout previously obtained from the server and performs some action in response. The server provides modifier state and keycodes, but it is up to the client how it wants to treat that key. It may ignore the modifiers completely or even change the symbols to something completely different.