xkbcommon 1.14.0-beta2
Reference library implementing the XKB specification for parsing keyboard descriptions and handling keyboard state
Loading...
Searching...
No Matches
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.
  4. A Wayland server, 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 for clients

Before we can do anything interesting, we need a library context:

if (!ctx) {
/* Handle error */
// ...
exit(EXIT_FAILURE);
}
XKB_EXPORT struct xkb_context * xkb_context_new(enum xkb_context_flags flags)
Create a new context.
@ XKB_CONTEXT_NO_FLAGS
Do not apply any context flags.
Definition xkbcommon.h:1193
Opaque top level library context object.
Main xkbcommon 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.

/* Example RMLVO for Canadian Dvorak. */
const struct xkb_rule_names names = {
.rules = NULL,
.model = "pc105",
.layout = "ca",
.variant = "fr-dvorak",
.options = "terminate:ctrl_alt_bksp"
};
);
if (!keymap) {
/* Handle error */
// ...
exit(EXIT_FAILURE);
}
XKB_EXPORT struct xkb_keymap * xkb_keymap_new_from_names2(struct xkb_context *context, const struct xkb_rule_names *names, enum xkb_keymap_format format, enum xkb_keymap_compile_flags flags)
Create a keymap from RMLVO names.
@ XKB_KEYMAP_COMPILE_NO_FLAGS
Do not apply any flags.
Definition xkbcommon.h:1504
Opaque compiled keymap object.
Names to compile a keymap with, also known as RMLVO.
Definition xkbcommon.h:753
const char * rules
The rules file to use.
Definition xkbcommon.h:762
@ XKB_KEYMAP_FORMAT_TEXT_V1
The classic XKB text format, as generated by xkbcomp -xkb.
Definition xkbcommon.h:1630

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:
* - format,
* - keymap_buffer,
* - keymap_buffer_size
*/
keymap_buffer,
keymap_buffer_size,
format,
if (!*keymap) {
/* Handle error */
// ...
exit(EXIT_FAILURE);
}
XKB_EXPORT struct xkb_keymap * xkb_keymap_new_from_buffer(struct xkb_context *context, const char *buffer, size_t length, enum xkb_keymap_format format, enum xkb_keymap_compile_flags flags)
Create a keymap from a memory buffer.

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:

