Add support for uinput device creation
authorPeter Hutterer <peter.hutterer@who-t.net>
Thu, 25 Jul 2013 05:56:11 +0000 (15:56 +1000)
committerPeter Hutterer <peter.hutterer@who-t.net>
Thu, 29 Aug 2013 03:54:46 +0000 (13:54 +1000)
This lets libevdev provide a relatively generic interface for the
creation of uinput devices so we don't need to duplicate this across
multiple projects.

Most of this is lifted from the current test implementation, with a
couple of minor changes.

EV_REP needs special handling:
   Kernel allows to set the EV_REP bit, it doesn't set REP_* bits (which we
   wrap anyway) but it will also set the default values (500, 33).

Device node is guessed based on the sysfs path:
   The sysfs path contains a eventN file, that corresponds to our
   /dev/input/eventN number. Use it so clients can quickly get the device
   node, without a libudev dependency.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
doc/libevdev.doxygen.in
libevdev/Makefile.am
libevdev/libevdev-uinput-int.h [new file with mode: 0644]
libevdev/libevdev-uinput.c [new file with mode: 0644]
libevdev/libevdev-uinput.h [new file with mode: 0644]
test/Makefile.am

index 6eec0af..fc5d7bb 100644 (file)
@@ -652,7 +652,8 @@ WARN_LOGFILE           =
 # directories like "/usr/src/myproject". Separate the files or directories
 # with spaces.
 
-INPUT                  = @top_srcdir@/libevdev/libevdev.h
+INPUT                  = @top_srcdir@/libevdev/libevdev.h \
+                        @top_srcdir@/libevdev/libevdev-uinput.h
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
index 3a67464..5ebbd42 100644 (file)
@@ -1,17 +1,20 @@
 lib_LTLIBRARIES=libevdev.la
 
-AM_CPPFLAGS = $(GCC_CFLAGS) $(GCOV_CFLAGS)
+AM_CPPFLAGS = $(GCC_CFLAGS) $(GCOV_CFLAGS) -I$(top_srcdir)
 
 libevdev_la_SOURCES = \
                    libevdev.h \
                    libevdev-int.h \
                    libevdev-util.h \
+                   libevdev-uinput.c \
+                   libevdev-uinput.h \
+                   libevdev-uinput-int.h \
                    libevdev.c
 
 libevdev_la_LDFLAGS = -version-info $(LIBEVDEV_LT_VERSION) -export-symbols-regex '^libevdev_' $(GCOV_LDFLAGS)
 
 libevdevincludedir = $(includedir)/libevdev-1.0/libevdev
-libevdevinclude_HEADERS = libevdev.h
+libevdevinclude_HEADERS = libevdev.h libevdev-uinput.h
 
 event-names.h: Makefile make-event-names.py
                $(srcdir)/make-event-names.py --output=c > $@
