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