[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

xserver-xorg-input-libinput: Changes to 'debian-unstable'



 configure.ac       |    2 
 debian/changelog   |    6 
 src/xf86libinput.c |  525 ++++++++++++++++++++++++++++++++++++++++++++---------
 3 files changed, 450 insertions(+), 83 deletions(-)

New commits:
commit ac18c3a1108d62e495a0cc9cf4d8bdb1e9080721
Author: Timo Aaltonen <tjaalton@debian.org>
Date:   Thu Jan 14 15:30:24 2016 +0200

    release to unstable

diff --git a/debian/changelog b/debian/changelog
index 1c57b02..1ab3369 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,8 +1,8 @@
-xserver-xorg-input-libinput (0.16.0-1) UNRELEASED; urgency=medium
+xserver-xorg-input-libinput (0.16.0-1) unstable; urgency=medium
 
   * New upstream release.
 
- -- Timo Aaltonen <tjaalton@debian.org>  Thu, 14 Jan 2016 14:45:17 +0200
+ -- Timo Aaltonen <tjaalton@debian.org>  Thu, 14 Jan 2016 14:59:15 +0200
 
 xserver-xorg-input-libinput (0.15.0-1) unstable; urgency=medium
 

commit 728ada14db50b42a2b17782a11aae07b617fd0e4
Author: Timo Aaltonen <tjaalton@debian.org>
Date:   Thu Jan 14 14:59:04 2016 +0200

    New upstream release.

diff --git a/debian/changelog b/debian/changelog
index 31913ea..1c57b02 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+xserver-xorg-input-libinput (0.16.0-1) UNRELEASED; urgency=medium
+
+  * New upstream release.
+
+ -- Timo Aaltonen <tjaalton@debian.org>  Thu, 14 Jan 2016 14:45:17 +0200
+
 xserver-xorg-input-libinput (0.15.0-1) unstable; urgency=medium
 
   * New upstream release.

commit 0d1851a000c5a80ba9b5787f516d2d72c62ce35e
Author: Peter Hutterer <peter.hutterer@who-t.net>
Date:   Wed Dec 23 13:53:38 2015 +1000

    xf86-input-libinput 0.16.0
    
    Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>

diff --git a/configure.ac b/configure.ac
index 268e030..67bebdc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -23,7 +23,7 @@
 # Initialize Autoconf
 AC_PREREQ([2.60])
 AC_INIT([xf86-input-libinput],
-        [0.15.0],
+        [0.16.0],
         [https://bugs.freedesktop.org/enter_bug.cgi?product=xorg],
         [xf86-input-libinput])
 AC_CONFIG_SRCDIR([Makefile.am])

commit ad8483b91387e99282a9b5a8360e8de7eed70257
Author: Peter Hutterer <peter.hutterer@who-t.net>
Date:   Tue Dec 15 13:20:55 2015 +1000

    Drain the fd after opening
    
    Make sure we don't send any events that may have been enqueued before we
    initialized ourselves. Specifically, if we're using systemd-logind the fd
    remains open when we disable/enable the device, allowing events to queue up on
    the fd. These events are then replayed once the device is re-opened.
    
    This is not the case when VT-switching, in that case logind closes the fd for
    us.
    
    Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
    Reviewed-by: Keith Packard <keithp@keithp.com>

diff --git a/src/xf86libinput.c b/src/xf86libinput.c
index ee2165a..7008fac 100644
--- a/src/xf86libinput.c
+++ b/src/xf86libinput.c
@@ -1175,7 +1175,12 @@ open_restricted(const char *path, int flags, void *data)
 	}
 
 	fd = xf86OpenSerial(pInfo->options);
-	return fd < 0 ? -errno : fd;
+	if (fd < 0)
+		return -errno;
+
+	xf86FlushInput(fd);
+
+	return fd;
 }
 
 static void

