/** * @file evdev.c Input event device UI module * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include "print.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) { warning("evdev: fd handler: FD_EXCEPT - device unplugged?\n"); evdev_close(st); return; } n = read(st->fd, evv, sizeof(evv)); if (n < (int) sizeof(struct input_event)) { warning("evdev: 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]; 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) { warning("evdev: 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)) { warning("evdev: 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) { warning("evdev: 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 };