With this patch, a user can keep a reference to a libinput_seat
instance, which will cause the seat to never be unlinked from the
libinput context nor destroyed.

Previously, a when the last device of a seat was removed, the seat was
unlinked and if a new device was discovered with a previously empty seat
a new seat instance would always be created, meaning two potential seat
instances with identical physical and logical seat name pairs.

Signed-off-by: Jonas Ådahl <jad...@gmail.com>
---
 src/libinput.c  |  1 +
 src/libinput.h  |  5 +++
 src/path.c      | 14 +++++++--
 src/path.h      |  9 +++---
 src/udev-seat.c |  9 ------
 test/path.c     | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 test/udev.c     | 77 ++++++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 193 insertions(+), 16 deletions(-)

diff --git a/src/libinput.c b/src/libinput.c
index cfce2c5..cc84fb5 100644
--- a/src/libinput.c
+++ b/src/libinput.c
@@ -508,6 +508,7 @@ libinput_seat_init(struct libinput_seat *seat,
        seat->logical_name = strdup(logical_name);
        seat->destroy = destroy;
        list_init(&seat->devices_list);
+       list_insert(&libinput->seat_list, &seat->link);
 }
 
 LIBINPUT_EXPORT void
diff --git a/src/libinput.h b/src/libinput.h
index 9cd9d5b..4a481d2 100644
--- a/src/libinput.h
+++ b/src/libinput.h
@@ -1023,6 +1023,11 @@ libinput_device_get_output_name(struct libinput_device 
*device);
  *
  * Get the seat associated with this input device.
  *
+ * A seat can be uniquely identified by the physical and logical seat name.
+ * There will ever be only one seat instance with a given physical and logical
+ * seat name pair at any given time, but if no external reference is kept, it
+ * may be destroyed if no device belonging to it is left.
+ *
  * @param device A previously obtained device
  * @return The seat this input device belongs to
  */
diff --git a/src/path.c b/src/path.c
index 2893ad4..4e2cf1f 100644
--- a/src/path.c
+++ b/src/path.c
@@ -68,7 +68,6 @@ path_seat_create(struct path_input *input,
 
        libinput_seat_init(&seat->base, &input->base, seat_name,
                           seat_logical_name, path_seat_destroy);
-       list_insert(&input->base.seat_list, &seat->base.link);
 
        return seat;
 }
@@ -142,13 +141,22 @@ path_input_enable(struct libinput *libinput)
                return -1;
        }
 
-       seat = path_seat_create(input, seat_name, seat_logical_name);
+       if (!input->seat) {
+               input->seat = path_seat_create(input,
+                                              seat_name,
+                                              seat_logical_name);
+               if (!input->seat) {
+                       close_restricted(libinput, fd);
+                       log_info("could not create seat '%s'.\n", seat_name);
+                       return -1;
+               }
+       }
+       seat = input->seat;
        free(seat_name);
        free(seat_logical_name);
 
        device = evdev_device_create(&seat->base, devnode, sysname, fd);
        free(sysname);
-       libinput_seat_unref(&seat->base);
 
        if (device == EVDEV_UNHANDLED_DEVICE) {
                close_restricted(libinput, fd);
diff --git a/src/path.h b/src/path.h
index b840acf..12bbfb8 100644
--- a/src/path.h
+++ b/src/path.h
@@ -26,16 +26,17 @@
 #include "config.h"
 #include "libinput-private.h"
 
+struct path_seat {
+       struct libinput_seat base;
+};
+
 struct path_input {
        struct libinput base;
        char *path;
+       struct path_seat *seat;
        struct evdev_device *device;
 };
 
-struct path_seat {
-       struct libinput_seat base;
-};
-
 int path_input_process_event(struct libinput_event);
 
 #endif
diff --git a/src/udev-seat.c b/src/udev-seat.c
index 5936511..161b294 100644
--- a/src/udev-seat.c
+++ b/src/udev-seat.c
@@ -221,14 +221,6 @@ udev_input_remove_devices(struct udev_input *input)
                                   &seat->base.devices_list, base.link) {
                        close_restricted(&input->base, device->fd);
                        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);
-                       }
                }
                libinput_seat_unref(&seat->base);
        }
@@ -329,7 +321,6 @@ udev_seat_create(struct udev_input *input,
        libinput_seat_init(&seat->base, &input->base,
                           device_seat, seat_name,
                           udev_seat_destroy);
-       list_insert(&input->base.seat_list, &seat->base.link);
 
        return seat;
 }
diff --git a/test/path.c b/test/path.c
index 24f647f..8374582 100644
--- a/test/path.c
+++ b/test/path.c
@@ -334,6 +334,98 @@ START_TEST(path_double_resume)
 }
 END_TEST
 