diff --git a/libevdev/libevdev-uinput-int.h b/libevdev/libevdev-uinput-int.h
new file mode 100644 (file)
index 0000000..fbc1c29
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+
+struct libevdev_uinput {
+       int fd; /**< file descriptor to uinput */
+       int fd_is_managed; /**< do we need to close it? */
+       char *name; /**< device name */
+       char *syspath; /**< /sys path */
+       char *devnode; /**< device node */
+       time_t ctime[2]; /**< before/after UI_DEV_CREATE */
+};
diff --git a/libevdev/libevdev-uinput.c b/libevdev/libevdev-uinput.c
new file mode 100644 (file)
index 0000000..45a95fa
--- /dev/null
@@ -0,0 +1,361 @@
+/*
+ * 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.
+ */
+
+#define _GNU_SOURCE
+#include <config.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <linux/uinput.h>
+
+#include "libevdev.h"
+#include "libevdev-int.h"
+#include "libevdev-uinput.h"
+#include "libevdev-uinput-int.h"
+#include "libevdev-util.h"
+
+#define SYS_INPUT_DIR "/sys/devices/virtual/input/"
+
+static struct libevdev_uinput *
+alloc_uinput_device(const char *name)
+{
+       struct libevdev_uinput *uinput_dev;
+
+       uinput_dev = calloc(1, sizeof(struct libevdev_uinput));
+       if (uinput_dev) {
+               uinput_dev->name = strdup(name);
+               uinput_dev->fd = -1;
+       }
+
+       return uinput_dev;
+}
+
+static int
+set_evbits(const struct libevdev *dev, int fd, struct uinput_user_dev *uidev)
+{
+       int rc = 0;
+       unsigned int type;
+
+       for (type = 0; type < EV_MAX; type++) {
+               unsigned int code;
+               int max;
+               int uinput_bit;
+               const unsigned long *mask;
+
+               if (!libevdev_has_event_type(dev, type))
+                       continue;
+
+               rc = ioctl(fd, UI_SET_EVBIT, type);
+               if (rc == -1)
+                       break;
+
+               /* uinput can't set EV_REP */
+               if (type == EV_REP)
+                       continue;
+
+               max = type_to_mask_const(dev, type, &mask);
+               if (max == -1)
+                       continue;
+
+               switch(type) {
+                       case EV_KEY: uinput_bit = UI_SET_KEYBIT; break;
+                       case EV_REL: uinput_bit = UI_SET_RELBIT; break;
+                       case EV_ABS: uinput_bit = UI_SET_ABSBIT; break;
+                       case EV_MSC: uinput_bit = UI_SET_MSCBIT; break;
+                       case EV_LED: uinput_bit = UI_SET_LEDBIT; break;
+                       case EV_SND: uinput_bit = UI_SET_SNDBIT; break;
+                       case EV_FF: uinput_bit = UI_SET_FFBIT; break;
+                       case EV_SW: uinput_bit = UI_SET_SWBIT; break;
+                       default:
+                                   rc = -1;
+                                   errno = EINVAL;
+                                   goto out;
+               }
+
+               for (code = 0; code < (unsigned int)max; code++) {
+                       if (!libevdev_has_event_code(dev, type, code))
+                               continue;
+
+                       rc = ioctl(fd, uinput_bit, code);
+                       if (rc == -1)
+                               goto out;
+
+                       if (type == EV_ABS) {
+                               const struct input_absinfo *abs = libevdev_get_abs_info(dev, code);
+                               uidev->absmin[code] = abs->minimum;
+                               uidev->absmax[code] = abs->maximum;
+                               uidev->absfuzz[code] = abs->fuzz;
+                               uidev->absflat[code] = abs->flat;
+                               /* uinput has no resolution in the device struct, this needs
+                                * to be fixed in the kernel */
+                       }
+               }
+
+       }
+
+out:
+       return rc;
+}
+
+static int
+set_props(const struct libevdev *dev, int fd, struct uinput_user_dev *uidev)
+{
+       unsigned int prop;
+       int rc = 0;
+
+       for (prop = 0; prop < INPUT_PROP_MAX; prop++) {
+               if (!libevdev_has_property(dev, prop))
+                       continue;
+
+               rc = ioctl(fd, UI_SET_PROPBIT, prop);
+               if (rc == -1)
+                       break;
+       }
+       return rc;
+}
+
+static int
+open_uinput(void)
+{
+       int fd = open("/dev/uinput", O_RDWR|O_CLOEXEC);
+       if (fd < 0)
+               return -errno;
+
+       return fd;
+}
+
+LIBEVDEV_EXPORT int
+libevdev_uinput_get_fd(const struct libevdev_uinput *uinput_dev)
+{
+       return uinput_dev->fd;
+}
+
+static int is_event_device(const struct dirent *dent) {
+       return strncmp("event", dent->d_name, 5) == 0;
+}
+
+static char *
+fetch_device_node(const char *path)
+{
+       char *devnode = NULL;
+       struct dirent **namelist;
+       int ndev, i;
+
+       ndev = scandir(path, &namelist, is_event_device, alphasort);
+       if (ndev <= 0)
+               return NULL;
+
+       /* ndev should only ever be 1 */
+
+       for (i = 0; i < ndev; i++) {
+               asprintf(&devnode, "/dev/input/%s", namelist[i]->d_name);
+               free(namelist[i]);
+       }
+
+       free(namelist);
+
+       return devnode;
+}
+
+static int is_input_device(const struct dirent *dent) {
+       return strncmp("input", dent->d_name, 5) == 0;
+}
+
+static int
+fetch_syspath_and_devnode(struct libevdev_uinput *uinput_dev)
+{
+       struct dirent **namelist;
+       int ndev, i;
+
+       /* FIXME: use new ioctl() here once kernel supports it */
+
+       ndev = scandir(SYS_INPUT_DIR, &namelist, is_input_device, alphasort);
+       if (ndev <= 0)
+               return -1;
+
+       for (i = 0; i < ndev; i++) {
+               int fd, len;
+               char buf[sizeof(SYS_INPUT_DIR) + 64];
+               struct stat st;
+
+               strcpy(buf, SYS_INPUT_DIR);
+               strcat(buf, namelist[i]->d_name);
+
+               if (stat(buf, &st) == -1)
+                       continue;
+
+               /* created before UI_DEV_CREATE, or after it finished */
+               if (st.st_ctime < uinput_dev->ctime[0] ||
+                   st.st_ctime > uinput_dev->ctime[1])
+                       continue;
+
+               /* created within time frame */
+               strcat(buf, "/name");
+               fd = open(buf, O_RDONLY);
+               if (fd < 0)
+                       continue;
+
+               len = read(fd, buf, sizeof(buf));
+               close(fd);
+               if (len <= 0)
+                       continue;
+
+               buf[len - 1] = '\0'; /* file contains \n */
+               if (strcmp(buf, uinput_dev->name) == 0) {
+                       strcpy(buf, SYS_INPUT_DIR);
+                       strcat(buf, namelist[i]->d_name);
+                       uinput_dev->syspath = strdup(buf);
+                       uinput_dev->devnode = fetch_device_node(buf);
+               }
+       }
+
+       for (i = 0; i < ndev; i++)
+               free(namelist[i]);
+       free(namelist);
+
+       return uinput_dev->devnode ? 0 : -1;
+}
+
+
+
+LIBEVDEV_EXPORT int
+libevdev_uinput_create_from_device(const struct libevdev *dev, int fd, struct libevdev_uinput** uinput_dev)
+{
+       int rc;
+       struct uinput_user_dev uidev;
+       struct libevdev_uinput *new_device;
+
+       new_device = alloc_uinput_device(libevdev_get_name(dev));
+       if (!new_device)
+               return -ENOMEM;
+
+       if (fd == LIBEVDEV_UINPUT_OPEN_MANAGED) {
+               fd = open_uinput();
+               if (fd < 0)
+                       return fd;
+
+               new_device->fd_is_managed = 1;
+       } else if (fd < 0)
+               return -EBADF;
+
+       memset(&uidev, 0, sizeof(uidev));
+
+       strncpy(uidev.name, libevdev_get_name(dev), UINPUT_MAX_NAME_SIZE - 1);
+       uidev.id.vendor = libevdev_get_id_vendor(dev);
+       uidev.id.product = libevdev_get_id_product(dev);
+       uidev.id.bustype = libevdev_get_id_bustype(dev);
+       uidev.id.version = libevdev_get_id_version(dev);
+
+       if (set_evbits(dev, fd, &uidev) != 0)
+               goto error;
+       if (set_props(dev, fd, &uidev) != 0)
+               goto error;
+
+       rc = write(fd, &uidev, sizeof(uidev));
+       if (rc < 0)
+               goto error;
+       else if ((size_t)rc < sizeof(uidev)) {
+               errno = EINVAL;
+               goto error;
+       }
+
+       /* ctime notes time before/after ioctl to help us filter out devices
+          when traversing /sys/devices/virtual/input to find the device
+          node.
+
+          this is in seconds, so ctime[0]/[1] will almost always be
+          identical but /sys doesn't give us sub-second ctime so...
+        */
+       new_device->ctime[0] = time(NULL);
+
+       rc = ioctl(fd, UI_DEV_CREATE, NULL);
+       if (rc == -1)
+               goto error;
+
+       new_device->ctime[1] = time(NULL);
+       new_device->fd = fd;
+
+       if (fetch_syspath_and_devnode(new_device) == -1) {
+               errno = ENODEV;
+               goto error;
+       }
+
+       *uinput_dev = new_device;
+
+       return 0;
+
+error:
+       libevdev_uinput_destroy(new_device);
+       return -errno;
+}
+
+LIBEVDEV_EXPORT void
+libevdev_uinput_destroy(struct libevdev_uinput *uinput_dev)
+{
+       ioctl(uinput_dev->fd, UI_DEV_DESTROY, NULL);
+       if (uinput_dev->fd_is_managed)
+               close(uinput_dev->fd);
+       free(uinput_dev->syspath);
+       free(uinput_dev->devnode);
+       free(uinput_dev->name);
+       free(uinput_dev);
+}
+
+LIBEVDEV_EXPORT const char*
+libevdev_uinput_get_syspath(struct libevdev_uinput *uinput_dev)
+{
+       return uinput_dev->syspath;
+}
+
+LIBEVDEV_EXPORT const char*
+libevdev_uinput_get_devnode(struct libevdev_uinput *uinput_dev)
+{
+       return uinput_dev->devnode;
+}
+
+LIBEVDEV_EXPORT int
+libevdev_uinput_write_event(const struct libevdev_uinput *uinput_dev,
+                           unsigned int type,
+                           unsigned int code,
+                           int value)
+{
+       struct input_event ev = { {0,0}, type, code, value };
+       int fd = libevdev_uinput_get_fd(uinput_dev);
+       int rc, max;
+
+       if (type > EV_MAX)
+               return -EINVAL;
+
+       max = libevdev_get_event_type_max(type);
+       if (max == -1 || code > (unsigned int)max)
+               return -EINVAL;
+
+       rc = write(fd, &ev, sizeof(ev));
+
+       return rc < 0 ? -errno : 0;
+}
diff --git a/libevdev/libevdev-uinput.h b/libevdev/libevdev-uinput.h
new file mode 100644 (file)
index 0000000..45b8a18
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * 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_uinput_H
+#define libevdev_uinput_H
+
+#include <libevdev/libevdev.h>
+
+struct libevdev_uinput;
+
+/**
+ * @defgroup uinput uinput device creation
+ *
+ * Creation of uinput devices based on existing libevdev devices. These functions
+ * help to create uinput devices that emulate libevdev devices. In the simplest
+ * form it serves to duplicate an existing device:
+ *
+ * @code
+ * int err;
+ * int new_fd;
+ * struct libevdev *dev;
+ * struct libevdev_uinput *uidev;
+ * struct input_event ev[2];
+ *
+ * err = libevdev_new_from_fd(&dev, fd);
+ * if (err != 0)
+ *     return err;
+ *
+ * uifd = open("/dev/uinput", O_RDWR);
+ * if (uidev < 0)
+ *     return -errno;
+ *
+ * err = libevdev_uinput_create_from_device(dev, uifd, &uidev);
+ * if (err != 0)
+ *     return err;
+ *
+ * // post a REL_X event
+ * err = libevdev_uinput_write(event(uidev, EV_REL, REL_X, -1);
+ * if (err != 0)
+ *     return err;
+ * libevdev_uinput_write(event(uidev, EV_SYN, SYN_REPORT, 0);
+ * if (err != 0)
+ *     return err;
+ *
+ * libevdev_uinput_destroy(uidev);
+ * close(uifd);
+ *
+ * @endcode
+ *
+ * Alternatively, a device can be constructed from scratch:
+ *
+ * @code
+ * int err;
+ * struct libevdev *dev;
+ * struct libevdev_uinput *uidev;
+ *
+ * dev = libevdev_new();
+ * libevdev_set_name(dev, "test device");
+ * libevdev_enable_event_type(dev, EV_REL);
+ * libevdev_enable_event_code(dev, EV_REL, REL_X);
+ * libevdev_enable_event_code(dev, EV_REL, REL_Y);
+ * libevdev_enable_event_type(dev, EV_KEY);
+ * libevdev_enable_event_code(dev, EV_KEY, BTN_LEFT);
+ * libevdev_enable_event_code(dev, EV_KEY, BTN_MIDDLE);
+ * libevdev_enable_event_code(dev, EV_KEY, BTN_RIGHT);
+ *
+ * err = libevdev_uinput_create_from_device(dev,
+ *                                          LIBEVDEV_UINPUT_OPEN_MANAGED,
+ *                                          &uidev);
+ * if (err != 0)
+ *     return err;
+ *
+ * // ... do something ...
+ *
+ * libevdev_uinput_destroy(uidev);
+ *
+ * @endcode
+ */
+
+enum libevdev_uinput_open_mode {
+       /* intentionally -2 to avoid to avoid code like the below from accidentally working:
+               fd = open("/dev/uinput", O_RDWR); // fails, fd is -1
+               libevdev_uinput_create_from_device(dev, fd, &uidev); // may hide the error */
+       LIBEVDEV_UINPUT_OPEN_MANAGED = -2, /**< let libevdev open and close @c /dev/uinput */
+};
+
+/**
+ * @ingroup uinput
+ *
+ * Create a uinput device based on the libevdev device given. The uinput device
+ * will be an exact copy of the libevdev device, minus the bits that uinput doesn't
+ * allow to be set.
+ *
+ * If uinput_fd is LIBEVDEV_UINPUT_OPEN_MANAGED, libevdev_uinput_create_from_device()
+ * will open @c /dev/uinput in read/write mode and manage the file descriptor.
+ * Otherwise, uinput_fd must be opened by the caller and opened with the
+ * appropriate permissions.
+ *
+ * The device's lifetime is tied to the uinput file descriptor, closing it will
+ * destroy the uinput device. You should call libevdev_uinput_destroy() before
+ * closing the file descriptor to free allocated resources.
+ * A file descriptor can only create one uinput device at a time; the second device
+ * will fail with -EINVAL.
+ *
+ * You don't need to keep the file descriptor variable around,
+ * libevdev_uinput_get_fd() will return it when needed.
+ *
+ * @note Due to limitations in the uinput kernel module, REP_DELAY and
+ * REP_PERIOD will default to the kernel defaults, not to the ones set in the
+ * source device.
+ *
+ * @param dev The device to duplicate
+ * @param uinput_fd LIBEVDEV_UINPUT_OPEN_MANAGED or a file descriptor to @c /dev/uinput,
+ * @param[out] uinput_dev The newly created libevdev device.
+ *
+ * @return 0 on success or a negative errno on failure. On failure, the value of
+ * uinput_dev is unmodified.
+ *
+ * @see libevdev_uinput_destroy
+ */
+int libevdev_uinput_create_from_device(const struct libevdev *dev,
+                                      int uinput_fd,
+                                      struct libevdev_uinput **uinput_dev);
+
+/**
+ * @ingroup uinput
+ *
+ * Destroy a previously created uinput device and free associated memory.
+ *
+ * If the device was opened with LIBEVDEV_UINPUT_OPEN_MANAGED, libevdev_uinput_destroy()
+ * also closes the file descriptor. Otherwise, the fd is left as-is and
+ * must be closed by the caller.
+ *
+ * @param uinput_dev A previously created uinput device.
+ *
+ * @return 0 on success or a negative errno on failure
+ */
+void libevdev_uinput_destroy(struct libevdev_uinput *uinput_dev);
+
+/**
+ * @ingroup uinput
+ *
+ * Return the file descriptor used to create this uinput device. This is the
+ * fd pointing to <strong>/dev/uinput</strong>. This file descriptor may be used to write
+ * events that are emitted by the uinput device.
+ * Closing this file descriptor will destroy the uinput device, you should
+ * call libevdev_uinput_destroy() first to free allocated resources.
+ *
+ * @param uinput_dev A previously created uinput device.
+ *
+ * @return The file descriptor used to create this device
+ */
+int libevdev_uinput_get_fd(const struct libevdev_uinput *uinput_dev);
+
+/**
+ * @ingroup uinput
+ *
+ * Return the syspath representing this uinput device.
+ * As of 3.11, the uinput kernel device does not
+ * provide a way to get the syspath directly through uinput so libevdev must guess.
+ * In some cases libevdev is unable to derive the syspath. If the running kernel
+ * supports the UI_GET_SYSPATH ioctl, the syspath is retrieved through that and will
+ * be reliable and not be NULL.
+ *
+ * @note This function may return NULL. libevdev currently uses ctime and
+ * the device name to guess devices. To avoid false positives, wait at least
+ * wait at least 1.5s between creating devices that have the same name.
+ * @param uinput_dev A previously created uinput device.
+ * @return The syspath for this device, including preceding /sys.
+ *
+ * @see libevdev_uinput_get_devnode
+ */
+const char*libevdev_uinput_get_syspath(struct libevdev_uinput *uinput_dev);
+
+/**
+ * @ingroup uinput
+ *
+ * Return the device node representing this uinput device.
+ *
+ * This relies on libevdev_uinput_get_syspath() to provide a valid syspath.
+ * See libevdev_uinput_get_syspath() for more details.
+ *
+ * @note This function may return NULL. libevdev currently has to guess the
+ * syspath and the device node. See libevdev_uinput_get_syspath() for details.
+ * @param uinput_dev A previously created uinput device.
+ * @return The device node for this device, in the form of /dev/input/eventN
+ *
+ * @see libevdev_uinput_get_syspath
+ */
+const char* libevdev_uinput_get_devnode(struct libevdev_uinput *uinput_dev);
+
+/**
+ * @ingroup uinput
+ *
+ * Post an event through the uinput device. It is the caller's responsibility
+ * that any event sequence is terminated with an EV_SYN/SYN_REPORT/0 event.
+ * Otherwise, listeners on the device node will not see the events until the
+ * next EV_SYN event is posted.
+ *
+ * @param uinput_dev A previously created uinput device.
+ * @param type Event type (EV_ABS, EV_REL, etc.)
+ * @param code Event code (ABS_X, REL_Y, etc.)
+ * @param value The event value
+ * @return 0 on success or a negative errno on error
+ */
+int libevdev_uinput_write_event(const struct libevdev_uinput *uinput_dev,
+                               unsigned int type,
+                               unsigned int code,
+                               int value);
+
+#endif /* libevdev_uinput_H */
index 12f7176..d3642c6 100644 (file)
@@ -5,6 +5,9 @@ TESTS = $(noinst_PROGRAMS)
 
 libevdev_sources = $(top_srcdir)/libevdev/libevdev.c \
                   $(top_srcdir)/libevdev/libevdev.h \
+                  $(top_srcdir)/libevdev/libevdev-uinput.h \
+                  $(top_srcdir)/libevdev/libevdev-uinput.c \
+                  $(top_srcdir)/libevdev/libevdev-uinput-int.h \
                   $(top_srcdir)/libevdev/libevdev-util.h \
                   $(top_srcdir)/libevdev/libevdev-int.h
 common_sources = $(libevdev_sources) \