uterm: input: use uxkb directly
This changes the uterm-input infrastructure to use XKB directly instead of using a modularized infrastructure. There is no need to use something else anymore. Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
This commit is contained in:
parent
fa566972e5
commit
fba4b9104b
@ -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);
|
||||
}
|
||||
|
@ -34,172 +34,49 @@
|
||||
#include <stdlib.h>
|
||||
#include <xkbcommon/xkbcommon-keysyms.h>
|
||||
#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 */
|
||||
|
@ -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,
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user