chiark / gitweb /
Remove src/libsystemd-terminal
[elogind.git] / src / libsystemd-terminal / idev-keyboard.c
diff --git a/src/libsystemd-terminal/idev-keyboard.c b/src/libsystemd-terminal/idev-keyboard.c
deleted file mode 100644 (file)
index f90f1b5..0000000
+++ /dev/null
@@ -1,1160 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdbool.h>
-#include <stdlib.h>
-#include <systemd/sd-bus.h>
-#include <systemd/sd-event.h>
-#include <xkbcommon/xkbcommon.h>
-#include <xkbcommon/xkbcommon-compose.h>
-#include "bus-util.h"
-#include "hashmap.h"
-#include "idev.h"
-#include "idev-internal.h"
-#include "macro.h"
-#include "term-internal.h"
-#include "util.h"
-
-typedef struct kbdtbl kbdtbl;
-typedef struct kbdmap kbdmap;
-typedef struct kbdctx kbdctx;
-typedef struct idev_keyboard idev_keyboard;
-
-struct kbdtbl {
-        unsigned long ref;
-        struct xkb_compose_table *xkb_compose_table;
-};
-
-struct kbdmap {
-        unsigned long ref;
-        struct xkb_keymap *xkb_keymap;
-        xkb_mod_index_t modmap[IDEV_KBDMOD_CNT];
-        xkb_led_index_t ledmap[IDEV_KBDLED_CNT];
-};
-
-struct kbdctx {
-        unsigned long ref;
-        idev_context *context;
-        struct xkb_context *xkb_context;
-        struct kbdmap *kbdmap;
-        struct kbdtbl *kbdtbl;
-
-        sd_bus_slot *slot_locale_props_changed;
-        sd_bus_slot *slot_locale_get_all;
-
-        char *locale_lang;
-        char *locale_x11_model;
-        char *locale_x11_layout;
-        char *locale_x11_variant;
-        char *locale_x11_options;
-        char *last_x11_model;
-        char *last_x11_layout;
-        char *last_x11_variant;
-        char *last_x11_options;
-};
-
-struct idev_keyboard {
-        idev_device device;
-        kbdctx *kbdctx;
-        kbdmap *kbdmap;
-        kbdtbl *kbdtbl;
-
-        struct xkb_state *xkb_state;
-        struct xkb_compose_state *xkb_compose;
-
-        usec_t repeat_delay;
-        usec_t repeat_rate;
-        sd_event_source *repeat_timer;
-
-        uint32_t n_syms;
-        idev_data evdata;
-        idev_data repdata;
-        uint32_t *compose_res;
-
-        bool repeating : 1;
-};
-
-#define keyboard_from_device(_d) container_of((_d), idev_keyboard, device)
-
-#define KBDCTX_KEY "keyboard.context"           /* hashmap key for global kbdctx */
-#define KBDXKB_SHIFT (8)                        /* xkb shifts evdev key-codes by 8 */
-#define KBDKEY_UP (0)                           /* KEY UP event value */
-#define KBDKEY_DOWN (1)                         /* KEY DOWN event value */
-#define KBDKEY_REPEAT (2)                       /* KEY REPEAT event value */
-
-static const idev_device_vtable keyboard_vtable;
-
-static int keyboard_update_kbdmap(idev_keyboard *k);
-static int keyboard_update_kbdtbl(idev_keyboard *k);
-
-/*
- * Keyboard Compose Tables
- */
-
-static kbdtbl *kbdtbl_ref(kbdtbl *kt) {
-        if (kt) {
-                assert_return(kt->ref > 0, NULL);
-                ++kt->ref;
-        }
-
-        return kt;
-}
-
-static kbdtbl *kbdtbl_unref(kbdtbl *kt) {
-        if (!kt)
-                return NULL;
-
-        assert_return(kt->ref > 0, NULL);
-
-        if (--kt->ref > 0)
-                return NULL;
-
-        xkb_compose_table_unref(kt->xkb_compose_table);
-        free(kt);
-
-        return 0;
-}
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(kbdtbl*, kbdtbl_unref);
-
-static int kbdtbl_new_from_locale(kbdtbl **out, kbdctx *kc, const char *locale) {
-        _cleanup_(kbdtbl_unrefp) kbdtbl *kt = NULL;
-
-        assert_return(out, -EINVAL);
-        assert_return(locale, -EINVAL);
-
-        kt = new0(kbdtbl, 1);
-        if (!kt)
-                return -ENOMEM;
-
-        kt->ref = 1;
-
-        kt->xkb_compose_table = xkb_compose_table_new_from_locale(kc->xkb_context,
-                                                                  locale,
-                                                                  XKB_COMPOSE_COMPILE_NO_FLAGS);
-        if (!kt->xkb_compose_table)
-                return errno > 0 ? -errno : -EFAULT;
-
-        *out = kt;
-        kt = NULL;
-        return 0;
-}
-
-/*
- * Keyboard Keymaps
- */
-
-static const char * const kbdmap_modmap[IDEV_KBDMOD_CNT] = {
-        [IDEV_KBDMOD_IDX_SHIFT]                 = XKB_MOD_NAME_SHIFT,
-        [IDEV_KBDMOD_IDX_CTRL]                  = XKB_MOD_NAME_CTRL,
-        [IDEV_KBDMOD_IDX_ALT]                   = XKB_MOD_NAME_ALT,
-        [IDEV_KBDMOD_IDX_LINUX]                 = XKB_MOD_NAME_LOGO,
-        [IDEV_KBDMOD_IDX_CAPS]                  = XKB_MOD_NAME_CAPS,
-};
-
-static const char * const kbdmap_ledmap[IDEV_KBDLED_CNT] = {
-        [IDEV_KBDLED_IDX_NUM]                   = XKB_LED_NAME_NUM,
-        [IDEV_KBDLED_IDX_CAPS]                  = XKB_LED_NAME_CAPS,
-        [IDEV_KBDLED_IDX_SCROLL]                = XKB_LED_NAME_SCROLL,
-};
-
-static kbdmap *kbdmap_ref(kbdmap *km) {
-        assert_return(km, NULL);
-        assert_return(km->ref > 0, NULL);
-
-        ++km->ref;
-        return km;
-}
-
-static kbdmap *kbdmap_unref(kbdmap *km) {
-        if (!km)
-                return NULL;
-
-        assert_return(km->ref > 0, NULL);
-
-        if (--km->ref > 0)
-                return NULL;
-
-        xkb_keymap_unref(km->xkb_keymap);
-        free(km);
-
-        return 0;
-}
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(kbdmap*, kbdmap_unref);
-
-static int kbdmap_new_from_names(kbdmap **out,
-                                 kbdctx *kc,
-                                 const char *model,
-                                 const char *layout,
-                                 const char *variant,
-                                 const char *options) {
-        _cleanup_(kbdmap_unrefp) kbdmap *km = NULL;
-        struct xkb_rule_names rmlvo = { };
-        unsigned int i;
-
-        assert_return(out, -EINVAL);
-
-        km = new0(kbdmap, 1);
-        if (!km)
-                return -ENOMEM;
-
-        km->ref = 1;
-
-        rmlvo.rules = "evdev";
-        rmlvo.model = model;
-        rmlvo.layout = layout;
-        rmlvo.variant = variant;
-        rmlvo.options = options;
-
-        errno = 0;
-        km->xkb_keymap = xkb_keymap_new_from_names(kc->xkb_context, &rmlvo, 0);
-        if (!km->xkb_keymap)
-                return errno > 0 ? -errno : -EFAULT;
-
-        for (i = 0; i < IDEV_KBDMOD_CNT; ++i) {
-                const char *t = kbdmap_modmap[i];
-
-                if (t)
-                        km->modmap[i] = xkb_keymap_mod_get_index(km->xkb_keymap, t);
-                else
-                        km->modmap[i] = XKB_MOD_INVALID;
-        }
-
-        for (i = 0; i < IDEV_KBDLED_CNT; ++i) {
-                const char *t = kbdmap_ledmap[i];
-
-                if (t)
-                        km->ledmap[i] = xkb_keymap_led_get_index(km->xkb_keymap, t);
-                else
-                        km->ledmap[i] = XKB_LED_INVALID;
-        }
-
-        *out = km;
-        km = NULL;
-        return 0;
-}
-
-/*
- * Keyboard Context
- */
-
-static int kbdctx_refresh_compose_table(kbdctx *kc, const char *lang) {
-        kbdtbl *kt;
-        idev_session *s;
-        idev_device *d;
-        Iterator i, j;
-        int r;
-
-        if (!lang)
-                lang = "C";
-
-        if (streq_ptr(kc->locale_lang, lang))
-                return 0;
-
-        r = free_and_strdup(&kc->locale_lang, lang);
-        if (r < 0)
-                return r;
-
-        log_debug("idev-keyboard: new default compose table: [ %s ]", lang);
-
-        r = kbdtbl_new_from_locale(&kt, kc, lang);
-        if (r < 0) {
-                /* TODO: We need to catch the case where no compose-file is
-                 * available. xkb doesn't tell us so far.. so we must not treat
-                 * it as a hard-failure but just continue. Preferably, we want
-                 * xkb to tell us exactly whether compilation failed or whether
-                 * there is no compose file available for this locale. */
-                log_debug_errno(r, "idev-keyboard: cannot load compose-table for '%s': %m",
-                                lang);
-                r = 0;
-                kt = NULL;
-        }
-
-        kbdtbl_unref(kc->kbdtbl);
-        kc->kbdtbl = kt;
-
-        HASHMAP_FOREACH(s, kc->context->session_map, i)
-                HASHMAP_FOREACH(d, s->device_map, j)
-                        if (idev_is_keyboard(d))
-                                keyboard_update_kbdtbl(keyboard_from_device(d));
-
-        return 0;
-}
-
-static void move_str(char **dest, char **src) {
-        free(*dest);
-        *dest = *src;
-        *src = NULL;
-}
-
-static int kbdctx_refresh_keymap(kbdctx *kc) {
-        idev_session *s;
-        idev_device *d;
-        Iterator i, j;
-        kbdmap *km;
-        int r;
-
-        if (kc->kbdmap &&
-            streq_ptr(kc->locale_x11_model, kc->last_x11_model) &&
-            streq_ptr(kc->locale_x11_layout, kc->last_x11_layout) &&
-            streq_ptr(kc->locale_x11_variant, kc->last_x11_variant) &&
-            streq_ptr(kc->locale_x11_options, kc->last_x11_options))
-                return 0 ;
-
-        move_str(&kc->last_x11_model, &kc->locale_x11_model);
-        move_str(&kc->last_x11_layout, &kc->locale_x11_layout);
-        move_str(&kc->last_x11_variant, &kc->locale_x11_variant);
-        move_str(&kc->last_x11_options, &kc->locale_x11_options);
-
-        log_debug("idev-keyboard: new default keymap: [%s / %s / %s / %s]",
-                  kc->last_x11_model, kc->last_x11_layout, kc->last_x11_variant, kc->last_x11_options);
-
-        /* TODO: add a fallback keymap that's compiled-in */
-        r = kbdmap_new_from_names(&km, kc, kc->last_x11_model, kc->last_x11_layout,
-                                  kc->last_x11_variant, kc->last_x11_options);
-        if (r < 0)
-                return log_debug_errno(r, "idev-keyboard: cannot create keymap from locale1: %m");
-
-        kbdmap_unref(kc->kbdmap);
-        kc->kbdmap = km;
-
-        HASHMAP_FOREACH(s, kc->context->session_map, i)
-                HASHMAP_FOREACH(d, s->device_map, j)
-                        if (idev_is_keyboard(d))
-                                keyboard_update_kbdmap(keyboard_from_device(d));
-
-        return 0;
-}
-
-static int kbdctx_set_locale(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
-        kbdctx *kc = userdata;
-        const char *s, *ctype = NULL, *lang = NULL;
-        int r;
-
-        r = sd_bus_message_enter_container(m, 'a', "s");
-        if (r < 0)
-                goto error;
-
-        while ((r = sd_bus_message_read(m, "s", &s)) > 0) {
-                if (!ctype)
-                        ctype = startswith(s, "LC_CTYPE=");
-                if (!lang)
-                        lang = startswith(s, "LANG=");
-        }
-
-        if (r < 0)
-                goto error;
-
-        r = sd_bus_message_exit_container(m);
-        if (r < 0)
-                goto error;
-
-        kbdctx_refresh_compose_table(kc, ctype ? : lang);
-        r = 0;
-
-error:
-        if (r < 0)
-                log_debug_errno(r, "idev-keyboard: cannot parse locale property from locale1: %m");
-
-        return r;
-}
-
-static const struct bus_properties_map kbdctx_locale_map[] = {
-        { "Locale",     "as",   kbdctx_set_locale, 0 },
-        { "X11Model",   "s",    NULL, offsetof(kbdctx, locale_x11_model) },
-        { "X11Layout",  "s",    NULL, offsetof(kbdctx, locale_x11_layout) },
-        { "X11Variant", "s",    NULL, offsetof(kbdctx, locale_x11_variant) },
-        { "X11Options", "s",    NULL, offsetof(kbdctx, locale_x11_options) },
-};
-
-static int kbdctx_locale_get_all_fn(sd_bus *bus,
-                                    sd_bus_message *m,
-                                    void *userdata,
-                                    sd_bus_error *ret_err) {
-        kbdctx *kc = userdata;
-        int r;
-
-        kc->slot_locale_get_all = sd_bus_slot_unref(kc->slot_locale_get_all);
-
-        if (sd_bus_message_is_method_error(m, NULL)) {
-                const sd_bus_error *error = sd_bus_message_get_error(m);
-
-                log_debug("idev-keyboard: GetAll() on locale1 failed: %s: %s",
-                          error->name, error->message);
-                return 0;
-        }
-
-        r = bus_message_map_all_properties(bus, m, kbdctx_locale_map, kc);
-        if (r < 0) {
-                log_debug("idev-keyboard: erroneous GetAll() reply from locale1");
-                return 0;
-        }
-
-        kbdctx_refresh_keymap(kc);
-        return 0;
-}
-
-static int kbdctx_query_locale(kbdctx *kc) {
-        _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
-        int r;
-
-        kc->slot_locale_get_all = sd_bus_slot_unref(kc->slot_locale_get_all);
-
-        r = sd_bus_message_new_method_call(kc->context->sysbus,
-                                           &m,
-                                           "org.freedesktop.locale1",
-                                           "/org/freedesktop/locale1",
-                                           "org.freedesktop.DBus.Properties",
-                                           "GetAll");
-        if (r < 0)
-                goto error;
-
-        r = sd_bus_message_append(m, "s", "org.freedesktop.locale1");
-        if (r < 0)
-                goto error;
-
-        r = sd_bus_call_async(kc->context->sysbus,
-                              &kc->slot_locale_get_all,
-                              m,
-                              kbdctx_locale_get_all_fn,
-                              kc,
-                              0);
-        if (r < 0)
-                goto error;
-
-        return 0;
-
-error:
-        return log_debug_errno(r, "idev-keyboard: cannot send GetAll to locale1: %m");
-}
-
-static int kbdctx_locale_props_changed_fn(sd_bus *bus,
-                                          sd_bus_message *signal,
-                                          void *userdata,
-                                          sd_bus_error *ret_err) {
-        kbdctx *kc = userdata;
-        int r;
-
-        kc->slot_locale_get_all = sd_bus_slot_unref(kc->slot_locale_get_all);
-
-        /* skip interface name */
-        r = sd_bus_message_skip(signal, "s");
-        if (r < 0)
-                goto error;
-
-        r = bus_message_map_properties_changed(bus, signal, kbdctx_locale_map, kc);
-        if (r < 0)
-                goto error;
-
-        if (r > 0) {
-                r = kbdctx_query_locale(kc);
-                if (r < 0)
-                        return r;
-        }
-
-        kbdctx_refresh_keymap(kc);
-        return 0;
-
-error:
-        return log_debug_errno(r, "idev-keyboard: cannot handle PropertiesChanged from locale1: %m");
-}
-
-static int kbdctx_setup_bus(kbdctx *kc) {
-        int r;
-
-        r = sd_bus_add_match(kc->context->sysbus,
-                             &kc->slot_locale_props_changed,
-                             "type='signal',"
-                             "sender='org.freedesktop.locale1',"
-                             "interface='org.freedesktop.DBus.Properties',"
-                             "member='PropertiesChanged',"
-                             "path='/org/freedesktop/locale1'",
-                             kbdctx_locale_props_changed_fn,
-                             kc);
-        if (r < 0)
-                return log_debug_errno(r, "idev-keyboard: cannot setup locale1 link: %m");
-
-        return kbdctx_query_locale(kc);
-}
-
-static void kbdctx_log_fn(struct xkb_context *ctx, enum xkb_log_level lvl, const char *format, va_list args) {
-        char buf[LINE_MAX];
-        int sd_lvl;
-
-        if (lvl >= XKB_LOG_LEVEL_DEBUG)
-                sd_lvl = LOG_DEBUG;
-        else if (lvl >= XKB_LOG_LEVEL_INFO)
-                sd_lvl = LOG_INFO;
-        else if (lvl >= XKB_LOG_LEVEL_WARNING)
-                sd_lvl = LOG_INFO; /* most XKB warnings really are informational */
-        else
-                /* XKB_LOG_LEVEL_ERROR and worse */
-                sd_lvl = LOG_ERR;
-
-        snprintf(buf, sizeof(buf), "idev-xkb: %s", format);
-        log_internalv(sd_lvl, 0, __FILE__, __LINE__, __func__, buf, args);
-}
-
-static kbdctx *kbdctx_ref(kbdctx *kc) {
-        assert_return(kc, NULL);
-        assert_return(kc->ref > 0, NULL);
-
-        ++kc->ref;
-        return kc;
-}
-
-static kbdctx *kbdctx_unref(kbdctx *kc) {
-        if (!kc)
-                return NULL;
-
-        assert_return(kc->ref > 0, NULL);
-
-        if (--kc->ref > 0)
-                return NULL;
-
-        free(kc->last_x11_options);
-        free(kc->last_x11_variant);
-        free(kc->last_x11_layout);
-        free(kc->last_x11_model);
-        free(kc->locale_x11_options);
-        free(kc->locale_x11_variant);
-        free(kc->locale_x11_layout);
-        free(kc->locale_x11_model);
-        free(kc->locale_lang);
-        kc->slot_locale_get_all = sd_bus_slot_unref(kc->slot_locale_get_all);
-        kc->slot_locale_props_changed = sd_bus_slot_unref(kc->slot_locale_props_changed);
-        kc->kbdtbl = kbdtbl_unref(kc->kbdtbl);
-        kc->kbdmap = kbdmap_unref(kc->kbdmap);
-        xkb_context_unref(kc->xkb_context);
-        hashmap_remove_value(kc->context->data_map, KBDCTX_KEY, kc);
-        free(kc);
-
-        return NULL;
-}
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(kbdctx*, kbdctx_unref);
-
-static int kbdctx_new(kbdctx **out, idev_context *c) {
-        _cleanup_(kbdctx_unrefp) kbdctx *kc = NULL;
-        int r;
-
-        assert_return(out, -EINVAL);
-        assert_return(c, -EINVAL);
-
-        kc = new0(kbdctx, 1);
-        if (!kc)
-                return -ENOMEM;
-
-        kc->ref = 1;
-        kc->context = c;
-
-        errno = 0;
-        kc->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
-        if (!kc->xkb_context)
-                return errno > 0 ? -errno : -EFAULT;
-
-        xkb_context_set_log_fn(kc->xkb_context, kbdctx_log_fn);
-        xkb_context_set_log_level(kc->xkb_context, XKB_LOG_LEVEL_DEBUG);
-
-        r = kbdctx_refresh_keymap(kc);
-        if (r < 0)
-                return r;
-
-        r = kbdctx_refresh_compose_table(kc, NULL);
-        if (r < 0)
-                return r;
-
-        if (c->sysbus) {
-                r = kbdctx_setup_bus(kc);
-                if (r < 0)
-                        return r;
-        }
-
-        r = hashmap_put(c->data_map, KBDCTX_KEY, kc);
-        if (r < 0)
-                return r;
-
-        *out = kc;
-        kc = NULL;
-        return 0;
-}
-
-static int get_kbdctx(idev_context *c, kbdctx **out) {
-        kbdctx *kc;
-
-        assert_return(c, -EINVAL);
-        assert_return(out, -EINVAL);
-
-        kc = hashmap_get(c->data_map, KBDCTX_KEY);
-        if (kc) {
-                *out = kbdctx_ref(kc);
-                return 0;
-        }
-
-        return kbdctx_new(out, c);
-}
-
-/*
- * Keyboard Devices
- */
-
-bool idev_is_keyboard(idev_device *d) {
-        return d && d->vtable == &keyboard_vtable;
-}
-
-idev_device *idev_find_keyboard(idev_session *s, const char *name) {
-        char *kname;
-
-        assert_return(s, NULL);
-        assert_return(name, NULL);
-
-        kname = strjoina("keyboard/", name);
-        return hashmap_get(s->device_map, kname);
-}
-
-static int keyboard_raise_data(idev_keyboard *k, idev_data *data) {
-        idev_device *d = &k->device;
-        int r;
-
-        r = idev_session_raise_device_data(d->session, d, data);
-        if (r < 0)
-                log_debug_errno(r, "idev-keyboard: %s/%s: error while raising data event: %m",
-                                d->session->name, d->name);
-
-        return r;
-}
-
-static int keyboard_resize_bufs(idev_keyboard *k, uint32_t n_syms) {
-        uint32_t *t;
-
-        if (n_syms <= k->n_syms)
-                return 0;
-
-        t = realloc(k->compose_res, sizeof(*t) * n_syms);
-        if (!t)
-                return -ENOMEM;
-        k->compose_res = t;
-
-        t = realloc(k->evdata.keyboard.keysyms, sizeof(*t) * n_syms);
-        if (!t)
-                return -ENOMEM;
-        k->evdata.keyboard.keysyms = t;
-
-        t = realloc(k->evdata.keyboard.codepoints, sizeof(*t) * n_syms);
-        if (!t)
-                return -ENOMEM;
-        k->evdata.keyboard.codepoints = t;
-
-        t = realloc(k->repdata.keyboard.keysyms, sizeof(*t) * n_syms);
-        if (!t)
-                return -ENOMEM;
-        k->repdata.keyboard.keysyms = t;
-
-        t = realloc(k->repdata.keyboard.codepoints, sizeof(*t) * n_syms);
-        if (!t)
-                return -ENOMEM;
-        k->repdata.keyboard.codepoints = t;
-
-        k->n_syms = n_syms;
-        return 0;
-}
-
-static unsigned int keyboard_read_compose(idev_keyboard *k, const xkb_keysym_t **out) {
-        _cleanup_free_ char *t = NULL;
-        term_utf8 u8 = { };
-        char buf[256], *p;
-        size_t flen = 0;
-        int i, r;
-
-        r = xkb_compose_state_get_utf8(k->xkb_compose, buf, sizeof(buf));
-        if (r >= (int)sizeof(buf)) {
-                t = malloc(r + 1);
-                if (!t)
-                        return 0;
-
-                xkb_compose_state_get_utf8(k->xkb_compose, t, r + 1);
-                p = t;
-        } else {
-                p = buf;
-        }
-
-        for (i = 0; i < r; ++i) {
-                uint32_t *ucs;
-                size_t len, j;
-
-                len = term_utf8_decode(&u8, &ucs, p[i]);
-                if (len > 0) {
-                        r = keyboard_resize_bufs(k, flen + len);
-                        if (r < 0)
-                                return 0;
-
-                        for (j = 0; j < len; ++j)
-                                k->compose_res[flen++] = ucs[j];
-                }
-        }
-
-        *out = k->compose_res;
-        return flen;
-}
-
-static void keyboard_arm(idev_keyboard *k, usec_t usecs) {
-        int r;
-
-        if (usecs != 0) {
-                usecs += now(CLOCK_MONOTONIC);
-                r = sd_event_source_set_time(k->repeat_timer, usecs);
-                if (r >= 0)
-                        sd_event_source_set_enabled(k->repeat_timer, SD_EVENT_ONESHOT);
-        } else {
-                sd_event_source_set_enabled(k->repeat_timer, SD_EVENT_OFF);
-        }
-}
-
-static int keyboard_repeat_timer_fn(sd_event_source *source, uint64_t usec, void *userdata) {
-        idev_keyboard *k = userdata;
-
-        /* never feed REPEAT keys into COMPOSE */
-
-        keyboard_arm(k, k->repeat_rate);
-        return keyboard_raise_data(k, &k->repdata);
-}
-
-int idev_keyboard_new(idev_device **out, idev_session *s, const char *name) {
-        _cleanup_(idev_device_freep) idev_device *d = NULL;
-        idev_keyboard *k;
-        char *kname;
-        int r;
-
-        assert_return(out, -EINVAL);
-        assert_return(s, -EINVAL);
-        assert_return(name, -EINVAL);
-
-        k = new0(idev_keyboard, 1);
-        if (!k)
-                return -ENOMEM;
-
-        d = &k->device;
-        k->device = IDEV_DEVICE_INIT(&keyboard_vtable, s);
-        k->repeat_delay = 250 * USEC_PER_MSEC;
-        k->repeat_rate = 30 * USEC_PER_MSEC;
-
-        /* TODO: add key-repeat configuration */
-
-        r = get_kbdctx(s->context, &k->kbdctx);
-        if (r < 0)
-                return r;
-
-        r = keyboard_update_kbdmap(k);
-        if (r < 0)
-                return r;
-
-        r = keyboard_update_kbdtbl(k);
-        if (r < 0)
-                return r;
-
-        r = keyboard_resize_bufs(k, 8);
-        if (r < 0)
-                return r;
-
-        r = sd_event_add_time(s->context->event,
-                              &k->repeat_timer,
-                              CLOCK_MONOTONIC,
-                              0,
-                              10 * USEC_PER_MSEC,
-                              keyboard_repeat_timer_fn,
-                              k);
-        if (r < 0)
-                return r;
-
-        r = sd_event_source_set_enabled(k->repeat_timer, SD_EVENT_OFF);
-        if (r < 0)
-                return r;
-
-        kname = strjoina("keyboard/", name);
-        r = idev_device_add(d, kname);
-        if (r < 0)
-                return r;
-
-        if (out)
-                *out = d;
-        d = NULL;
-        return 0;
-}
-
-static void keyboard_free(idev_device *d) {
-        idev_keyboard *k = keyboard_from_device(d);
-
-        xkb_compose_state_unref(k->xkb_compose);
-        xkb_state_unref(k->xkb_state);
-        free(k->repdata.keyboard.codepoints);
-        free(k->repdata.keyboard.keysyms);
-        free(k->evdata.keyboard.codepoints);
-        free(k->evdata.keyboard.keysyms);
-        free(k->compose_res);
-        k->repeat_timer = sd_event_source_unref(k->repeat_timer);
-        k->kbdtbl = kbdtbl_unref(k->kbdtbl);
-        k->kbdmap = kbdmap_unref(k->kbdmap);
-        k->kbdctx = kbdctx_unref(k->kbdctx);
-        free(k);
-}
-
-static int8_t guess_ascii(struct xkb_state *state, uint32_t code, uint32_t n_syms, const uint32_t *syms) {
-        xkb_layout_index_t n_lo, lo;
-        xkb_level_index_t lv;
-        struct xkb_keymap *keymap;
-        const xkb_keysym_t *s;
-        int num;
-
-        if (n_syms == 1 && syms[0] < 128 && syms[0] > 0)
-                return syms[0];
-
-        keymap = xkb_state_get_keymap(state);
-        n_lo = xkb_keymap_num_layouts_for_key(keymap, code + KBDXKB_SHIFT);
-
-        for (lo = 0; lo < n_lo; ++lo) {
-                lv = xkb_state_key_get_level(state, code + KBDXKB_SHIFT, lo);
-                num = xkb_keymap_key_get_syms_by_level(keymap, code + KBDXKB_SHIFT, lo, lv, &s);
-                if (num == 1 && s[0] < 128 && s[0] > 0)
-                        return s[0];
-        }
-
-        return -1;
-}
-
-static int keyboard_fill(idev_keyboard *k,
-                         idev_data *dst,
-                         bool resync,
-                         uint16_t code,
-                         uint32_t value,
-                         uint32_t n_syms,
-                         const uint32_t *keysyms) {
-        idev_data_keyboard *kev;
-        uint32_t i;
-        int r;
-
-        assert(dst == &k->evdata || dst == &k->repdata);
-
-        r = keyboard_resize_bufs(k, n_syms);
-        if (r < 0)
-                return r;
-
-        dst->type = IDEV_DATA_KEYBOARD;
-        dst->resync = resync;
-        kev = &dst->keyboard;
-        kev->ascii = guess_ascii(k->xkb_state, code, n_syms, keysyms);
-        kev->value = value;
-        kev->keycode = code;
-        kev->mods = 0;
-        kev->consumed_mods = 0;
-        kev->n_syms = n_syms;
-        memcpy(kev->keysyms, keysyms, sizeof(*keysyms) * n_syms);
-
-        for (i = 0; i < n_syms; ++i) {
-                kev->codepoints[i] = xkb_keysym_to_utf32(keysyms[i]);
-                if (!kev->codepoints[i])
-                        kev->codepoints[i] = 0xffffffffUL;
-        }
-
-        for (i = 0; i < IDEV_KBDMOD_CNT; ++i) {
-                if (k->kbdmap->modmap[i] == XKB_MOD_INVALID)
-                        continue;
-
-                r = xkb_state_mod_index_is_active(k->xkb_state, k->kbdmap->modmap[i], XKB_STATE_MODS_EFFECTIVE);
-                if (r > 0)
-                        kev->mods |= 1 << i;
-
-                r = xkb_state_mod_index_is_consumed(k->xkb_state, code + KBDXKB_SHIFT, k->kbdmap->modmap[i]);
-                if (r > 0)
-                        kev->consumed_mods |= 1 << i;
-        }
-
-        return 0;
-}
-
-static void keyboard_repeat(idev_keyboard *k) {
-        idev_data *evdata = &k->evdata;
-        idev_data *repdata = &k->repdata;
-        idev_data_keyboard *evkbd = &evdata->keyboard;
-        idev_data_keyboard *repkbd = &repdata->keyboard;
-        const xkb_keysym_t *keysyms;
-        idev_device *d = &k->device;
-        bool repeats;
-        int r, num;
-
-        if (evdata->resync) {
-                /*
-                 * We received a re-sync event. During re-sync, any number of
-                 * key-events may have been lost and sync-events may be
-                 * re-ordered. Always disable key-repeat for those events. Any
-                 * following event will trigger it again.
-                 */
-
-                k->repeating = false;
-                keyboard_arm(k, 0);
-                return;
-        }
-
-        repeats = xkb_keymap_key_repeats(k->kbdmap->xkb_keymap, evkbd->keycode + KBDXKB_SHIFT);
-
-        if (k->repeating && repkbd->keycode == evkbd->keycode) {
-                /*
-                 * We received an event for the key we currently repeat. If it
-                 * was released, stop key-repeat. Otherwise, ignore the event.
-                 */
-
-                if (evkbd->value == KBDKEY_UP) {
-                        k->repeating = false;
-                        keyboard_arm(k, 0);
-                }
-        } else if (evkbd->value == KBDKEY_DOWN && repeats) {
-                /*
-                 * We received a key-down event for a key that repeats. The
-                 * previous condition caught keys we already repeat, so we know
-                 * this is a different key or no key-repeat is running. Start
-                 * new key-repeat.
-                 */
-
-                errno = 0;
-                num = xkb_state_key_get_syms(k->xkb_state, evkbd->keycode + KBDXKB_SHIFT, &keysyms);
-                if (num < 0)
-                        r = errno > 0 ? errno : -EFAULT;
-                else
-                        r = keyboard_fill(k, repdata, false, evkbd->keycode, KBDKEY_REPEAT, num, keysyms);
-
-                if (r < 0) {
-                        log_debug_errno(r, "idev-keyboard: %s/%s: cannot set key-repeat: %m",
-                                        d->session->name, d->name);
-                        k->repeating = false;
-                        keyboard_arm(k, 0);
-                } else {
-                        k->repeating = true;
-                        keyboard_arm(k, k->repeat_delay);
-                }
-        } else if (k->repeating && !repeats) {
-                /*
-                 * We received an event for a key that does not repeat, but we
-                 * currently repeat a previously received key. The new key is
-                 * usually a modifier, but might be any kind of key. In this
-                 * case, we continue repeating the old key, but update the
-                 * symbols according to the new state.
-                 */
-
-                errno = 0;
-                num = xkb_state_key_get_syms(k->xkb_state, repkbd->keycode + KBDXKB_SHIFT, &keysyms);
-                if (num < 0)
-                        r = errno > 0 ? errno : -EFAULT;
-                else
-                        r = keyboard_fill(k, repdata, false, repkbd->keycode, KBDKEY_REPEAT, num, keysyms);
-
-                if (r < 0) {
-                        log_debug_errno(r, "idev-keyboard: %s/%s: cannot update key-repeat: %m",
-                                        d->session->name, d->name);
-                        k->repeating = false;
-                        keyboard_arm(k, 0);
-                }
-        }
-}
-
-static int keyboard_feed_evdev(idev_keyboard *k, idev_data *data) {
-        struct input_event *ev = &data->evdev.event;
-        enum xkb_state_component compch;
-        enum xkb_compose_status cstatus;
-        const xkb_keysym_t *keysyms;
-        idev_device *d = &k->device;
-        int num, r;
-
-        if (ev->type != EV_KEY || ev->value > KBDKEY_DOWN)
-                return 0;
-
-        /* TODO: We should audit xkb-actions, whether they need @resync as
-         * flag. Most actions should just be executed, however, there might
-         * be actions that depend on modifier-orders. Those should be
-         * suppressed. */
-
-        num = xkb_state_key_get_syms(k->xkb_state, ev->code + KBDXKB_SHIFT, &keysyms);
-        compch = xkb_state_update_key(k->xkb_state, ev->code + KBDXKB_SHIFT, ev->value);
-
-        if (compch & XKB_STATE_LEDS) {
-                /* TODO: update LEDs */
-        }
-
-        if (num < 0) {
-                r = num;
-                goto error;
-        }
-
-        if (k->xkb_compose && ev->value == KBDKEY_DOWN) {
-                if (num == 1 && !data->resync) {
-                        xkb_compose_state_feed(k->xkb_compose, keysyms[0]);
-                        cstatus = xkb_compose_state_get_status(k->xkb_compose);
-                } else {
-                        cstatus = XKB_COMPOSE_CANCELLED;
-                }
-
-                switch (cstatus) {
-                case XKB_COMPOSE_NOTHING:
-                        /* keep produced keysyms and forward unchanged */
-                        break;
-                case XKB_COMPOSE_COMPOSING:
-                        /* consumed by compose-state, drop keysym */
-                        keysyms = NULL;
-                        num = 0;
-                        break;
-                case XKB_COMPOSE_COMPOSED:
-                        /* compose-state produced sth, replace keysym */
-                        num = keyboard_read_compose(k, &keysyms);
-                        xkb_compose_state_reset(k->xkb_compose);
-                        break;
-                case XKB_COMPOSE_CANCELLED:
-                        /* canceled compose, reset, forward cancellation sym */
-                        xkb_compose_state_reset(k->xkb_compose);
-                        break;
-                }
-        } else if (k->xkb_compose &&
-                   num == 1 &&
-                   keysyms[0] == XKB_KEY_Multi_key &&
-                   !data->resync &&
-                   ev->value == KBDKEY_UP) {
-                /* Reset compose state on Multi-Key UP events. This effectively
-                 * requires you to hold the key during the whole sequence. I
-                 * think it's pretty handy to avoid accidental
-                 * Compose-sequences, but this may break Compose for disabled
-                 * people. We really need to make this opional! (TODO) */
-                xkb_compose_state_reset(k->xkb_compose);
-        }
-
-        if (ev->value == KBDKEY_UP) {
-                /* never produce keysyms for UP */
-                keysyms = NULL;
-                num = 0;
-        }
-
-        r = keyboard_fill(k, &k->evdata, data->resync, ev->code, ev->value, num, keysyms);
-        if (r < 0)
-                goto error;
-
-        keyboard_repeat(k);
-        return keyboard_raise_data(k, &k->evdata);
-
-error:
-        log_debug_errno(r, "idev-keyboard: %s/%s: cannot handle event: %m",
-                        d->session->name, d->name);
-        k->repeating = false;
-        keyboard_arm(k, 0);
-        return 0;
-}
-
-static int keyboard_feed(idev_device *d, idev_data *data) {
-        idev_keyboard *k = keyboard_from_device(d);
-
-        switch (data->type) {
-        case IDEV_DATA_RESYNC:
-                /*
-                 * If the underlying device is re-synced, key-events might be
-                 * sent re-ordered. Thus, we don't know which key was pressed
-                 * last. Key-repeat might get confused, hence, disable it
-                 * during re-syncs. The first following event will enable it
-                 * again.
-                 */
-
-                k->repeating = false;
-                keyboard_arm(k, 0);
-                return 0;
-        case IDEV_DATA_EVDEV:
-                return keyboard_feed_evdev(k, data);
-        default:
-                return 0;
-        }
-}
-
-static int keyboard_update_kbdmap(idev_keyboard *k) {
-        idev_device *d = &k->device;
-        struct xkb_state *state;
-        kbdmap *km;
-        int r;
-
-        assert(k);
-
-        km = k->kbdctx->kbdmap;
-        if (km == k->kbdmap)
-                return 0;
-
-        errno = 0;
-        state = xkb_state_new(km->xkb_keymap);
-        if (!state) {
-                r = errno > 0 ? -errno : -EFAULT;
-                goto error;
-        }
-
-        kbdmap_unref(k->kbdmap);
-        k->kbdmap = kbdmap_ref(km);
-        xkb_state_unref(k->xkb_state);
-        k->xkb_state = state;
-
-        /* TODO: On state-change, we should trigger a resync so the whole
-         * event-state is flushed into the new xkb-state. libevdev currently
-         * does not support that, though. */
-
-        return 0;
-
-error:
-        return log_debug_errno(r, "idev-keyboard: %s/%s: cannot adopt new keymap: %m",
-                               d->session->name, d->name);
-}
-
-static int keyboard_update_kbdtbl(idev_keyboard *k) {
-        idev_device *d = &k->device;
-        struct xkb_compose_state *compose = NULL;
-        kbdtbl *kt;
-        int r;
-
-        assert(k);
-
-        kt = k->kbdctx->kbdtbl;
-        if (kt == k->kbdtbl)
-                return 0;
-
-        if (kt) {
-                errno = 0;
-                compose = xkb_compose_state_new(kt->xkb_compose_table, XKB_COMPOSE_STATE_NO_FLAGS);
-                if (!compose) {
-                        r = errno > 0 ? -errno : -EFAULT;
-                        goto error;
-                }
-        }
-
-        kbdtbl_unref(k->kbdtbl);
-        k->kbdtbl = kbdtbl_ref(kt);
-        xkb_compose_state_unref(k->xkb_compose);
-        k->xkb_compose = compose;
-
-        return 0;
-
-error:
-        return log_debug_errno(r, "idev-keyboard: %s/%s: cannot adopt new compose table: %m",
-                               d->session->name, d->name);
-}
-
-static const idev_device_vtable keyboard_vtable = {
-        .free                   = keyboard_free,
-        .feed                   = keyboard_feed,
-};