[RFC PATCH libinput] udev: add libinput_udev_rescan_devices()

Peter Hutterer peter.hutterer at who-t.net
Wed Mar 12 23:18:20 PDT 2014


When a libinput context for a given seat is initialized, not all devices may
be available. Some or all devices may be paused by systemd-logind. Waiting for
a unplug event is not appropriate here as the devices are physically
available, just prevented from getting access.

Add a libinput_udev_rescan_devices() function that triggers a scan of all
devices on the current udev seat. Devices that do not exist on the seat are
added, devices that already exist on the seat but have been revoked are
removed.

Note that devices that have been physically removed are not removed, instead
we wait for the udev event to handle this for us.

Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
The idea here is basically to start a udev context as usual. If the
compositor doesn't have the session, open_restricted will fail. Once the
ResumeDevice signals are handled by the compositor it can ask libinput to
rescan the device list to add the ones that failed before (or remove revoked
ones).

Notes on why this approach:
* libinput_device_suspend()/resume() seems nicer at first but if a device
  fails to open, we don't have a libinput_device context. handling that
  would make the API complicated since we cannot guarantee that all
  libinput_device_* functions (specificall has_capability) work on all
  devices anymore.
* I suspect in the 90% case the the PauseDevice/ResumeDevice signals come in
  in a batch anyway, so the compositor should be able to optimise this to
  one single call
* this is a udev specific call, for the path backend the compositor can and
  should maintain the devices manually anyway
* EVIOCGVERSION was picked because it always succeeds, except after revoke

This is an RFC at this point, let me know if that approach works. Still need
to write tests and better evdev duplicate detection - right now there is a
race condition that could remove the wrong device.

 src/evdev.c     | 15 +++++++++++++++
 src/evdev.h     |  2 ++
 src/libinput.h  | 21 +++++++++++++++++++++
 src/udev-seat.c | 46 +++++++++++++++++++++++++++++++++++++++++-----
 4 files changed, 79 insertions(+), 5 deletions(-)

diff --git a/src/evdev.c b/src/evdev.c
index ba7c8b3..018fbb1 100644
--- a/src/evdev.c
+++ b/src/evdev.c
@@ -790,3 +790,18 @@ evdev_device_destroy(struct evdev_device *device)
 	free(device->sysname);
 	free(device);
 }
+
+int
+evdev_device_is_alive(struct evdev_device *device)
+{
+	int rc;
+	int version;
+
+	rc = ioctl(device->fd, EVIOCGVERSION, &version);
+
+	if (rc < 0 && errno != ENODEV)
+		log_info("evdev: %s failed with errno %d (%s)\n",
+			 __func__, errno, strerror(errno));
+
+	return rc != -1;
+}
diff --git a/src/evdev.h b/src/evdev.h
index b83a2f9..82a3873 100644
--- a/src/evdev.h
+++ b/src/evdev.h
@@ -156,4 +156,6 @@ evdev_device_remove(struct evdev_device *device);
 void
 evdev_device_destroy(struct evdev_device *device);
 
+int
+evdev_device_is_alive(struct evdev_device *device);
 #endif /* EVDEV_H */
diff --git a/src/libinput.h b/src/libinput.h
index 3e09871..dadcac2 100644
--- a/src/libinput.h
+++ b/src/libinput.h
@@ -715,6 +715,27 @@ libinput_udev_create_for_seat(const struct libinput_interface *interface,
 /**
  * @ingroup base
  *
+ * Re-scan the list of devices available to this context. Devices in the
+ * seat specified in libinput_udev_create_for_seat() that previously have
+ * failed to initialize are re-initialized. Devices that have successfully
+ * re-initialized but are now revoked are removed.
+ *
+ * Calling libinput_udev_rescan_devices() on a context suspended with
+ * libinput_suspend() does nothing.
+ *
+ * @note This function should not be used for detection of physically added
+ * or removed devices, libinput_dispatch() detects those. This function
+ * should only be used to re-open or close existing devices, e.g. if
+ * systemd-logind prevented access to a device before.
+ *
+ * @param libinput The previously initialized libinput context
+ */
+void
+libinput_udev_rescan_devices(struct libinput *libinput);
+
+/**
+ * @ingroup base
+ *
  * Create a new libinput context that requires the caller to manually add or
  * remove devices with libinput_path_add_device() and
  * libinput_path_remove_device().
diff --git a/src/udev-seat.c b/src/udev-seat.c
index 366c92b..5b09e4b 100644
--- a/src/udev-seat.c
+++ b/src/udev-seat.c
@@ -136,12 +136,28 @@ device_removed(struct udev_device *udev_device, struct udev_input *input)
 	}
 }
 
+static struct evdev_device*
+udev_input_find_device_by_sysname(struct udev_input *input, const char *sysname)
+{
+	struct udev_seat *seat;
+	struct evdev_device *device;
+
+	list_for_each(seat, &input->base.seat_list, base.link) {
+		list_for_each(device, &seat->base.devices_list, base.link)
+			if (!strcmp(device->sysname, sysname)) {
+				return device;
+			}
+	}
+	return NULL;
+}
+
 static int
 udev_input_add_devices(struct udev_input *input, struct udev *udev)
 {
 	struct udev_enumerate *e;
 	struct udev_list_entry *entry;
 	struct udev_device *device;
+	struct evdev_device *evdev;
 	const char *path, *sysname;
 
 	e = udev_enumerate_new(udev);
@@ -157,11 +173,15 @@ udev_input_add_devices(struct udev_input *input, struct udev *udev)
 			continue;
 		}
 
-		if (device_added(device, input) < 0) {
-			udev_device_unref(device);
-			udev_enumerate_unref(e);
-			return -1;
-		}
+		evdev = udev_input_find_device_by_sysname(input, sysname);
+		if (!evdev) {
+			if (device_added(device, input) < 0) {
+				udev_device_unref(device);
+				udev_enumerate_unref(e);
+				return -1;
+			}
+		} else if (!evdev_device_is_alive(evdev))
+			device_removed(device, input);
 
 		udev_device_unref(device);
 	}
@@ -364,3 +384,19 @@ libinput_udev_create_for_seat(const struct libinput_interface *interface,
 
 	return &input->base;
 }
+
+LIBINPUT_EXPORT void
+libinput_udev_rescan_devices(struct libinput *libinput)
+{
+	struct udev_input *udev_input = (struct udev_input*)libinput;
+
+	if (libinput->interface_backend != &interface_backend) {
+		log_error("Mismatching backends. This is an application bug.\n");
+		return;
+	}
+
+	if (udev_input->udev_monitor == NULL)
+		return;
+
+	udev_input_add_devices(udev_input, udev_input->udev);
+}
-- 
1.8.5.3



More information about the wayland-devel mailing list