Compose Key Support in Weston

I recently added support to Weston for compose sequences via the configured compose key. This is now available in all of the Weston clients.

What are “compose sequences”? Let’s say I need to write to someone named Zoë, but I don’t have an “ë” key on my keyboard. I can create the letter using separate key strokes:

    RAlt, Shift+', e

The first key, RAlt (the Alt key on the right side of the keyboard), is the compose key (also called the Multi-key). It signals that a compose sequence is beginning. The next key is double-quote, constructed by holding one of the Shift keys while pressing the single-quote key.

Third we type the letter e. This completes the sequence. Or, more correctly, the system finds a match for this sequence in a table of available sequences, and thus considers it finished. The entry in the table indicates that the ‘ë’ symbol should be printed.

Improving Weston’s Limited Compose Key Implementation

I’ve written more extensively about use of the Compose key in X.org and how to configure symbols and use its various capabilities. Up until now compose didn’t work universally in Weston, but there was limited functionality available from the weston-simple-im demo input method. However, this input method module barely provides any compose sequences: the five vowels with umlauts, a heart, and a dozen other random symbols. Since they’re hardcoded in the input method’s source code there is no way to add to or configure them, and only clients using the zwp_input_method protocol even have access to them. In Weston, only a few clients like weston-editor use this protocol; notably weston-terminal does not, and thus has no way to enable the compose key at all.

Weston-simple-im’s compose support is serviceable and, with a fair bit of work, could be enhanced with the ability to load compose tables from external files to get a more complete set of compose sequences.

Libxkbcommon to the Rescue!

Fortunately, there is already a library called libxkbcommon that provides a complete compose key implementation including the loading of both system and user compose files (i.e. ~/.XCompose). Libxkbcommon is an evolutionary descendant of X.org’s classic XKB API (X11/extensions/XKB.h) that was generalized to be usable beyond X11. At its heart, it is a keyboard keymap compiler that maps physical keyboard presses to the appropriate letter or character.

In fact, Weston already includes and uses libxkbcommon in Toytoolkit for key mapping. Toytoolkit is a lightweight “demo-quality” widget system and Wayland client framework that implements the core input and display protocols as well as several optional protocols including relative-pointer and xdg-shell. All of the demo client programs included with Weston use toytoolkit for creating and managing their user interfaces and for handling input.

Toytoolkit registers a callback with Wayland, named keyboard_handle_key(), that is invoked each time a key on the keyboard is pressed or released. For the purposes of compose key handling we only care about presses. First, keyboard_handle_key() translates the keycode into its symbol:

    static void
    keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
                        uint32_t serial, uint32_t time, uint32_t key,
                        uint32_t state_w)
    {
        ...
        code = key + 8;
        ...
        num_syms = xkb_state_key_get_syms(input->xkb.state, code, &syms);

        sym = XKB_KEY_NoSymbol;
        if (num_syms == 1)
                sym = syms[0];

Then, the system is run through a compose key processing function, process_key_press():

        if (state == WL_KEYBOARD_KEY_STATE_PRESSED)
            sym = process_key_press(sym, input);

Process_key_press() hands things off to xkbcommon’s compose module by passing the sym through xkb_compose_state_feed(). It detects if the user’s configured compose key has been hit (e.g. RAlt) and begins accumulating subsequent keystrokes until the sequence is either completed or canceled, as indicated by xkb_compose_state_get_status():

    static xkb_keysym_t
    process_key_press(xkb_keysym_t sym, struct input *input)
    {
        if (sym == XKB_KEY_NoSymbol)
                return sym;
        if (xkb_compose_state_feed(input->xkb.compose_state, sym) != XKB_COMPOSE_FEED_ACCEPTED)
                return sym;

        switch (xkb_compose_state_get_status(input->xkb.compose_state))
        {
        case XKB_COMPOSE_COMPOSING:
                return XKB_KEY_NoSymbol;
        case XKB_COMPOSE_COMPOSED:
                return xkb_compose_state_get_one_sym(input->xkb.compose_state);
        case XKB_COMPOSE_CANCELLED:
                return XKB_KEY_NoSymbol;
        case XKB_COMPOSE_NOTHING:
                return sym;
        default:
                return sym;
        }
    }

Once a completed compose sequence is recognized, it’s retrieved from the XKB compose_state and the resultant symbol is returned as provided from the xkb_compose_state_get_one_sym() call.

There are also some steps required for initializing the XKB compose structures and dereferencing them once we’re done with them, but that’s all fairly straightforward; see my patch for full details.

Weston’s New & Improved Compose Key System

With these changes in place, Weston now recognizes the compose key and allows entry of all configured compose sequences in all Weston clients that accept text entry. Weston-editor and weston-terminal will accept the sequences and display the resulting symbol graphically. Weston-eventdemo accepts key events and compose sequences, and prints the symbols to its stdout. Get out there and start using your compose key!

Compose Key Support in Weston - weston_compose

Author: Bryce Harrington

Bryce is a founder and developer of the Inkscape project, but began his career in the aerospace industry as a spacecraft propulsions engineer.