Edit

kc3-lang/libxkbcommon/doc/quick-guide.md

Branch :

  • Show log

    Commit

  • Author : Pierre Le Marre
    Date : 2025-05-06 18:05:30
    Hash : 58397e94
    Message : Deprecate xkb_keymap_new_from_names() - Deprecate `xkb_keymap_new_from_names()` in favor of `xkb_keymap_new_from_names2()` - Add new changelog fragment type `deprecated`. - Change documentation to use the new function.

  • doc/quick-guide.md
  • # Quick Guide
    
    ## Introduction
    
    This document contains a quick walk-through of the often-used parts of
    the library. We will employ a few use-cases to lead the examples:
    
    1. An evdev client. "evdev" is the Linux kernel's input subsystem; it
       only reports to the client which keys are pressed and released.
    
    2. An X11 client, using the XCB library to communicate with the X
       server and the xcb-xkb library for using the XKB protocol.
    
    3. A Wayland client, using the standard protocol.
    
    The snippets are not complete, and some support code is omitted. You
    can find complete and more complex examples in the source directory:
    
    1. tools/interactive-evdev.c contains an interactive evdev client.
    
    2. tools/interactive-x11.c contains an interactive X11 client.
    
    3. tools/interactive-wayland.c contains an interactive Wayland client.
    
    Also, the library contains many more functions for examining and using
    the library context, the keymap and the keyboard state. See the
    hyper-linked reference documentation or go through the header files in
    xkbcommon/ for more details.
    
    ## Code
    
    Before we can do anything interesting, we need a library context:
    
    ~~~{.c}
        #include <xkbcommon/xkbcommon.h>
    
        struct xkb_context *ctx;
    
        ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
        if (!ctx) <error>
    ~~~
    
    The `xkb_context` contains the keymap include paths, the log level and
    functions, and other general customizable administrativia.
    
    Next we need to create a keymap, `xkb_keymap`. This is an immutable object
    which contains all of the information about the keys, layouts, etc. There
    are different ways to do this.
    
    If we are an evdev client, we have nothing to go by, so we need to ask
    the user for his/her keymap preferences (for example, an Icelandic
    keyboard with a Dvorak layout). The configuration format is commonly
    called RMLVO (Rules+Model+Layout+Variant+Options), the same format used
    by the X server. With it, we can fill a struct called `xkb_rule_names`;
    passing `NULL` chooses the system's default.
    
    ~~~{.c}
        struct xkb_keymap *keymap;
        /* Example RMLVO for Icelandic Dvorak. */
        struct xkb_rule_names names = {
            .rules = NULL,
            .model = "pc105",
            .layout = "is",
            .variant = "dvorak",
            .options = "terminate:ctrl_alt_bksp"
        };
    
        keymap = xkb_keymap_new_from_names2(ctx, &names,
                                            XKB_KEYMAP_FORMAT_TEXT_V1,
                                            XKB_KEYMAP_COMPILE_NO_FLAGS);
        if (!keymap) <error>
    ~~~
    
    If we are a Wayland client, the compositor gives us a string complete
    with a keymap. In this case, we can create the keymap object like this:
    
    ~~~{.c}
        /* From the wl_keyboard::keymap event. */
        const char *keymap_string = <...>;
        struct xkb_keymap *keymap;
    
        keymap = xkb_keymap_new_from_string(ctx, keymap_string,
                                            XKB_KEYMAP_FORMAT_TEXT_V1,
                                            XKB_KEYMAP_COMPILE_NO_FLAGS);
        if (!keymap) <error>
    ~~~
    
    If we are an X11 client, we are better off getting the keymap from the
    X server directly. For this we need to choose the XInput device; here
    we will use the core keyboard device:
    
    ~~~{.c}
        #include <xkbcommon/xkbcommon-x11.h>
    
        xcb_connection_t *conn = <...>;
        int32_t device_id;
    
        device_id = xkb_x11_get_core_keyboard_device_id(conn);
        if (device_id == -1) <error>
    
        keymap = xkb_x11_keymap_new_from_device(ctx, conn, device_id,
                                                XKB_KEYMAP_COMPILE_NO_FLAGS);
        if (!keymap) <error>
    ~~~
    
    Now that we have the keymap, we are ready to handle the keyboard devices.
    For each device, we create an `xkb_state`, which remembers things like which
    keyboard modifiers and LEDs are active:
    
    ~~~{.c}
        struct xkb_state *state;
    
        state = xkb_state_new(keymap);
        if (!state) <error>
    ~~~
    
    For X11/XCB clients, this is better:
    
    ~~~{.c}
        state = xkb_x11_state_new_from_device(keymap, conn, device_id);
        if (!state) <error>
    ~~~
    
    When we have an `xkb_state` for a device, we can start handling key events
    from it.  Given a keycode for a key, we can get its keysym:
    
    ~~~{.c}
        <key event structure> event;
        xkb_keycode_t keycode;
        xkb_keysym_t keysym;
    
        keycode = event->keycode;
        keysym = xkb_state_key_get_one_sym(state, keycode);
    ~~~
    
    We can see which keysym we got, and get its name:
    
    ~~~{.c}
        char keysym_name[64];
    
        if (keysym == XKB_KEY_Space)
            <got a space>
    
        xkb_keysym_get_name(keysym, keysym_name, sizeof(keysym_name));
    ~~~
    
    libxkbcommon also supports an extension to the classic XKB, whereby a
    single event can result in multiple keysyms. Here's how to use it:
    
    ~~~{.c}
        const xkb_keysym_t *keysyms;
        int num_keysyms;
    
        num_keysyms = xkb_state_key_get_syms(state, keycode, &keysyms);
    ~~~
    
    We can also get a UTF-8 string representation for this key:
    
    ~~~{.c}
        char *buffer;
        int size;
    
        // First find the needed size; return value is the same as snprintf(3).
        size = xkb_state_key_get_utf8(state, keycode, NULL, 0) + 1;
        if (size <= 1) <nothing to do>
        buffer = <allocate size bytes>
    
        xkb_state_key_get_utf8(state, keycode, buffer, size);
    ~~~
    
    Of course, we also need to keep the `xkb_state` up-to-date with the
    keyboard device, if we want to get the correct keysyms in the future.
    
    If we are an evdev client, we must let the library know whether a key
    is pressed or released at any given time:
    
    ~~~{.c}
        enum xkb_state_component changed;
    
        if (<key press>)
            changed = xkb_state_update_key(state, keycode, XKB_KEY_DOWN);
        else if (<key release>)
            changed = xkb_state_update_key(state, keycode, XKB_KEY_UP);
    ~~~
    
    The `changed` return value tells us exactly which parts of the state
    have changed.
    
    If it is a key-repeat event, we can ask the keymap what to do with it:
    
    ~~~{.c}
        if (<key repeat> && !xkb_keymap_key_repeats(keymap, keycode))
            <discard event>
    ~~~
    
    On the other hand, if we are an X or Wayland client, the server already
    does the hard work for us. It notifies us when the device's state
    changes, and we can simply use what it tells us (the necessary
    information usually comes in a form of some "state changed" event):
    
    ~~~{.c}
        changed = xkb_state_update_mask(state,
                                        event->depressed_mods,
                                        event->latched_mods,
                                        event->locked_mods,
                                        event->depressed_layout,
                                        event->latched_layout,
                                        event->locked_layout);
    ~~~
    
    Now that we have an always-up-to-date `xkb_state`, we can examine it.
    For example, we can check whether the Control modifier is active, or
    whether the Num Lock LED is active:
    
    ~~~{.c}
        if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_CTRL,
                                         XKB_STATE_MODS_EFFECTIVE) > 0)
            <The Control modifier is active>
    
        if (xkb_state_led_name_is_active(state, XKB_LED_NAME_NUM) > 0)
            <The Num Lock LED is active>
    ~~~
    
    And that's it! Eventually, we should free the objects we've created:
    
    ~~~{.c}
        xkb_state_unref(state);
        xkb_keymap_unref(keymap);
        xkb_context_unref(ctx);
    ~~~