diff options
author | Peter Hutterer <peter.hutterer@who-t.net> | 2013-05-27 14:59:41 +1000 |
---|---|---|
committer | Peter Hutterer <peter.hutterer@who-t.net> | 2013-05-29 15:33:21 +1000 |
commit | a3255d3ec78c80918daac7f001dbb246a5970552 (patch) | |
tree | 5eedd5c3a187e98cb483b42df2b9ff8107a24014 | |
download | libevdev-a3255d3ec78c80918daac7f001dbb246a5970552.tar.gz |
libevdev is a library to handle evdev devices
Two main goals of this library:
- 'transparently' handle SYN_DROPPED events
- avoid errors in ioctl handling by providing a simpler interface.
Keeps a cached copy of the device for quick querying.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
-rw-r--r-- | .gitignore | 45 | ||||
-rw-r--r-- | COPYING | 19 | ||||
-rw-r--r-- | Makefile.am | 6 | ||||
-rw-r--r-- | README.md | 33 | ||||
-rw-r--r-- | configure.ac | 47 | ||||
-rw-r--r-- | libevdev.pc.in | 10 | ||||
-rw-r--r-- | libevdev/Makefile.am | 11 | ||||
-rw-r--r-- | libevdev/libevdev-int.h | 66 | ||||
-rw-r--r-- | libevdev/libevdev.c | 790 | ||||
-rw-r--r-- | libevdev/libevdev.h | 427 | ||||
-rw-r--r-- | test/.gitignore | 2 | ||||
-rw-r--r-- | test/Makefile.am | 11 | ||||
-rw-r--r-- | test/event-names.h | 704 | ||||
-rw-r--r-- | test/libevdev-events.c | 120 | ||||
-rw-r--r-- | test/libevdev-print.c | 143 |
15 files changed, 2434 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b23d36f --- /dev/null +++ b/.gitignore @@ -0,0 +1,45 @@ +aclocal.m4 +autom4te.cache/ +autoscan.log +ChangeLog +compile +config.guess +config.h +config.h.in +config.log +config-ml.in +config.py +config.status +config.status.lineno +config.sub +configure +configure.scan +depcomp +.deps/ +INSTALL +install-sh +.libs/ +libtool +libtool.m4 +ltmain.sh +lt~obsolete.m4 +ltoptions.m4 +ltsugar.m4 +ltversion.m4 +Makefile +Makefile.in +mdate-sh +missing +mkinstalldirs +*.pc +py-compile +stamp-h? +symlink-tree +texinfo.tex +ylwrap +*.la +*.lo +*.o +*~ +*.swp +.vimdir @@ -0,0 +1,19 @@ +Copyright © 2013 Red Hat, Inc. + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that copyright +notice and this permission notice appear in supporting documentation, and +that the name of the copyright holders not be used in advertising or +publicity pertaining to distribution of the software without specific, +written prior permission. The copyright holders make no representations +about the suitability of this software for any purpose. It is provided "as +is" without express or implied warranty. + +THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, +DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +OF THIS SOFTWARE. diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..83f98e5 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,6 @@ +SUBDIRS = libevdev test + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = libevdev.pc + +EXTRA_DIST = libevdev.pc.in diff --git a/README.md b/README.md new file mode 100644 index 0000000..d7ddd2b --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +libevemu - wrapper library for evdev input devices +================================================== + +libevdevdev is a wrapper library for evdev devices. it moves the common +tasks when dealing with evdev devices into a library and provides a library +interface to the callers, thus avoiding erroneous ioctls, etc. + +http://github.com/whot/libevdev + +**libevdev is currently in early stages of development. Use at your own risk** + +Device capabilities +------------------- +libevdev provides interfaces to query a device's capabilities. These +interfaces are type-safe (as opposed to the ioctl bits) and protect against +invalid codes, etc. + +SYN_DROPPED handling +-------------------- +SYN_DROPPED is sent by the kernel if userspace cannot keep up with the +reporting rate of the device. Once the kernel's buffer is full, it will +issue a SYN_DROPPED event signalling dropped event. The userspace process +must re-sync the device. + +libevdevdev semi-transparently handles SYN_DROPPED events, providing an +interface to the caller to sync up device state without having to manually +compare bitfields. Instead, libevdev sends the 'missing' events to the +caller, allowing it to use the same event processing paths as it would +otherwise. + +Changing devices +---------------- +libevdev provides interfaces to **modify** the kernel device. diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..f3607b8 --- /dev/null +++ b/configure.ac @@ -0,0 +1,47 @@ +AC_PREREQ([2.64]) + +AC_INIT([libevdev], + [0.1], + [], + [libevdev], + []) + +AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_MACRO_DIR([m4]) + +AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz]) + +# Before making a release, the LIBEVDEV_LT_VERSION string should be +# modified. +# The string is of the form C:R:A. +# - If interfaces have been changed or added, but binary compatibility has +# been preserved, change to C+1:0:A+1 +# - If binary compatibility has been broken (eg removed or changed interfaces) +# change to C+1:0:0 +# - If the interface is the same as the previous version, change to C:R+1:A +LIBEVDEV_LT_VERSION=1:0:0 +AC_SUBST(LIBEVDEV_LT_VERSION) + + +AM_SILENT_RULES([yes]) + +# Check for programs +AC_PROG_CC + +# Initialize libtool +LT_PREREQ([2.2]) +LT_INIT + +PKG_PROG_PKG_CONFIG() +PKG_CHECK_MODULES(FFI, [libffi]) + +if test "x$GCC" = "xyes"; then + GCC_CFLAGS="-Wall -Wextra -Wno-unused-parameter -g -Wstrict-prototypes -Wmissing-prototypes -fvisibility=hidden" +fi +AC_SUBST(GCC_CFLAGS) + +AC_CONFIG_FILES([Makefile + libevdev/Makefile + test/Makefile + libevdev.pc]) +AC_OUTPUT diff --git a/libevdev.pc.in b/libevdev.pc.in new file mode 100644 index 0000000..96d1a5d --- /dev/null +++ b/libevdev.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: evdev_read +Description: Handler library for evdev events +Version: @EVDEV_READ_VERSION@ +Cflags: -I${includedir}/libevdev-1.0/ +Libs: -L${libdir} -levdev diff --git a/libevdev/Makefile.am b/libevdev/Makefile.am new file mode 100644 index 0000000..e03be9b --- /dev/null +++ b/libevdev/Makefile.am @@ -0,0 +1,11 @@ +lib_LTLIBRARIES=libevdev.la + +libevdev_la_SOURCES = \ + libevdev.h \ + libevdev-int.h \ + libevdev.c + +libevdev_la_LDFLAGS = -version-info $(LIBEVDEV_LT_VERSION) -export-symbols-regex '^libevdev_' + +libevdevincludedir = $(includedir)/libevdev-1.0/libevdev +libevdevinclude_HEADERS = libevdev.h diff --git a/libevdev/libevdev-int.h b/libevdev/libevdev-int.h new file mode 100644 index 0000000..c86a656 --- /dev/null +++ b/libevdev/libevdev-int.h @@ -0,0 +1,66 @@ +/* + * Copyright © 2013 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef libevdev_INT_H +#define libevdev_INT_H + +#include <config.h> +#include "libevdev.h" + +#define LONG_BITS (sizeof(long) * 8) +#define NLONGS(x) (((x) + LONG_BITS - 1) / LONG_BITS) +#define ARRAY_LENGTH(a) (sizeof(a) / (sizeof((a)[0]))) +#define MAX_NAME 256 +#define MAX_SLOTS 32 +#define ABS_MT_MIN ABS_MT_SLOT +#define ABS_MT_MAX ABS_MT_TOOL_Y +#define ABS_MT_CNT (ABS_MT_MAX - ABS_MT_MIN + 1) + +struct libevdev { + int fd; + libevdev_callback_proc callback; + libevdev_callback_proc sync_callback; + void *userdata; + + char name[MAX_NAME]; + struct input_id ids; + unsigned long bits[NLONGS(EV_CNT)]; + unsigned long props[NLONGS(INPUT_PROP_CNT)]; + unsigned long key_bits[NLONGS(KEY_CNT)]; + unsigned long rel_bits[NLONGS(REL_CNT)]; + unsigned long abs_bits[NLONGS(ABS_CNT)]; + unsigned long led_bits[NLONGS(LED_CNT)]; + unsigned long key_values[NLONGS(KEY_CNT)]; + struct input_absinfo abs_info[ABS_CNT]; + unsigned int mt_slot_vals[MAX_SLOTS][ABS_MT_CNT]; + int num_slots; /**< valid slots in mt_slot_vals */ + + int need_sync; + int grabbed; + + struct input_event *queue; + size_t queue_size; /**< size of queue in elements */ + size_t queue_next; /**< next event index */ +}; + +#endif + diff --git a/libevdev/libevdev.c b/libevdev/libevdev.c new file mode 100644 index 0000000..70ca5de --- /dev/null +++ b/libevdev/libevdev.c @@ -0,0 +1,790 @@ +/* + * Copyright © 2013 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include <config.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "libevdev.h" +#include "libevdev-int.h" + +#define MAXEVENTS 64 + +static inline int +bit_is_set(const unsigned long *array, int bit) +{ + return !!(array[bit / LONG_BITS] & (1LL << (bit % LONG_BITS))); +} + +static inline void +set_bit(unsigned long *array, int bit) +{ + array[bit / LONG_BITS] |= (1LL << (bit % LONG_BITS)); +} + +static inline void +clear_bit(unsigned long *array, int bit) +{ + array[bit / LONG_BITS] &= ~(1LL << (bit % LONG_BITS)); +} + +static inline void +set_bit_state(unsigned long *array, int bit, int state) +{ + if (state) + set_bit(array, bit); + else + clear_bit(array, bit); +} + +static unsigned int +type_to_mask_const(const struct libevdev *dev, unsigned int type, const unsigned long **mask) +{ + unsigned int max; + + switch(type) { + case EV_ABS: + *mask = dev->abs_bits; + max = ABS_MAX; + break; + case EV_REL: + *mask = dev->rel_bits; + max = REL_MAX; + break; + case EV_KEY: + *mask = dev->key_bits; + max = KEY_MAX; + break; + case EV_LED: + *mask = dev->led_bits; + max = LED_MAX; + break; + default: + return 0; + } + + return max; +} + +static unsigned int +type_to_mask(struct libevdev *dev, unsigned int type, unsigned long **mask) +{ + unsigned int max; + + switch(type) { + case EV_ABS: + *mask = dev->abs_bits; + max = ABS_MAX; + break; + case EV_REL: + *mask = dev->rel_bits; + max = REL_MAX; + break; + case EV_KEY: + *mask = dev->key_bits; + max = KEY_MAX; + break; + case EV_LED: + *mask = dev->led_bits; + max = LED_MAX; + break; + default: + return 0; + } + + return max; +} + +static int +init_event_queue(struct libevdev *dev) +{ + /* FIXME: count the number of axes, keys, etc. to get a better idea at how many events per + EV_SYN we could possibly get. Then multiply that by the actual buffer size we care about */ + + const int QUEUE_SIZE = 256; + + dev->queue = calloc(QUEUE_SIZE, sizeof(struct input_event)); + if (!dev->queue) + return -ENOSPC; + + dev->queue_size = QUEUE_SIZE; + dev->queue_next = 0; + + return 0; +} + +struct libevdev* +libevdev_new(int fd) +{ + struct libevdev *dev; + + dev = calloc(1, sizeof(*dev)); + dev->num_slots = -1; + + if (fd >= 0) + libevdev_set_fd(dev, fd); + dev->fd = fd; + + return dev; +} + +void +libevdev_free(struct libevdev *dev) +{ + free(dev); +} + +int +libevdev_change_fd(struct libevdev *dev, int fd) +{ + if (dev->fd == -1) + return -1; + dev->fd = fd; + return 0; +} + +int +libevdev_set_fd(struct libevdev* dev, int fd) +{ + int rc; + int i; + + if (dev->fd == -1) { + libevdev_callback_proc cb, scb; + void *userdata; + + /* these may be set before set_fd */ + cb = dev->callback; + scb = dev->sync_callback; + userdata = dev->userdata; + + memset(dev, 0, sizeof(*dev)); + + dev->fd = -1; + dev->callback = cb; + dev->sync_callback = scb; + dev->userdata = userdata; + } + + rc = ioctl(fd, EVIOCGBIT(0, sizeof(dev->bits)), dev->bits); + if (rc < 0) + goto out; + + rc = ioctl(fd, EVIOCGNAME(sizeof(dev->name) - 1), dev->name); + if (rc < 0) + goto out; + + rc = ioctl(fd, EVIOCGID, &dev->ids); + if (rc < 0) + goto out; + + rc = ioctl(fd, EVIOCGPROP(sizeof(dev->props)), dev->props); + if (rc < 0) + goto out; + + rc = ioctl(fd, EVIOCGBIT(EV_REL, sizeof(dev->rel_bits)), dev->rel_bits); + if (rc < 0) + goto out; + + rc = ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(dev->abs_bits)), dev->abs_bits); + if (rc < 0) + goto out; + + rc = ioctl(fd, EVIOCGBIT(EV_LED, sizeof(dev->led_bits)), dev->led_bits); + if (rc < 0) + goto out; + + rc = ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(dev->key_bits)), dev->key_bits); + if (rc < 0) + goto out; + + for (i = ABS_X; i <= ABS_MAX; i++) { + if (bit_is_set(dev->abs_bits, i)) { + struct input_absinfo abs_info; + rc = ioctl(fd, EVIOCGABS(i), &dev->abs_info[i]); + if (rc < 0) + goto out; + + if (i == ABS_MT_SLOT) + dev->num_slots = abs_info.maximum + 1; /* FIXME: non-zero min? */ + + } + } + + rc = init_event_queue(dev); + if (rc < 0) + return -rc; + + /* not copying key state because we won't know when we'll start to + * use this fd and key's are likely to change state by then. + * Same with the valuators, really, but they may not change. + */ + + dev->fd = fd; + +out: + return rc ? -errno : 0; +} + +int +libevdev_get_fd(const struct libevdev* dev) +{ + return dev->fd; +} + +int +libevdev_set_callbacks(struct libevdev *dev, + libevdev_callback_proc callback, + libevdev_callback_proc sync_callback, + void *userdata) +{ + dev->callback = callback; + dev->sync_callback = sync_callback; + dev->userdata = userdata; + + return 0; +} + +static inline void +init_event(struct input_event *ev, int type, int code, int value) +{ + ev->time.tv_sec = 0; /* FIXME: blah! */ + ev->time.tv_usec = 0; /* FIXME: blah! */ + ev->type = type; + ev->code = code; + ev->value = value; +} + +static int +sync_key_state(struct libevdev *dev) +{ + int rc; + int i; + unsigned long keystate[NLONGS(KEY_MAX)]; + struct input_event ev; + + rc = ioctl(dev->fd, EVIOCGKEY(sizeof(keystate)), keystate); + if (rc < 0) + goto out; + + for (i = 0; i < KEY_MAX; i++) { + int old, new; + old = bit_is_set(dev->key_values, i); + new = bit_is_set(keystate, i); + if (old ^ new) { + init_event(&ev, EV_KEY, i, new ? 1 : 0); + dev->sync_callback(dev, &ev, dev->userdata); + } + set_bit_state(dev->key_values, i, new); + } + + rc = 0; +out: + return rc ? -errno : 0; +} + +static int +sync_abs_state(struct libevdev *dev) +{ + int rc; + int i; + struct input_event ev; + + for (i = ABS_X; i <= ABS_MAX; i++) { + if (i >= ABS_MT_MIN && i <= ABS_MT_MAX) + continue; + + if (bit_is_set(dev->abs_bits, i)) { + struct input_absinfo abs_info; + rc = ioctl(dev->fd, EVIOCGABS(i), &abs_info); + if (rc < 0) + goto out; + + if (dev->abs_info[i].value != abs_info.value) { + init_event(&ev, EV_ABS, i, abs_info.value); + dev->sync_callback(dev, &ev, dev->userdata); + dev->abs_info[i].value = abs_info.value; + } + } + } + + rc = 0; +out: + return rc ? -errno : 0; +} + +static int +sync_mt_state(struct libevdev *dev) +{ + int rc; + int i; + struct mt_state { + int code; + int val[MAX_SLOTS]; + } mt_state[ABS_MT_CNT]; + struct input_event ev; + + for (i = ABS_MT_MIN; i < ABS_MT_MAX; i++) { + if (i == ABS_MT_SLOT) + continue; + + mt_state[i].code = i; + rc = ioctl(dev->fd, EVIOCGMTSLOTS(sizeof(struct mt_state)), &mt_state[i]); + if (rc < 0) + goto out; + } + + for (i = 0; i < dev->num_slots; i++) { + int j; + init_event(&ev, EV_ABS, ABS_MT_SLOT, i); + dev->sync_callback(dev, &ev, dev->userdata); + for (j = ABS_MT_MIN; j < ABS_MT_MAX; j++) { + if (dev->mt_slot_vals[i][j] != mt_state[j].val[i]) { + init_event(&ev, EV_ABS, j, mt_state[j].val[i]); + dev->sync_callback(dev, &ev, dev->userdata); + dev->mt_slot_vals[i][j] = mt_state[j].val[i]; + } + } + } + + rc = 0; +out: + return rc ? -errno : 0; +} + +static int +sync_state(struct libevdev *dev, unsigned int flags) +{ + int rc = 0; + struct input_event ev; + + if (libevdev_has_event_type(dev, EV_KEY)) + rc = sync_key_state(dev); /* FIXME: handle ER_SINGLE */ + if (rc == 0 && libevdev_has_event_type(dev, EV_ABS)) + rc = sync_abs_state(dev); + if (rc == 0 && libevdev_has_event_code(dev, EV_ABS, ABS_MT_SLOT)) + rc = sync_mt_state(dev); + + init_event(&ev, EV_SYN, SYN_REPORT, 0); + dev->sync_callback(dev, &ev, dev->userdata); + + dev->need_sync = 0; + + return rc; +} + +static int +update_key_state(struct libevdev *dev, const struct input_event *e) +{ + if (!libevdev_has_event_type(dev, EV_KEY)) + return 1; + + if (e->code > KEY_MAX) + return 1; + + if (e->value == 0) + clear_bit(dev->key_values, e->code); + else + set_bit(dev->key_values, e->code); + + return 0; +} + +static int +update_abs_state(struct libevdev *dev, const struct input_event *e) +{ + if (!libevdev_has_event_type(dev, EV_ABS)) + return 1; + + if (e->code > ABS_MAX) + return 1; + + dev->abs_info[e->code].value = e->value; + + return 0; +} + +static int +update_state(struct libevdev *dev, const struct input_event *e) +{ + int rc = 0; + + switch(e->type) { + case EV_SYN: + case EV_REL: + break; + case EV_KEY: + rc = update_key_state(dev, e); + break; + case EV_ABS: + rc = update_abs_state(dev, e); + break; + } + + return rc; +} + +static int +read_more_events(struct libevdev *dev) +{ + int free_elem; + int len; + + free_elem = dev->queue_size - dev->queue_next - 1; + if (free_elem <= 0) + return 0; + + len = read(dev->fd, &dev->queue[dev->queue_next], free_elem * sizeof(struct input_event)); + if (len < 0) { + if (errno != EAGAIN || dev->queue_next == 0) + return -errno; + } else if (len > 0 && len % sizeof(struct input_event) != 0) + return -EINVAL; + + dev->queue_next += len/sizeof(struct input_event); + + return 0; +} + +int libevdev_read_events(struct libevdev *dev, + unsigned int flags) +{ + int nevents, processed; + int i; + int rc = 0; + + if (dev->fd < 0) + return -ENODEV; + + if (flags & ER_SYNC) { + if (!dev->need_sync) + return 0; + return sync_state(dev, flags); + } + + /* Always read in some more events. Best case this smoothes over a potential SYN_DROPPED, + worst case we don't read fast enough and end up with SYN_DROPPED anyway */ + rc = read_more_events(dev); + if (rc < 0) + goto out; + + nevents = dev->queue_next; + for (processed = 0; processed < nevents; processed++) { + struct input_event *e = &dev->queue[processed]; + + update_state(dev, e); + + if (e->type == EV_SYN && e->code == SYN_DROPPED) { + dev->need_sync = 0; + rc = 1; + break; + } else if (libevdev_has_event_code(dev, e->type, e->code)) { + if (dev->callback(dev, e, dev->userdata) != 0) { + rc = -ECANCELED; + break; + } + } + } + + for (i = 0; i < nevents - processed; i++) { + dev->queue[i] = dev->queue[i + processed]; + } + + dev->queue_next -= processed; +out: + return rc; +} + +const char * +libevdev_get_name(const struct libevdev *dev) +{ + return dev->name; +} + +int libevdev_get_pid(const struct libevdev *dev) +{ + return dev->ids.product; +} + +int libevdev_get_vid(const struct libevdev *dev) +{ + return dev->ids.vendor; +} + +int libevdev_get_bustype(const struct libevdev *dev) +{ + return dev->ids.bustype; +} + +int +libevdev_has_property(const struct libevdev *dev, unsigned int prop) +{ + return (prop <= INPUT_PROP_MAX) && bit_is_set(dev->props, prop); +} + +int +libevdev_has_event_type(const struct libevdev *dev, unsigned int type) +{ + return (type <= EV_MAX) && bit_is_set(dev->bits, type); +} + +int +libevdev_has_event_code(const struct libevdev *dev, unsigned int type, unsigned int code) +{ + const unsigned long *mask; + unsigned int max; + + if (!libevdev_has_event_type(dev, type)) + return 0; + + if (type == EV_SYN) + return 1; + + max = type_to_mask_const(dev, type, &mask); + + if (code > max) + return 0; + + return bit_is_set(mask, code); +} + +int +libevdev_get_event_value(const struct libevdev *dev, unsigned int type, unsigned int code) +{ + int value; + + if (!libevdev_has_event_type(dev, type) || !libevdev_has_event_code(dev, type, code)) + return 0; + + switch (type) { + case EV_ABS: value = dev->abs_info[code].value; break; + case EV_KEY: value = bit_is_set(dev->key_values, code); break; + default: + value = 0; + break; + } + + return value; +} + +int +libevdev_fetch_event_value(const struct libevdev *dev, unsigned int type, unsigned int code, int *value) +{ + if (libevdev_has_event_type(dev, type) && + libevdev_has_event_code(dev, type, code)) { + *value = libevdev_get_event_value(dev, type, code); + return 1; + } else + return 0; +} + +int +libevdev_get_slot_value(const struct libevdev *dev, unsigned int slot, unsigned int code) +{ + if (!libevdev_has_event_type(dev, EV_ABS) || !libevdev_has_event_code(dev, EV_ABS, code)) + return 0; + + if (slot >= dev->num_slots || slot >= MAX_SLOTS) + return 0; + + if (code > ABS_MT_MAX || code < ABS_MT_MIN) + return 0; + + return dev->mt_slot_vals[slot][code - ABS_MT_MIN]; +} + +int +libevdev_fetch_slot_value(const struct libevdev *dev, unsigned int slot, unsigned int code, int *value) +{ + if (libevdev_has_event_type(dev, EV_ABS) && + libevdev_has_event_code(dev, EV_ABS, code) && + slot < dev->num_slots && slot < MAX_SLOTS) { + *value = libevdev_get_slot_value(dev, slot, code); + return 1; + } else + return 0; +} + +int +libevdev_get_num_slots(const struct libevdev *dev) +{ + return dev->num_slots; +} + +const struct input_absinfo* +libevdev_get_abs_info(const struct libevdev *dev, unsigned int code) +{ + if (!libevdev_has_event_type(dev, EV_ABS) || + !libevdev_has_event_code(dev, EV_ABS, code)) + return NULL; + + return &dev->abs_info[code]; +} + +int +libevdev_get_abs_min(const struct libevdev *dev, unsigned int code) +{ + const struct input_absinfo *absinfo = libevdev_get_abs_info(dev, code); + + return absinfo ? absinfo->minimum : 0; +} + +int +libevdev_get_abs_max(const struct libevdev *dev, unsigned int code) +{ + const struct input_absinfo *absinfo = libevdev_get_abs_info(dev, code); + + return absinfo ? absinfo->maximum : 0; +} + +int +libevdev_get_abs_fuzz(const struct libevdev *dev, unsigned int code) +{ + const struct input_absinfo *absinfo = libevdev_get_abs_info(dev, code); + + return absinfo ? absinfo->fuzz : 0; +} + +int +libevdev_get_abs_flat(const struct libevdev *dev, unsigned int code) +{ + const struct input_absinfo *absinfo = libevdev_get_abs_info(dev, code); + + return absinfo ? absinfo->flat : 0; +} + +int +libevdev_get_abs_resolution(const struct libevdev *dev, unsigned int code) +{ + const struct input_absinfo *absinfo = libevdev_get_abs_info(dev, code); + + return absinfo ? absinfo->resolution : 0; +} + +int +libevdev_enable_event_type(struct libevdev *dev, unsigned int type) +{ + if (type > EV_MAX) + return 1; + + set_bit(dev->bits, type); + + /* FIXME: pass through to kernel */ + + return 0; +} + +int +libevdev_disable_event_type(struct libevdev *dev, unsigned int type) +{ + if (type > EV_MAX) + return 1; + + clear_bit(dev->bits, type); + + /* FIXME: pass through to kernel */ + + return 0; +} + +int +libevdev_enable_event_code(struct libevdev *dev, unsigned int type, + unsigned int code, const void *data) +{ + unsigned int max; + unsigned long *mask; + + if (libevdev_enable_event_type(dev, type)) + return 1; + + max = type_to_mask(dev, type, &mask); + + if (code > max) + return 1; + + set_bit(mask, code); + + if (type == EV_ABS) { + const struct input_absinfo *abs = data; + dev->abs_info[code] = *abs; + } + + /* FIXME: pass through to kernel */ + + return 0; +} + +int +libevdev_disable_event_code(struct libevdev *dev, unsigned int type, unsigned int code) +{ + unsigned int max; + unsigned long *mask; + + if (type > EV_MAX) + return 1; + + max = type_to_mask(dev, type, &mask); + + if (code > max) + return 1; + + clear_bit(mask, code); + + /* FIXME: pass through to kernel */ + + return 0; +} + +int +libevdev_kernel_set_abs_value(struct libevdev *dev, unsigned int code, const struct input_absinfo *abs) +{ + int rc; + + if (code > ABS_MAX) + return -EINVAL; + + rc = ioctl(dev->fd, EVIOCSABS(code), *abs); + if (rc < 0) + rc = -errno; + else + rc = libevdev_enable_event_code(dev, EV_ABS, code, abs); + + return rc; +} + +int +libevdev_grab(struct libevdev *dev, int grab) +{ + int rc = 0; + + if (grab && !dev->grabbed) + rc = ioctl(dev->fd, EVIOCGRAB, (void *)1); + else if (!grab && dev->grabbed) + rc = ioctl(dev->fd, EVIOCGRAB, (void *)0); + + if (rc == 0) + dev->grabbed = grab; + + return rc < 0 ? -errno : 0; +} diff --git a/libevdev/libevdev.h b/libevdev/libevdev.h new file mode 100644 index 0000000..a1787ee --- /dev/null +++ b/libevdev/libevdev.h @@ -0,0 +1,427 @@ +/* + * Copyright © 2013 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef libevdev_H +#define libevdev_H + +#include <config.h> +#include <linux/input.h> + +struct libevdev; + +enum EvdevReadFlags { + ER_SINGLE = 1, /**< Read until including the first EV_SYN or EAGAIN */ + ER_SYNC = 2, /**< Process data in sync mode */ + ER_ALL = 4, /**< Read until EAGAIN */ +}; + + +/** + * Initialize a new libevdev struct. + * + * @param fd If fd >= 0, the device is initialised for the fd. Otherwise, a + * caller must call evdev_set_fd() before attempting to read events. + * + * @see libevdev_set_fd + */ +struct libevdev* libevdev_new(int fd); + +/** + * Clean up and free the libevdev struct. + * + * @note This function may be called before libevdev_set_fd. + */ +void libevdev_free(struct libevdev *dev); + +/** + * Grab or ungrab the device through a kernel EVIOCGRAB. This prevents other + * clients (including kernel-internal ones such as rfkill) from receiving + * events from this device. + * + * This is generally a bad idea. Don't do this. + * + * Grabbing an already grabbed device, or ungrabbing an ungrabbed device is + * a noop and always succeeds. + * + * @param grab If true, grab the device. Otherwise ungrab the device. + * + * @return 0 if the device was successfull grabbed or ungrabbed, or a + * negative errno in case of failure. + */ +int libevdev_grab(struct libevdev *dev, int grab); + +/** + * Set the fd for this struct and initialize internal data. + * The fd must be open for reading and ioctl. + * + * This function may only be called once per device. If you need to re-read + * a device, use libevdev_free and libevdev_new. If you need to change the + * fd, use libevdev_change_fd. + * + * Unless otherwise specified, libevdev function behavior is undefined until + * a successfull call to libevdev_set_fd. + * + * @param fd The file descriptor for the device + * + * @return 0 on success, or a negative error code on failure + * + * @see libevdev_change_fd + * @see libevdev_new + * @see libevdev_free + */ +int libevdev_set_fd(struct libevdev* dev, int fd); + +/** + * Change the fd for this device, without re-reading the actual device. + * + * It is an error to call this function before calling libevdev_set_fd. + * + * @param fd The new fd + * + * @return 0 on success, or -1 on failure. + * + * @see libevdev_set_fd + */ +int libevdev_change_fd(struct libevdev* dev, int fd); + +/** + * + * @return The previously set fd, or -1 if none had been set previously. + * @note This function may be called before libevdev_set_fd. + */ +int libevdev_get_fd(const struct libevdev* dev); + +/** + * Event callback used to report events back to the client. + * + * If this function returns -1, event processing is interrupted and returned + * to the caller of libevdev_read_events. A future call to + * libevdev_read_events will continue with the next event in the queue. + * + * @param dev The device this callback was invoked on + * @param ev The event read from the kernel + * @param userdata Previously assigned caller-specific data + * + * @return 0 on success, or -1 on failure. + * + * @see libevdev_read_events + */ +typedef int (*libevdev_callback_proc)(struct libevdev *dev, struct input_event *ev, void *userdata); + +/** + * Set the callbacks to be used when reading events off the fd. + * Two callbacks are used, one for reporting events during normal operation, one for + * reporting events during an event sync. If either is NULL, no events are + * reported for that mode. + * + * @param callback Callback used for regular events when read off the fd + * @param sync_callback Callback used for events while re-syncing after a + * SYN_DROPPED event. + * @param userdata Caller-specific data, passed back through the callbacks. + * + * @return zero on success, -1 on failure + * + * @see libevdev_read_events + * @note This function may be called before libevdev_set_fd. + */ +int libevdev_set_callbacks(struct libevdev *dev, + libevdev_callback_proc callback, + libevdev_callback_proc sync_callback, + void *userdata); + +/** + * Read events off the fd and call the matching callback. + * + * Depending on the flags, the behaviour changes as follows: + * - ER_SINGLE: read a single event off the fd (i.e. up to the next EV_SYN). + * This should only be used if the caller is time-sensitive and event + * processing of multiple events may prevent other computation. + * - ER_ALL: read all the events off the fd until a read would block or + * an SYN_DROPPED event is read. + * - ER_SYNC: switch to sync mode and report all events that are required to + * bring the device state back in sync with the kernel device state. + * + * @param fd The file descriptor previously set, open in non-blocking mode. If + * the file descriptor differs from the previous one, -EBADF is returned. + * @param flags The set of flags to decide how to handle events. + * + * @return On failure, a negative errno is returned. + * @retval 0 One or more events where read of the fd + * @retval -EAGAIN No events are currently on the fd + * @retval -ECANCELLED The callback returned non-zero + * @retval 1 A SYN_DROPPED event was received + * + * @see libevdev_set_callbacks + * + * @note This function is signal-safe. + */ +int libevdev_read_events(struct libevdev *dev, unsigned int flags); + +/** + * @return The device name as read off the kernel device + * + * @note This function is signal-safe. + */ +const char* libevdev_get_name(const struct libevdev *dev); + +/** + * @return The device's product ID + * + * @note This function is signal-safe. + */ +int libevdev_get_pid(const struct libevdev *dev); +/** + * @return The device's vendor ID + * + * @note This function is signal-safe. + */ +int libevdev_get_vid(const struct libevdev *dev); + +/** + * @return The device's bus type + * + * @note This function is signal-safe. + */ +int libevdev_get_bustype(const struct libevdev *dev); + +/** + * @return 1 if the device supports this event type, or 0 otherwise. + * + * @note This function is signal-safe + */ +int libevdev_has_property(const struct libevdev *dev, unsigned int prop); + +/** + * @return 1 if the device supports this event type, or 0 otherwise. + * + * @note This function is signal-safe. + */ +int libevdev_has_event_type(const struct libevdev *dev, unsigned int type); + +/** + * @return 1 if the device supports this event type, or 0 otherwise. + * + * @note This function is signal-safe. + */ +int libevdev_has_event_code(const struct libevdev *dev, unsigned int type, unsigned int code); + +/** + * @return axis minimum for the given axis or 0 if the axis is invalid + */ +int libevdev_get_abs_min(const struct libevdev *dev, unsigned int code); +/** + * @return axis maximum for the given axis or 0 if the axis is invalid + */ +int libevdev_get_abs_max(const struct libevdev *dev, unsigned int code); +/** + * @return axis fuzz for the given axis or 0 if the axis is invalid + */ +int libevdev_get_abs_fuzz(const struct libevdev *dev, unsigned int code); +/** + * @return axis flat for the given axis or 0 if the axis is invalid + */ +int libevdev_get_abs_flat(const struct libevdev *dev, unsigned int code); +/** + * @return axis resolution for the given axis or 0 if the axis is invalid + */ +int libevdev_get_abs_resolution(const struct libevdev *dev, unsigned int code); + +/** + * @return The input_absinfo for the given code, or NULL if the device does + * not support this event code. + */ +const struct input_absinfo* libevdev_get_abs_info(const struct libevdev *dev, unsigned int code); + +/** + * Behaviour of this function is undefined if the device does not provide + * the event. + * + * @return The current value of the event. + * + * @note This function is signal-safe. + * @note The value for ABS_MT_ events is undefined, use + * libevdev_get_slot_value instead + * + * @see libevdev_get_slot_value + */ +int libevdev_get_event_value(const struct libevdev *dev, unsigned int type, unsigned int code); + +/** + * Fetch the current value of the event type. This is a shortcut for + * + * <pre> + * if (libevdev_has_event_type(dev, t) && libevdev_has_event_code(dev, t, c)) + * val = libevdev_get_event_value(dev, t, c); + * </pre> + * + * @return non-zero if the device supports this event code, or zero + * otherwise. On return of zero, value is unmodified. + * + * @note This function is signal-safe. + * @note The value for ABS_MT_ events is undefined, use + * libevdev_fetch_slot_value instead + * + * @see libevdev_fetch_slot_value + */ +int libevdev_fetch_event_value(const struct libevdev *dev, unsigned int type, unsigned int code, int *value); + +/** + * Return the current value of the code for the given slot. + * + * The return value is undefined for a slot exceeding the available slots on + * the device, or for a device that does not have slots. + * + * @note This function is signal-safe. + * @note The value for events other than ABS_MT_ is undefined, use + * libevdev_fetch_value instead + * + * @see libevdev_get_value + */ +int libevdev_get_slot_value(const struct libevdev *dev, unsigned int slot, unsigned int code); + +/** + * Fetch the current value of the code for the given slot. This is a shortcut for + * + * <pre> + * if (libevdev_has_event_type(dev, EV_ABS) && + * libevdev_has_event_code(dev, EV_ABS, c) && + * slot < device->number_of_slots) + * val = libevdev_get_slot_value(dev, slot, c); + * </pre> + * + * @return non-zero if the device supports this event code, or zero + * otherwise. On return of zero, value is unmodified. + * + * @note This function is signal-safe. + * @note The value for ABS_MT_ events is undefined, use + * libevdev_fetch_slot_value instead + * + * @see libevdev_fetch_slot_value + */ +int libevdev_fetch_slot_value(const struct libevdev *dev, unsigned int slot, unsigned int code, int *value); + +/** + * Get the number of slots supported by this device. + * + * Note that the slot offset may be non-zero, use libevdev_get_abs_min() or + * libevdev_get_abs_info() to get the minimum slot number. + * + * @return The number of slots supported, or -1 + */ +int libevdev_get_num_slots(const struct libevdev *dev); + +/** + * Forcibly enable an event type on this device, even if the underlying + * device does not support it. While this cannot make the device actually + * report such events, it will now return true for libevdev_has_event_type. + * + * This is a local modification only affecting only this process and only + * this device. + * + * @param type The event type to enable (EV_ABS, EV_KEY, ...) + * + * @return 0 on success or -1 otherwise + * + * @see libevdev_has_event_type + */ +int libevdev_enable_event_type(struct libevdev *dev, unsigned int type); + +/** + * Forcibly disable an event type on this device, even if the underlying + * device provides it, effectively muting all keys or axes. libevdev will + * filter any events matching this type and none will reach the caller. + * libevdev_has_event_type will return false for this type. + * + * In most cases, a caller likely only wants to disable a single code, not + * the whole type. Use libevdev_disable_event_code for that. + * + * This is a local modification only affecting only this process and only + * this device. + * + * @param type The event type to enable (EV_ABS, EV_KEY, ...) + * + * @return 0 on success or -1 otherwise + * + * @see libevdev_has_event_type + * @see libevdev_disable_event_type + */ +int libevdev_disable_event_type(struct libevdev *dev, unsigned int type); + +/** + * Forcibly enable an event type on this device, even if the underlying + * device does not support it. While this cannot make the device actually + * report such events, it will now return true for libevdev_has_event_code. + * + * The last argument depends on the type and code: + * - If type is EV_ABS, the vararg must be a pointer to a struct input_absinfo + * containing the data for this axis. + * - For all other types, the argument is ignored. + * + * This function calls libevdev_enable_event_type if necessary. + * + * This is a local modification only affecting only this process and only + * this device. + * + * @param type The event type to enable (EV_ABS, EV_KEY, ...) + * @param code The event code to enable (ABS_X, REL_X, etc.) + * @param data Axis/key data, depending on type and code + * + * @return 0 on success or -1 otherwise + * + * @see libevdev_enable_event_type + */ +int libevdev_enable_event_code(struct libevdev *dev, unsigned int type, unsigned int code, const void *data); + +/** + * Forcibly disable an event code on this device, even if the underlying + * device provides it, effectively muting this key or axis. libevdev will + * filter any events matching this type and code and none will reach the + * caller. + * libevdev_has_event_code will return false for this code combination. + * + * Disabling all event codes for a given type will not disable the event + * type. Use libevdev_disable_event_type for that. + * + * This is a local modification only affecting only this process and only + * this device. + * + * @param type The event type to enable (EV_ABS, EV_KEY, ...) + * @param code The event code to enable (ABS_X, REL_X, etc.) + * + * @return 0 on success or -1 otherwise + * + * @see libevdev_has_event_code + * @see libevdev_disable_event_type + */ +int libevdev_disable_event_code(struct libevdev *dev, unsigned int type, unsigned int code); + +/** + * Set the device's EV_ABS/<code> axis to the value defined in the abs + * parameter. This will be written to the kernel. + * + * @return zero on success, or a negative errno on failure + * + * @see libevdev_enable_event_code + */ +int libevdev_kernel_set_abs_value(struct libevdev *dev, unsigned int code, const struct input_absinfo *abs); + +#endif /* libevdev_H */ diff --git a/test/.gitignore b/test/.gitignore new file mode 100644 index 0000000..daf063e --- /dev/null +++ b/test/.gitignore @@ -0,0 +1,2 @@ +libevdev-print +libevdev-events diff --git a/test/Makefile.am b/test/Makefile.am new file mode 100644 index 0000000..173e4bb --- /dev/null +++ b/test/Makefile.am @@ -0,0 +1,11 @@ +noinst_PROGRAMS = libevdev-print libevdev-events + +AM_CPPFLAGS = -I$(top_srcdir)/libevdev +libevdev_ldadd = $(top_builddir)/libevdev/libevdev.la + +libevdev_print_SOURCES = libevdev-print.c event-names.h +libevdev_print_LDADD = $(libevdev_ldadd) + +libevdev_events_SOURCES = libevdev-events.c event-names.h +libevdev_events_LDADD = $(libevdev_ldadd) + diff --git a/test/event-names.h b/test/event-names.h new file mode 100644 index 0000000..c687ba8 --- /dev/null +++ b/test/event-names.h @@ -0,0 +1,704 @@ +/* THIS FILE IS GENERATED, DO NOT EDIT */ +/* to regenerate this file, get evemu from + git://git.freedesktop.org/git/evemu + */ + +#ifndef EVENT_NAMES_H +#define EVENT_NAMES_H + +#define SYN_MAX 3 /* linux/input.h doesn't define that */ + +static const char * const ev_map[EV_MAX + 1] = { + [0 ... EV_MAX] = NULL, + [EV_SYN] = "EV_SYN", + [EV_KEY] = "EV_KEY", + [EV_REL] = "EV_REL", + [EV_ABS] = "EV_ABS", + [EV_MSC] = "EV_MSC", + [EV_SW] = "EV_SW", + [EV_LED] = "EV_LED", + [EV_SND] = "EV_SND", + [EV_REP] = "EV_REP", + [EV_FF] = "EV_FF", + [EV_PWR] = "EV_PWR", + [EV_FF_STATUS] = "EV_FF_STATUS", + [EV_MAX] = "EV_MAX", +}; + +static const char * const rel_map[REL_MAX + 1] = { + [0 ... REL_MAX] = NULL, + [REL_X] = "REL_X", + [REL_Y] = "REL_Y", + [REL_Z] = "REL_Z", + [REL_RX] = "REL_RX", + [REL_RY] = "REL_RY", + [REL_RZ] = "REL_RZ", + [REL_HWHEEL] = "REL_HWHEEL", + [REL_DIAL] = "REL_DIAL", + [REL_WHEEL] = "REL_WHEEL", + [REL_MISC] = "REL_MISC", + [REL_MAX] = "REL_MAX", +}; + +static const char * const abs_map[ABS_MAX + 1] = { + [0 ... ABS_MAX] = NULL, + [ABS_X] = "ABS_X", + [ABS_Y] = "ABS_Y", + [ABS_Z] = "ABS_Z", + [ABS_RX] = "ABS_RX", + [ABS_RY] = "ABS_RY", + [ABS_RZ] = "ABS_RZ", + [ABS_THROTTLE] = "ABS_THROTTLE", + [ABS_RUDDER] = "ABS_RUDDER", + [ABS_WHEEL] = "ABS_WHEEL", + [ABS_GAS] = "ABS_GAS", + [ABS_BRAKE] = "ABS_BRAKE", + [ABS_HAT0X] = "ABS_HAT0X", + [ABS_HAT0Y] = "ABS_HAT0Y", + [ABS_HAT1X] = "ABS_HAT1X", + [ABS_HAT1Y] = "ABS_HAT1Y", + [ABS_HAT2X] = "ABS_HAT2X", + [ABS_HAT2Y] = "ABS_HAT2Y", + [ABS_HAT3X] = "ABS_HAT3X", + [ABS_HAT3Y] = "ABS_HAT3Y", + [ABS_PRESSURE] = "ABS_PRESSURE", + [ABS_DISTANCE] = "ABS_DISTANCE", + [ABS_TILT_X] = "ABS_TILT_X", + [ABS_TILT_Y] = "ABS_TILT_Y", + [ABS_TOOL_WIDTH] = "ABS_TOOL_WIDTH", + [ABS_VOLUME] = "ABS_VOLUME", + [ABS_MISC] = "ABS_MISC", + [ABS_MT_SLOT] = "ABS_MT_SLOT", + [ABS_MT_TOUCH_MAJOR] = "ABS_MT_TOUCH_MAJOR", + [ABS_MT_TOUCH_MINOR] = "ABS_MT_TOUCH_MINOR", + [ABS_MT_WIDTH_MAJOR] = "ABS_MT_WIDTH_MAJOR", + [ABS_MT_WIDTH_MINOR] = "ABS_MT_WIDTH_MINOR", + [ABS_MT_ORIENTATION] = "ABS_MT_ORIENTATION", + [ABS_MT_POSITION_X] = "ABS_MT_POSITION_X", + [ABS_MT_POSITION_Y] = "ABS_MT_POSITION_Y", + [ABS_MT_TOOL_TYPE] = "ABS_MT_TOOL_TYPE", + [ABS_MT_BLOB_ID] = "ABS_MT_BLOB_ID", + [ABS_MT_TRACKING_ID] = "ABS_MT_TRACKING_ID", + [ABS_MT_PRESSURE] = "ABS_MT_PRESSURE", + [ABS_MT_DISTANCE] = "ABS_MT_DISTANCE", + [ABS_MT_TOOL_X] = "ABS_MT_TOOL_X", + [ABS_MT_TOOL_Y] = "ABS_MT_TOOL_Y", + [ABS_MAX] = "ABS_MAX", +}; + +static const char * const key_map[KEY_MAX + 1] = { + [0 ... KEY_MAX] = NULL, + [KEY_RESERVED] = "KEY_RESERVED", + [KEY_ESC] = "KEY_ESC", + [KEY_1] = "KEY_1", + [KEY_2] = "KEY_2", + [KEY_3] = "KEY_3", + [KEY_4] = "KEY_4", + [KEY_5] = "KEY_5", + [KEY_6] = "KEY_6", + [KEY_7] = "KEY_7", + [KEY_8] = "KEY_8", + [KEY_9] = "KEY_9", + [KEY_0] = "KEY_0", + [KEY_MINUS] = "KEY_MINUS", + [KEY_EQUAL] = "KEY_EQUAL", + [KEY_BACKSPACE] = "KEY_BACKSPACE", + [KEY_TAB] = "KEY_TAB", + [KEY_Q] = "KEY_Q", + [KEY_W] = "KEY_W", + [KEY_E] = "KEY_E", + [KEY_R] = "KEY_R", + [KEY_T] = "KEY_T", + [KEY_Y] = "KEY_Y", + [KEY_U] = "KEY_U", + [KEY_I] = "KEY_I", + [KEY_O] = "KEY_O", + [KEY_P] = "KEY_P", + [KEY_LEFTBRACE] = "KEY_LEFTBRACE", + [KEY_RIGHTBRACE] = "KEY_RIGHTBRACE", + [KEY_ENTER] = "KEY_ENTER", + [KEY_LEFTCTRL] = "KEY_LEFTCTRL", + [KEY_A] = "KEY_A", + [KEY_S] = "KEY_S", + [KEY_D] = "KEY_D", + [KEY_F] = "KEY_F", + [KEY_G] = "KEY_G", + [KEY_H] = "KEY_H", + [KEY_J] = "KEY_J", + [KEY_K] = "KEY_K", + [KEY_L] = "KEY_L", + [KEY_SEMICOLON] = "KEY_SEMICOLON", + [KEY_APOSTROPHE] = "KEY_APOSTROPHE", + [KEY_GRAVE] = "KEY_GRAVE", + [KEY_LEFTSHIFT] = "KEY_LEFTSHIFT", + [KEY_BACKSLASH] = "KEY_BACKSLASH", + [KEY_Z] = "KEY_Z", + [KEY_X] = "KEY_X", + [KEY_C] = "KEY_C", + [KEY_V] = "KEY_V", + [KEY_B] = "KEY_B", + [KEY_N] = "KEY_N", + [KEY_M] = "KEY_M", + [KEY_COMMA] = "KEY_COMMA", + [KEY_DOT] = "KEY_DOT", + [KEY_SLASH] = "KEY_SLASH", + [KEY_RIGHTSHIFT] = "KEY_RIGHTSHIFT", + [KEY_KPASTERISK] = "KEY_KPASTERISK", + [KEY_LEFTALT] = "KEY_LEFTALT", + [KEY_SPACE] = "KEY_SPACE", + [KEY_CAPSLOCK] = "KEY_CAPSLOCK", + [KEY_F1] = "KEY_F1", + [KEY_F2] = "KEY_F2", + [KEY_F3] = "KEY_F3", + [KEY_F4] = "KEY_F4", + [KEY_F5] = "KEY_F5", + [KEY_F6] = "KEY_F6", + [KEY_F7] = "KEY_F7", + [KEY_F8] = "KEY_F8", + [KEY_F9] = "KEY_F9", + [KEY_F10] = "KEY_F10", + [KEY_NUMLOCK] = "KEY_NUMLOCK", + [KEY_SCROLLLOCK] = "KEY_SCROLLLOCK", + [KEY_KP7] = "KEY_KP7", + [KEY_KP8] = "KEY_KP8", + [KEY_KP9] = "KEY_KP9", + [KEY_KPMINUS] = "KEY_KPMINUS", + [KEY_KP4] = "KEY_KP4", + [KEY_KP5] = "KEY_KP5", + [KEY_KP6] = "KEY_KP6", + [KEY_KPPLUS] = "KEY_KPPLUS", + [KEY_KP1] = "KEY_KP1", + [KEY_KP2] = "KEY_KP2", + [KEY_KP3] = "KEY_KP3", + [KEY_KP0] = "KEY_KP0", + [KEY_KPDOT] = "KEY_KPDOT", + [KEY_ZENKAKUHANKAKU] = "KEY_ZENKAKUHANKAKU", + [KEY_102ND] = "KEY_102ND", + [KEY_F11] = "KEY_F11", + [KEY_F12] = "KEY_F12", + [KEY_RO] = "KEY_RO", + [KEY_KATAKANA] = "KEY_KATAKANA", + [KEY_HIRAGANA] = "KEY_HIRAGANA", + [KEY_HENKAN] = "KEY_HENKAN", + [KEY_KATAKANAHIRAGANA] = "KEY_KATAKANAHIRAGANA", + [KEY_MUHENKAN] = "KEY_MUHENKAN", + [KEY_KPJPCOMMA] = "KEY_KPJPCOMMA", + [KEY_KPENTER] = "KEY_KPENTER", + [KEY_RIGHTCTRL] = "KEY_RIGHTCTRL", + [KEY_KPSLASH] = "KEY_KPSLASH", + [KEY_SYSRQ] = "KEY_SYSRQ", + [KEY_RIGHTALT] = "KEY_RIGHTALT", + [KEY_LINEFEED] = "KEY_LINEFEED", + [KEY_HOME] = "KEY_HOME", + [KEY_UP] = "KEY_UP", + [KEY_PAGEUP] = "KEY_PAGEUP", + [KEY_LEFT] = "KEY_LEFT", + [KEY_RIGHT] = "KEY_RIGHT", + [KEY_END] = "KEY_END", + [KEY_DOWN] = "KEY_DOWN", + [KEY_PAGEDOWN] = "KEY_PAGEDOWN", + [KEY_INSERT] = "KEY_INSERT", + [KEY_DELETE] = "KEY_DELETE", + [KEY_MACRO] = "KEY_MACRO", + [KEY_MUTE] = "KEY_MUTE", + [KEY_VOLUMEDOWN] = "KEY_VOLUMEDOWN", + [KEY_VOLUMEUP] = "KEY_VOLUMEUP", + [KEY_POWER] = "KEY_POWER", + [KEY_KPEQUAL] = "KEY_KPEQUAL", + [KEY_KPPLUSMINUS] = "KEY_KPPLUSMINUS", + [KEY_PAUSE] = "KEY_PAUSE", + [KEY_SCALE] = "KEY_SCALE", + [KEY_KPCOMMA] = "KEY_KPCOMMA", + [KEY_HANGEUL] = "KEY_HANGEUL", + [KEY_HANJA] = "KEY_HANJA", + [KEY_YEN] = "KEY_YEN", + [KEY_LEFTMETA] = "KEY_LEFTMETA", + [KEY_RIGHTMETA] = "KEY_RIGHTMETA", + [KEY_COMPOSE] = "KEY_COMPOSE", + [KEY_STOP] = "KEY_STOP", + [KEY_AGAIN] = "KEY_AGAIN", + [KEY_PROPS] = "KEY_PROPS", + [KEY_UNDO] = "KEY_UNDO", + [KEY_FRONT] = "KEY_FRONT", + [KEY_COPY] = "KEY_COPY", + [KEY_OPEN] = "KEY_OPEN", + [KEY_PASTE] = "KEY_PASTE", + [KEY_FIND] = "KEY_FIND", + [KEY_CUT] = "KEY_CUT", + [KEY_HELP] = "KEY_HELP", + [KEY_MENU] = "KEY_MENU", + [KEY_CALC] = "KEY_CALC", + [KEY_SETUP] = "KEY_SETUP", + [KEY_SLEEP] = "KEY_SLEEP", + [KEY_WAKEUP] = "KEY_WAKEUP", + [KEY_FILE] = "KEY_FILE", + [KEY_SENDFILE] = "KEY_SENDFILE", + [KEY_DELETEFILE] = "KEY_DELETEFILE", + [KEY_XFER] = "KEY_XFER", + [KEY_PROG1] = "KEY_PROG1", + [KEY_PROG2] = "KEY_PROG2", + [KEY_WWW] = "KEY_WWW", + [KEY_MSDOS] = "KEY_MSDOS", + [KEY_COFFEE] = "KEY_COFFEE", + [KEY_DIRECTION] = "KEY_DIRECTION", + [KEY_CYCLEWINDOWS] = "KEY_CYCLEWINDOWS", + [KEY_MAIL] = "KEY_MAIL", + [KEY_BOOKMARKS] = "KEY_BOOKMARKS", + [KEY_COMPUTER] = "KEY_COMPUTER", + [KEY_BACK] = "KEY_BACK", + [KEY_FORWARD] = "KEY_FORWARD", + [KEY_CLOSECD] = "KEY_CLOSECD", + [KEY_EJECTCD] = "KEY_EJECTCD", + [KEY_EJECTCLOSECD] = "KEY_EJECTCLOSECD", + [KEY_NEXTSONG] = "KEY_NEXTSONG", + [KEY_PLAYPAUSE] = "KEY_PLAYPAUSE", + [KEY_PREVIOUSSONG] = "KEY_PREVIOUSSONG", + [KEY_STOPCD] = "KEY_STOPCD", + [KEY_RECORD] = "KEY_RECORD", + [KEY_REWIND] = "KEY_REWIND", + [KEY_PHONE] = "KEY_PHONE", + [KEY_ISO] = "KEY_ISO", + [KEY_CONFIG] = "KEY_CONFIG", + [KEY_HOMEPAGE] = "KEY_HOMEPAGE", + [KEY_REFRESH] = "KEY_REFRESH", + [KEY_EXIT] = "KEY_EXIT", + [KEY_MOVE] = "KEY_MOVE", + [KEY_EDIT] = "KEY_EDIT", + [KEY_SCROLLUP] = "KEY_SCROLLUP", + [KEY_SCROLLDOWN] = "KEY_SCROLLDOWN", + [KEY_KPLEFTPAREN] = "KEY_KPLEFTPAREN", + [KEY_KPRIGHTPAREN] = "KEY_KPRIGHTPAREN", + [KEY_NEW] = "KEY_NEW", + [KEY_REDO] = "KEY_REDO", + [KEY_F13] = "KEY_F13", + [KEY_F14] = "KEY_F14", + [KEY_F15] = "KEY_F15", + [KEY_F16] = "KEY_F16", + [KEY_F17] = "KEY_F17", + [KEY_F18] = "KEY_F18", + [KEY_F19] = "KEY_F19", + [KEY_F20] = "KEY_F20", + [KEY_F21] = "KEY_F21", + [KEY_F22] = "KEY_F22", + [KEY_F23] = "KEY_F23", + [KEY_F24] = "KEY_F24", + [KEY_PLAYCD] = "KEY_PLAYCD", + [KEY_PAUSECD] = "KEY_PAUSECD", + [KEY_PROG3] = "KEY_PROG3", + [KEY_PROG4] = "KEY_PROG4", + [KEY_DASHBOARD] = "KEY_DASHBOARD", + [KEY_SUSPEND] = "KEY_SUSPEND", + [KEY_CLOSE] = "KEY_CLOSE", + [KEY_PLAY] = "KEY_PLAY", + [KEY_FASTFORWARD] = "KEY_FASTFORWARD", + [KEY_BASSBOOST] = "KEY_BASSBOOST", + [KEY_PRINT] = "KEY_PRINT", + [KEY_HP] = "KEY_HP", + [KEY_CAMERA] = "KEY_CAMERA", + [KEY_SOUND] = "KEY_SOUND", + [KEY_QUESTION] = "KEY_QUESTION", + [KEY_EMAIL] = "KEY_EMAIL", + [KEY_CHAT] = "KEY_CHAT", + [KEY_SEARCH] = "KEY_SEARCH", + [KEY_CONNECT] = "KEY_CONNECT", + [KEY_FINANCE] = "KEY_FINANCE", + [KEY_SPORT] = "KEY_SPORT", + [KEY_SHOP] = "KEY_SHOP", + [KEY_ALTERASE] = "KEY_ALTERASE", + [KEY_CANCEL] = "KEY_CANCEL", + [KEY_BRIGHTNESSDOWN] = "KEY_BRIGHTNESSDOWN", + [KEY_BRIGHTNESSUP] = "KEY_BRIGHTNESSUP", + [KEY_MEDIA] = "KEY_MEDIA", + [KEY_SWITCHVIDEOMODE] = "KEY_SWITCHVIDEOMODE", + [KEY_KBDILLUMTOGGLE] = "KEY_KBDILLUMTOGGLE", + [KEY_KBDILLUMDOWN] = "KEY_KBDILLUMDOWN", + [KEY_KBDILLUMUP] = "KEY_KBDILLUMUP", + [KEY_SEND] = "KEY_SEND", + [KEY_REPLY] = "KEY_REPLY", + [KEY_FORWARDMAIL] = "KEY_FORWARDMAIL", + [KEY_SAVE] = "KEY_SAVE", + [KEY_DOCUMENTS] = "KEY_DOCUMENTS", + [KEY_BATTERY] = "KEY_BATTERY", + [KEY_BLUETOOTH] = "KEY_BLUETOOTH", + [KEY_WLAN] = "KEY_WLAN", + [KEY_UWB] = "KEY_UWB", + [KEY_UNKNOWN] = "KEY_UNKNOWN", + [KEY_VIDEO_NEXT] = "KEY_VIDEO_NEXT", + [KEY_VIDEO_PREV] = "KEY_VIDEO_PREV", + [KEY_BRIGHTNESS_CYCLE] = "KEY_BRIGHTNESS_CYCLE", + [KEY_BRIGHTNESS_ZERO] = "KEY_BRIGHTNESS_ZERO", + [KEY_DISPLAY_OFF] = "KEY_DISPLAY_OFF", + [KEY_WIMAX] = "KEY_WIMAX", + [KEY_RFKILL] = "KEY_RFKILL", + [KEY_MICMUTE] = "KEY_MICMUTE", + [BTN_0] = "BTN_0", + [BTN_1] = "BTN_1", + [BTN_2] = "BTN_2", + [BTN_3] = "BTN_3", + [BTN_4] = "BTN_4", + [BTN_5] = "BTN_5", + [BTN_6] = "BTN_6", + [BTN_7] = "BTN_7", + [BTN_8] = "BTN_8", + [BTN_9] = "BTN_9", + [BTN_LEFT] = "BTN_LEFT", + [BTN_RIGHT] = "BTN_RIGHT", + [BTN_MIDDLE] = "BTN_MIDDLE", + [BTN_SIDE] = "BTN_SIDE", + [BTN_EXTRA] = "BTN_EXTRA", + [BTN_FORWARD] = "BTN_FORWARD", + [BTN_BACK] = "BTN_BACK", + [BTN_TASK] = "BTN_TASK", + [BTN_TRIGGER] = "BTN_TRIGGER", + [BTN_THUMB] = "BTN_THUMB", + [BTN_THUMB2] = "BTN_THUMB2", + [BTN_TOP] = "BTN_TOP", + [BTN_TOP2] = "BTN_TOP2", + [BTN_PINKIE] = "BTN_PINKIE", + [BTN_BASE] = "BTN_BASE", + [BTN_BASE2] = "BTN_BASE2", + [BTN_BASE3] = "BTN_BASE3", + [BTN_BASE4] = "BTN_BASE4", + [BTN_BASE5] = "BTN_BASE5", + [BTN_BASE6] = "BTN_BASE6", + [BTN_DEAD] = "BTN_DEAD", + [BTN_A] = "BTN_A", + [BTN_B] = "BTN_B", + [BTN_C] = "BTN_C", + [BTN_X] = "BTN_X", + [BTN_Y] = "BTN_Y", + [BTN_Z] = "BTN_Z", + [BTN_TL] = "BTN_TL", + [BTN_TR] = "BTN_TR", + [BTN_TL2] = "BTN_TL2", + [BTN_TR2] = "BTN_TR2", + [BTN_SELECT] = "BTN_SELECT", + [BTN_START] = "BTN_START", + [BTN_MODE] = "BTN_MODE", + [BTN_THUMBL] = "BTN_THUMBL", + [BTN_THUMBR] = "BTN_THUMBR", + [BTN_TOOL_PEN] = "BTN_TOOL_PEN", + [BTN_TOOL_RUBBER] = "BTN_TOOL_RUBBER", + [BTN_TOOL_BRUSH] = "BTN_TOOL_BRUSH", + [BTN_TOOL_PENCIL] = "BTN_TOOL_PENCIL", + [BTN_TOOL_AIRBRUSH] = "BTN_TOOL_AIRBRUSH", + [BTN_TOOL_FINGER] = "BTN_TOOL_FINGER", + [BTN_TOOL_MOUSE] = "BTN_TOOL_MOUSE", + [BTN_TOOL_LENS] = "BTN_TOOL_LENS", + [BTN_TOOL_QUINTTAP] = "BTN_TOOL_QUINTTAP", + [BTN_TOUCH] = "BTN_TOUCH", + [BTN_STYLUS] = "BTN_STYLUS", + [BTN_STYLUS2] = "BTN_STYLUS2", + [BTN_TOOL_DOUBLETAP] = "BTN_TOOL_DOUBLETAP", + [BTN_TOOL_TRIPLETAP] = "BTN_TOOL_TRIPLETAP", + [BTN_TOOL_QUADTAP] = "BTN_TOOL_QUADTAP", + [BTN_GEAR_DOWN] = "BTN_GEAR_DOWN", + [BTN_GEAR_UP] = "BTN_GEAR_UP", + [KEY_OK] = "KEY_OK", + [KEY_SELECT] = "KEY_SELECT", + [KEY_GOTO] = "KEY_GOTO", + [KEY_CLEAR] = "KEY_CLEAR", + [KEY_POWER2] = "KEY_POWER2", + [KEY_OPTION] = "KEY_OPTION", + [KEY_INFO] = "KEY_INFO", + [KEY_TIME] = "KEY_TIME", + [KEY_VENDOR] = "KEY_VENDOR", + [KEY_ARCHIVE] = "KEY_ARCHIVE", + [KEY_PROGRAM] = "KEY_PROGRAM", + [KEY_CHANNEL] = "KEY_CHANNEL", + [KEY_FAVORITES] = "KEY_FAVORITES", + [KEY_EPG] = "KEY_EPG", + [KEY_PVR] = "KEY_PVR", + [KEY_MHP] = "KEY_MHP", + [KEY_LANGUAGE] = "KEY_LANGUAGE", + [KEY_TITLE] = "KEY_TITLE", + [KEY_SUBTITLE] = "KEY_SUBTITLE", + [KEY_ANGLE] = "KEY_ANGLE", + [KEY_ZOOM] = "KEY_ZOOM", + [KEY_MODE] = "KEY_MODE", + [KEY_KEYBOARD] = "KEY_KEYBOARD", + [KEY_SCREEN] = "KEY_SCREEN", + [KEY_PC] = "KEY_PC", + [KEY_TV] = "KEY_TV", + [KEY_TV2] = "KEY_TV2", + [KEY_VCR] = "KEY_VCR", + [KEY_VCR2] = "KEY_VCR2", + [KEY_SAT] = "KEY_SAT", + [KEY_SAT2] = "KEY_SAT2", + [KEY_CD] = "KEY_CD", + [KEY_TAPE] = "KEY_TAPE", + [KEY_RADIO] = "KEY_RADIO", + [KEY_TUNER] = "KEY_TUNER", + [KEY_PLAYER] = "KEY_PLAYER", + [KEY_TEXT] = "KEY_TEXT", + [KEY_DVD] = "KEY_DVD", + [KEY_AUX] = "KEY_AUX", + [KEY_MP3] = "KEY_MP3", + [KEY_AUDIO] = "KEY_AUDIO", + [KEY_VIDEO] = "KEY_VIDEO", + [KEY_DIRECTORY] = "KEY_DIRECTORY", + [KEY_LIST] = "KEY_LIST", + [KEY_MEMO] = "KEY_MEMO", + [KEY_CALENDAR] = "KEY_CALENDAR", + [KEY_RED] = "KEY_RED", + [KEY_GREEN] = "KEY_GREEN", + [KEY_YELLOW] = "KEY_YELLOW", + [KEY_BLUE] = "KEY_BLUE", + [KEY_CHANNELUP] = "KEY_CHANNELUP", + [KEY_CHANNELDOWN] = "KEY_CHANNELDOWN", + [KEY_FIRST] = "KEY_FIRST", + [KEY_LAST] = "KEY_LAST", + [KEY_AB] = "KEY_AB", + [KEY_NEXT] = "KEY_NEXT", + [KEY_RESTART] = "KEY_RESTART", + [KEY_SLOW] = "KEY_SLOW", + [KEY_SHUFFLE] = "KEY_SHUFFLE", + [KEY_BREAK] = "KEY_BREAK", + [KEY_PREVIOUS] = "KEY_PREVIOUS", + [KEY_DIGITS] = "KEY_DIGITS", + [KEY_TEEN] = "KEY_TEEN", + [KEY_TWEN] = "KEY_TWEN", + [KEY_VIDEOPHONE] = "KEY_VIDEOPHONE", + [KEY_GAMES] = "KEY_GAMES", + [KEY_ZOOMIN] = "KEY_ZOOMIN", + [KEY_ZOOMOUT] = "KEY_ZOOMOUT", + [KEY_ZOOMRESET] = "KEY_ZOOMRESET", + [KEY_WORDPROCESSOR] = "KEY_WORDPROCESSOR", + [KEY_EDITOR] = "KEY_EDITOR", + [KEY_SPREADSHEET] = "KEY_SPREADSHEET", + [KEY_GRAPHICSEDITOR] = "KEY_GRAPHICSEDITOR", + [KEY_PRESENTATION] = "KEY_PRESENTATION", + [KEY_DATABASE] = "KEY_DATABASE", + [KEY_NEWS] = "KEY_NEWS", + [KEY_VOICEMAIL] = "KEY_VOICEMAIL", + [KEY_ADDRESSBOOK] = "KEY_ADDRESSBOOK", + [KEY_MESSENGER] = "KEY_MESSENGER", + [KEY_DISPLAYTOGGLE] = "KEY_DISPLAYTOGGLE", + [KEY_SPELLCHECK] = "KEY_SPELLCHECK", + [KEY_LOGOFF] = "KEY_LOGOFF", + [KEY_DOLLAR] = "KEY_DOLLAR", + [KEY_EURO] = "KEY_EURO", + [KEY_FRAMEBACK] = "KEY_FRAMEBACK", + [KEY_FRAMEFORWARD] = "KEY_FRAMEFORWARD", + [KEY_CONTEXT_MENU] = "KEY_CONTEXT_MENU", + [KEY_MEDIA_REPEAT] = "KEY_MEDIA_REPEAT", + [KEY_10CHANNELSUP] = "KEY_10CHANNELSUP", + [KEY_10CHANNELSDOWN] = "KEY_10CHANNELSDOWN", + [KEY_IMAGES] = "KEY_IMAGES", + [KEY_DEL_EOL] = "KEY_DEL_EOL", + [KEY_DEL_EOS] = "KEY_DEL_EOS", + [KEY_INS_LINE] = "KEY_INS_LINE", + [KEY_DEL_LINE] = "KEY_DEL_LINE", + [KEY_FN] = "KEY_FN", + [KEY_FN_ESC] = "KEY_FN_ESC", + [KEY_FN_F1] = "KEY_FN_F1", + [KEY_FN_F2] = "KEY_FN_F2", + [KEY_FN_F3] = "KEY_FN_F3", + [KEY_FN_F4] = "KEY_FN_F4", + [KEY_FN_F5] = "KEY_FN_F5", + [KEY_FN_F6] = "KEY_FN_F6", + [KEY_FN_F7] = "KEY_FN_F7", + [KEY_FN_F8] = "KEY_FN_F8", + [KEY_FN_F9] = "KEY_FN_F9", + [KEY_FN_F10] = "KEY_FN_F10", + [KEY_FN_F11] = "KEY_FN_F11", + [KEY_FN_F12] = "KEY_FN_F12", + [KEY_FN_1] = "KEY_FN_1", + [KEY_FN_2] = "KEY_FN_2", + [KEY_FN_D] = "KEY_FN_D", + [KEY_FN_E] = "KEY_FN_E", + [KEY_FN_F] = "KEY_FN_F", + [KEY_FN_S] = "KEY_FN_S", + [KEY_FN_B] = "KEY_FN_B", + [KEY_BRL_DOT1] = "KEY_BRL_DOT1", + [KEY_BRL_DOT2] = "KEY_BRL_DOT2", + [KEY_BRL_DOT3] = "KEY_BRL_DOT3", + [KEY_BRL_DOT4] = "KEY_BRL_DOT4", + [KEY_BRL_DOT5] = "KEY_BRL_DOT5", + [KEY_BRL_DOT6] = "KEY_BRL_DOT6", + [KEY_BRL_DOT7] = "KEY_BRL_DOT7", + [KEY_BRL_DOT8] = "KEY_BRL_DOT8", + [KEY_BRL_DOT9] = "KEY_BRL_DOT9", + [KEY_BRL_DOT10] = "KEY_BRL_DOT10", + [KEY_NUMERIC_0] = "KEY_NUMERIC_0", + [KEY_NUMERIC_1] = "KEY_NUMERIC_1", + [KEY_NUMERIC_2] = "KEY_NUMERIC_2", + [KEY_NUMERIC_3] = "KEY_NUMERIC_3", + [KEY_NUMERIC_4] = "KEY_NUMERIC_4", + [KEY_NUMERIC_5] = "KEY_NUMERIC_5", + [KEY_NUMERIC_6] = "KEY_NUMERIC_6", + [KEY_NUMERIC_7] = "KEY_NUMERIC_7", + [KEY_NUMERIC_8] = "KEY_NUMERIC_8", + [KEY_NUMERIC_9] = "KEY_NUMERIC_9", + [KEY_NUMERIC_STAR] = "KEY_NUMERIC_STAR", + [KEY_NUMERIC_POUND] = "KEY_NUMERIC_POUND", + [KEY_CAMERA_FOCUS] = "KEY_CAMERA_FOCUS", + [KEY_WPS_BUTTON] = "KEY_WPS_BUTTON", + [KEY_TOUCHPAD_TOGGLE] = "KEY_TOUCHPAD_TOGGLE", + [KEY_TOUCHPAD_ON] = "KEY_TOUCHPAD_ON", + [KEY_TOUCHPAD_OFF] = "KEY_TOUCHPAD_OFF", + [KEY_CAMERA_ZOOMIN] = "KEY_CAMERA_ZOOMIN", + [KEY_CAMERA_ZOOMOUT] = "KEY_CAMERA_ZOOMOUT", + [KEY_CAMERA_UP] = "KEY_CAMERA_UP", + [KEY_CAMERA_DOWN] = "KEY_CAMERA_DOWN", + [KEY_CAMERA_LEFT] = "KEY_CAMERA_LEFT", + [KEY_CAMERA_RIGHT] = "KEY_CAMERA_RIGHT", + [BTN_TRIGGER_HAPPY1] = "BTN_TRIGGER_HAPPY1", + [BTN_TRIGGER_HAPPY2] = "BTN_TRIGGER_HAPPY2", + [BTN_TRIGGER_HAPPY3] = "BTN_TRIGGER_HAPPY3", + [BTN_TRIGGER_HAPPY4] = "BTN_TRIGGER_HAPPY4", + [BTN_TRIGGER_HAPPY5] = "BTN_TRIGGER_HAPPY5", + [BTN_TRIGGER_HAPPY6] = "BTN_TRIGGER_HAPPY6", + [BTN_TRIGGER_HAPPY7] = "BTN_TRIGGER_HAPPY7", + [BTN_TRIGGER_HAPPY8] = "BTN_TRIGGER_HAPPY8", + [BTN_TRIGGER_HAPPY9] = "BTN_TRIGGER_HAPPY9", + [BTN_TRIGGER_HAPPY10] = "BTN_TRIGGER_HAPPY10", + [BTN_TRIGGER_HAPPY11] = "BTN_TRIGGER_HAPPY11", + [BTN_TRIGGER_HAPPY12] = "BTN_TRIGGER_HAPPY12", + [BTN_TRIGGER_HAPPY13] = "BTN_TRIGGER_HAPPY13", + [BTN_TRIGGER_HAPPY14] = "BTN_TRIGGER_HAPPY14", + [BTN_TRIGGER_HAPPY15] = "BTN_TRIGGER_HAPPY15", + [BTN_TRIGGER_HAPPY16] = "BTN_TRIGGER_HAPPY16", + [BTN_TRIGGER_HAPPY17] = "BTN_TRIGGER_HAPPY17", + [BTN_TRIGGER_HAPPY18] = "BTN_TRIGGER_HAPPY18", + [BTN_TRIGGER_HAPPY19] = "BTN_TRIGGER_HAPPY19", + [BTN_TRIGGER_HAPPY20] = "BTN_TRIGGER_HAPPY20", + [BTN_TRIGGER_HAPPY21] = "BTN_TRIGGER_HAPPY21", + [BTN_TRIGGER_HAPPY22] = "BTN_TRIGGER_HAPPY22", + [BTN_TRIGGER_HAPPY23] = "BTN_TRIGGER_HAPPY23", + [BTN_TRIGGER_HAPPY24] = "BTN_TRIGGER_HAPPY24", + [BTN_TRIGGER_HAPPY25] = "BTN_TRIGGER_HAPPY25", + [BTN_TRIGGER_HAPPY26] = "BTN_TRIGGER_HAPPY26", + [BTN_TRIGGER_HAPPY27] = "BTN_TRIGGER_HAPPY27", + [BTN_TRIGGER_HAPPY28] = "BTN_TRIGGER_HAPPY28", + [BTN_TRIGGER_HAPPY29] = "BTN_TRIGGER_HAPPY29", + [BTN_TRIGGER_HAPPY30] = "BTN_TRIGGER_HAPPY30", + [BTN_TRIGGER_HAPPY31] = "BTN_TRIGGER_HAPPY31", + [BTN_TRIGGER_HAPPY32] = "BTN_TRIGGER_HAPPY32", + [BTN_TRIGGER_HAPPY33] = "BTN_TRIGGER_HAPPY33", + [BTN_TRIGGER_HAPPY34] = "BTN_TRIGGER_HAPPY34", + [BTN_TRIGGER_HAPPY35] = "BTN_TRIGGER_HAPPY35", + [BTN_TRIGGER_HAPPY36] = "BTN_TRIGGER_HAPPY36", + [BTN_TRIGGER_HAPPY37] = "BTN_TRIGGER_HAPPY37", + [BTN_TRIGGER_HAPPY38] = "BTN_TRIGGER_HAPPY38", + [BTN_TRIGGER_HAPPY39] = "BTN_TRIGGER_HAPPY39", + [BTN_TRIGGER_HAPPY40] = "BTN_TRIGGER_HAPPY40", + [KEY_MAX] = "KEY_MAX", +}; + +static const char * const led_map[LED_MAX + 1] = { + [0 ... LED_MAX] = NULL, + [LED_NUML] = "LED_NUML", + [LED_CAPSL] = "LED_CAPSL", + [LED_SCROLLL] = "LED_SCROLLL", + [LED_COMPOSE] = "LED_COMPOSE", + [LED_KANA] = "LED_KANA", + [LED_SLEEP] = "LED_SLEEP", + [LED_SUSPEND] = "LED_SUSPEND", + [LED_MUTE] = "LED_MUTE", + [LED_MISC] = "LED_MISC", + [LED_MAIL] = "LED_MAIL", + [LED_CHARGING] = "LED_CHARGING", + [LED_MAX] = "LED_MAX", +}; + +static const char * const snd_map[SND_MAX + 1] = { + [0 ... SND_MAX] = NULL, + [SND_CLICK] = "SND_CLICK", + [SND_BELL] = "SND_BELL", + [SND_TONE] = "SND_TONE", + [SND_MAX] = "SND_MAX", +}; + +static const char * const msc_map[MSC_MAX + 1] = { + [0 ... MSC_MAX] = NULL, + [MSC_SERIAL] = "MSC_SERIAL", + [MSC_PULSELED] = "MSC_PULSELED", + [MSC_GESTURE] = "MSC_GESTURE", + [MSC_RAW] = "MSC_RAW", + [MSC_SCAN] = "MSC_SCAN", + [MSC_TIMESTAMP] = "MSC_TIMESTAMP", + [MSC_MAX] = "MSC_MAX", +}; + +static const char * const sw_map[SW_MAX + 1] = { + [0 ... SW_MAX] = NULL, + [SW_LID] = "SW_LID", + [SW_TABLET_MODE] = "SW_TABLET_MODE", + [SW_HEADPHONE_INSERT] = "SW_HEADPHONE_INSERT", + [SW_RFKILL_ALL] = "SW_RFKILL_ALL", + [SW_MICROPHONE_INSERT] = "SW_MICROPHONE_INSERT", + [SW_DOCK] = "SW_DOCK", + [SW_LINEOUT_INSERT] = "SW_LINEOUT_INSERT", + [SW_JACK_PHYSICAL_INSERT] = "SW_JACK_PHYSICAL_INSERT", + [SW_VIDEOOUT_INSERT] = "SW_VIDEOOUT_INSERT", + [SW_CAMERA_LENS_COVER] = "SW_CAMERA_LENS_COVER", + [SW_KEYPAD_SLIDE] = "SW_KEYPAD_SLIDE", + [SW_FRONT_PROXIMITY] = "SW_FRONT_PROXIMITY", + [SW_ROTATE_LOCK] = "SW_ROTATE_LOCK", + [SW_LINEIN_INSERT] = "SW_LINEIN_INSERT", + [SW_MAX] = "SW_MAX", +}; + +static const char * const ff_map[FF_MAX + 1] = { + [0 ... FF_MAX] = NULL, + [FF_STATUS_STOPPED] = "FF_STATUS_STOPPED", + [FF_STATUS_MAX] = "FF_STATUS_MAX", + [FF_AUTOCENTER] = "FF_AUTOCENTER", + [FF_GAIN] = "FF_GAIN", + [FF_RUMBLE] = "FF_RUMBLE", + [FF_PERIODIC] = "FF_PERIODIC", + [FF_CONSTANT] = "FF_CONSTANT", + [FF_SPRING] = "FF_SPRING", + [FF_FRICTION] = "FF_FRICTION", + [FF_DAMPER] = "FF_DAMPER", + [FF_INERTIA] = "FF_INERTIA", + [FF_RAMP] = "FF_RAMP", + [FF_SQUARE] = "FF_SQUARE", + [FF_TRIANGLE] = "FF_TRIANGLE", + [FF_SINE] = "FF_SINE", + [FF_SAW_UP] = "FF_SAW_UP", + [FF_SAW_DOWN] = "FF_SAW_DOWN", + [FF_CUSTOM] = "FF_CUSTOM", + [FF_MAX] = "FF_MAX", +}; + +static const char * const syn_map[SYN_MAX + 1] = { + [0 ... SYN_MAX] = NULL, + [SYN_REPORT] = "SYN_REPORT", + [SYN_CONFIG] = "SYN_CONFIG", + [SYN_MT_REPORT] = "SYN_MT_REPORT", + [SYN_DROPPED] = "SYN_DROPPED", +}; + +static const char * const input_prop_map[INPUT_PROP_MAX + 1] = { + [0 ... INPUT_PROP_MAX] = NULL, + [INPUT_PROP_POINTER] = "INPUT_PROP_POINTER", + [INPUT_PROP_DIRECT] = "INPUT_PROP_DIRECT", + [INPUT_PROP_BUTTONPAD] = "INPUT_PROP_BUTTONPAD", + [INPUT_PROP_SEMI_MT] = "INPUT_PROP_SEMI_MT", + [INPUT_PROP_MAX] = "INPUT_PROP_MAX", +}; + +static const char * const * const map[EV_MAX + 1] = { + [0 ... EV_MAX] = NULL, + [EV_REL] = rel_map, + [EV_ABS] = abs_map, + [EV_KEY] = key_map, + [EV_LED] = led_map, + [EV_SND] = snd_map, + [EV_MSC] = msc_map, + [EV_SW] = sw_map, + [EV_FF] = ff_map, + [EV_SYN] = syn_map, +}; + +static const char * event_get_type_name(int type) { + return ev_map[type]; + } + +static const char * event_get_code_name(int type, int code) { + return map[type] ? map[type][code] : NULL; +} + +#endif /* EVENT_NAMES_H */ diff --git a/test/libevdev-events.c b/test/libevdev-events.c new file mode 100644 index 0000000..982116c --- /dev/null +++ b/test/libevdev-events.c @@ -0,0 +1,120 @@ +/* + * Copyright © 2013 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include <config.h> + +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <assert.h> +#include <linux/input.h> + +#include "libevdev.h" +#include "event-names.h" + +void +print_code_bits(struct libevdev *dev, unsigned int type, unsigned int max) +{ + unsigned int i; + for (i = 0; i <= max; i++) { + if (libevdev_has_event_code(dev, type, i)) + printf("%s: %s\n", + event_get_type_name(type), + event_get_code_name(type, i)); + } +} + +int callback(struct libevdev *dev, struct input_event *ev, void *userdata) +{ + if (ev->type == EV_SYN) + printf("Event: time %ld.%06ld, ++++++++++++++++++++ %s +++++++++++++++\n", + ev->time.tv_sec, + ev->time.tv_usec, + event_get_type_name(ev->type)); + else + printf("Event: time %ld.%06ld, type %d (%s), code %d (%s), value %d\n", + ev->time.tv_sec, + ev->time.tv_usec, + ev->type, + event_get_type_name(ev->type), + ev->code, + event_get_code_name(ev->type, ev->code), + ev->value); + return 0; +} + +int sync_callback(struct libevdev *dev, struct input_event *ev, void *userdata) +{ + printf("SYNC: "); + callback(dev, ev, userdata); + return 0; +} + +int +main(int argc, char **argv) +{ + struct libevdev *dev = NULL; + const char *file; + int fd; + int rc = 1; + + if (argc < 2) + goto out; + + file = argv[1]; + fd = open(file, O_RDONLY | O_NONBLOCK); + if (fd < 0) { + perror("Failed to open device"); + goto out; + } + + dev = libevdev_new(fd); + if (!dev) { + fprintf(stderr, "Failed to init libevdev\n"); + goto out; + } + + if (libevdev_set_callbacks(dev, &callback, &sync_callback, NULL) != 0) { + fprintf(stderr, "Failed to set callbacks"); + goto out; + } + + do { + rc = libevdev_read_events(dev, ER_ALL); + if (rc == 1) { + printf("::::::::::::::::::::: dropped ::::::::::::::::::::::\n"); + rc = libevdev_read_events(dev, ER_ALL | ER_SYNC); + } + } while (rc == 1 || rc == 0 || rc == -EAGAIN); + + if (rc != 0 && rc != -EAGAIN) + fprintf(stderr, "Failed to handle events: %s\n", strerror(rc)); + + rc = 0; +out: + libevdev_free(dev); + + return rc; +} diff --git a/test/libevdev-print.c b/test/libevdev-print.c new file mode 100644 index 0000000..8152cb5 --- /dev/null +++ b/test/libevdev-print.c @@ -0,0 +1,143 @@ +/* + * Copyright © 2013 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include <config.h> + +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <assert.h> +#include <linux/input.h> + +#include "libevdev.h" +#include "event-names.h" + +static void +print_abs_bits(struct libevdev *dev, int axis) +{ + const struct input_absinfo *abs; + + if (!libevdev_has_event_code(dev, EV_ABS, axis)) + return; + + abs = libevdev_get_abs_info(dev, axis); + + printf(" Value %6d\n", abs->value); + printf(" Min %6d\n", abs->minimum); + printf(" Max %6d\n", abs->maximum); + if (abs->fuzz) + printf(" Fuzz %6d\n", abs->fuzz); + if (abs->flat) + printf(" Flat %6d\n", abs->flat); + if (abs->resolution) + printf(" Resolution %6d\n", abs->resolution); +} + +static void +print_code_bits(struct libevdev *dev, unsigned int type, unsigned int max) +{ + unsigned int i; + for (i = 0; i <= max; i++) { + if (!libevdev_has_event_code(dev, type, i)) + continue; + + printf(" Event code %i (%s)\n", i, event_get_code_name(type, i)); + if (type == EV_ABS) + print_abs_bits(dev, i); + } +} + +static void +print_bits(struct libevdev *dev) +{ + unsigned int i; + printf("Supported events:\n"); + + for (i = 0; i <= EV_MAX; i++) { + if (libevdev_has_event_type(dev, i)) + printf(" Event type %d (%s)\n", i, event_get_type_name(i)); + switch(i) { + case EV_KEY: + print_code_bits(dev, EV_KEY, KEY_MAX); + break; + case EV_REL: + print_code_bits(dev, EV_REL, REL_MAX); + break; + case EV_ABS: + print_code_bits(dev, EV_ABS, ABS_MAX); + break; + case EV_LED: + print_code_bits(dev, EV_LED, LED_MAX); + break; + } + } +} + +static void +print_props(struct libevdev *dev) +{ + unsigned int i; + printf("Properties:\n"); + + for (i = 0; i <= INPUT_PROP_MAX; i++) { + if (libevdev_has_property(dev, i)) + printf(" Property type %d (%s)\n", i, input_prop_map[i]); + } +} + +int +main(int argc, char **argv) +{ + struct libevdev *dev; + const char *file; + int fd; + + + if (argc < 2) + return 1; + + file = argv[1]; + fd = open(file, O_RDONLY | O_NONBLOCK); + if (fd < 0) { + perror("Failed to open device"); + return 1; + } + + dev = libevdev_new(fd); + if (!dev) { + fprintf(stderr, "Failed to init libevdev\n"); + return 1; + } + + printf("Input device ID: bus %#x vendor %#x product %#x\n", + libevdev_get_bustype(dev), + libevdev_get_vid(dev), + libevdev_get_pid(dev)); + printf("Input device name: \"%s\"\n", libevdev_get_name(dev)); + print_bits(dev); + print_props(dev); + + libevdev_free(dev); + + return 0; +} |