[systemd-commits] 2 commits - configure.ac src/libsystemd-terminal

David Herrmann dvdhrm at kemper.freedesktop.org
Mon Nov 24 09:30:22 PST 2014


 configure.ac                            |    2 
 src/libsystemd-terminal/idev-keyboard.c |  348 +++++++++++++++++++++++++++++---
 2 files changed, 322 insertions(+), 28 deletions(-)

New commits:
commit a2ce1730e12eda3112b3e84e0810f9913ce5c57c
Author: David Herrmann <dh.herrmann at gmail.com>
Date:   Mon Nov 24 18:25:30 2014 +0100

    terminal/idev: use compose tables
    
    Before forwarding keyboard events, feed them into possible compose tables.
    This enables Compose-key and Dead-key features.
    
    Few notes:
     * REPEAT events are never fed into compose tables. It just doesn't make
       sense and is usually not wanted. Compose-sequences are usually hard to
       remember and take time to type. Thus, the REPEAT event of the
       Compose-key itself would often cancel the compose sequence already.
    
     * Stop resolving symbols for UP events. Anything but keycodes is never
       associated to a physical key, but is a one-time action. There is
       nothing like UP events for key-symbols!
    
     * Cancel compose-sequences on Multi-Key UP. See the inline comment. We
       should make this configurable!

diff --git a/src/libsystemd-terminal/idev-keyboard.c b/src/libsystemd-terminal/idev-keyboard.c
index 0684789..eca38fc 100644
--- a/src/libsystemd-terminal/idev-keyboard.c
+++ b/src/libsystemd-terminal/idev-keyboard.c
@@ -31,6 +31,7 @@
 #include "idev.h"
 #include "idev-internal.h"
 #include "macro.h"
+#include "term-internal.h"
 #include "util.h"
 
 typedef struct kbdtbl kbdtbl;
@@ -87,6 +88,7 @@ struct idev_keyboard {
         uint32_t n_syms;
         idev_data evdata;
         idev_data repdata;
+        uint32_t *compose_res;
 
         bool repeating : 1;
 };
@@ -628,6 +630,79 @@ static int keyboard_raise_data(idev_keyboard *k, idev_data *data) {
         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;
 
@@ -644,6 +719,8 @@ static void keyboard_arm(idev_keyboard *k, usec_t usecs) {
 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);
 }
@@ -681,6 +758,10 @@ int idev_keyboard_new(idev_device **out, idev_session *s, const char *name) {
         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,
@@ -715,6 +796,7 @@ static void keyboard_free(idev_device *d) {
         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);
@@ -754,34 +836,13 @@ static int keyboard_fill(idev_keyboard *k,
                          const uint32_t *keysyms) {
         idev_data_keyboard *kev;
         uint32_t i;
+        int r;
 
         assert(dst == &k->evdata || dst == &k->repdata);
 
-        if (n_syms > k->n_syms) {
-                uint32_t *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;
-        }
+        r = keyboard_resize_bufs(k, n_syms);
+        if (r < 0)
+                return r;
 
         dst->type = IDEV_DATA_KEYBOARD;
         dst->resync = resync;
@@ -801,8 +862,6 @@ static int keyboard_fill(idev_keyboard *k,
         }
 
         for (i = 0; i < IDEV_KBDMOD_CNT; ++i) {
-                int r;
-
                 if (k->kbdmap->modmap[i] == XKB_MOD_INVALID)
                         continue;
 
@@ -905,6 +964,7 @@ static void keyboard_repeat(idev_keyboard *k) {
 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;
@@ -929,6 +989,52 @@ static int keyboard_feed_evdev(idev_keyboard *k, idev_data *data) {
                 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;

commit cdcd0ccdbe427de53d8e5ff8f2f1b06b3f477bde
Author: David Herrmann <dh.herrmann at gmail.com>
Date:   Mon Nov 24 16:32:54 2014 +0100

    terminal/idev: add compose-file support
    
    Add support for compose files to idev-keyboard. This requires
    libxkbcommon-0.5.0, which is pretty new, but should be fine.
    
    We don't use the compose-files, yet. Further commits will put life into
    them.

diff --git a/configure.ac b/configure.ac
index bd3cc0e..a4e91e3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1080,7 +1080,7 @@ AM_CONDITIONAL(ENABLE_EFI, [test "x$have_efi" = "xyes"])
 have_terminal=no
 AC_ARG_ENABLE(terminal, AS_HELP_STRING([--enable-terminal], [enable terminal support]))
 if test "x$enable_terminal" = "xyes"; then
-        PKG_CHECK_MODULES([TERMINAL], [ libevdev >= 1.2 xkbcommon >= 0.4 libdrm >= 2.4], [have_terminal=yes])
+        PKG_CHECK_MODULES([TERMINAL], [ libevdev >= 1.2 xkbcommon >= 0.5 libdrm >= 2.4], [have_terminal=yes])
         AS_IF([test "x$have_terminal" != xyes -a "x$enable_terminal" = xyes],
               [AC_MSG_ERROR([*** terminal support requested but required dependencies not available])],
               [test "x$have_terminal" = xyes],
diff --git a/src/libsystemd-terminal/idev-keyboard.c b/src/libsystemd-terminal/idev-keyboard.c
index 8dc1c20..0684789 100644
--- a/src/libsystemd-terminal/idev-keyboard.c
+++ b/src/libsystemd-terminal/idev-keyboard.c
@@ -25,6 +25,7 @@
 #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"
@@ -32,10 +33,16 @@
 #include "macro.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;
@@ -48,10 +55,12 @@ struct kbdctx {
         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;
@@ -66,8 +75,10 @@ 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;
@@ -91,6 +102,60 @@ struct idev_keyboard {
 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
@@ -191,6 +256,49 @@ static int kbdmap_new_from_names(kbdmap **out,
  * 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("idev-keyboard: cannot load compose-table for '%s': %s",
+                          lang, strerror(-r));
+                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;
@@ -239,7 +347,41 @@ static int kbdctx_refresh_keymap(kbdctx *kc) {
         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("idev-keyboard: cannot parse locale property from locale1: %s", strerror(-r));
+
+        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) },
@@ -385,8 +527,10 @@ static kbdctx *kbdctx_unref(kbdctx *kc) {
         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);
@@ -420,6 +564,10 @@ static int kbdctx_new(kbdctx **out, idev_context *c) {
         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)
@@ -529,6 +677,10 @@ int idev_keyboard_new(idev_device **out, idev_session *s, const char *name) {
         if (r < 0)
                 return r;
 
+        r = keyboard_update_kbdtbl(k);
+        if (r < 0)
+                return r;
+
         r = sd_event_add_time(s->context->event,
                               &k->repeat_timer,
                               CLOCK_MONOTONIC,
@@ -557,12 +709,14 @@ int idev_keyboard_new(idev_device **out, idev_session *s, const char *name) {
 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);
         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);
@@ -849,6 +1003,40 @@ error:
         return r;
 }
 
+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:
+        log_debug("idev-keyboard: %s/%s: cannot adopt new compose table: %s",
+                  d->session->name, d->name, strerror(-r));
+        return r;
+}
+
 static const idev_device_vtable keyboard_vtable = {
         .free                   = keyboard_free,
         .feed                   = keyboard_feed,



More information about the systemd-commits mailing list