commit 1f43f3921f6ceebd9a0cb92ef998a930d5fc3a3e
Author: Peter Hutterer <peter.hutterer@who-t.net>
Date:   Wed Nov 11 10:10:58 2015 +1000

    Split mixed pointer/keyboard devices into two separate X devices
    
    The server struggles with devices that are both, the protocol (especially XI2)
    requires a fairly strict separation of pointer vs keyboard devices. Though the
    server has a couple of hacks to route events correctly, mixed
    devices still experience bugs like [1].
    
    Instead of advertising the device as a single mixed device, split the device
    into two X devices, one with only a pointer/touch component, one with only a
    keyboard component. This ensures that the device is effectively attached to
    both the VCP and the VCK, something the XI2 protocol doesn't really allow.
    
    This patch drops the keyboard capability on a mixed device, duplicates the
    input options and attributes and queues a NewInputDeviceRequest call. The new
    device only has the keyboard capability but is otherwise unchanged. The
    wacom driver has used this approach for years.
    
    The WorkProc is necessary to avoid inconsistent state, the server doesn't
    handle a NewInputDeviceRequest during PreInit well.
    
    The approach:
    During pre-init we create a struct xf86libinput_device with the
    libinput_device and a unique ID. The child device has that ID added to the
    options and will look for the other device during its pre-init. The two
    devices then share the xf86libinput_device struct.
    
    We only have a single epollfd for all devices  and the server calls read_input
    on the first device in the list with the epollfd as pInfo->fd. That shared
    struct is used as the userdata on the libinput_device we get back from the
    event, and each device is in the xorg_list device_list of that shared struct.
    We loop through those to find the ones with the right capabilities and
    post the event through that device.
    
    Since devices can be enabled and disabled independently, the rest of the code
    makes sure that we only ever add the device to libinput when the first shared
    device is enabled, and remove it accordingly.
    
    The server uses pInfo->major/minor to detect if another device is using the
    same path for a logind-controlled fd. If so, it reuses that device's
    pInfo->fd and sets the "fd" option to that value. That pInfo->fd is the
    libinput epollfd though, not the actual device fd.
    
    This doesn't matter for us, since we manage the fds largely ourselves and the
    pInfo->fd we use is the epollfd anyway. On unplug however, the udev code
    triggers a device removal for all devices, including the duplicated ones. When
    we disable device, we restore the pInfo->fd from the "fd" option so that the
    server can request logind to close the fd.
    
    That only works if the "fd" option is correct, otherwise the server asks
    logind to close the epollfd and everyone is unhappy.
    
    [1] https://bugs.freedesktop.org/show_bug.cgi?id=49950
    
    https://bugs.freedesktop.org/show_bug.cgi?id=92896
    
    Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
    Reviewed-by: Hans de Goede <hdegoede@redhat.com>

