[PATCH xf86-input-libinput] Support server-side fds

Peter Hutterer peter.hutterer at who-t.net
Mon Dec 1 21:01:46 PST 2014


libinput's device handling and server-side fd handling are a bit of a
mismatch, so this is hackier than one would hope for.

The server sets pInfo->fd and the options "fd" and "device".
The server requires pInfo->fd to be the one triggering select(2) to call the
correct pInfo->read_input. We can't pass an fd to use into libinput, all we
have is the open_restricted callback. That callback gives us the context, but
not the pInfo with the fd we want.

The solution:
1) In PreInit, store the patch + fd combination in a driver-wide list. Search
that list for an fd in open_restricted, return the pre-openend fd.

2) Overwrite all devices' pInfo->fd with the libinput epollfd. In this driver
we don't care about which device read_input is called on, we get the correct
pInfo to post events from through the struct libinput_device of the libinput
events.

3) When a device is closed, swap the real fd back in so systemd-logind closes the
right fd.

This saves us worrying about keeping the right refcount on who currently has
the fd set to the libinput fd. We just set it for all devices, let the server
figure out which device to call (the first in inputInfo.devices) and we only
need to remember to swap it back during DEVICE_OFF.

If the server calls close on a pInfo->fd without telling the driver, that's a
bug anyway.

This patch also drops the pInfo->fd = -1 calls, they've been unnecessary since
at least 1.11, possibly earlier.

Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
 src/libinput.c | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 121 insertions(+), 5 deletions(-)

diff --git a/src/libinput.c b/src/libinput.c
index 04681e9..e00a428 100644
--- a/src/libinput.c
+++ b/src/libinput.c
@@ -31,6 +31,7 @@
 #include <time.h>
 #include <unistd.h>
 #include <xorg-server.h>
+#include <list.h>
 #include <exevents.h>
 #include <xkbsrv.h>
 #include <xf86Xinput.h>
@@ -40,6 +41,10 @@
 
 #include <X11/Xatom.h>
 
+#ifndef XI86_SERVER_FD
+#define XI86_SERVER_FD 0x20
+#endif
+
 #define TOUCHPAD_NUM_AXES 4 /* x, y, hscroll, vscroll */
 #define TOUCH_MAX_SLOTS 15
 #define XORG_KEYCODE_OFFSET 8
@@ -61,6 +66,7 @@
 struct xf86libinput_driver {
 	struct libinput *libinput;
 	int device_enabled_count;
+	struct xorg_list server_fds;
 };
 
 static struct xf86libinput_driver driver_context;
@@ -95,6 +101,80 @@ struct xf86libinput {
 	} options;
 };
 
