X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Flibsystemd-terminal%2Fidev-keyboard.c;h=ce9cc5318b2993e21438b3a14836de864eb2ba83;hp=068478978d57dc8917266fd63cbd4f60b8fd2989;hb=b5a1e50495179a5995d56936f429c0b77dca8ecc;hpb=cdcd0ccdbe427de53d8e5ff8f2f1b06b3f477bde diff --git a/src/libsystemd-terminal/idev-keyboard.c b/src/libsystemd-terminal/idev-keyboard.c index 068478978..ce9cc5318 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; }; @@ -502,6 +504,27 @@ static int kbdctx_setup_bus(kbdctx *kc) { 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 if (lvl >= XKB_LOG_LEVEL_ERROR) + sd_lvl = LOG_ERR; + else if (lvl >= XKB_LOG_LEVEL_CRITICAL) + sd_lvl = LOG_CRIT; + else + sd_lvl = LOG_CRIT; + + 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); @@ -556,10 +579,13 @@ static int kbdctx_new(kbdctx **out, idev_context *c) { kc->context = c; errno = 0; - kc->xkb_context = xkb_context_new(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; @@ -628,6 +654,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 +743,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 +782,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 +820,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 +860,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 +886,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 +988,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 +1013,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;