diff --git a/Makefile.am b/Makefile.am index 2ae0218..fc33494 100644 --- a/Makefile.am +++ b/Makefile.am @@ -152,6 +152,9 @@ libuterm_la_SOURCES = \ src/uterm_video.c \ src/uterm_monitor.c \ src/uterm_input.c \ + src/uterm_input_plain.c \ + external/imKStoUCS.h \ + external/imKStoUCS.c \ src/uterm_vt.c \ src/vt.h \ src/vt.c @@ -195,12 +198,7 @@ endif if UTERM_HAVE_XKBCOMMON libuterm_la_SOURCES += \ - src/uterm_input_xkb.c -else -libuterm_la_SOURCES += \ - src/uterm_input_dumb.c \ - external/imKStoUCS.h \ - external/imKStoUCS.c + src/uterm_input_uxkb.c endif include_HEADERS += \ diff --git a/configure.ac b/configure.ac index 655b704..af71388 100644 --- a/configure.ac +++ b/configure.ac @@ -336,7 +336,8 @@ if test ! x$enable_xkbcommon = xno ; then fi if test x$xkbcommon_enabled = xyes ; then - test # dummy + AC_DEFINE([UTERM_HAVE_XKBCOMMON], [1], + [Use xkbcommon as input keyboard handling backend]) else XKBCOMMON_CFLAGS="" XKBCOMMON_LIBS="" diff --git a/src/main.c b/src/main.c index 0a7f8c9..02e9d1f 100644 --- a/src/main.c +++ b/src/main.c @@ -182,8 +182,10 @@ static void seat_add_video(struct kmscon_seat *seat, ret = kmscon_ui_new(&seat->ui, seat->app->eloop, seat->video, seat->input); - if (ret) + if (ret) { + log_error("cannot create UI object"); goto err_video; + } seat->vdev = dev; log_debug("new graphics device on seat %s", seat->sname); diff --git a/src/uterm_input.c b/src/uterm_input.c index baa0232..387e845 100644 --- a/src/uterm_input.c +++ b/src/uterm_input.c @@ -88,7 +88,7 @@ static void notify_key(struct uterm_input_dev *dev, if (type != EV_KEY) return; - ret = kbd_dev_process_key(dev->kbd, value, code, &ev); + ret = kbd_dev_process(dev->kbd, value, code, &ev); if (ret) return; @@ -200,7 +200,7 @@ static void input_new_dev(struct uterm_input *input, if (!dev->node) goto err_free; - ret = kbd_dev_new(&dev->kbd, input->desc); + ret = kbd_desc_alloc(input->desc, &dev->kbd); if (ret) goto err_node; @@ -254,11 +254,22 @@ int uterm_input_new(struct uterm_input **out, goto err_free; ret = kbd_desc_new(&input->desc, - conf_global.xkb_layout, - conf_global.xkb_variant, - conf_global.xkb_options); - if (ret) + conf_global.xkb_layout, + conf_global.xkb_variant, + conf_global.xkb_options, + KBD_UXKB); + if (ret == -EOPNOTSUPP) { + log_info("XKB keyboard backend not available, trying plain backend"); + ret = kbd_desc_new(&input->desc, + conf_global.xkb_layout, + conf_global.xkb_variant, + conf_global.xkb_options, + KBD_PLAIN); + if (ret) + goto err_hook; + } else if (ret) { goto err_hook; + } log_debug("new object %p", input); ev_eloop_ref(input->eloop); diff --git a/src/uterm_input_dumb.c b/src/uterm_input_plain.c similarity index 85% rename from src/uterm_input_dumb.c rename to src/uterm_input_plain.c index b443a8e..38b4a25 100644 --- a/src/uterm_input_dumb.c +++ b/src/uterm_input_plain.c @@ -47,12 +47,7 @@ #include "uterm.h" #include "uterm_internal.h" -#define LOG_SUBSYSTEM "input_dumb" - -struct kbd_dev { - unsigned long ref; - unsigned int mods; -}; +#define LOG_SUBSYSTEM "input_plain" /* * These tables do not contain all possible keys from linux/input.h. @@ -284,21 +279,7 @@ static const struct { [KEY_RIGHTMETA] = { UTERM_MOD4_MASK, MOD_NORMAL }, }; -int kbd_dev_new(struct kbd_dev **out, struct kbd_desc *desc) -{ - struct kbd_dev *kbd; - - kbd = malloc(sizeof(*kbd)); - if (!kbd) - return -ENOMEM; - memset(kbd, 0, sizeof(*kbd)); - kbd->ref = 1; - - *out = kbd; - return 0; -} - -void kbd_dev_ref(struct kbd_dev *kbd) +static void plain_dev_ref(struct kbd_dev *kbd) { if (!kbd || !kbd->ref) return; @@ -306,7 +287,7 @@ void kbd_dev_ref(struct kbd_dev *kbd) ++kbd->ref; } -void kbd_dev_unref(struct kbd_dev *kbd) +static void plain_dev_unref(struct kbd_dev *kbd) { if (!kbd || !kbd->ref || --kbd->ref) return; @@ -314,23 +295,23 @@ void kbd_dev_unref(struct kbd_dev *kbd) free(kbd); } -void kbd_dev_reset(struct kbd_dev *kbd, const unsigned long *ledbits) +static void plain_dev_reset(struct kbd_dev *kbd, const unsigned long *ledbits) { if (!kbd) return; - kbd->mods = 0; + kbd->plain.mods = 0; if (input_bit_is_set(ledbits, LED_NUML)) - kbd->mods |= UTERM_MOD2_MASK; + kbd->plain.mods |= UTERM_MOD2_MASK; if (input_bit_is_set(ledbits, LED_CAPSL)) - kbd->mods |= UTERM_LOCK_MASK; + kbd->plain.mods |= UTERM_LOCK_MASK; } -int kbd_dev_process_key(struct kbd_dev *kbd, - uint16_t key_state, - uint16_t code, - struct uterm_input_event *out) +static int plain_dev_process(struct kbd_dev *kbd, + uint16_t key_state, + uint16_t code, + struct uterm_input_event *out) { uint32_t keysym; unsigned int mod; @@ -353,12 +334,12 @@ int kbd_dev_process_key(struct kbd_dev *kbd, */ if (key_state == 1) { if (mod_type == MOD_NORMAL) - kbd->mods |= mod; + kbd->plain.mods |= mod; else if (mod_type == MOD_LOCK) - kbd->mods ^= mod; + kbd->plain.mods ^= mod; } else if (key_state == 0) { if (mod_type == MOD_NORMAL) - kbd->mods &= ~mod; + kbd->plain.mods &= ~mod; } /* Don't deliver events purely for modifiers. */ @@ -370,11 +351,11 @@ int kbd_dev_process_key(struct kbd_dev *kbd, keysym = 0; - if (!keysym && kbd->mods & UTERM_MOD2_MASK) + if (!keysym && kbd->plain.mods & UTERM_MOD2_MASK) keysym = keytab_numlock[code]; - if (!keysym && kbd->mods & UTERM_SHIFT_MASK) + if (!keysym && kbd->plain.mods & UTERM_SHIFT_MASK) keysym = keytab_shift[code]; - if (!keysym && kbd->mods & UTERM_LOCK_MASK) + if (!keysym && kbd->plain.mods & UTERM_LOCK_MASK) keysym = keytab_capslock[code]; if (!keysym) keysym = keytab_normal[code]; @@ -385,34 +366,81 @@ int kbd_dev_process_key(struct kbd_dev *kbd, out->keycode = code; out->keysym = keysym; out->unicode = KeysymToUcs4(keysym) ?: UTERM_INPUT_INVALID; - out->mods = kbd->mods; + out->mods = kbd->plain.mods; return 0; } -int kbd_desc_new(struct kbd_desc **out, - const char *layout, - const char *variant, - const char *options) +static int plain_desc_init(struct kbd_desc **out, + const char *layout, + const char *variant, + const char *options) { + struct kbd_desc *desc; + if (!out) return -EINVAL; + desc = malloc(sizeof(*desc)); + if (!desc) + return -ENOMEM; + memset(desc, 0, sizeof(*desc)); + desc->ops = &plain_desc_ops; + log_debug("new keyboard description (%s, %s, %s)", - layout, variant, options); - *out = NULL; + layout, variant, options); + *out = desc; return 0; } -void kbd_desc_ref(struct kbd_desc *desc) +static void plain_desc_ref(struct kbd_desc *desc) { + if (!desc || !desc->ref) + return; + + ++desc->ref; } -void kbd_desc_unref(struct kbd_desc *desc) +static void plain_desc_unref(struct kbd_desc *desc) { + if (!desc || !desc->ref || --desc->ref) + return; + + log_debug("destroying keyboard description"); + free(desc); } -void kbd_keysym_to_string(uint32_t keysym, char *str, size_t size) +static int plain_desc_alloc(struct kbd_desc *desc, struct kbd_dev **out) +{ + struct kbd_dev *kbd; + + kbd = malloc(sizeof(*kbd)); + if (!kbd) + return -ENOMEM; + memset(kbd, 0, sizeof(*kbd)); + kbd->ref = 1; + kbd->ops = &plain_dev_ops; + + *out = kbd; + return 0; +} + +static void plain_keysym_to_string(uint32_t keysym, char *str, size_t size) { snprintf(str, size, "%#x", keysym); } + +const struct kbd_desc_ops plain_desc_ops = { + .init = plain_desc_init, + .ref = plain_desc_ref, + .unref = plain_desc_unref, + .alloc = plain_desc_alloc, + .keysym_to_string = plain_keysym_to_string, +}; + +const struct kbd_dev_ops plain_dev_ops = { + .ref = plain_dev_ref, + .unref = plain_dev_unref, + .reset = plain_dev_reset, + .process = plain_dev_process, +}; diff --git a/src/uterm_input_xkb.c b/src/uterm_input_uxkb.c similarity index 75% rename from src/uterm_input_xkb.c rename to src/uterm_input_uxkb.c index f786cc6..717cda4 100644 --- a/src/uterm_input_xkb.c +++ b/src/uterm_input_uxkb.c @@ -35,44 +35,9 @@ #include "uterm.h" #include "uterm_internal.h" -#define LOG_SUBSYSTEM "input_xkb" +#define LOG_SUBSYSTEM "input_uxkb" -struct kbd_desc { - unsigned long ref; - struct xkb_context *ctx; - struct xkb_keymap *keymap; -}; - -struct kbd_dev { - unsigned long ref; - struct kbd_desc *desc; - struct xkb_state *state; -}; - -int kbd_dev_new(struct kbd_dev **out, struct kbd_desc *desc) -{ - struct kbd_dev *kbd; - - kbd = malloc(sizeof(*kbd)); - if (!kbd) - return -ENOMEM; - - memset(kbd, 0, sizeof(*kbd)); - kbd->ref = 1; - kbd->desc = desc; - - kbd->state = xkb_state_new(desc->keymap); - if (!kbd->state) { - free(kbd); - return -ENOMEM; - } - - kbd_desc_ref(desc); - *out = kbd; - return 0; -} - -void kbd_dev_ref(struct kbd_dev *kbd) +static void uxkb_dev_ref(struct kbd_dev *kbd) { if (!kbd || !kbd->ref) return; @@ -80,12 +45,12 @@ void kbd_dev_ref(struct kbd_dev *kbd) ++kbd->ref; } -void kbd_dev_unref(struct kbd_dev *kbd) +static void uxkb_dev_unref(struct kbd_dev *kbd) { if (!kbd || !kbd->ref || --kbd->ref) return; - xkb_state_unref(kbd->state); + xkb_state_unref(kbd->uxkb.state); kbd_desc_unref(kbd->desc); free(kbd); } @@ -120,10 +85,10 @@ static unsigned int get_effective_modmask(struct xkb_state *state) return mods; } -int kbd_dev_process_key(struct kbd_dev *kbd, - uint16_t key_state, - uint16_t code, - struct uterm_input_event *out) +static int uxkb_dev_process(struct kbd_dev *kbd, + uint16_t key_state, + uint16_t code, + struct uterm_input_event *out) { struct xkb_state *state; struct xkb_keymap *keymap; @@ -134,7 +99,7 @@ int kbd_dev_process_key(struct kbd_dev *kbd, if (!kbd) return -EINVAL; - state = kbd->state; + state = kbd->uxkb.state; keymap = xkb_state_get_map(state); keycode = code + EVDEV_KEYCODE_OFFSET; @@ -161,8 +126,8 @@ int kbd_dev_process_key(struct kbd_dev *kbd, */ out->keycode = code; out->keysym = keysyms[0]; - out->mods = get_effective_modmask(state);; - out->unicode = xkb_keysym_to_utf32(out->keysym) ?: UTERM_INPUT_INVALID; + out->mods = get_effective_modmask(state); + out->unicode = xkb_keysym_to_utf32(out->keysym) ? : UTERM_INPUT_INVALID; return 0; } @@ -172,7 +137,7 @@ int kbd_dev_process_key(struct kbd_dev *kbd, * We don't reset the locked group, this should survive a VT switch, etc. The * locked modifiers are reset according to the keyboard LEDs. */ -void kbd_dev_reset(struct kbd_dev *kbd, const unsigned long *ledbits) +static void uxkb_dev_reset(struct kbd_dev *kbd, const unsigned long *ledbits) { unsigned int i; struct xkb_state *state; @@ -188,7 +153,7 @@ void kbd_dev_reset(struct kbd_dev *kbd, const unsigned long *ledbits) if (!kbd) return; - state = kbd->state; + state = kbd->uxkb.state; for (i = 0; i < sizeof(led_names) / sizeof(*led_names); i++) { if (!input_bit_is_set(ledbits, led_names[i].led)) @@ -205,10 +170,10 @@ void kbd_dev_reset(struct kbd_dev *kbd, const unsigned long *ledbits) (void)state; } -int kbd_desc_new(struct kbd_desc **out, - const char *layout, - const char *variant, - const char *options) +static int uxkb_desc_init(struct kbd_desc **out, + const char *layout, + const char *variant, + const char *options) { int ret; struct kbd_desc *desc; @@ -229,15 +194,16 @@ int kbd_desc_new(struct kbd_desc **out, memset(desc, 0, sizeof(*desc)); desc->ref = 1; + desc->ops = &uxkb_desc_ops; - desc->ctx = xkb_context_new(0); - if (!desc->ctx) { + desc->uxkb.ctx = xkb_context_new(0); + if (!desc->uxkb.ctx) { ret = -ENOMEM; goto err_desc; } - desc->keymap = xkb_map_new_from_names(desc->ctx, &rmlvo, 0); - if (!desc->keymap) { + desc->uxkb.keymap = xkb_map_new_from_names(desc->uxkb.ctx, &rmlvo, 0); + if (!desc->uxkb.keymap) { log_warn("failed to create keymap (%s, %s, %s), " "reverting to default US keymap", layout, variant, options); @@ -246,8 +212,9 @@ int kbd_desc_new(struct kbd_desc **out, rmlvo.variant = ""; rmlvo.options = ""; - desc->keymap = xkb_map_new_from_names(desc->ctx, &rmlvo, 0); - if (!desc->keymap) { + desc->uxkb.keymap = xkb_map_new_from_names(desc->uxkb.ctx, + &rmlvo, 0); + if (!desc->uxkb.keymap) { log_warn("failed to create keymap"); ret = -EFAULT; goto err_ctx; @@ -260,13 +227,13 @@ int kbd_desc_new(struct kbd_desc **out, return 0; err_ctx: - xkb_context_unref(desc->ctx); + xkb_context_unref(desc->uxkb.ctx); err_desc: free(desc); return ret; } -void kbd_desc_ref(struct kbd_desc *desc) +static void uxkb_desc_ref(struct kbd_desc *desc) { if (!desc || !desc->ref) return; @@ -274,18 +241,57 @@ void kbd_desc_ref(struct kbd_desc *desc) ++desc->ref; } -void kbd_desc_unref(struct kbd_desc *desc) +static void uxkb_desc_unref(struct kbd_desc *desc) { if (!desc || !desc->ref || --desc->ref) return; log_debug("destroying keyboard description"); - xkb_map_unref(desc->keymap); - xkb_context_unref(desc->ctx); + xkb_map_unref(desc->uxkb.keymap); + xkb_context_unref(desc->uxkb.ctx); free(desc); } -void kbd_keysym_to_string(uint32_t keysym, char *str, size_t size) +static int uxkb_desc_alloc(struct kbd_desc *desc, struct kbd_dev **out) +{ + struct kbd_dev *kbd; + + kbd = malloc(sizeof(*kbd)); + if (!kbd) + return -ENOMEM; + + memset(kbd, 0, sizeof(*kbd)); + kbd->ref = 1; + kbd->desc = desc; + kbd->ops = &uxkb_dev_ops; + + kbd->uxkb.state = xkb_state_new(desc->uxkb.keymap); + if (!kbd->uxkb.state) { + free(kbd); + return -ENOMEM; + } + + kbd_desc_ref(desc); + *out = kbd; + return 0; +} + +static void uxkb_keysym_to_string(uint32_t keysym, char *str, size_t size) { xkb_keysym_get_name(keysym, str, size); } + +const struct kbd_desc_ops uxkb_desc_ops = { + .init = uxkb_desc_init, + .ref = uxkb_desc_ref, + .unref = uxkb_desc_unref, + .alloc = uxkb_desc_alloc, + .keysym_to_string = uxkb_keysym_to_string, +}; + +const struct kbd_dev_ops uxkb_dev_ops = { + .ref = uxkb_dev_ref, + .unref = uxkb_dev_unref, + .reset = uxkb_dev_reset, + .process = uxkb_dev_process, +}; diff --git a/src/uterm_internal.h b/src/uterm_internal.h index 8336a95..6637666 100644 --- a/src/uterm_internal.h +++ b/src/uterm_internal.h @@ -372,37 +372,189 @@ static inline bool input_bit_is_set(const unsigned long *array, int bit) struct kbd_desc; struct kbd_dev; -int kbd_desc_new(struct kbd_desc **out, - const char *layout, - const char *variant, - const char *options); -void kbd_desc_ref(struct kbd_desc *desc); -void kbd_desc_unref(struct kbd_desc *desc); +struct kbd_desc_ops { + int (*init) (struct kbd_desc **out, const char *layout, + const char *variant, const char *options); + void (*ref) (struct kbd_desc *desc); + void (*unref) (struct kbd_desc *desc); + int (*alloc) (struct kbd_desc *desc, struct kbd_dev **out); + void (*keysym_to_string) (uint32_t keysym, char *str, size_t size); +}; -int kbd_dev_new(struct kbd_dev **out, struct kbd_desc *desc); -void kbd_dev_ref(struct kbd_dev *state); -void kbd_dev_unref(struct kbd_dev *state); - -/* - * This resets the keyboard state in case it got out of sync. It's mainly used - * to sync our notion of the keyboard state with what the keyboard LEDs show. - */ -void kbd_dev_reset(struct kbd_dev *kbd, const unsigned long *ledbits); - -/* - * This is the entry point to the keyboard processing. - * We get an evdev scancode and the keyboard state, and should put out a - * proper input event. - * Some evdev input events shouldn't result in us sending an input event - * (e.g. a key release): - * - If the event was filled out, 0 is returned. - * - Otherwise, if there was no error, -ENOKEY is returned. - */ -int kbd_dev_process_key(struct kbd_dev *kbd, - uint16_t key_state, - uint16_t code, +struct kbd_dev_ops { + void (*ref) (struct kbd_dev *dev); + void (*unref) (struct kbd_dev *dev); + void (*reset) (struct kbd_dev *dev, const unsigned long *ledbits); + int (*process) (struct kbd_dev *dev, uint16_t state, uint16_t code, struct uterm_input_event *out); +}; -void kbd_dev_keysym_to_string(uint32_t keysym, char *str, size_t size); +struct plain_desc { + int unused; +}; + +struct plain_dev { + unsigned int mods; +}; + +static const bool plain_available = true; +extern const struct kbd_desc_ops plain_desc_ops; +extern const struct kbd_dev_ops plain_dev_ops; + +#ifdef UTERM_HAVE_XKBCOMMON + +struct uxkb_desc { + struct xkb_context *ctx; + struct xkb_keymap *keymap; +}; + +struct uxkb_dev { + struct xkb_state *state; +}; + +static const bool uxkb_available = true; +extern const struct kbd_desc_ops uxkb_desc_ops; +extern const struct kbd_dev_ops uxkb_dev_ops; + +#else /* !UTERM_HAVE_XKBCOMMON */ + +struct uxkb_desc { + int unused; +}; + +struct uxkb_dev { + int unused; +}; + +static const bool xkb_available = false; +static const struct kbd_desc_ops uxkb_desc_ops; +static const struct kbd_dev_ops uxkb_dev_ops; + +#endif /* UTERM_HAVE_XKBCOMMON */ + +struct kbd_desc { + unsigned long ref; + const struct kbd_desc_ops *ops; + + union { + struct plain_desc plain; + struct uxkb_desc uxkb; + }; +}; + +struct kbd_dev { + unsigned long ref; + struct kbd_desc *desc; + const struct kbd_dev_ops *ops; + + union { + struct plain_dev plain; + struct uxkb_dev uxkb; + }; +}; + +enum kbd_mode { + KBD_PLAIN, + KBD_UXKB, +}; + +static inline int kbd_desc_new(struct kbd_desc **out, const char *layout, + const char *variant, const char *options, + unsigned int mode) +{ + const struct kbd_desc_ops *ops; + + switch (mode) { + case KBD_UXKB: + if (!uxkb_available) { + log_error("XKB KBD backend not available"); + return -EOPNOTSUPP; + } + ops = &uxkb_desc_ops; + break; + case KBD_PLAIN: + if (!plain_available) { + log_error("plain KBD backend not available"); + return -EOPNOTSUPP; + } + ops = &plain_desc_ops; + break; + default: + log_error("unknown KBD backend %u", mode); + return -EINVAL; + } + + return ops->init(out, layout, variant, options); +} + +static inline void kbd_desc_ref(struct kbd_desc *desc) +{ + if (!desc) + return; + + return desc->ops->ref(desc); +} + +static inline void kbd_desc_unref(struct kbd_desc *desc) +{ + if (!desc) + return; + + return desc->ops->unref(desc); +} + +static inline int kbd_desc_alloc(struct kbd_desc *desc, struct kbd_dev **out) +{ + if (!desc) + return -EINVAL; + + return desc->ops->alloc(desc, out); +} + +static inline void kbd_desc_keysym_to_string(struct kbd_desc *desc, + uint32_t keysym, + char *str, size_t size) +{ + if (!desc) + return; + + return desc->ops->keysym_to_string(keysym, str, size); +} + +static inline void kbd_dev_ref(struct kbd_dev *dev) +{ + if (!dev) + return; + + return dev->ops->ref(dev); +} + +static inline void kbd_dev_unref(struct kbd_dev *dev) +{ + if (!dev) + return; + + return dev->ops->unref(dev); +} + +static inline void kbd_dev_reset(struct kbd_dev *dev, + const unsigned long *ledbits) +{ + if (!dev) + return; + + return dev->ops->reset(dev, ledbits); +} + +static inline int kbd_dev_process(struct kbd_dev *dev, + uint16_t key_state, + uint16_t code, + struct uterm_input_event *out) +{ + if (!dev) + return -EINVAL; + + return dev->ops->process(dev, key_state, code, out); +} #endif /* UTERM_INTERNAL_H */