summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Hutterer <peter.hutterer@who-t.net>2013-05-27 14:59:41 +1000
committerPeter Hutterer <peter.hutterer@who-t.net>2013-05-29 15:33:21 +1000
commita3255d3ec78c80918daac7f001dbb246a5970552 (patch)
tree5eedd5c3a187e98cb483b42df2b9ff8107a24014
downloadlibevdev-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--.gitignore45
-rw-r--r--COPYING19
-rw-r--r--Makefile.am6
-rw-r--r--README.md33
-rw-r--r--configure.ac47
-rw-r--r--libevdev.pc.in10
-rw-r--r--libevdev/Makefile.am11
-rw-r--r--libevdev/libevdev-int.h66
-rw-r--r--libevdev/libevdev.c790
-rw-r--r--libevdev/libevdev.h427
-rw-r--r--test/.gitignore2
-rw-r--r--test/Makefile.am11
-rw-r--r--test/event-names.h704
-rw-r--r--test/libevdev-events.c120
-rw-r--r--test/libevdev-print.c143
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
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..2a942c0
--- /dev/null
+++ b/COPYING
@@ -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;
+}