baresip/modules/evdev/evdev.c
2023-09-18 10:09:28 +02:00

353 lines
6.2 KiB
C

/**
* @file evdev.c Input event device UI module
*
* Copyright (C) 2010 Alfred E. Heggestad
*/
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/input.h>
#include <string.h>
#include <re.h>
#include <baresip.h>
#include "print.h"
/**
* @defgroup evdev evdev
*
* User-Interface (UI) module using the Linux input subsystem.
*
* The following options can be configured:
*
\verbatim
evdev_device /dev/input/event0 # Name of the input device to use
\endverbatim
*/
struct ui_st {
re_sock_t fd;
struct re_fhs *fhs;
};
static struct ui_st *evdev;
static char evdev_device[64] = "/dev/input/event0";
static void evdev_close(struct ui_st *st)
{
if (st->fd < 0)
return;
st->fhs = fd_close(st->fhs);
(void)close(st->fd);
st->fd = RE_BAD_SOCK;
}
static void evdev_destructor(void *arg)
{
struct ui_st *st = arg;
evdev_close(st);
}
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)
{
static struct re_printf pf_stderr = {stderr_handler, NULL};
(void)st;
ui_input_key(baresip_uis(), ascii, &pf_stderr);
}
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, KEYCODE_REL);
}
}
}
static int evdev_alloc(struct ui_st **stp, const char *dev)
{
struct ui_st *st;
int err = 0;
if (!stp)
return EINVAL;
st = mem_zalloc(sizeof(*st), evdev_destructor);
if (!st)
return ENOMEM;
st->fhs = NULL;
st->fd = open(dev, O_RDWR);
if (st->fd < 0) {
err = errno;
warning("evdev: failed to open device '%s' (%m)\n", dev, err);
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->fhs, st->fd, FD_READ, evdev_fd_handler, st);
if (err)
goto out;
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;
memset(&ev, 0, sizeof(ev));
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(const char *str)
{
struct ui_st *st = evdev;
int err = 0;
if (!st || !str)
return EINVAL;
while (*str) {
switch (*str++) {
case '\a':
err |= buzz(st, 1);
break;
default:
err |= buzz(st, 0);
break;
}
}
return err;
}
static struct ui ui_evdev = {
.name = "evdev",
.outputh = evdev_output
};
static int module_init(void)
{
int err;
conf_get_str(conf_cur(), "evdev_device",
evdev_device, sizeof(evdev_device));
err = evdev_alloc(&evdev, evdev_device);
if (err)
return err;
ui_register(baresip_uis(), &ui_evdev);
return 0;
}
static int module_close(void)
{
ui_unregister(&ui_evdev);
evdev = mem_deref(evdev);
return 0;
}
const struct mod_export DECL_EXPORTS(evdev) = {
"evdev",
"ui",
module_init,
module_close
};