Hash :
f0cd7abc
Author :
Date :
2014-09-21T00:59:11
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:
An evdev client. “evdev” is the Linux kernel’s input subsystem; it only reports to the client which keys are pressed and released.
An X11 client, using the XCB library to communicate with the X server and the xcb-xkb library for using the XKB protocol.
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:
test/interactive-evdev.c contains an interactive evdev client.
test/interactive-x11.c contains an interactive X11 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.
Before we can do anything interesting, we need a library context. So let’s create one:
#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. 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;
struct xkb_rule_names names = <...>;
keymap = xkb_keymap_new_from_names(ctx, &names,
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:
const char *keymap_string = <...>;
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:
#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:
struct xkb_state *state;
state = xkb_state_new(keymap);
if (!state) <error>
For X11/XCB clients, this is better:
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:
<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:
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:
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:
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:
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 is 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>
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);
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>
And that’s it! When we’re finished, we should free the objects we’ve created:
xkb_state_unref(state);
xkb_keymap_unref(keymap);
xkb_context_unref(ctx);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
# Quick Guide
## Intro
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. test/interactive-evdev.c contains an interactive evdev client.
2. test/interactive-x11.c contains an interactive X11 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. So
let's create one:
~~~{.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. 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;
struct xkb_rule_names names = <...>;
keymap = xkb_keymap_new_from_names(ctx, &names,
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}
const char *keymap_string = <...>;
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:
~~~{.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 is 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! When we're finished, we should free the objects we've
created:
~~~{.c}
xkb_state_unref(state);
xkb_keymap_unref(keymap);
xkb_context_unref(ctx);
~~~