libxkbcommon 1.2.1

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:

#include <xkbcommon/xkbcommon.h>
struct xkb_context *ctx;
ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
if (!ctx) <error>
xkb_context::xkb_context_new
struct xkb_context * xkb_context_new(enum xkb_context_flags flags)
Create a new context.
XKB_CONTEXT_NO_FLAGS
@ XKB_CONTEXT_NO_FLAGS
Do not apply any context flags.
Definition: xkbcommon.h:578
xkb_context
Opaque top level library context object.
xkbcommon.h
Main libxkbcommon API.

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.

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_names(ctx, &names,
XKB_KEYMAP_COMPILE_NO_FLAGS);
if (!keymap) <error>
xkb_keymap::xkb_keymap_new_from_names
struct xkb_keymap * xkb_keymap_new_from_names(struct xkb_context *context, const struct xkb_rule_names *names, enum xkb_keymap_compile_flags flags)
Create a keymap from RMLVO names.
XKB_KEYMAP_COMPILE_NO_FLAGS
@ XKB_KEYMAP_COMPILE_NO_FLAGS
Do not apply any flags.
Definition: xkbcommon.h:840
xkb_keymap
Opaque compiled keymap object.
Definition: xkbcommon.h:111
xkb_rule_names
Names to compile a keymap with, also known as RMLVO.
Definition: xkbcommon.h:327
xkb_rule_names::rules
const char * rules
The rules file to use.
Definition: xkbcommon.h:336

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:

