mirror of
https://github.com/baresip/baresip
synced 2026-08-07 00:26:06 -04:00
353 lines
6.2 KiB
C
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
|
|
};
|