So, in my earlier post, I mentioned that I’d been working on a library called xkbcommon. Since it’s got a massively misleading name (and renaming hasn’t been totally ruled out yet …), it’s probably worth an introductory post.
tl;dr: It loads XKB keymaps and can manage tricky things like modifier state for you.
tell me more!
At its core, xkbcommon is nothing to do, as you might think, with X11. It’s about two things: parsing and loading keymaps, and managing their ongoing state. State, in terms of keyboards, is the usual suspects — modifiers (e.g. Shift, Alt), multiple layouts (mostly for multiple languages), and LEDs. In general, you only want one person keeping a canonical copy of the state, and distributing it to its clients, as both the X server and Wayland do today. xkbcommon allows for this mode of operation, and is indeed how all current Wayland clients handle keyboard input.
One notable thing xkbcommon isn’t, is an input method. Anything more complex than ‘I press one key and get a symbol’, such as phonetic compound composition for CJK/Thai/etc (where you type out a word, which, when completed, becomes much fewer characters on screen) or menu-based selections as common in both Arabic and Mandarin (where you type out the beginnings of a word, and are then offered a selection of choices to complete), or even just the seemingly-straightforward compose key (e.g. right Alt + o + / → ø), are implemented internally to the toolkit, and don’t really use xkbcommon at all. Supporting the two (key-based vs. string-based) methods in the same protocol seems straightforward, but quickly becomes hugely unwieldy if you ever need to do anything weird like, say, process shortcuts. Anyway, if you want to find out more about input methods on Wayland, Michael Hasselmann has a very nice update about Maalit and ibus.
This makes life rather more simple for the keyboard-based protocols: all we have to do is tell xkbcommon every time someone presses (or releases) a key, and ask it which symbols resulted from that.
There are two methods of operation. The self-contained approach, suitable if you’re writing programs which back directly on to a terminal and have absolute full control over the entire keyboard, is used by most of our tests, such as interactive.c, which is a reasonably summary of how to implement one of these clients. Here, the same client manages the entire state, and never needs to deal with any external entities.
However, the most common method of operation is the one used by Wayland and X11, where a central server manages the state and informs the clients. Under this model, the clients never update the state implicitly through xkb_state_update_key(); they only ever use xkb_state_update_mask() in response to its master sending it a new and complete copy of the state.
ok — so how do I use it?
Firstly, create a context:
ctx = xkb_context_new(0);
which will be the base for all your xkbcommon operations. Secondly, we need to compile a keymap. If you have a named keymap you want to compile from (e.g. the user has specified ‘Icelandic Dvorak with Caps Lock mapped to Ctrl’), use:
keymap = xkb_keymap_new_from_names(ctx, rules, model, layout, variant, options);
On Linux, the rules and model are almost always ‘evdev’. Layout is usually the ISO two-character country (not language!) code, variant is specific to the layout, and options are global. In this case, we would use:
keymap = xkb_keymap_new_from_names(ctx, "evdev", "evdev", "is", "dvorak", "ctrl:nocaps");
A keymap is a static and immutable ruleset describing the translation between key events and the resulting output (e.g. if d is pressed while shift is held down, then output D). On top of the keymap, for every keyboard we use, we generate a new state object:
state = xkb_state_new(keymap);
which will track things like which keys are currently held down. Every time a key is pressed or released, we first get the symbols it produces:
sym = xkb_state_key_get_one_sym(state, keycode);
which returns a key symbol (listed in xkbcommon-keysyms.h, and identical to the X11 keysyms; xkb_keysym_to_utf8() may be used to translate this to a string) generated by that keypress. For example, pressing the B key on Icelandic Dvorak with AltGr held down would return XKB_KEY_ssharp (i.e. ß). After we have got the symbol to be used, we then update the state object with the keypress. It is crucial the order of these two operations is not reversed! Anyhow, we update the state object like so for sole-control programs:
changed = xkb_state_update_key(state, keycode, XKB_KEY_DOWN);
or thusly for clients who get their master state from an external program, e.g. Wayland compositor:
changed = xkb_state_update_mask(state, mods_down, mods_latched, mods_locked, ...);
and we have now completed our key processing. Hurrah!
Note that you do not need any special cases anywhere: xkbcommon takes care of the mechanics of handling special keys under the hood for you. The one case where you need extended functionality is shortcut processing (e.g. capture Ctrl+P), but that’s a story for another day.
shouldn’t all this be in the documentation?
PS: Funny story: I was planning to explain in this footnote how I’d just picked Icelandic Dvorak for a laugh and no-one would make a layout that ridiculous. But no, it actually exists …