diff --git a/src/xf86libinput.c b/src/xf86libinput.c
index bc6e677..ee2165a 100644
--- a/src/xf86libinput.c
+++ b/src/xf86libinput.c
@@ -77,9 +77,18 @@ struct xf86libinput_driver {
 
 static struct xf86libinput_driver driver_context;
 
+struct xf86libinput_device {
+	int refcount;
+	int enabled_count;
+	uint32_t id;
+	struct libinput_device *device;
+	struct xorg_list device_list;
+	int server_fd;
+};
+
 struct xf86libinput {
+	InputInfoPtr pInfo;
 	char *path;
-	struct libinput_device *device;
 	uint32_t capabilities;
 
 	struct {
@@ -122,6 +131,9 @@ struct xf86libinput {
 	} options;
 
 	struct draglock draglock;
+
+	struct xf86libinput_device *shared_device;
+	struct xorg_list shared_device_link;
 };
 
 static inline int
@@ -164,6 +176,141 @@ btn_xorg2linux(unsigned int b)
 	return button;
 }
 
+static inline InputInfoPtr
+xf86libinput_get_parent(InputInfoPtr pInfo)
+{
+	InputInfoPtr parent;
+	int parent_id;
+
+	parent_id = xf86CheckIntOption(pInfo->options, "_libinput/shared-device", -1);
+	if (parent_id == -1)
+		return NULL;
+
+	nt_list_for_each_entry(parent, xf86FirstLocalDevice(), next) {
+		int id = xf86CheckIntOption(parent->options,
+					    "_libinput/shared-device",
+					    -1);
+		if (id == parent_id)
+			return parent;
+	}
+
+	return NULL;
+}
+
+static inline struct xf86libinput_device*
+xf86libinput_shared_create(struct libinput_device *device)
+{
+	static uint32_t next_shared_device_id;
+	struct xf86libinput_device *shared_device;
+
+	shared_device = calloc(1, sizeof(*shared_device));
+	if (!shared_device)
+		return NULL;
+
+	shared_device->device = device;
+	shared_device->refcount = 1;
+	shared_device->id = ++next_shared_device_id;
+	xorg_list_init(&shared_device->device_list);
+
+	return shared_device;
+}
+
+static inline struct xf86libinput_device*
+xf86libinput_shared_ref(struct xf86libinput_device *shared_device)
+{
+	shared_device->refcount++;
+
+	return shared_device;
+}
+
+static inline struct xf86libinput_device*
+xf86libinput_shared_unref(struct xf86libinput_device *shared_device)
+{
+	shared_device->refcount--;
+
+	if (shared_device->refcount > 0)
+		return shared_device;
+
+	free(shared_device);
+
+	return NULL;
+}
+
+static inline struct libinput_device *
+xf86libinput_shared_enable(InputInfoPtr pInfo,
+			   struct xf86libinput_device *shared_device,
+			   const char *path)
+{
+	struct libinput_device *device;
+	struct libinput *libinput = driver_context.libinput;
+
+	/* With systemd-logind the server requests the fd from logind, sets
+	 * pInfo->fd and sets the "fd" option to the fd number.
+	 *
+	 * If we have a second device that uses the same path, the server
+	 * checks all pInfo->major/minor for a match and returns the matched
+	 * device's pInfo->fd. In this driver, this fd is the epollfd, not
+	 * the actual device. This causes troubles when removing the
+	 * device.
+	 *
+	 * What we need to do here is: after enabling the device the first
+	 * time extract the real fd and store it in the shared device
+	 * struct. The second device replaces the pInfo->options "fd" with
+	 * the real fd we're using.
+	 *
+	 * When the device is unplugged, the server now correctly finds two
+	 * devices on the real fd and releases them in order.
+	 */
+	shared_device->enabled_count++;
+	if (shared_device->enabled_count > 1) {
+		if (pInfo->flags & XI86_SERVER_FD) {
+			pInfo->options = xf86ReplaceIntOption(pInfo->options,
+							      "fd",
+							      shared_device->server_fd);
+		}
+
+		return shared_device->device;
+	}
+
+	device = libinput_path_add_device(libinput, path);
+	if (!device)
+		return NULL;
+
+	libinput_device_set_user_data(device, shared_device);
+	shared_device->device = libinput_device_ref(device);
+
+	if (pInfo->flags & XI86_SERVER_FD)
+		shared_device->server_fd = xf86CheckIntOption(pInfo->options,
+							      "fd",
+							      -1);
+	return device;
+}
+
+static inline void
+xf86libinput_shared_disable(struct xf86libinput_device *shared_device)
+{
+	struct libinput_device *device = shared_device->device;
+
+	shared_device->enabled_count--;
+
+	if (shared_device->enabled_count > 0)
+		return;
+
+	if (!device)
+		return;
+
+	libinput_device_set_user_data(device, NULL);
+	libinput_path_remove_device(device);
+	device = libinput_device_unref(device);
+	shared_device->device = NULL;
+}
+
+static inline bool
+xf86libinput_shared_is_enabled(struct xf86libinput_device *shared_device)
+{
+	return shared_device->enabled_count > 0;
+}
+
 static int
 LibinputSetProperty(DeviceIntPtr dev, Atom atom, XIPropertyValuePtr val,
                  BOOL checkonly);
@@ -175,7 +322,7 @@ LibinputApplyConfig(DeviceIntPtr dev)
 {
 	InputInfoPtr pInfo = dev->public.devicePrivate;
 	struct xf86libinput *driver_data = pInfo->private;
-	struct libinput_device *device = driver_data->device;
+	struct libinput_device *device = driver_data->shared_device->device;
 	unsigned int scroll_button;
 
 	if (libinput_device_config_send_events_get_modes(device) != LIBINPUT_CONFIG_SEND_EVENTS_ENABLED &&
@@ -316,17 +463,16 @@ xf86libinput_on(DeviceIntPtr dev)
 {
 	InputInfoPtr pInfo = dev->public.devicePrivate;
 	struct xf86libinput *driver_data = pInfo->private;
+	struct xf86libinput_device *shared_device = driver_data->shared_device;
 	struct libinput *libinput = driver_context.libinput;
-	struct libinput_device *device = driver_data->device;
+	struct libinput_device *device;
 
-	device = libinput_path_add_device(libinput, driver_data->path);
+	device = xf86libinput_shared_enable(pInfo,
+					    shared_device,
+					    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
@@ -351,6 +497,7 @@ xf86libinput_off(DeviceIntPtr dev)
 {
 	InputInfoPtr pInfo = dev->public.devicePrivate;
 	struct xf86libinput *driver_data = pInfo->private;
+	struct xf86libinput_device *shared_device = driver_data->shared_device;
 
 	if (--driver_context.device_enabled_count == 0) {
 		RemoveEnabledDevice(pInfo->fd);
@@ -364,10 +511,7 @@ xf86libinput_off(DeviceIntPtr dev)
 
 	dev->public.on = FALSE;
 
-	libinput_device_set_user_data(driver_data->device, NULL);
-	libinput_path_remove_device(driver_data->device);
-	libinput_device_unref(driver_data->device);
-	driver_data->device = NULL;
+	xf86libinput_shared_disable(shared_device);
 
 	return Success;
 }
@@ -421,6 +565,7 @@ xf86libinput_init_pointer(InputInfoPtr pInfo)
 {
 	DeviceIntPtr dev= pInfo->dev;
 	struct xf86libinput *driver_data = pInfo->private;
+	struct libinput_device *device = driver_data->shared_device->device;
 	int min, max, res;
 	int nbuttons = 7;
 	int i;
@@ -429,7 +574,7 @@ xf86libinput_init_pointer(InputInfoPtr pInfo)
 	Atom axislabels[TOUCHPAD_NUM_AXES];
 
 	for (i = BTN_JOYSTICK - 1; i >= BTN_SIDE; i--) {
-		if (libinput_device_pointer_has_button(driver_data->device, i)) {
+		if (libinput_device_pointer_has_button(device, i)) {
 			nbuttons += i - BTN_SIDE + 1;
 			break;
 		}
@@ -468,6 +613,7 @@ xf86libinput_init_pointer_absolute(InputInfoPtr pInfo)
 {
 	DeviceIntPtr dev= pInfo->dev;
 	struct xf86libinput *driver_data = pInfo->private;
+	struct libinput_device *device = driver_data->shared_device->device;
 	int min, max, res;
 	int nbuttons = 7;
 	int i;
@@ -476,7 +622,7 @@ xf86libinput_init_pointer_absolute(InputInfoPtr pInfo)
 	Atom axislabels[TOUCHPAD_NUM_AXES];
 
 	for (i = BTN_BACK; i >= BTN_SIDE; i--) {
-		if (libinput_device_pointer_has_button(driver_data->device, i)) {
+		if (libinput_device_pointer_has_button(device, i)) {
 			nbuttons += i - BTN_SIDE + 1;
 			break;
 		}
@@ -528,7 +674,7 @@ xf86libinput_kbd_ctrl(DeviceIntPtr device, KeybdCtrl *ctrl)
     enum libinput_led leds = 0;
     InputInfoPtr pInfo = device->public.devicePrivate;
     struct xf86libinput *driver_data = pInfo->private;
-    struct libinput_device *ldevice = driver_data->device;
+    struct libinput_device *ldevice = driver_data->shared_device->device;
 
     while (bits[i].xbit) {
 	    if (ctrl->leds & bits[i].xbit)
@@ -612,7 +758,10 @@ xf86libinput_init(DeviceIntPtr dev)
 {
 	InputInfoPtr pInfo = dev->public.devicePrivate;
 	struct xf86libinput *driver_data = pInfo->private;
-	struct libinput_device *device = driver_data->device;
+	struct xf86libinput_device *shared_device = driver_data->shared_device;
+	struct libinput_device *device = shared_device->device;
+
+	BUG_RETURN_VAL(device == NULL, !Success);
 
 	dev->public.on = FALSE;
 
@@ -632,10 +781,14 @@ xf86libinput_init(DeviceIntPtr dev)
 	LibinputInitProperty(dev);
 	XIRegisterPropertyHandler(dev, LibinputSetProperty, NULL, NULL);
 
-	/* unref the device now, because we'll get a new ref during
-	   DEVICE_ON */
-	libinput_device_unref(device);
-	driver_data->device = NULL;
+	/* If we have a device but it's not yet enabled it's the
+	 * already-removed device from PreInit. Drop the ref to clean up,
+	 * we'll get a new libinput_device during DEVICE_ON when we re-add
+	 * it. */
+	if (!xf86libinput_shared_is_enabled(shared_device)) {
+		libinput_device_unref(device);
+		shared_device->device = NULL;
+	}
 
 	return 0;
 }
@@ -643,6 +796,13 @@ xf86libinput_init(DeviceIntPtr dev)
 static void
 xf86libinput_destroy(DeviceIntPtr dev)
 {
+	InputInfoPtr pInfo = dev->public.devicePrivate;
+	struct xf86libinput *driver_data = pInfo->private;
+	struct xf86libinput_device *shared_device = driver_data->shared_device;
+
+	xorg_list_del(&driver_data->shared_device_link);
+
+	xf86libinput_shared_unref(shared_device);
 }
 
 static int
@@ -677,6 +837,9 @@ xf86libinput_handle_motion(InputInfoPtr pInfo, struct libinput_event_pointer *ev
 	ValuatorMask *mask = driver_data->valuators;
 	double x, y;
 
+	if ((driver_data->capabilities & CAP_POINTER) == 0)
+		return;
+
 	x = libinput_event_pointer_get_dx(event);
 	y = libinput_event_pointer_get_dy(event);
 
@@ -714,6 +877,9 @@ xf86libinput_handle_absmotion(InputInfoPtr pInfo, struct libinput_event_pointer
 		return;
 	}
 
+	if ((driver_data->capabilities & CAP_POINTER) == 0)
+		return;
+
 	x = libinput_event_pointer_get_absolute_x_transformed(event, TOUCH_AXIS_MAX);
 	y = libinput_event_pointer_get_absolute_y_transformed(event, TOUCH_AXIS_MAX);
 
@@ -732,6 +898,9 @@ xf86libinput_handle_button(InputInfoPtr pInfo, struct libinput_event_pointer *ev
 	int button;
 	int is_press;
 
+	if ((driver_data->capabilities & CAP_POINTER) == 0)
+		return;
+
 	button = btn_linux2xorg(libinput_event_pointer_get_button(event));
 	is_press = (libinput_event_pointer_get_button_state(event) == LIBINPUT_BUTTON_STATE_PRESSED);
 
@@ -746,9 +915,13 @@ static void
 xf86libinput_handle_key(InputInfoPtr pInfo, struct libinput_event_keyboard *event)
 {
 	DeviceIntPtr dev = pInfo->dev;
+	struct xf86libinput *driver_data = pInfo->private;
 	int is_press;
 	int key = libinput_event_keyboard_get_key(event);
 
+	if ((driver_data->capabilities & CAP_KEYBOARD) == 0)
+		return;
+
 	key += XORG_KEYCODE_OFFSET;
 
 	is_press = (libinput_event_keyboard_get_key_state(event) == LIBINPUT_KEY_STATE_PRESSED);
@@ -765,6 +938,9 @@ xf86libinput_handle_axis(InputInfoPtr pInfo, struct libinput_event_pointer *even
 	enum libinput_pointer_axis axis;
 	enum libinput_pointer_axis_source source;
 
+	if ((driver_data->capabilities & CAP_POINTER) == 0)
+		return;
+
 	valuator_mask_zero(mask);
 
 	source = libinput_event_pointer_get_axis_source(event);
@@ -823,6 +999,9 @@ xf86libinput_handle_touch(InputInfoPtr pInfo,
 	static unsigned int next_touchid;
 	static unsigned int touchids[TOUCH_MAX_SLOTS] = {0};
 
+	if ((driver_data->capabilities & CAP_TOUCH) == 0)
+		return;
+
 	slot = libinput_event_touch_get_slot(event);
 
 	switch (event_type) {
@@ -853,19 +1032,50 @@ xf86libinput_handle_touch(InputInfoPtr pInfo,
 	xf86PostTouchEvent(dev, touchids[slot], type, 0, m);
 }
 
+static InputInfoPtr
+xf86libinput_pick_device(struct xf86libinput_device *shared_device,
+			 enum libinput_event_type type)
+{
+	struct xf86libinput *driver_data;
+	uint32_t needed_cap;
+
+	if (shared_device == NULL)
+		return NULL;
+
+	switch(type) {
+	case LIBINPUT_EVENT_KEYBOARD_KEY:
+		needed_cap = CAP_KEYBOARD;
+		break;
+	default:
+		needed_cap = ~CAP_KEYBOARD;
+		break;
+	}
+
+	xorg_list_for_each_entry(driver_data,
+				 &shared_device->device_list,
+				 shared_device_link) {
+		if (driver_data->capabilities & needed_cap)
+			return driver_data->pInfo;
+	}
+
+	return NULL;
+}
+
 static void
 xf86libinput_handle_event(struct libinput_event *event)
 {
 	struct libinput_device *device;
+	enum libinput_event_type type;
 	InputInfoPtr pInfo;
 
+	type = libinput_event_get_type(event);
 	device = libinput_event_get_device(event);
-	pInfo = libinput_device_get_user_data(device);
+	pInfo = xf86libinput_pick_device(libinput_device_get_user_data(device), type);
 
 	if (!pInfo || !pInfo->dev->public.on)
 		return;
 
-	switch (libinput_event_get_type(event)) {
+	switch (type) {
 		case LIBINPUT_EVENT_NONE:
 		case LIBINPUT_EVENT_DEVICE_ADDED:
 		case LIBINPUT_EVENT_DEVICE_REMOVED:
@@ -1520,15 +1730,91 @@ xf86libinput_init_driver_context(void)
 	}
 }
 
+struct xf86libinput_hotplug_info {
+	InputAttributes *attrs;
+	InputOption *input_options;
+};
+
+static Bool
+xf86libinput_hotplug_device(ClientPtr client, pointer closure)
+{
+	struct xf86libinput_hotplug_info *hotplug = closure;
+	DeviceIntPtr unused;
+
+	NewInputDeviceRequest(hotplug->input_options,
+			      hotplug->attrs,
+			      &unused);
+
+	input_option_free_list(&hotplug->input_options);
+	FreeInputAttributes(hotplug->attrs);
+	free(hotplug);
+
+	return TRUE;
+}
+
+static void
+xf86libinput_create_keyboard_subdevice(InputInfoPtr pInfo)
+{
+	struct xf86libinput *driver_data = pInfo->private;
+	struct xf86libinput_device *shared_device;
+	struct xf86libinput_hotplug_info *hotplug;
+	InputOption *iopts = NULL;
+	XF86OptionPtr options, o;
+
+	shared_device = driver_data->shared_device;
+	pInfo->options = xf86ReplaceIntOption(pInfo->options,
+					      "_libinput/shared-device",
+					      shared_device->id);
+
+	options = xf86OptionListDuplicate(pInfo->options);
+	options = xf86ReplaceStrOption(options, "_source", "_driver/libinput");
+	options = xf86ReplaceStrOption(options, "_libinput/caps", "keyboard");
+
+	/* need convert from one option list to the other. woohoo. */
+	o = options;
+	while (o) {
+		iopts = input_option_new(iopts,
+					 xf86OptionName(o),
+					 xf86OptionValue(o));
+		o = xf86NextOption(o);
+	}
+	xf86OptionListFree(options);
+
+	hotplug = calloc(1, sizeof(*hotplug));
+	if (!hotplug)
+		return;
+
+	hotplug->input_options = iopts;
+	hotplug->attrs = DuplicateInputAttributes(pInfo->attrs);
+
+	xf86IDrvMsg(pInfo, X_INFO, "needs a virtual subdevice\n");
+	QueueWorkProc(xf86libinput_hotplug_device, serverClient, hotplug);
+}
+
+static BOOL
+xf86libinput_is_subdevice(InputInfoPtr pInfo)
+{
+	char *source;
+	BOOL is_subdevice;
+
+	source = xf86SetStrOption(pInfo->options, "_source", "");
+	is_subdevice = strcmp(source, "_driver/libinput") == 0;
+	free(source);
+
+	return is_subdevice;
+}
+
 static int
 xf86libinput_pre_init(InputDriverPtr drv,
 		      InputInfoPtr pInfo,
 		      int flags)
 {
 	struct xf86libinput *driver_data = NULL;
-        struct libinput *libinput = NULL;
+	struct xf86libinput_device *shared_device = NULL;
+	struct libinput *libinput = NULL;
 	struct libinput_device *device;
 	char *path = NULL;
+	bool is_subdevice;
 
 	pInfo->type_name = 0;
 	pInfo->device_control = xf86libinput_device_control;
@@ -1548,9 +1834,6 @@ xf86libinput_pre_init(InputDriverPtr drv,
 	if (!driver_data->valuators_unaccelerated)
 		goto fail;
 
-	driver_data->scroll.vdist = 15;
-	driver_data->scroll.hdist = 15;
-
 	path = xf86SetStrOption(pInfo->options, "Device", NULL);
 	if (!path)
 		goto fail;
@@ -1563,28 +1846,60 @@ xf86libinput_pre_init(InputDriverPtr drv,
 		goto fail;
 	}
 
-	device = libinput_path_add_device(libinput, path);
-	if (!device) {
-		xf86IDrvMsg(pInfo, X_ERROR, "Failed to create a device for %s\n", path);
-		goto fail;
-	}
+	is_subdevice = xf86libinput_is_subdevice(pInfo);
+	if (!is_subdevice) {
+		device = libinput_path_add_device(libinput, path);
+		if (!device) {
+			xf86IDrvMsg(pInfo, X_ERROR, "Failed to create a device for %s\n", path);
+			goto fail;
+		}
 
-	/* We ref the device but remove it afterwards. The hope is that
-	   between now and DEVICE_INIT/DEVICE_ON, the device doesn't change.
-	  */
-	libinput_device_ref(device);
-	libinput_path_remove_device(device);
+		/* We ref the device above, then remove it. It get's
+		   re-added with the same path in DEVICE_ON, we hope
+		   it doesn't change until then */
+		libinput_device_ref(device);
+		libinput_path_remove_device(device);
+
+		shared_device = xf86libinput_shared_create(device);
+		if (!shared_device) {
+			libinput_device_unref(device);
+			goto fail;
+		}
+	} else {
+		InputInfoPtr parent;
+		struct xf86libinput *parent_driver_data;
+
+		parent = xf86libinput_get_parent(pInfo);
+		if (!parent) {
+			xf86IDrvMsg(pInfo, X_ERROR, "Failed to find parent device\n");
+			goto fail;
+		}
+		xf86IDrvMsg(pInfo, X_INFO, "is a virtual subdevice\n");
+
+		parent_driver_data = parent->private;
+		shared_device = xf86libinput_shared_ref(parent_driver_data->shared_device);
+		device = shared_device->device;
+	}
 
 	pInfo->private = driver_data;
+	driver_data->pInfo = pInfo;
+	driver_data->scroll.vdist = 15;
+	driver_data->scroll.hdist = 15;
 	driver_data->path = path;
-	driver_data->device = device;
-
-	if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_POINTER))
-		driver_data->capabilities |= CAP_POINTER;
-	if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_KEYBOARD))
-		driver_data->capabilities |= CAP_KEYBOARD;
-	if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_TOUCH))
-		driver_data->capabilities |= CAP_TOUCH;
+	driver_data->shared_device = shared_device;
+	xorg_list_append(&driver_data->shared_device_link,
+			 &shared_device->device_list);
+
+	if (!is_subdevice) {
+		if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_POINTER))
+			driver_data->capabilities |= CAP_POINTER;
+		if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_KEYBOARD))
+			driver_data->capabilities |= CAP_KEYBOARD;
+		if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_TOUCH))
+			driver_data->capabilities |= CAP_TOUCH;
+	} else {
+		driver_data->capabilities = CAP_KEYBOARD;
+	}
 
 	/* Disable acceleration in the server, libinput does it for us */
 	pInfo->options = xf86ReplaceIntOption(pInfo->options, "AccelerationProfile", -1);
@@ -1592,6 +1907,15 @@ xf86libinput_pre_init(InputDriverPtr drv,
 
 	xf86libinput_parse_options(pInfo, driver_data, device);
 
+	/* Device is both keyboard and pointer. Drop the keyboard cap from
+	 * this device, create a separate device instead */
+	if (!is_subdevice &&
+	    driver_data->capabilities & CAP_KEYBOARD &&
+	    driver_data->capabilities & (CAP_POINTER|CAP_TOUCH)) {
+		driver_data->capabilities &= ~CAP_KEYBOARD;
+		xf86libinput_create_keyboard_subdevice(pInfo);
+	}
+
 	pInfo->type_name = xf86libinput_get_type_name(device, driver_data);
 
 	return Success;
@@ -1601,6 +1925,8 @@ fail:
 	if (driver_data->valuators_unaccelerated)
 		valuator_mask_free(&driver_data->valuators_unaccelerated);
 	free(path);
+	if (shared_device)
+		xf86libinput_shared_unref(shared_device);
 	free(driver_data);
 	if (libinput)
 		driver_context.libinput = libinput_unref(libinput);
@@ -1711,7 +2037,7 @@ xf86libinput_check_device (DeviceIntPtr dev,
 {
 	InputInfoPtr pInfo = dev->public.devicePrivate;
 	struct xf86libinput *driver_data = pInfo->private;
-	struct libinput_device *device = driver_data->device;
+	struct libinput_device *device = driver_data->shared_device->device;
 
 	if (device == NULL) {
 		BUG_WARN(dev->public.on);
@@ -1733,7 +2059,7 @@ LibinputSetPropertyTap(DeviceIntPtr dev,
 {
 	InputInfoPtr pInfo = dev->public.devicePrivate;
 	struct xf86libinput *driver_data = pInfo->private;
-	struct libinput_device *device = driver_data->device;
+	struct libinput_device *device = driver_data->shared_device->device;
 	BOOL* data;
 
 	if (val->format != 8 || val->size != 1 || val->type != XA_INTEGER)
@@ -1764,7 +2090,7 @@ LibinputSetPropertyTapDragLock(DeviceIntPtr dev,
 {
 	InputInfoPtr pInfo = dev->public.devicePrivate;
 	struct xf86libinput *driver_data = pInfo->private;
-	struct libinput_device *device = driver_data->device;
+	struct libinput_device *device = driver_data->shared_device->device;
 	BOOL* data;
 
 	if (val->format != 8 || val->size != 1 || val->type != XA_INTEGER)
@@ -1795,7 +2121,7 @@ LibinputSetPropertyCalibration(DeviceIntPtr dev,
 {
 	InputInfoPtr pInfo = dev->public.devicePrivate;
 	struct xf86libinput *driver_data = pInfo->private;
-	struct libinput_device *device = driver_data->device;
+	struct libinput_device *device = driver_data->shared_device->device;
 	float* data;
 
 	if (val->format != 32 || val->size != 9 || val->type != prop_float)
@@ -1831,7 +2157,7 @@ LibinputSetPropertyAccel(DeviceIntPtr dev,
 {
 	InputInfoPtr pInfo = dev->public.devicePrivate;
 	struct xf86libinput *driver_data = pInfo->private;
-	struct libinput_device *device = driver_data->device;
+	struct libinput_device *device = driver_data->shared_device->device;
 	float* data;
 
 	if (val->format != 32 || val->size != 1 || val->type != prop_float)
@@ -1863,7 +2189,7 @@ LibinputSetPropertyAccelProfile(DeviceIntPtr dev,
 {
 	InputInfoPtr pInfo = dev->public.devicePrivate;
 	struct xf86libinput *driver_data = pInfo->private;
-	struct libinput_device *device = driver_data->device;
+	struct libinput_device *device = driver_data->shared_device->device;
 	BOOL* data;
 	uint32_t profiles = 0;
 
@@ -1904,7 +2230,7 @@ LibinputSetPropertyNaturalScroll(DeviceIntPtr dev,
 {
 	InputInfoPtr pInfo = dev->public.devicePrivate;
 	struct xf86libinput *driver_data = pInfo->private;
-	struct libinput_device *device = driver_data->device;
+	struct libinput_device *device = driver_data->shared_device->device;
 	BOOL* data;
 
 	if (val->format != 8 || val->size != 1 || val->type != XA_INTEGER)
@@ -1936,7 +2262,7 @@ LibinputSetPropertySendEvents(DeviceIntPtr dev,
 {
 	InputInfoPtr pInfo = dev->public.devicePrivate;
 	struct xf86libinput *driver_data = pInfo->private;
-	struct libinput_device *device = driver_data->device;
+	struct libinput_device *device = driver_data->shared_device->device;
 	BOOL* data;
 	uint32_t modes = 0;
 
@@ -1975,7 +2301,7 @@ LibinputSetPropertyLeftHanded(DeviceIntPtr dev,
 {
 	InputInfoPtr pInfo = dev->public.devicePrivate;
 	struct xf86libinput *driver_data = pInfo->private;
-	struct libinput_device *device = driver_data->device;
+	struct libinput_device *device = driver_data->shared_device->device;
 	BOOL* data;
 
 	if (val->format != 8 || val->size != 1 || val->type != XA_INTEGER)
@@ -2008,7 +2334,7 @@ LibinputSetPropertyScrollMethods(DeviceIntPtr dev,
 {
 	InputInfoPtr pInfo = dev->public.devicePrivate;
 	struct xf86libinput *driver_data = pInfo->private;
-	struct libinput_device *device = driver_data->device;
+	struct libinput_device *device = driver_data->shared_device->device;
 	BOOL* data;
 	uint32_t modes = 0;
 
@@ -2051,7 +2377,7 @@ LibinputSetPropertyScrollButton(DeviceIntPtr dev,
 {
 	InputInfoPtr pInfo = dev->public.devicePrivate;
 	struct xf86libinput *driver_data = pInfo->private;
-	struct libinput_device *device = driver_data->device;
+	struct libinput_device *device = driver_data->shared_device->device;
 	CARD32* data;
 
 	if (val->format != 32 || val->size != 1 || val->type != XA_CARDINAL)
@@ -2085,7 +2411,7 @@ LibinputSetPropertyClickMethod(DeviceIntPtr dev,
 {
 	InputInfoPtr pInfo = dev->public.devicePrivate;
 	struct xf86libinput *driver_data = pInfo->private;
-	struct libinput_device *device = driver_data->device;
+	struct libinput_device *device = driver_data->shared_device->device;
 	BOOL* data;
 	uint32_t modes = 0;
 
@@ -2126,7 +2452,7 @@ LibinputSetPropertyMiddleEmulation(DeviceIntPtr dev,
 {
 	InputInfoPtr pInfo = dev->public.devicePrivate;
 	struct xf86libinput *driver_data = pInfo->private;
-	struct libinput_device *device = driver_data->device;
+	struct libinput_device *device = driver_data->shared_device->device;
 	BOOL* data;
 
 	if (val->format != 8 || val->size != 1 || val->type != XA_INTEGER)
@@ -2157,7 +2483,7 @@ LibinputSetPropertyDisableWhileTyping(DeviceIntPtr dev,
 {
 	InputInfoPtr pInfo = dev->public.devicePrivate;
 	struct xf86libinput *driver_data = pInfo->private;
-	struct libinput_device *device = driver_data->device;
+	struct libinput_device *device = driver_data->shared_device->device;
 	BOOL* data;
 
 	if (val->format != 8 || val->size != 1 || val->type != XA_INTEGER)
@@ -2906,7 +3232,7 @@ LibinputInitProperty(DeviceIntPtr dev)
 {
 	InputInfoPtr pInfo  = dev->public.devicePrivate;
 	struct xf86libinput *driver_data = pInfo->private;
-	struct libinput_device *device = driver_data->device;
+	struct libinput_device *device = driver_data->shared_device->device;
 	const char *device_node;
 	CARD32 product[2];
 	int rc;

commit 83dfd31ec8ec2596648c33059fffb93b19691fae
Author: Peter Hutterer <peter.hutterer@who-t.net>
Date:   Fri Nov 20 10:51:38 2015 +1000

    Revert "Split mixed pointer/keyboard devices into two separate X devices"
    
    When using logind, this causes the server to hang when a split device is
    unplugged. The reason is mostly in the server, when open the device by
    requesting the logind fd, the server loops through the device list to check if
    any other device has the same major/minor (see systemd_logind_take_fd()) and
    returns the pInfo->fd for that device instead of requesting the fd again from
    logind.
    
    For libinput devices, the pInfo->fd is the epollfd, not the actual device, so
    our second device gets the epollfd assigned. When the devices are removed, we
    keep the device fd open and release the epollfd through logind.
    
    This reverts commit c943739a2bfd4c380db0b21bc35b73deb7496c8a.

diff --git a/src/xf86libinput.c b/src/xf86libinput.c
index 430fc9a..bc6e677 100644
--- a/src/xf86libinput.c
+++ b/src/xf86libinput.c
@@ -77,17 +77,9 @@ struct xf86libinput_driver {
 
 static struct xf86libinput_driver driver_context;
 
-struct xf86libinput_device {
-	int refcount;
-	int enabled_count;
-	uint32_t id;
-	struct libinput_device *device;
-	struct xorg_list device_list;
-};
-
 struct xf86libinput {
-	InputInfoPtr pInfo;
 	char *path;
+	struct libinput_device *device;
 	uint32_t capabilities;
 
 	struct {
@@ -130,9 +122,6 @@ struct xf86libinput {
 	} options;
 
 	struct draglock draglock;
-
-	struct xf86libinput_device *shared_device;
-	struct xorg_list shared_device_link;
 };
 
 static inline int
@@ -175,112 +164,6 @@ btn_xorg2linux(unsigned int b)
 	return button;
 }


Reply to: