[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