+START_TEST(path_seat_recycle)
+{
+       struct libinput *li;
+       struct libevdev *evdev;
+       struct libevdev_uinput *uinput;
+       int rc;
+       void *userdata = &rc;
+       struct libinput_event *ev;
+       struct libinput_device *device;
+       struct libinput_seat *saved_seat = NULL;
+       struct libinput_seat *seat;
+       int data = 0;
+       int found = 0;
+       void *user_data;
+
+       evdev = libevdev_new();
+       ck_assert(evdev != NULL);
+
+       libevdev_set_name(evdev, "test device");
+       libevdev_enable_event_code(evdev, EV_KEY, BTN_LEFT, NULL);
+       libevdev_enable_event_code(evdev, EV_KEY, BTN_RIGHT, NULL);
+       libevdev_enable_event_code(evdev, EV_REL, REL_X, NULL);
+       libevdev_enable_event_code(evdev, EV_REL, REL_Y, NULL);
+
+       rc = libevdev_uinput_create_from_device(evdev,
+                                               LIBEVDEV_UINPUT_OPEN_MANAGED,
+                                               &uinput);
+       ck_assert_int_eq(rc, 0);
+       libevdev_free(evdev);
+
+       li = libinput_create_from_path(&simple_interface, userdata,
+                                      libevdev_uinput_get_devnode(uinput));
+       ck_assert(li != NULL);
+
+       libinput_dispatch(li);
+       while ((ev = libinput_get_event(li))) {
+               switch (libinput_event_get_type(ev)) {
+               case LIBINPUT_EVENT_DEVICE_ADDED:
+                       if (saved_seat)
+                               break;
+
+                       device = libinput_event_get_device(ev);
+                       ck_assert(device != NULL);
+                       saved_seat = libinput_device_get_seat(device);
+                       libinput_seat_set_user_data(saved_seat, &data);
+                       libinput_seat_ref(saved_seat);
+                       break;
+               default:
+                       break;
+               }
+
+               libinput_event_destroy(ev);
+       }
+
+       ck_assert(saved_seat != NULL);
+
+       libinput_suspend(li);
+
+       libinput_dispatch(li);
+       while ((ev = libinput_get_event(li)))
+               libinput_event_destroy(ev);
+
+       libinput_resume(li);
+
+       libinput_dispatch(li);
+       while ((ev = libinput_get_event(li))) {
+               switch (libinput_event_get_type(ev)) {
+               case LIBINPUT_EVENT_DEVICE_ADDED:
+                       device = libinput_event_get_device(ev);
+                       ck_assert(device != NULL);
+
+                       seat = libinput_device_get_seat(device);
+                       user_data = libinput_seat_get_user_data(seat);
+                       if (user_data == &data) {
+                               found = 1;
+                               ck_assert(seat == saved_seat);
+                       }
+                       break;
+               default:
+                       break;
+               }
+
+               libinput_event_destroy(ev);
+       }
+
+       ck_assert(found == 1);
+
+       libinput_destroy(li);
+}
+END_TEST
+
+
 int main (int argc, char **argv) {
 
        litest_add("path:create", path_create_NULL, LITEST_ANY, LITEST_ANY);
@@ -345,6 +437,8 @@ 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:seat", path_seat_recycle,
+                  LITEST_DISABLE_DEVICE, LITEST_DISABLE_DEVICE);
 
        return litest_run(argc, argv);
 }
diff --git a/test/udev.c b/test/udev.c
index 235d10e..29f87db 100644
--- a/test/udev.c
+++ b/test/udev.c
@@ -327,6 +327,82 @@ START_TEST(udev_device_sysname)
 }
 END_TEST
 
+START_TEST(udev_seat_recycle)
+{
+       struct udev *udev;
+       struct libinput *li;
+       struct libinput_event *ev;
+       struct libinput_device *device;
+       struct libinput_seat *saved_seat = NULL;
+       struct libinput_seat *seat;
+       int data = 0;
+       int found = 0;
+       void *user_data;
+
+       udev = udev_new();
+       ck_assert(udev != NULL);
+
+       li = libinput_create_from_udev(&simple_interface, NULL, udev, "seat0");
+       ck_assert(li != NULL);
+
+       libinput_dispatch(li);
+       while ((ev = libinput_get_event(li))) {
+               switch (libinput_event_get_type(ev)) {
+               case LIBINPUT_EVENT_DEVICE_ADDED:
+                       if (saved_seat)
+                               break;
+
+                       device = libinput_event_get_device(ev);
+                       ck_assert(device != NULL);
+                       saved_seat = libinput_device_get_seat(device);
+                       libinput_seat_set_user_data(saved_seat, &data);
+                       libinput_seat_ref(saved_seat);
+                       break;
+               default:
+                       break;
+               }
+
+               libinput_event_destroy(ev);
+       }
+
+       ck_assert(saved_seat != NULL);
+
+       libinput_suspend(li);
+
+       libinput_dispatch(li);
+       while ((ev = libinput_get_event(li)))
+               libinput_event_destroy(ev);
+
+       libinput_resume(li);
+
+       libinput_dispatch(li);
+       while ((ev = libinput_get_event(li))) {
+               switch (libinput_event_get_type(ev)) {
+               case LIBINPUT_EVENT_DEVICE_ADDED:
+                       device = libinput_event_get_device(ev);
+                       ck_assert(device != NULL);
+
+                       seat = libinput_device_get_seat(device);
+                       user_data = libinput_seat_get_user_data(seat);
+                       if (user_data == &data) {
+                               found = 1;
+                               ck_assert(seat == saved_seat);
+                       }
+                       break;
+               default:
+                       break;
+               }
+
+               libinput_event_destroy(ev);
+       }
+
+       ck_assert(found == 1);
+
+       libinput_destroy(li);
+       udev_unref(udev);
+}
+END_TEST
+
 int main (int argc, char **argv) {
 
        litest_add_no_device("udev:create", udev_create_NULL);
@@ -339,6 +415,7 @@ int main (int argc, char **argv) {
        litest_add("udev:suspend", udev_double_resume, LITEST_ANY, LITEST_ANY);
        litest_add("udev:suspend", udev_suspend_resume, LITEST_ANY, LITEST_ANY);
        litest_add("udev:device events", udev_device_sysname, LITEST_ANY, 
LITEST_ANY);
+       litest_add("udev:seat", udev_seat_recycle, LITEST_ANY, LITEST_ANY);
 
        return litest_run(argc, argv);
 }
-- 
1.8.3.2

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

Reply via email to