This allows multiple devices to share a single libinput context. The new
function returns the newly added device immediately. Unlike the udev seat
where devices may or may not be added - over the lifetime of the seat - a
path-based backend knows immediately if device exists or doesn't exist.

Returning the device is required by callers that have the event processing
separate from adding devices - by the time we have the DEVICE_ADDED event in
the queue we may have other events to process first. And the DEVICE_ADDED
event won't easily link to the path we gave it anyway, so it's hard to figure
out which DEVICE_ADDED event corresponds to the new device.

Signed-off-by: Peter Hutterer <peter.hutte...@who-t.net>
---
 src/libinput.h |  45 +++++++++++++++++++++++
 src/path.c     | 104 ++++++++++++++++++++++++++++++++++++++++++++++------
 test/path.c    | 114 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 252 insertions(+), 11 deletions(-)

diff --git a/src/libinput.h b/src/libinput.h
index e2d83bf..e1d1ffb 100644
--- a/src/libinput.h
+++ b/src/libinput.h
@@ -729,6 +729,51 @@ libinput_create_from_path(const struct libinput_interface 
*interface,
 /**
  * @ingroup base
  *
+ * Add a device to a libinput context initialized with
+ * libinput_path_create_from_device(). If successful, the device will be
+ * added to the internal list and re-opened on libinput_resume(). The device
+ * can be removed with libinput_path_remove_device().
+ *
+ * If the device was successfully initialized, it is returned in the device
+ * argument. The lifetime of the returned device pointer is limited until
+ * the next linput_dispatch(), use libinput_device_ref() to keep a permanent
+ * reference.
+ *
+ * @param libinput A previously initialized libinput context
+ * @param path Path to an input device
+ * @return The newly initiated device on success, or NULL on failure.
+ *
+ * @note It is an application bug to call this function on a libinput
+ * context initialize with libinput_udev_create_for_seat().
+ */
+struct libinput_device *
+libinput_path_add_device(struct libinput *libinput,
+                        const char *path);
+
+/**
+ * @ingroup base
+ *
+ * Remove a device from a libinput context initialized with
+ * libinput_path_create_from_device() or added to such a context with
+ * libinput_path_add_device().
+ *
+ * Events already processed from this input device are kept in the queue,
+ * the LIBINPUT_EVENT_DEVICE_REMOVED event marks the end of events for this
+ * device.
+ *
+ * If no matching device exists, this function does nothing.
+ *
+ * @param device A libinput device
+ *
+ * @note It is an application bug to call this function on a libinput
+ * context initialize with libinput_udev_create_for_seat().
+ */
+void
+libinput_path_remove_device(struct libinput_device *device);
+
+/**
+ * @ingroup base
+ *
  * libinput keeps a single file descriptor for all events. Call into
  * libinput_dispatch() if any events become available on this fd.
  *
diff --git a/src/path.c b/src/path.c
index 32483df..a5b3338 100644
--- a/src/path.c
+++ b/src/path.c
@@ -22,6 +22,7 @@
 
 #include "config.h"
 
+#include <errno.h>
 #include <fcntl.h>
 #include <string.h>
 #include <libudev.h>
@@ -36,6 +37,31 @@ int path_input_process_event(struct libinput_event);
 static void path_seat_destroy(struct libinput_seat *seat);
 
 static void
+path_disable_device(struct libinput *libinput,
+                   struct evdev_device *device)
+{
+       struct libinput_seat *seat = device->base.seat;
+       struct evdev_device *dev, *next;
+
+       list_for_each_safe(dev, next,
+                          &seat->devices_list, base.link) {
+               if (dev != device)
+                       continue;
+
+               evdev_device_remove(device);
+               if (list_empty(&seat->devices_list)) {
+                       /* if the seat may be referenced by the
+                          client, so make sure it's dropped from
+                          the seat list now, to be freed whenever
+                        * the device is removed */
+                       list_remove(&seat->link);
+                       list_init(&seat->link);
+               }
+               break;
+       }
+}
+
+static void
 path_input_disable(struct libinput *libinput)
 {
        struct path_input *input = (struct path_input*)libinput;
@@ -45,17 +71,8 @@ path_input_disable(struct libinput *libinput)
        list_for_each_safe(seat, tmp, &input->base.seat_list, base.link) {
                libinput_seat_ref(&seat->base);
                list_for_each_safe(device, next,
-                                  &seat->base.devices_list, base.link) {
-                       evdev_device_remove(device);
-                       if (list_empty(&seat->base.devices_list)) {
-                               /* if the seat may be referenced by the
-                                  client, so make sure it's dropped from
-                                  the seat list now, to be freed whenever
-                                * the device is removed */
-                               list_remove(&seat->base.link);
-                               list_init(&seat->base.link);
-                       }
-               }
+                                  &seat->base.devices_list, base.link)
+                       path_disable_device(libinput, device);
                libinput_seat_unref(&seat->base);
        }
 }
