When changing the fd, reset our grab state to ungrabbed
authorPeter Hutterer <peter.hutterer@who-t.net>
Tue, 12 Dec 2017 23:20:55 +0000 (09:20 +1000)
committerPeter Hutterer <peter.hutterer@who-t.net>
Wed, 13 Dec 2017 00:01:58 +0000 (10:01 +1000)
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@who-t.net>
libevdev/libevdev.c
libevdev/libevdev.h
test/test-libevdev-init.c

index 41bfd25127a58b9e1c5900c880eefaa75dddccd2..de0c476dc939a62b8624a8b9333516f00e16498a 100644 (file)
@@ -311,6 +311,7 @@ libevdev_change_fd(struct libevdev *dev, int fd)
                return -1;
        }
        dev->fd = fd;
+       dev->grabbed = LIBEVDEV_UNGRAB;
        return 0;
 }
 
index 3871bad6714668dc58673e8f3370c4894a6845e7..b36d3b4fffcc842659ae249e7b8d2b20bf09c71e 100644 (file)
@@ -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()
index f673a5890eca22f436becdad06f92e97617ed60b..5600441029392f9120786a7af2605b3db19810e8 100644 (file)
@@ -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");