int ret = EXIT_SUCCESS;
xcb_connection_t *conn = xcb_connect(display, NULL);
if (!conn || xcb_connection_has_error(conn)) {
/* Handle connection error */
// ...
ret = EXIT_FAILURE;
goto connection_error;
}
NULL, NULL, NULL, NULL);
if (!ret) {
goto connection_error;
}
int32_t device_id = xkb_x11_get_core_keyboard_device_id(conn);
if (device_id == -1) {
/* Handle device error */
// ...
ret = EXIT_FAILURE;
goto device_error;
}
ctx, conn, device_id, XKB_KEYMAP_COMPILE_NO_FLAGS
);
if (!keymap) {
/* Handle keymap error */
// ...
ret = EXIT_FAILURE;
goto keymap_error;
}
XKB_EXPORT int xkb_x11_setup_xkb_extension(xcb_connection_t *connection, uint16_t major_xkb_version, uint16_t minor_xkb_version, enum xkb_x11_setup_xkb_extension_flags flags, uint16_t *major_xkb_version_out, uint16_t *minor_xkb_version_out, uint8_t *base_event_out, uint8_t *base_error_out)
Setup the XKB X11 extension for this X client.
XKB_EXPORT 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.
#define XKB_X11_MIN_MAJOR_XKB_VERSION
The minimal compatible major version of the XKB X11 extension which this library can use.
Definition xkbcommon-x11.h:97
XKB_EXPORT int32_t xkb_x11_get_core_keyboard_device_id(xcb_connection_t *connection)
Get the keyboard device ID of the core X11 keyboard.
#define XKB_X11_MIN_MINOR_XKB_VERSION
The minimal compatible minor version of the XKB X11 extension which this library can use (for the min...
Definition xkbcommon-x11.h:102
@ XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS
Do not apply any flags.
Definition xkbcommon-x11.h:107
xkbcommon-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:

Wayland
enum xkb_error_code error;
struct xkb_state *state;
if (!state) {
/* Handle error */
assert(error != XKB_SUCCESS);
switch (error) {
// ...
default:
exit(EXIT_FAILURE);
}
}
xkb_error_code
Error codes returned by the API.
Definition xkbcommon-errors.h:53
@ XKB_SUCCESS
The operation completed successfully.
Definition xkbcommon-errors.h:68
XKB_EXPORT struct xkb_state * xkb_state_new_with_mode(struct xkb_keymap *keymap, enum xkb_state_mode mode, enum xkb_error_code *error)
Create a new keyboard state object with an explicit mode.
@ XKB_STATE_MODE_CLIENT
State driven by serialized state updates via xkb_state::xkb_state_update_mask().
Definition xkbcommon.h:4907
Opaque keyboard state object.
X11/XCB
struct xkb_state *state = xkb_x11_state_new_from_device(keymap, conn, device_id);
if (!state) {
/* Handle error */
// ...
ret = EXIT_FAILURE;
goto state_error;
}
XKB_EXPORT 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.
evdev
struct xkb_state *state = xkb_state_new(keymap);
if (!state) {
/* Handle error */
// ...
exit(EXIT_FAILURE);
}
XKB_EXPORT struct xkb_state * xkb_state_new(struct xkb_keymap *keymap)
Create a new keyboard state object.

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 was delivered in `*event` */
xkb_keycode_t keycode = event->keycode;
xkb_keysym_t keysym;
keysym = xkb_state_key_get_one_sym(state, keycode);
uint32_t xkb_keysym_t
A number used to represent the symbols generated from a key on a keyboard.
Definition xkbcommon.h:247
XKB_EXPORT 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.
uint32_t xkb_keycode_t
A number used to represent a physical key on a keyboard.
Definition xkbcommon.h:206

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

switch(keysym) {
/* Got a space */
// ...
break;
// ...
default:
/* Default handle */
// ...
assert(!"unhandled keysym");
}
char keysym_name[64];
xkb_keysym_get_name(keysym, keysym_name, sizeof(keysym_name));
XKB_EXPORT int xkb_keysym_get_name(xkb_keysym_t keysym, char *buffer, size_t size)
Get the name of a keysym.
#define XKB_KEY_space
Keysym space
Definition xkbcommon-keysyms.h:4722

xkbcommon 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;
const int num_keysyms = xkb_state_key_get_syms(state, keycode, &keysyms);
for (int k = 0; k < num_keysyms; k++) {
/* Handle keysym */
// ...
handle_keysym(keysyms[k]);
}
XKB_EXPORT 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:

/* First find the needed size; return value is the same as snprintf(3). */
int size = xkb_state_key_get_utf8(state, keycode, NULL, 0) + 1;
if (size <= 1) {
/* Handle keysym with no UTF-8 representation */
assert(!"no UTF-8 representation");
} else {
char *utf8 = malloc((size_t)size);
if (!utf8) {
/* Handle error */
// ...
exit(EXIT_FAILURE);
}
xkb_state_key_get_utf8(state, keycode, utf8, size);
assert(*utf8 != '\0');
// ...
free(utf8);
}
XKB_EXPORT 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;
enum {
KEY_STATE_RELEASE = 0,
KEY_STATE_PRESS = 1,
KEY_STATE_REPEAT = 2,
};
switch (direction) {
case KEY_STATE_RELEASE:
changed = xkb_state_update_key(state, keycode, XKB_KEY_UP);
break;
case KEY_STATE_PRESS:
changed = xkb_state_update_key(state, keycode, XKB_KEY_DOWN);
break;
case KEY_STATE_REPEAT:
changed = xkb_state_update_key(state, keycode, XKB_KEY_REPEATED);
break;
default:
/* Handle error */
// ...
exit(EXIT_FAILURE);
}
xkb_state_component
Component types for state objects, which belong to the following categories:
Definition xkbcommon.h:3039
XKB_EXPORT 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
The key was pressed.
Definition xkbcommon.h:2973
@ XKB_KEY_REPEATED
The key was repeated.
Definition xkbcommon.h:2982
@ XKB_KEY_UP
The key was released.
Definition xkbcommon.h:2971

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 (direction == KEY_STATE_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):

state,
event->depressed_mods,
event->latched_mods,
event->locked_mods,
event->depressed_layout,
event->latched_layout,
event->locked_layout
);
XKB_EXPORT 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 (changed & XKB_STATE_MODS_EFFECTIVE) {
if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_CTRL,
/* Handle active Control modifier */
// ...
}
}
if (changed & XKB_STATE_LEDS) {
if (xkb_state_led_name_is_active(state, XKB_LED_NAME_NUM) > 0) {
/* Handle active Num Lock LED */
// ...
}
}
XKB_EXPORT 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_EXPORT 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
Definition xkbcommon.h:3085
@ XKB_STATE_LEDS
LEDs (derived from the other state components).
Definition xkbcommon.h:3125

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

