[PATCH libevdev] When changing the fd, reset our grab state to ungrabbed
Peter Hutterer
peter.hutterer at who-t.net
Wed Dec 13 00:12:01 UTC 2017
Previously, calling grabbing a device after changing the fd was a no-op
because libevdev's grab state didn't match the fd:
libevdev_grab(LIBEVDEV_GRAB);
.. fd is grabbed
.. internal state is 'grabbed'
libevdev_change_fd();
.. new fd is ungrabbed
.. internal state is 'grabbed'
libevdev_grab(LIBEVDEV_GRAB);
.. argument matches internal state and we exit without grabbing the device
Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
libevdev/libevdev.c | 1 +
libevdev/libevdev.h | 7 ++++
test/test-libevdev-init.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 96 insertions(+)
diff --git a/libevdev/libevdev.c b/libevdev/libevdev.c
index 41bfd25..de0c476 100644
--- a/libevdev/libevdev.c
+++ b/libevdev/libevdev.c
@@ -311,6 +311,7 @@ libevdev_change_fd(struct libevdev *dev, int fd)
return -1;
}
dev->fd = fd;
+ dev->grabbed = LIBEVDEV_UNGRAB;
return 0;
}
diff --git a/libevdev/libevdev.h b/libevdev/libevdev.h
index 3871bad..b36d3b4 100644
--- a/libevdev/libevdev.h
+++ b/libevdev/libevdev.h
@@ -966,6 +966,10 @@ enum libevdev_grab_mode {
* Grabbing an already grabbed device, or ungrabbing an ungrabbed device is
* a noop and always succeeds.
*
+ * A grab is an operation tied to a file descriptor, not a device. If a
+ * client changes the file descriptor with libevdev_change_fd(), it must
+ * also re-issue a grab with libevdev_grab().
+ *
* @param dev The evdev device, already initialized with libevdev_set_fd()
* @param grab If true, grab the device. Otherwise ungrab the device.
*
@@ -1034,6 +1038,9 @@ int libevdev_set_fd(struct libevdev* dev, int fd);
*
* The fd may be open in O_RDONLY or O_RDWR.
*
+ * After changing the fd, the device is assumed ungrabbed and a caller must
+ * call libevdev_grab() again.
+ *
* It is an error to call this function before calling libevdev_set_fd().
*
* @param dev The evdev device, already initialized with libevdev_set_fd()
diff --git a/test/test-libevdev-init.c b/test/test-libevdev-init.c
index f673a58..5600441 100644
--- a/test/test-libevdev-init.c
+++ b/test/test-libevdev-init.c
@@ -468,6 +468,93 @@ START_TEST(test_device_grab_invalid_fd)
}
END_TEST
+START_TEST(test_device_grab_change_fd)
+{
+ struct libevdev_uinput *uidev;
+ struct libevdev *dev, *other;
+ struct input_event e;
+ int rc;
+ int other_fd;
+ int dev_fd;
+
+ dev = libevdev_new();
+ libevdev_set_name(dev, "libevdev test device");
+ libevdev_enable_event_code(dev, EV_REL, REL_X, NULL);
+ libevdev_enable_event_code(dev, EV_REL, REL_Y, NULL);
+ libevdev_enable_event_code(dev, EV_KEY, BTN_LEFT, NULL);
+
+ rc = libevdev_uinput_create_from_device(dev,
+ LIBEVDEV_UINPUT_OPEN_MANAGED,
+ &uidev);
+ ck_assert_int_eq(rc, 0);
+ libevdev_free(dev);
+
+ dev_fd = open(libevdev_uinput_get_devnode(uidev),
+ O_RDONLY|O_NONBLOCK);
+ ck_assert_int_ne(dev_fd, -1);
+ rc = libevdev_new_from_fd(dev_fd, &dev);
+ ck_assert_int_eq(rc, 0);
+
+ other_fd = open(libevdev_uinput_get_devnode(uidev),
+ O_RDONLY|O_NONBLOCK);
+ ck_assert_int_ne(other_fd, -1);
+ rc = libevdev_new_from_fd(other_fd, &other);
+ ck_assert_int_eq(rc, 0);
+
+ /* check we're getting the events before the grab */
+ libevdev_uinput_write_event(uidev, EV_REL, REL_X, -1);
+ libevdev_uinput_write_event(uidev, EV_SYN, SYN_REPORT, 0);
+ rc = libevdev_next_event(other, LIBEVDEV_READ_FLAG_NORMAL, &e);
+ ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SUCCESS);
+ rc = libevdev_next_event(other, LIBEVDEV_READ_FLAG_NORMAL, &e);
+ ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SUCCESS);
+ rc = libevdev_next_event(other, LIBEVDEV_READ_FLAG_NORMAL, &e);
+ ck_assert_int_eq(rc, -EAGAIN);
+
+ /* no events after the grab */
+ rc = libevdev_grab(dev, LIBEVDEV_GRAB);
+ ck_assert_int_eq(rc, 0);
+ libevdev_uinput_write_event(uidev, EV_REL, REL_X, -1);
+ libevdev_uinput_write_event(uidev, EV_SYN, SYN_REPORT, 0);
+ rc = libevdev_grab(dev, LIBEVDEV_GRAB);
+ ck_assert_int_eq(rc, 0);
+ rc = libevdev_next_event(other, LIBEVDEV_READ_FLAG_NORMAL, &e);
+ ck_assert_int_eq(rc, -EAGAIN);
+
+ /* swapping the fd removes the grab */
+ close(dev_fd);
+ dev_fd = open(libevdev_uinput_get_devnode(uidev),
+ O_RDONLY|O_NONBLOCK);
+ ck_assert_int_ne(dev_fd, -1);
+ rc = libevdev_change_fd(dev, dev_fd);
+ ck_assert_int_eq(rc, 0);
+
+ /* check we're getting the events again */
+ libevdev_uinput_write_event(uidev, EV_REL, REL_X, -1);
+ libevdev_uinput_write_event(uidev, EV_SYN, SYN_REPORT, 0);
+ rc = libevdev_next_event(other, LIBEVDEV_READ_FLAG_NORMAL, &e);
+ ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SUCCESS);
+ rc = libevdev_next_event(other, LIBEVDEV_READ_FLAG_NORMAL, &e);
+ ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SUCCESS);
+ rc = libevdev_next_event(other, LIBEVDEV_READ_FLAG_NORMAL, &e);
+ ck_assert_int_eq(rc, -EAGAIN);
+
+ /* no events after the grab */
+ rc = libevdev_grab(dev, LIBEVDEV_GRAB);
+ ck_assert_int_eq(rc, 0);
+ libevdev_uinput_write_event(uidev, EV_REL, REL_X, -1);
+ libevdev_uinput_write_event(uidev, EV_SYN, SYN_REPORT, 0);
+ rc = libevdev_next_event(other, LIBEVDEV_READ_FLAG_NORMAL, &e);
+ ck_assert_int_eq(rc, -EAGAIN);
+
+ libevdev_uinput_destroy(uidev);
+ libevdev_free(dev);
+ libevdev_free(other);
+ close(dev_fd);
+ close(other_fd);
+}
+END_TEST
+
START_TEST(test_set_clock_id)
{
struct uinput_device* uidev;
@@ -625,6 +712,7 @@ libevdev_init_test(void)
tc = tcase_create("device grab");
tcase_add_test(tc, test_device_grab);
tcase_add_test(tc, test_device_grab_invalid_fd);
+ tcase_add_test(tc, test_device_grab_change_fd);
suite_add_tcase(s, tc);
tc = tcase_create("clock id");
--
2.13.6
More information about the Input-tools
mailing list