aboutsummaryrefslogtreecommitdiff
path: root/doc/quick-guide.md
diff options
context:
space:
mode:
Diffstat (limited to 'doc/quick-guide.md')
-rw-r--r--doc/quick-guide.md229
1 files changed, 229 insertions, 0 deletions
diff --git a/doc/quick-guide.md b/doc/quick-guide.md
new file mode 100644
index 0000000..eb1a757
--- /dev/null
+++ b/doc/quick-guide.md
@@ -0,0 +1,229 @@
+# 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_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}
+ /* 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);
+~~~