@@ -270,3 +287,68 @@ libinput_create_from_path(const struct libinput_interface 
*interface,
 
        return &input->base;
 }
+
+LIBINPUT_EXPORT struct libinput_device *
+libinput_path_add_device(struct libinput *libinput,
+                        const char *path)
+{
+       struct path_input *input = (struct path_input*)libinput;
+       struct path_device *dev;
+       struct libinput_device *device;
+
+       if (libinput->interface_backend->backend_type != BACKEND_PATH) {
+               log_info("Mismatching backends. This is an application bug.\n");
+               return NULL;
+       }
+
+       dev = zalloc(sizeof *dev);
+       if (!dev)
+               return NULL;
+
+       dev->path = strdup(path);
+       if (!dev->path) {
+               free(dev);
+               return NULL;
+       }
+
+       list_insert(&input->path_list, &dev->link);
+
+       device = path_device_enable(input, dev->path);
+
+       if (!device) {
+               list_remove(&dev->link);
+               free(dev->path);
+               free(dev);
+       }
+
+       return device;
+}
+
+LIBINPUT_EXPORT void
+libinput_path_remove_device(struct libinput_device *device)
+{
+       struct libinput *libinput = device->seat->libinput;
+       struct path_input *input = (struct path_input*)libinput;
+       struct libinput_seat *seat;
+       struct evdev_device *evdev = (struct evdev_device*)device;
+       struct path_device *dev;
+
+       if (libinput->interface_backend->backend_type != BACKEND_PATH) {
+               log_info("Mismatching backends. This is an application bug.\n");
+               return;
+       }
+
+       list_for_each(dev, &input->path_list, link) {
+               if (strcmp(evdev->devnode, dev->path) == 0) {
+                       list_remove(&dev->link);
+                       free(dev->path);
+                       free(dev);
+                       break;
+               }
+       }
+
+       seat = device->seat;
+       libinput_seat_ref(seat);
+       path_disable_device(libinput, evdev);
+       libinput_seat_unref(seat);
+}
diff --git a/test/path.c b/test/path.c
index c272e3a..be47175 100644
--- a/test/path.c
+++ b/test/path.c
@@ -196,6 +196,56 @@ START_TEST(path_added_device)
 }
 END_TEST
 
