summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Hutterer <peter.hutterer@who-t.net>2014-01-29 16:37:45 +1000
committerPeter Hutterer <peter.hutterer@who-t.net>2014-02-10 11:23:36 +1000
commit69dcea11b9e8a19541bdb7c47b5a285c99b72f7f (patch)
tree1f2d161307ed254d76a9b225c2f80d62790f1b14
parent6764e7e19a0b44e997707e6621974b1cc9c3d737 (diff)
downloadlibinput-69dcea11b9e8a19541bdb7c47b5a285c99b72f7f.tar.gz
path: add libinput_path_add_device() and libinput_path_remove_device()
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@who-t.net>
-rw-r--r--src/libinput.h45
-rw-r--r--src/path.c104
-rw-r--r--test/path.c114
3 files changed, 252 insertions, 11 deletions
diff --git a/src/libinput.h b/src/libinput.h
index e2d83bf4..e1d1ffb4 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 e68304dc..ce087f4f 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);
}
}
@@ -269,3 +286,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 != &interface_backend) {
+ 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 != &interface_backend) {
+ 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 c272e3a7..be47175a 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);
}