/* 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>
xkb_keymap::xkb_keymap_new_from_string
struct xkb_keymap * xkb_keymap_new_from_string(struct xkb_context *context, const char *string, enum xkb_keymap_format format, enum xkb_keymap_compile_flags flags)
Create a keymap from a keymap string.
XKB_KEYMAP_FORMAT_TEXT_V1
@ XKB_KEYMAP_FORMAT_TEXT_V1
The current/classic XKB text format, as generated by xkbcomp -xkb.
Definition: xkbcommon.h:867

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:

#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>
xkb_x11_get_core_keyboard_device_id
int32_t xkb_x11_get_core_keyboard_device_id(xcb_connection_t *connection)
Get the keyboard device ID of the core X11 keyboard.
xkb_keymap::xkb_x11_keymap_new_from_device
struct xkb_keymap * xkb_x11_keymap_new_from_device(struct xkb_context *context, xcb_connection_t *connection, int32_t device_id, enum xkb_keymap_compile_flags flags)
Create a keymap from an X11 keyboard device.
xkbcommon-x11.h
libxkbcommon-x11 API - Additional X11 support for xkbcommon.

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:

struct xkb_state *state;
state = xkb_state_new(keymap);
if (!state) <error>
xkb_state::xkb_state_new
struct xkb_state * xkb_state_new(struct xkb_keymap *keymap)
Create a new keyboard state object.
xkb_state
Opaque keyboard state object.
Definition: xkbcommon.h:123

For X11/XCB clients, this is better:

state = xkb_x11_state_new_from_device(keymap, conn, device_id);
if (!state) <error>
xkb_state::xkb_x11_state_new_from_device
struct xkb_state * xkb_x11_state_new_from_device(struct xkb_keymap *keymap, xcb_connection_t *connection, int32_t device_id)
Create a new keyboard state object from an X11 keyboard device.

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:

<key event structure> event;
xkb_keycode_t keycode;
xkb_keysym_t keysym;
keycode = event->keycode;
keysym = xkb_state_key_get_one_sym(state, keycode);
xkb_state::xkb_state_key_get_one_sym
xkb_keysym_t xkb_state_key_get_one_sym(struct xkb_state *state, xkb_keycode_t key)
Get the single keysym obtained from pressing a particular key in a given keyboard state.
xkb_keysym_t
uint32_t xkb_keysym_t
A number used to represent the symbols generated from a key on a keyboard.
Definition: xkbcommon.h:195
xkb_keycode_t
uint32_t xkb_keycode_t
A number used to represent a physical key on a keyboard.
Definition: xkbcommon.h:134

We can see which keysym we got, and get its name:

char keysym_name[64];
if (keysym == XKB_KEY_Space)
<got a space>
xkb_keysym_get_name(keysym, keysym_name, sizeof(keysym_name));
xkb_keysym_get_name
int xkb_keysym_get_name(xkb_keysym_t keysym, char *buffer, size_t size)
Get the name of a keysym.

libxkbcommon also supports an extension to the classic XKB, whereby a single event can result in multiple keysyms. Here's how to use it:

const xkb_keysym_t *keysyms;
int num_keysyms;
num_keysyms = xkb_state_key_get_syms(state, keycode, &keysyms);
xkb_state::xkb_state_key_get_syms
int xkb_state_key_get_syms(struct xkb_state *state, xkb_keycode_t key, const xkb_keysym_t **syms_out)
Get the keysyms obtained from pressing a particular key in a given keyboard state.

We can also get a UTF-8 string representation for this key:

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);
xkb_state::xkb_state_key_get_utf8
int xkb_state_key_get_utf8(struct xkb_state *state, xkb_keycode_t key, char *buffer, size_t size)
Get the Unicode/UTF-8 string obtained from pressing a particular key in a given keyboard state.

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:

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);
xkb_state_component
xkb_state_component
Modifier and layout types for state objects.
Definition: xkbcommon.h:1349
xkb_state::xkb_state_update_key
enum xkb_state_component xkb_state_update_key(struct xkb_state *state, xkb_keycode_t key, enum xkb_key_direction direction)
Update the keyboard state to reflect a given key being pressed or released.
XKB_KEY_DOWN
@ XKB_KEY_DOWN
The key was pressed.
Definition: xkbcommon.h:1339
XKB_KEY_UP
@ XKB_KEY_UP
The key was released.
Definition: xkbcommon.h:1338

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:

if (<key repeat> && !xkb_keymap_key_repeats(keymap, keycode))
<discard event>
xkb_keymap::xkb_keymap_key_repeats
int xkb_keymap_key_repeats(struct xkb_keymap *keymap, xkb_keycode_t key)
Determine whether a key should repeat or not.

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):

changed = xkb_state_update_mask(state,
event->depressed_mods,
event->latched_mods,
event->locked_mods,
event->depressed_layout,
event->latched_layout,
event->locked_layout);
xkb_state::xkb_state_update_mask
enum xkb_state_component xkb_state_update_mask(struct xkb_state *state, xkb_mod_mask_t depressed_mods, xkb_mod_mask_t latched_mods, xkb_mod_mask_t locked_mods, xkb_layout_index_t depressed_layout, xkb_layout_index_t latched_layout, xkb_layout_index_t locked_layout)
Update a keyboard state from a set of explicit masks.

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:

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>
xkb_state::xkb_state_mod_name_is_active
int xkb_state_mod_name_is_active(struct xkb_state *state, const char *name, enum xkb_state_component type)
Test whether a modifier is active in a given keyboard state by name.
xkb_state::xkb_state_led_name_is_active
int xkb_state_led_name_is_active(struct xkb_state *state, const char *name)
Test whether a LED is active in a given keyboard state by name.
XKB_STATE_MODS_EFFECTIVE
@ XKB_STATE_MODS_EFFECTIVE
Effective modifiers, i.e.
Definition: xkbcommon.h:1361

And that's it! Eventually, we should free the objects we've created:

xkb_state_unref(state);
xkb_keymap_unref(keymap);
xkb_context_unref(ctx);
xkb_context::xkb_context_unref
void xkb_context_unref(struct xkb_context *context)
Release a reference on a context, and possibly free it.
xkb_keymap::xkb_keymap_unref
void xkb_keymap_unref(struct xkb_keymap *keymap)
Release a reference on a keymap, and possibly free it.
xkb_state::xkb_state_unref
void xkb_state_unref(struct xkb_state *state)
Release a reference on a keybaord state object, and possibly free it.