Make it possible to have persistent libinput_seat instances

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 <jadahl@gmail.com>
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This commit is contained in:
Jonas Ådahl 2014-02-10 11:40:55 +10:00
parent 26a1fff787
commit b34139c9e7
6 changed files with 177 additions and 18 deletions

View file

@ -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

View file

@ -1069,6 +1069,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
*/

View file

@ -49,14 +49,6 @@ path_disable_device(struct libinput *libinput,
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;
}
}
@ -97,7 +89,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;
}

View file

@ -203,14 +203,6 @@ udev_input_remove_devices(struct udev_input *input)
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);
}
}
libinput_seat_unref(&seat->base);
}
@ -311,7 +303,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;
}

View file

@ -778,6 +778,100 @@ START_TEST(path_add_device_suspend_resume_remove_device)
}
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_path_create_context(&simple_interface, userdata);
ck_assert(li != NULL);
device = libinput_path_add_device(li,
libevdev_uinput_get_devnode(uinput));
ck_assert(device != 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);
litest_drain_events(li);
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);
libevdev_uinput_destroy(uinput);
}
END_TEST
int main (int argc, char **argv) {
litest_add("path:create", path_create_NULL, LITEST_ANY, LITEST_ANY);
@ -796,6 +890,8 @@ int main (int argc, char **argv) {
litest_add("path:device events", path_add_invalid_path, 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);
litest_add("path:seat", path_seat_recycle,
LITEST_DISABLE_DEVICE, LITEST_DISABLE_DEVICE);
return litest_run(argc, argv);
}

View file

@ -327,6 +327,80 @@ 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_udev_create_for_seat(&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);
litest_drain_events(li);
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 +413,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);
}