+/*
+   libinput provides a userdata for the context, but not per path device. so
+   the open_restricted call has the libinput context, but no reference to
+   the pInfo->fd that we actually need to return.
+   To avoid this, we store each path/fd combination during pre_init in the
+   context, then return that during open_restricted. If a device is added
+   twice with two different fds this may give us the wrong fd but why are
+   you doing that anyway.
+ */
+struct serverfd {
+	struct xorg_list node;
+	int fd;
+	char *path;
+};
+
+static inline int
+use_server_fd(const InputInfoPtr pInfo) {
+	return pInfo->fd > -1 && (pInfo->flags & XI86_SERVER_FD);
+}
+
+static inline void
+fd_push(struct xf86libinput_driver *context,
+	int fd,
+	const char *path)
+{
+	struct serverfd *sfd = calloc(1, sizeof(*sfd));
+
+	sfd->fd = fd;
+	sfd->path = strdup(path);
+	xorg_list_add(&sfd->node, &context->server_fds);
+}
+
+static inline int
+fd_get(struct xf86libinput_driver *context,
+       const char *path)
+{
+	struct serverfd *sfd;
+
+	xorg_list_for_each_entry(sfd, &context->server_fds, node) {
+		if (strcmp(path, sfd->path) == 0)
+			return sfd->fd;
+	}
+
+	return -1;
+}
+
+static inline void
+fd_pop(struct xf86libinput_driver *context, int fd)
+{
+	struct serverfd *sfd;
+
+	xorg_list_for_each_entry(sfd, &context->server_fds, node) {
+		if (fd != sfd->fd)
+			continue;
+
+		xorg_list_del(&sfd->node);
+		free(sfd->path);
+		free(sfd);
+		break;
+	}
+}
+
+static inline int
+fd_find(struct xf86libinput_driver *context, int fd)
+{
+	struct serverfd *sfd;
+
+	xorg_list_for_each_entry(sfd, &context->server_fds, node) {
+		if (fd == sfd->fd)
+			return fd;
+	}
+	return -1;
+}
+
 static inline unsigned int
 btn_linux2xorg(unsigned int b)
 {
@@ -222,13 +302,24 @@ xf86libinput_on(DeviceIntPtr dev)
 	struct libinput *libinput = driver_context.libinput;
 	struct libinput_device *device = driver_data->device;
 
+	if (use_server_fd(pInfo)) {
+		char *path = xf86SetStrOption(pInfo->options, "Device", NULL);
+		fd_push(&driver_context, pInfo->fd, path);
+		free(path);
+	}
+
 	device = libinput_path_add_device(libinput, driver_data->path);
 	if (!device)
 		return !Success;
+
 	libinput_device_ref(device);
 	libinput_device_set_user_data(device, pInfo);
 	driver_data->device = device;
 
+	/* if we use server fds, overwrite the fd with the one from
+	   libinput nonetheless, otherwise the server won't call ReadInput
+	   for our device. This must be swapped back to the real fd in
+	   DEVICE_OFF so systemd-logind closes the right fd */
 	pInfo->fd = libinput_get_fd(libinput);
 
 	if (driver_context.device_enabled_count == 0) {
@@ -252,7 +343,13 @@ xf86libinput_off(DeviceIntPtr dev)
 		RemoveEnabledDevice(pInfo->fd);
 	}
 
-	pInfo->fd = -1;
+	if (use_server_fd(pInfo)) {
+		fd_pop(&driver_context, pInfo->fd);
+		pInfo->fd = xf86SetIntOption(pInfo->options, "fd", -1);
+	} else {
+		pInfo->fd = -1;
+	}
+
 	dev->public.on = FALSE;
 
 	libinput_device_set_user_data(driver_data->device, NULL);
@@ -693,14 +790,22 @@ xf86libinput_read_input(InputInfoPtr pInfo)
 static int
 open_restricted(const char *path, int flags, void *data)
 {
-	int fd = open(path, flags);
+	struct xf86libinput_driver *context = data;
+	int fd;
+
+	fd = fd_get(context, path);
+	if (fd == -1)
+		fd = open(path, flags);
 	return fd < 0 ? -errno : fd;
 }
 
 static void
 close_restricted(int fd, void *data)
 {
-	close(fd);
+	struct xf86libinput_driver *context = data;
+
+	if (fd_find(context, fd) == -1)
+		close(fd);
 }
 
 const struct libinput_interface interface = {
@@ -934,7 +1039,6 @@ static int xf86libinput_pre_init(InputDriverPtr drv,
 	struct libinput_device *device;
 	char *path;
 
-	pInfo->fd = -1;
 	pInfo->type_name = 0;
 	pInfo->device_control = xf86libinput_device_control;
 	pInfo->read_input = xf86libinput_read_input;
@@ -963,6 +1067,7 @@ static int xf86libinput_pre_init(InputDriverPtr drv,
 		/* we want all msgs, let the server filter */
 		libinput_log_set_priority(driver_context.libinput,
 					  LIBINPUT_LOG_PRIORITY_DEBUG);
+		xorg_list_init(&driver_context.server_fds);
 	} else {
 		libinput_ref(driver_context.libinput);
 	}
@@ -974,6 +1079,9 @@ static int xf86libinput_pre_init(InputDriverPtr drv,
 		goto fail;
 	}
 
+	if (use_server_fd(pInfo))
+		fd_push(&driver_context, pInfo->fd, path);
+
 	device = libinput_path_add_device(libinput, path);
 	if (!device) {
 		xf86IDrvMsg(pInfo, X_ERROR, "Failed to create a device for %s\n", path);
@@ -985,8 +1093,9 @@ static int xf86libinput_pre_init(InputDriverPtr drv,
 	  */
 	libinput_device_ref(device);
 	libinput_path_remove_device(device);
+	if (use_server_fd(pInfo))
+	    fd_pop(&driver_context, pInfo->fd);
 
-	pInfo->fd = -1;
 	pInfo->private = driver_data;
 	driver_data->path = path;
 	driver_data->device = device;
@@ -1011,6 +1120,8 @@ static int xf86libinput_pre_init(InputDriverPtr drv,
 
 	return Success;
 fail:
+	if (use_server_fd(pInfo) && driver_context.libinput != NULL)
+		fd_pop(&driver_context, pInfo->fd);
 	if (driver_data->valuators)
 		valuator_mask_free(&driver_data->valuators);
 	free(path);
@@ -1039,6 +1150,11 @@ InputDriverRec xf86libinput_driver = {
 	.driverName	= "libinput",
 	.PreInit	= xf86libinput_pre_init,
 	.UnInit		= xf86libinput_uninit,
+	.module		= NULL,
+	.default_options= NULL,
+#ifdef XI86_DRV_CAP_SERVER_FD
+	.capabilities	= XI86_DRV_CAP_SERVER_FD
+#endif
 };
 
 static XF86ModuleVersionInfo xf86libinput_version_info = {
-- 
2.1.0



More information about the xorg-devel mailing list