+START_TEST(path_add_device)
+{
+       struct litest_device *dev = litest_current_device();
+       struct libinput *li = dev->libinput;
+       struct libinput_event *event;
+       struct libinput_device *device;
+       const char *sysname1 = NULL, *sysname2 = NULL;
+
+       libinput_dispatch(li);
+
+       while ((event = libinput_get_event(li))) {
+               enum libinput_event_type type;
+               type = libinput_event_get_type(event);
+
+               if (type == LIBINPUT_EVENT_DEVICE_ADDED) {
+                       ck_assert(sysname1 == NULL);
+                       device = libinput_event_get_device(event);
+                       ck_assert(device != NULL);
+                       sysname1 = libinput_device_get_sysname(device);
+               }
+
+               libinput_event_destroy(event);
+       }
+
+       device = libinput_path_add_device(li,
+                                         
libevdev_uinput_get_devnode(dev->uinput));
+       ck_assert(device != NULL);
+
+       libinput_dispatch(li);
+
+       while ((event = libinput_get_event(li))) {
+               enum libinput_event_type type;
+               type = libinput_event_get_type(event);
+
+               if (type == LIBINPUT_EVENT_DEVICE_ADDED) {
+                       ck_assert(sysname2 == NULL);
+                       device = libinput_event_get_device(event);
+                       ck_assert(device != NULL);
+                       sysname2 = libinput_device_get_sysname(device);
+               }
+
+               libinput_event_destroy(event);
+       }
+
+       ck_assert_int_eq(strcmp(sysname1, sysname2), 0);
+
+       libinput_event_destroy(event);
+}
+END_TEST
+
 START_TEST(path_device_sysname)
 {
        struct litest_device *dev = litest_current_device();
@@ -220,6 +270,67 @@ START_TEST(path_device_sysname)
 }
 END_TEST
 
+START_TEST(path_remove_device)
+{
+       struct litest_device *dev = litest_current_device();
+       struct libinput *li = dev->libinput;
+       struct libinput_event *event;
+       struct libinput_device *device;
+       int remove_event = 0;
+
+       device = libinput_path_add_device(li,
+                                         
libevdev_uinput_get_devnode(dev->uinput));
+       ck_assert(device != NULL);
+       litest_drain_events(li);
+
+       libinput_path_remove_device(device);
+       libinput_dispatch(li);
+
+       while ((event = libinput_get_event(li))) {
+               enum libinput_event_type type;
+               type = libinput_event_get_type(event);
+
+               if (type == LIBINPUT_EVENT_DEVICE_REMOVED)
+                       remove_event++;
+
+               libinput_event_destroy(event);
+       }
+
+       ck_assert_int_eq(remove_event, 1);
+}
+END_TEST
+
+START_TEST(path_double_remove_device)
+{
+       struct litest_device *dev = litest_current_device();
+       struct libinput *li = dev->libinput;
+       struct libinput_event *event;
+       struct libinput_device *device;
+       int remove_event = 0;
+
+       device = libinput_path_add_device(li,
+                                         
libevdev_uinput_get_devnode(dev->uinput));
+       ck_assert(device != NULL);
+       litest_drain_events(li);
+
+       libinput_path_remove_device(device);
+       libinput_path_remove_device(device);
+       libinput_dispatch(li);
+
+       while ((event = libinput_get_event(li))) {
+               enum libinput_event_type type;
+               type = libinput_event_get_type(event);
+
+               if (type == LIBINPUT_EVENT_DEVICE_REMOVED)
+                       remove_event++;
+
+               libinput_event_destroy(event);
+       }
+
+       ck_assert_int_eq(remove_event, 1);
+}
+END_TEST
+
 START_TEST(path_suspend)
 {
        struct libinput *li;
@@ -347,6 +458,9 @@ int main (int argc, char **argv) {
        litest_add("path:seat events", path_added_seat, LITEST_ANY, LITEST_ANY);
        litest_add("path:device events", path_added_device, LITEST_ANY, 
LITEST_ANY);
        litest_add("path:device events", path_device_sysname, LITEST_ANY, 
LITEST_ANY);
+       litest_add("path:device events", path_add_device, LITEST_ANY, 
LITEST_ANY);
+       litest_add("path:device events", path_remove_device, LITEST_ANY, 
LITEST_ANY);
+       litest_add("path:device events", path_double_remove_device, LITEST_ANY, 
LITEST_ANY);
 
        return litest_run(argc, argv);
 }
-- 
1.8.4.2

_______________________________________________
wayland-devel mailing list
wayland-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/wayland-devel

Reply via email to