diff options
Diffstat (limited to 'modules/evdev/evdev.c')
-rw-r--r-- | modules/evdev/evdev.c | 348 |
1 files changed, 348 insertions, 0 deletions
diff --git a/modules/evdev/evdev.c b/modules/evdev/evdev.c new file mode 100644 index 0000000..e54ba6b --- /dev/null +++ b/modules/evdev/evdev.c @@ -0,0 +1,348 @@ +/** + * @file evdev.c Input event device UI module + * + * Copyright (C) 2010 Creytiv.com + */ +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <linux/input.h> +#include <re.h> +#include <baresip.h> +#include "print.h" + + +#define DEBUG_MODULE "evdev" +#define DEBUG_LEVEL 5 +#include <re_dbg.h> + + +/* Note: + * + * KEY_NUMERIC_xyz added in linux kernel 2.6.28 + */ + + +struct ui_st { + struct ui *ui; /* base class */ + int fd; + ui_input_h *h; + void *arg; +}; + + +static struct ui *evdev; +static char evdev_device[64] = "/dev/event0"; + + +static void evdev_close(struct ui_st *st) +{ + if (st->fd < 0) + return; + + fd_close(st->fd); + (void)close(st->fd); + st->fd = -1; +} + + +static void evdev_destructor(void *arg) +{ + struct ui_st *st = arg; + + evdev_close(st); + mem_deref(st->ui); +} + + +static int code2ascii(uint16_t modifier, uint16_t code) +{ + switch (code) { + + case KEY_0: return '0'; + case KEY_1: return '1'; + case KEY_2: return '2'; + case KEY_3: return KEY_LEFTSHIFT==modifier ? '#' : '3'; + case KEY_4: return '4'; + case KEY_5: return '5'; + case KEY_6: return '6'; + case KEY_7: return '7'; + case KEY_8: return '8'; + case KEY_9: return '9'; + case KEY_BACKSPACE: return '\b'; + case KEY_ENTER: return '\n'; + case KEY_ESC: return 0x1b; + case KEY_KPASTERISK: return '*'; +#ifdef KEY_NUMERIC_0 + case KEY_NUMERIC_0: return '0'; +#endif +#ifdef KEY_NUMERIC_1 + case KEY_NUMERIC_1: return '1'; +#endif +#ifdef KEY_NUMERIC_2 + case KEY_NUMERIC_2: return '2'; +#endif +#ifdef KEY_NUMERIC_3 + case KEY_NUMERIC_3: return '3'; +#endif +#ifdef KEY_NUMERIC_4 + case KEY_NUMERIC_4: return '4'; +#endif +#ifdef KEY_NUMERIC_5 + case KEY_NUMERIC_5: return '5'; +#endif +#ifdef KEY_NUMERIC_6 + case KEY_NUMERIC_6: return '6'; +#endif +#ifdef KEY_NUMERIC_7 + case KEY_NUMERIC_7: return '7'; +#endif +#ifdef KEY_NUMERIC_8 + case KEY_NUMERIC_8: return '8'; +#endif +#ifdef KEY_NUMERIC_9 + case KEY_NUMERIC_9: return '9'; +#endif +#ifdef KEY_NUMERIC_STAR + case KEY_NUMERIC_STAR: return '*'; +#endif +#ifdef KEY_NUMERIC_POUND + case KEY_NUMERIC_POUND: return '#'; +#endif +#ifdef KEY_KP0 + case KEY_KP0: return '0'; +#endif +#ifdef KEY_KP1 + case KEY_KP1: return '1'; +#endif +#ifdef KEY_KP2 + case KEY_KP2: return '2'; +#endif +#ifdef KEY_KP3 + case KEY_KP3: return '3'; +#endif +#ifdef KEY_KP4 + case KEY_KP4: return '4'; +#endif +#ifdef KEY_KP5 + case KEY_KP5: return '5'; +#endif +#ifdef KEY_KP6 + case KEY_KP6: return '6'; +#endif +#ifdef KEY_KP7 + case KEY_KP7: return '7'; +#endif +#ifdef KEY_KP8 + case KEY_KP8: return '8'; +#endif +#ifdef KEY_KP9 + case KEY_KP9: return '9'; +#endif +#ifdef KEY_KPDOT + case KEY_KPDOT: return 0x1b; +#endif +#ifdef KEY_KPENTER + case KEY_KPENTER: return '\n'; +#endif + default: return -1; + } +} + + +static int stderr_handler(const char *p, size_t sz, void *arg) +{ + (void)arg; + + if (write(STDERR_FILENO, p, sz) < 0) + return errno; + + return 0; +} + + +static void reportkey(struct ui_st *st, int ascii) +{ + struct re_printf pf; + + pf.vph = stderr_handler; + + if (!st->h) + return; + + st->h(ascii, &pf, st->arg); +} + + +static void evdev_fd_handler(int flags, void *arg) +{ + struct ui_st *st = arg; + struct input_event evv[64]; /* the events (up to 64 at once) */ + uint16_t modifier = 0; + size_t n; + int i; + + /* This might happen if you unplug a USB device */ + if (flags & FD_EXCEPT) { + DEBUG_WARNING("fd handler: FD_EXCEPT - device unplugged?\n"); + evdev_close(st); + return; + } + + if (FD_READ != flags) { + DEBUG_WARNING("fd_handler: unexpected flags 0x%02x\n", flags); + return; + } + + n = read(st->fd, evv, sizeof(evv)); + + if (n < (int) sizeof(struct input_event)) { + DEBUG_WARNING("event: short read (%m)\n", errno); + return; + } + + for (i = 0; i < (int) (n / sizeof(struct input_event)); i++) { + const struct input_event *ev = &evv[i]; + + DEBUG_INFO("Event: type %u, code %u, value %d\n", + ev->type, ev->code, ev->value); + + if (EV_KEY != ev->type) + continue; + + if (KEY_LEFTSHIFT == ev->code) { + modifier = KEY_LEFTSHIFT; + continue; + } + + if (1 == ev->value) { + const int ascii = code2ascii(modifier, ev->code); + if (-1 == ascii) { + DEBUG_WARNING("unhandled key code %u\n", + ev->code); + } + else + reportkey(st, ascii); + modifier = 0; + } + else if (0 == ev->value) { + reportkey(st, 0x00); + } + } +} + + +static int evdev_alloc(struct ui_st **stp, struct ui_prm *prm, + ui_input_h *uih, void *arg) +{ + const char *dev = str_isset(prm->device) ? prm->device : evdev_device; + struct ui_st *st; + int err = 0; + + if (!stp) + return EINVAL; + + st = mem_zalloc(sizeof(*st), evdev_destructor); + if (!st) + return ENOMEM; + + st->ui = mem_ref(evdev); + st->fd = open(dev, O_RDWR); + if (st->fd < 0) { + err = errno; + goto out; + } + +#if 0 + /* grab the event device to prevent it from propagating + its events to the regular keyboard driver */ + if (-1 == ioctl(st->fd, EVIOCGRAB, (void *)1)) { + DEBUG_WARNING("ioctl EVIOCGRAB on %s (%m)\n", dev, errno); + } +#endif + + print_name(st->fd); + print_events(st->fd); + print_keys(st->fd); + print_leds(st->fd); + + err = fd_listen(st->fd, FD_READ, evdev_fd_handler, st); + if (err) + goto out; + + st->h = uih; + st->arg = arg; + + out: + if (err) + mem_deref(st); + else + *stp = st; + + return err; +} + + +static int buzz(const struct ui_st *st, int value) +{ + struct input_event ev; + ssize_t n; + + ev.type = EV_SND; + ev.code = SND_BELL; + ev.value = value; + + n = write(st->fd, &ev, sizeof(ev)); + if (n < 0) { + DEBUG_WARNING("output: write fd=%d (%m)\n", st->fd, errno); + } + + return errno; +} + + +static int evdev_output(struct ui_st *st, const char *str) +{ + int err = 0; + + if (!str) + return EINVAL; + + while (*str) { + switch (*str++) { + + case '\a': + err |= buzz(st, 1); + break; + + default: + err |= buzz(st, 0); + break; + } + } + + return err; +} + + +static int module_init(void) +{ + return ui_register(&evdev, "evdev", evdev_alloc, evdev_output); +} + + +static int module_close(void) +{ + evdev = mem_deref(evdev); + return 0; +} + + +const struct mod_export DECL_EXPORTS(evdev) = { + "evdev", + "ui", + module_init, + module_close +}; |