xkb_keymap_unref(keymap);
xkb_context_unref(ctx);
XKB_EXPORT void xkb_state_unref(struct xkb_state *state)
Release a reference on a keyboard state object, and possibly free it.

Code for a Wayland server

The code is very similar to the evdev client presented hereinabove. The main difference is the use of the xkb_machine API instead of the xkb_state API.

enum {
EVDEV_OFFSET = 8,
};
struct my_keyboard {
struct xkb_context *ctx;
struct xkb_keymap *keymap;
struct xkb_machine *machine;
struct xkb_events *events;
};
static int
new_keyboard(struct my_keyboard *keyboard, const struct xkb_rule_names *names)
{
assert(keyboard->ctx);
/*
* Initialize the keymap
*/
struct xkb_keymap *keymap =
xkb_keymap_new_from_names2(keyboard->ctx, names,
if (!keymap) {
/* Handle error */
// ...
exit(EXIT_FAILURE);
}
/*
* Initialize the state machine
*/
enum xkb_error_code error;
struct xkb_machine_builder_config config = { .size = sizeof(config) };
struct xkb_machine_builder *machine_builder =
xkb_machine_builder_new(keymap, &config, &error);
if (!machine_builder) {
assert(error != XKB_SUCCESS);
switch (error) {
// ...
default:
exit(EXIT_FAILURE);
}
}
struct xkb_machine *machine = xkb_machine_new(machine_builder, &error);
xkb_machine_builder_unref(machine_builder);
if (!machine) {
assert(error != XKB_SUCCESS);
switch (error) {
// ...
default:
exit(EXIT_FAILURE);
}
}
struct xkb_events *events = xkb_events_new(keyboard->ctx, NULL, &error);
if (!events) {
assert(error != XKB_SUCCESS);
switch (error) {
// ...
default:
exit(EXIT_FAILURE);
}
}
char *keymap_string =
xkb_keymap_get_as_string(keymap, XKB_KEYMAP_FORMAT_TEXT_V1);
if (!keymap_string) {
/* Handle error */
// ...
exit(EXIT_FAILURE);
}
/* Send keymap to the clients */
// ...
free(keymap_string);
/* Save the objects for further use */
keyboard->keymap = keymap;
keyboard->machine = machine;
keyboard->events = events;
return EXIT_SUCCESS;
}
static int
destroy_keyboard(struct my_keyboard *keyboard)
{
xkb_events_unref(keyboard->events);
xkb_machine_unref(keyboard->machine);
xkb_keymap_unref(keyboard->keymap);
xkb_context_unref(keyboard->ctx);
return EXIT_SUCCESS;
}
static int
handle_key(struct my_keyboard *keyboard, uint32_t key, uint32_t state)
{
/*
* Update the server state with the key event
*/
const xkb_keycode_t keycode = key + EVDEV_OFFSET;
enum xkb_key_direction direction = WL_KEYBOARD_KEY_STATE_RELEASED
: WL_KEYBOARD_KEY_STATE_REPEATED
enum xkb_error_code error =
xkb_machine_process_key(keyboard->machine, keycode, direction,
keyboard->events);
if (error != XKB_SUCCESS) {
/* Handle error */
// ...
exit(EXIT_FAILURE);
}
/*
* Process the generated XKB events
*/
const struct xkb_event *event;
while ((event = xkb_events_next(keyboard->events)) != NULL) {
const enum xkb_event_type event_type =
switch (event_type) {
/* Report error */
// ...
exit(EXIT_FAILURE);
error = xkb_event_get_keycode(event, &kc, &direction);
if (error != XKB_SUCCESS) {
/* Handle error */
// ...
exit(EXIT_FAILURE);
}
/* Send key event to clients */
// ...
assert(kc != XKB_KEYCODE_INVALID);
break;
}
struct xkb_event_components components = {
.size = sizeof(components)
};
error = xkb_event_get_components(event, &components);
if (error != XKB_SUCCESS) {
/* Handle error */
// ...
exit(EXIT_FAILURE);
}
if (components.changed) {
/* Send component changes to clients */
// ...
}
break;
}
struct xkb_event_pointer_motion motion = {
.size = sizeof(motion)
};
error = xkb_event_get_pointer_motion(event, &motion);
if (error != XKB_SUCCESS) {
/* Handle error */
// ...
exit(EXIT_FAILURE);
}
/* Move cursor */
// ...
break;
}
.size = sizeof(button)
};
error = xkb_event_get_pointer_button(event, &button);
assert(error == XKB_SUCCESS);
if (error != XKB_SUCCESS) {
/* Handle error */
// ...
exit(EXIT_FAILURE);
}
/* Operate button */
// ...
break;
}
default:
/* Report unhandled event */
// ...
exit(EXIT_FAILURE);
}
}
return EXIT_SUCCESS;
}
XKB_EXPORT struct xkb_machine * xkb_machine_new(const struct xkb_machine_builder *builder, enum xkb_error_code *error)
Create a new keyboard state machine object.
xkb_key_direction
Specifies the direction of the key (press / release) or a repetition.
Definition xkbcommon.h:2969
xkb_event_type
Denotes the type of a state event.
Definition xkbcommon.h:2885
XKB_EXPORT enum xkb_error_code xkb_event_get_keycode(const struct xkb_event *event, xkb_keycode_t *keycode, enum xkb_key_direction *direction)
Get the keycode and direction associated to a state event of type XKB_EVENT_TYPE_KEY.
XKB_EXPORT enum xkb_event_type xkb_event_get_type(const struct xkb_event *event)
Get the type of an event.
XKB_EXPORT void xkb_events_unref(struct xkb_events *events)
Release a reference on an event collection object, and possibly free it.
XKB_EXPORT struct xkb_events * xkb_events_new(struct xkb_context *context, const struct xkb_events_config *config, enum xkb_error_code *error)
Create a new event collection object.
XKB_EXPORT struct xkb_machine_builder * xkb_machine_builder_new(struct xkb_keymap *keymap, const struct xkb_machine_builder_config *config, enum xkb_error_code *error)
Create a new xkb_machine builder object.
@ XKB_EVENT_TYPE_POINTER_BUTTON
Pointer button event
Definition xkbcommon.h:2925
@ XKB_EVENT_TYPE_STATE_COMPONENTS
State components change event
Definition xkbcommon.h:2909
@ XKB_EVENT_TYPE_KEY
Key event
Definition xkbcommon.h:2901
@ XKB_EVENT_TYPE_INVALID
Invalid event.
Definition xkbcommon.h:2893
@ XKB_EVENT_TYPE_POINTER_MOTION
Pointer motion event
Definition xkbcommon.h:2917
Serialized state components.
Definition xkbcommon.h:3203
uint32_t changed
Mask of the changed state components.
Definition xkbcommon.h:3221
uint32_t size
Size of this structure in bytes.
Definition xkbcommon.h:3211
Description of a pointer button action.
Definition xkbcommon.h:3533
uint32_t size
Size of this structure in bytes.
Definition xkbcommon.h:3541
uint32_t button
Button index.
Definition xkbcommon.h:3547
Description of a pointer motion.
Definition xkbcommon.h:3424
uint32_t size
Size of this structure in bytes.
Definition xkbcommon.h:3432
Opaque keyboard state event object.
Opaque keyboard event collection object.
Configuration for xkb_machine_builder::xkb_machine_builder_new().
Definition xkbcommon.h:3906
uint32_t size
Size of this structure in bytes.
Definition xkbcommon.h:3914
Opaque builder object to configure an xkb_machine.
Opaque XKB state machine object.
#define XKB_KEYCODE_INVALID
Invalid keycode.
Definition xkbcommon.h:406

Synthetic updates are handled using xkb_machine::xkb_machine_process_synthetic().