[PATCH libinput 3/6] path: add libinput_path_add_device() and libinput_path_remove_device()

Peter Hutterer peter.hutterer at who-t.net
Wed Feb 5 20:13:07 PST 2014


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.hutterer at 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



More information about the wayland-devel mailing list