[PATCH 8/9] evdev: add hotplug support

Tiago Vignatti tiago.vignatti at intel.com
Mon Oct 24 07:42:21 PDT 2011


Signed-off-by: Tiago Vignatti <tiago.vignatti at intel.com>
---
 compositor/evdev.c |  112 +++++++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 106 insertions(+), 6 deletions(-)

diff --git a/compositor/evdev.c b/compositor/evdev.c
index b81e513..18ebe04 100644
--- a/compositor/evdev.c
+++ b/compositor/evdev.c
@@ -34,6 +34,8 @@
 
 struct evdev_input {
 	struct wlsc_input_device base;
+	struct wl_list devices_list;
+	struct udev_monitor *udev_monitor;
 };
 
 /* Event queue used to defer keyboard/button events until EV_SYN time. */
@@ -49,6 +51,7 @@ struct evdev_event_queue {
 
 struct evdev_input_device {
 	struct evdev_input *master;
+	struct wl_list link;
 	struct wl_event_source *source;
 	struct wlsc_output *output;
 	int fd;
@@ -62,6 +65,7 @@ struct evdev_input_device {
 
 	int is_touchpad, old_x_value, old_y_value, reset_x_value, reset_y_value;
 	uint32_t time;
+	char *devnode;
 
 	/* Event queue used to defer keyboard/button events until EV_SYN time */
 	int num_queue;
@@ -384,8 +388,11 @@ evdev_input_device_data(int fd, uint32_t mask, void *data)
 	{
 		len = read(fd, &ev, sizeof(ev));
 		if (len <= 0) {
-			/* FIXME: handle error... reopen device? */
-			fprintf(stderr, "input read error %s", strerror(errno));
+			if (errno == ENODEV)
+				close(device->fd);
+			else
+				fprintf(stderr, "input read error %s\n",
+				        strerror(errno));
 			break;
 		}
 		/* The kernel promises that we always only read a complete
@@ -473,6 +480,7 @@ evdev_input_device_create(struct evdev_input *master,
 	device->dx = 0;
 	device->dy = 0;
 	device->num_queue = 0;
+	device->devnode = strdup(path);
 
 	ec = (struct wlsc_compositor *) master->base.input_device.compositor;
 	device->output = 
@@ -483,6 +491,7 @@ evdev_input_device_create(struct evdev_input *master,
 
 	device->fd = open(path, O_RDONLY);
 	if (device->fd < 0) {
+		free(device->devnode);
 		free(device);
 		fprintf(stderr, "couldn't create pointer for %s: %m\n", path);
 		return NULL;
@@ -490,6 +499,7 @@ evdev_input_device_create(struct evdev_input *master,
 
 	if (evdev_configure_device(device) == -1) {
 		close(device->fd);
+		free(device->devnode);
 		free(device);
 		return NULL;
 	}
@@ -500,15 +510,101 @@ evdev_input_device_create(struct evdev_input *master,
 					      evdev_input_device_data, device);
 	if (device->source == NULL) {
 		close(device->fd);
+		free(device->devnode);
 		free(device);
 		return NULL;
 	}
 
+	wl_list_insert(master->devices_list.prev, &device->link);
 	return device;
 }
 
+static void
+device_added(struct udev_device *udev_device, struct evdev_input *master)
+{
+	struct wlsc_compositor *c =
+	    (struct wlsc_compositor *) master->base.input_device.compositor;
+
+	const char *devnode = udev_device_get_devnode(udev_device);
+
+	if (evdev_input_device_create(master, c->wl_display, devnode))
+		fprintf(stderr, "evdev input device: added: %s\n", devnode);
+}
+
+static void
+device_removed(struct udev_device *udev_device, struct evdev_input *master)
+{
+	const char *devnode = udev_device_get_devnode(udev_device);
+	struct evdev_input_device *device, *next;
+
+	wl_list_for_each_safe(device, next, &master->devices_list, link) {
+		if (!strcmp(device->devnode, devnode)) {
+			wl_event_source_remove(device->source);
+			wl_list_remove(&device->link);
+			close(device->fd);
+			free(device->devnode);
+			free(device);
+			break;
+		}
+	}
+	fprintf(stderr, "evdev input device: removed: %s\n", devnode);
+}
+
 static const char default_seat[] = "seat0";
 
+static int
+evdev_udev_handler(int fd, uint32_t mask, void *data)
+{
+	struct evdev_input *master = data;
+	struct udev_device *udev_device;
+	const char *action;
+
+	udev_device = udev_monitor_receive_device(master->udev_monitor);
+	if (!udev_device)
+		return 1;
+
+	action = udev_device_get_action(udev_device);
+	if (action) {
+		if (strncmp("event", udev_device_get_sysname(udev_device), 5) != 0)
+			return 0;
+
+		if (!strcmp(action, "add")) {
+			device_added(udev_device, master);
+		}
+		else if (!strcmp(action, "remove"))
+			device_removed(udev_device, master);
+	}
+	udev_device_unref(udev_device);
+
+	return 0;
+}
+
+static int
+evdev_config_udev_monitor(struct udev *udev, struct evdev_input *master)
+{
+	struct wl_event_loop *loop;
+	struct wlsc_compositor *c =
+	    (struct wlsc_compositor *) master->base.input_device.compositor;
+
+	master->udev_monitor = udev_monitor_new_from_netlink(udev, "udev");
+	if (!master->udev_monitor)
+		return 0;
+
+	udev_monitor_filter_add_match_subsystem_devtype(master->udev_monitor,
+			"input", NULL);
+
+	if (udev_monitor_enable_receiving(master->udev_monitor)) {
+		fprintf(stderr, "udev: failed to bind the udev monitor\n");
+		return 0;
+	}
+
+	loop = wl_display_get_event_loop(c->wl_display);
+	wl_event_loop_add_fd(loop, udev_monitor_get_fd(master->udev_monitor),
+			     WL_EVENT_READABLE, evdev_udev_handler, master);
+
+	return 1;
+}
+
 void
 evdev_input_add_devices(struct wlsc_compositor *c,
 			struct udev *udev, const char *seat)
@@ -527,6 +623,12 @@ evdev_input_add_devices(struct wlsc_compositor *c,
 	memset(input, 0, sizeof *input);
 	wlsc_input_device_init(&input->base, c);
 
+	wl_list_init(&input->devices_list);
+	if (!evdev_config_udev_monitor(udev, input)) {
+		free(input);
+		return;
+	}
+
 	e = udev_enumerate_new(udev);
 	udev_enumerate_add_match_subsystem(e, "input");
 	udev_enumerate_scan_devices(e);
@@ -542,10 +644,8 @@ evdev_input_add_devices(struct wlsc_compositor *c,
 		if (!device_seat)
 			device_seat = default_seat;
 
-		if (strcmp(device_seat, seat) == 0) {
-			evdev_input_device_create(input, c->wl_display,
-						  udev_device_get_devnode(device));
-		}
+		if (strcmp(device_seat, seat) == 0)
+			device_added(device, input);
 
 		udev_device_unref(device);
 	}
-- 
1.7.5.4



More information about the wayland-devel mailing list