diff --git a/src/uterm_input.c b/src/uterm_input.c index 7c7379e..0d15dd6 100644 --- a/src/uterm_input.c +++ b/src/uterm_input.c @@ -53,28 +53,6 @@ enum device_feature { FEATURE_HAS_LEDS = 0x02, }; -struct uterm_input_dev { - struct shl_dlist list; - struct uterm_input *input; - - unsigned int features; - int rfd; - char *node; - struct ev_fd *fd; - struct kbd_dev *kbd; -}; - -struct uterm_input { - unsigned long ref; - struct ev_eloop *eloop; - int awake; - - struct shl_hook *hook; - struct kbd_desc *desc; - - struct shl_dlist devices; -}; - static void input_free_dev(struct uterm_input_dev *dev); static void notify_key(struct uterm_input_dev *dev, @@ -89,7 +67,7 @@ static void notify_key(struct uterm_input_dev *dev, return; memset(&ev, 0, sizeof(ev)); - ret = kbd_dev_process(dev->kbd, value, code, &ev); + ret = uxkb_dev_process(dev, value, code, &ev); if (ret) return; @@ -156,7 +134,7 @@ static int input_wake_up_dev(struct uterm_input_dev *dev) } /* rediscover the keyboard state if sth changed during sleep */ - kbd_dev_reset(dev->kbd, ledbits); + uxkb_dev_reset(dev, ledbits); ret = ev_eloop_new_fd(dev->input->eloop, &dev->fd, dev->rfd, EV_READABLE, @@ -201,7 +179,7 @@ static void input_new_dev(struct uterm_input *input, if (!dev->node) goto err_free; - ret = kbd_desc_alloc(input->desc, &dev->kbd); + ret = uxkb_dev_init(dev); if (ret) goto err_node; @@ -216,7 +194,7 @@ static void input_new_dev(struct uterm_input *input, return; err_kbd: - kbd_dev_unref(dev->kbd); + uxkb_dev_destroy(dev); err_node: free(dev->node); err_free: @@ -228,7 +206,7 @@ static void input_free_dev(struct uterm_input_dev *dev) log_debug("free device %s", dev->node); input_sleep_dev(dev); shl_dlist_unlink(&dev->list); - kbd_dev_unref(dev->kbd); + uxkb_dev_destroy(dev); free(dev->node); free(dev); } @@ -257,11 +235,7 @@ int uterm_input_new(struct uterm_input **out, if (ret) goto err_free; - ret = kbd_desc_new(&input->desc, - layout, - variant, - options, - KBD_UXKB); + ret = uxkb_desc_init(input, layout, variant, options); if (ret) goto err_hook; @@ -301,7 +275,7 @@ void uterm_input_unref(struct uterm_input *input) input_free_dev(dev); } - kbd_desc_unref(input->desc); + uxkb_desc_destroy(input); shl_hook_free(input->hook); ev_eloop_unref(input->eloop); free(input); @@ -467,28 +441,3 @@ bool uterm_input_is_awake(struct uterm_input *input) return input->awake > 0; } - -void uterm_input_keysym_to_string(struct uterm_input *input, - uint32_t keysym, char *str, size_t size) -{ - if (!str || !size) - return; - if (!input) { - *str = 0; - return; - } - - kbd_desc_keysym_to_string(input->desc, keysym, str, size); -} - -int uterm_input_string_to_keysym(struct uterm_input *input, const char *n, - uint32_t *out) -{ - if (!n || !out) - return -EINVAL; - - if (input) - return kbd_desc_string_to_keysym(input->desc, n, out); - - return uxkb_string_to_keysym(n, out); -} diff --git a/src/uterm_input.h b/src/uterm_input.h index b3d7b02..18e1997 100644 --- a/src/uterm_input.h +++ b/src/uterm_input.h @@ -34,172 +34,49 @@ #include #include #include "eloop.h" +#include "shl_dlist.h" #include "uterm.h" -struct kbd_desc; -struct kbd_dev; +struct uterm_input_dev { + struct shl_dlist list; + struct uterm_input *input; -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 (*string_to_keysym) (const char *n, uint32_t *out); -}; - -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); -}; - -struct uxkb_desc { - struct xkb_context *ctx; - struct xkb_keymap *keymap; -}; - -struct uxkb_dev { + unsigned int features; + int rfd; + char *node; + struct ev_fd *fd; 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; - -extern int uxkb_string_to_keysym(const char *n, uint32_t *out); - -struct kbd_desc { +struct uterm_input { unsigned long ref; - const struct kbd_desc_ops *ops; + struct ev_eloop *eloop; + int awake; - union { - struct uxkb_desc uxkb; - }; + struct shl_hook *hook; + struct xkb_context *ctx; + struct xkb_keymap *keymap; + + struct shl_dlist devices; }; -struct kbd_dev { - unsigned long ref; - struct kbd_desc *desc; - const struct kbd_dev_ops *ops; - - union { - struct uxkb_dev uxkb; - }; -}; - -enum kbd_mode { - 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; - 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 int kbd_desc_string_to_keysym(struct kbd_desc *desc, - const char *n, - uint32_t *out) -{ - if (!desc) - return -EINVAL; - - return desc->ops->string_to_keysym(n, out); -} - -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); -} - static inline bool input_bit_is_set(const unsigned long *array, int bit) { return !!(array[bit / LONG_BIT] & (1LL << (bit % LONG_BIT))); } +int uxkb_desc_init(struct uterm_input *input, + const char *layout, + const char *variant, + const char *options); +void uxkb_desc_destroy(struct uterm_input *input); + +int uxkb_dev_init(struct uterm_input_dev *dev); +void uxkb_dev_destroy(struct uterm_input_dev *dev); +int uxkb_dev_process(struct uterm_input_dev *dev, + uint16_t key_state, + uint16_t code, + struct uterm_input_event *out); +void uxkb_dev_reset(struct uterm_input_dev *dev, const unsigned long *ledbits); + #endif /* UTERM_INPUT_H */ diff --git a/src/uterm_input_uxkb.c b/src/uterm_input_uxkb.c index ddc9b67..a046c9a 100644 --- a/src/uterm_input_uxkb.c +++ b/src/uterm_input_uxkb.c @@ -38,22 +38,71 @@ #define LOG_SUBSYSTEM "input_uxkb" -static void uxkb_dev_ref(struct kbd_dev *kbd) +int uxkb_desc_init(struct uterm_input *input, + const char *layout, + const char *variant, + const char *options) { - if (!kbd || !kbd->ref) - return; + int ret; + struct xkb_rule_names rmlvo = { + .rules = "evdev", + .model = "evdev", + .layout = layout, + .variant = variant, + .options = options, + }; - ++kbd->ref; + input->ctx = xkb_context_new(0); + if (!input->ctx) { + log_error("cannot create XKB context"); + return -ENOMEM; + } + + input->keymap = xkb_map_new_from_names(input->ctx, &rmlvo, 0); + if (!input->keymap) { + log_warn("failed to create keymap (%s, %s, %s), " + "reverting to default US keymap", + layout, variant, options); + + rmlvo.layout = "us"; + rmlvo.variant = ""; + rmlvo.options = ""; + + input->keymap = xkb_map_new_from_names(input->ctx, &rmlvo, 0); + if (!input->keymap) { + log_warn("failed to create XKB keymap"); + ret = -EFAULT; + goto err_ctx; + } + } + + log_debug("new keyboard description (%s, %s, %s)", + layout, variant, options); + return 0; + +err_ctx: + xkb_context_unref(input->ctx); + return ret; } -static void uxkb_dev_unref(struct kbd_dev *kbd) +void uxkb_desc_destroy(struct uterm_input *input) { - if (!kbd || !kbd->ref || --kbd->ref) - return; + xkb_map_unref(input->keymap); + xkb_context_unref(input->ctx); +} - xkb_state_unref(kbd->uxkb.state); - kbd_desc_unref(kbd->desc); - free(kbd); +int uxkb_dev_init(struct uterm_input_dev *dev) +{ + dev->state = xkb_state_new(dev->input->keymap); + if (!dev->state) + return -ENOMEM; + + return 0; +} + +void uxkb_dev_destroy(struct uterm_input_dev *dev) +{ + xkb_state_unref(dev->state); } #define EVDEV_KEYCODE_OFFSET 8 @@ -63,10 +112,10 @@ enum { KEY_REPEATED = 2, }; -static int uxkb_dev_process(struct kbd_dev *kbd, - uint16_t key_state, - uint16_t code, - struct uterm_input_event *out) +int uxkb_dev_process(struct uterm_input_dev *dev, + uint16_t key_state, + uint16_t code, + struct uterm_input_event *out) { struct xkb_state *state; struct xkb_keymap *keymap; @@ -74,10 +123,7 @@ static int uxkb_dev_process(struct kbd_dev *kbd, const xkb_keysym_t *keysyms; int num_keysyms; - if (!kbd) - return -EINVAL; - - state = kbd->uxkb.state; + state = dev->state; keymap = xkb_state_get_map(state); keycode = code + EVDEV_KEYCODE_OFFSET; @@ -115,7 +161,7 @@ static int uxkb_dev_process(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. */ -static void uxkb_dev_reset(struct kbd_dev *kbd, const unsigned long *ledbits) +void uxkb_dev_reset(struct uterm_input_dev *dev, const unsigned long *ledbits) { unsigned int i; struct xkb_state *state; @@ -128,9 +174,6 @@ static void uxkb_dev_reset(struct kbd_dev *kbd, const unsigned long *ledbits) { LED_SCROLLL, XKB_LED_NAME_SCROLL }, }; - if (!kbd) - return; - /* TODO: Urghs, while the input device was closed we might have missed * some events that affect internal state. As xkbcommon does not provide * a way to reset the internal state, we simply recreate the state. This @@ -138,13 +181,13 @@ static void uxkb_dev_reset(struct kbd_dev *kbd, const unsigned long *ledbits) * It also has a bug that if the CTRL-Release event is skipped, then * every further release will never perform a _real_ release. Kind of * buggy so we should fix it upstream. */ - state = xkb_state_new(kbd->desc->uxkb.keymap); + state = xkb_state_new(dev->input->keymap); if (!state) { log_warning("cannot recreate xkb-state"); return; } - xkb_state_unref(kbd->uxkb.state); - kbd->uxkb.state = state; + xkb_state_unref(dev->state); + dev->state = state; for (i = 0; i < sizeof(led_names) / sizeof(*led_names); i++) { if (!input_bit_is_set(ledbits, led_names[i].led)) @@ -158,144 +201,3 @@ static void uxkb_dev_reset(struct kbd_dev *kbd, const unsigned long *ledbits) */ } } - -static int uxkb_desc_init(struct kbd_desc **out, - const char *layout, - const char *variant, - const char *options) -{ - int ret; - struct kbd_desc *desc; - struct xkb_rule_names rmlvo = { - .rules = "evdev", - .model = "evdev", - .layout = layout, - .variant = variant, - .options = options, - }; - - if (!out) - return -EINVAL; - - desc = malloc(sizeof(*desc)); - if (!desc) - return -ENOMEM; - - memset(desc, 0, sizeof(*desc)); - desc->ref = 1; - desc->ops = &uxkb_desc_ops; - - desc->uxkb.ctx = xkb_context_new(0); - if (!desc->uxkb.ctx) { - ret = -ENOMEM; - goto err_desc; - } - - 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); - - rmlvo.layout = "us"; - rmlvo.variant = ""; - rmlvo.options = ""; - - 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; - } - } - - log_debug("new keyboard description (%s, %s, %s)", - layout, variant, options); - *out = desc; - return 0; - -err_ctx: - xkb_context_unref(desc->uxkb.ctx); -err_desc: - free(desc); - return ret; -} - -static void uxkb_desc_ref(struct kbd_desc *desc) -{ - if (!desc || !desc->ref) - return; - - ++desc->ref; -} - -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->uxkb.keymap); - xkb_context_unref(desc->uxkb.ctx); - free(desc); -} - -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); -} - -int uxkb_string_to_keysym(const char *n, uint32_t *out) -{ - uint32_t keysym; - - /* TODO: fix xkbcommon upstream to be case-insensitive if case-sensitive - * match fails. */ - keysym = xkb_keysym_from_name(n); - if (!keysym) - return -EFAULT; - - *out = keysym; - return 0; -} - -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, - .string_to_keysym = uxkb_string_to_keysym, -}; - -const struct kbd_dev_ops uxkb_dev_ops = { - .ref = uxkb_dev_ref, - .unref = uxkb_dev_unref, - .reset = uxkb_dev_reset, - .process = uxkb_dev_process, -};