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

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



 .gitignore                    |    8 
 Makefile.am                   |    2 
 configure.ac                  |    3 
 debian/changelog              |    6 
 include/libinput-properties.h |   10 
 man/libinput.man              |   58 
 src/Makefile.am               |    6 
 src/draglock.c                |  282 ++++
 src/draglock.h                |  159 ++
 src/libinput.c                | 2616 ---------------------------------------
 src/xf86libinput.c            | 2765 ++++++++++++++++++++++++++++++++++++++++++
 test/.gitignore               |    1 
 test/Makefile.am              |   13 
 test/test-draglock.c          |  540 ++++++++
 14 files changed, 3847 insertions(+), 2622 deletions(-)

New commits:
commit 081150b64f2a5d2c771d914ea6d5df615c6f5723
Author: Timo Aaltonen <tjaalton@debian.org>
Date:   Wed Sep 23 13:14:53 2015 +0300

    release to unstable

diff --git a/debian/changelog b/debian/changelog
index 4dcdc95..f8223ea 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,8 +1,8 @@
-xserver-xorg-input-libinput (0.14.0-1) UNRELEASED; urgency=medium
+xserver-xorg-input-libinput (0.14.0-1) unstable; urgency=medium
 
   * New upstream release.
 
- -- Timo Aaltonen <tjaalton@debian.org>  Wed, 23 Sep 2015 13:11:44 +0300
+ -- Timo Aaltonen <tjaalton@debian.org>  Wed, 23 Sep 2015 13:12:50 +0300
 
 xserver-xorg-input-libinput (0.13.0-1) unstable; urgency=medium
 

commit 2625956e46836b2979860a0122807905dd5707c5
Author: Timo Aaltonen <tjaalton@debian.org>
Date:   Wed Sep 23 13:12:48 2015 +0300

    update the changelog

diff --git a/debian/changelog b/debian/changelog
index df868fb..4dcdc95 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+xserver-xorg-input-libinput (0.14.0-1) UNRELEASED; urgency=medium
+
+  * New upstream release.
+
+ -- Timo Aaltonen <tjaalton@debian.org>  Wed, 23 Sep 2015 13:11:44 +0300
+
 xserver-xorg-input-libinput (0.13.0-1) unstable; urgency=medium
 
   * New upstream release.

commit f48b64c8cd6f280ba8c589842ec2522a4bfe9b5c
Author: Peter Hutterer <peter.hutterer@who-t.net>
Date:   Mon Aug 31 13:27:09 2015 +1000

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

diff --git a/configure.ac b/configure.ac
index 26e0e70..bfb1d75 100644
--- a/configure.ac
+++ b/configure.ac
@@ -23,7 +23,7 @@
 # Initialize Autoconf
 AC_PREREQ([2.60])
 AC_INIT([xf86-input-libinput],
-        [0.13.0],
+        [0.14.0],
         [https://bugs.freedesktop.org/enter_bug.cgi?product=xorg],
         [xf86-input-libinput])
 AC_CONFIG_SRCDIR([Makefile.am])

commit b55239ef2552c43efd4c4fb7d39e22c255dd4e6d
Author: Yomi0 <abyomi0@gmail.com>
Date:   Sun Aug 30 23:14:25 2015 -0400

    Fix typo in libinput.man
    
    Correct typo. Draging to dragging.

diff --git a/man/libinput.man b/man/libinput.man
index 3c35776..196686d 100644
--- a/man/libinput.man
+++ b/man/libinput.man
@@ -123,7 +123,7 @@ Enables or disables tap-to-click behavior.
 .BI "Option \*qTappingDragLock\*q \*q" bool \*q
 Enables or disables drag lock during tapping behavior. When enabled, a
 finger up during tap-and-drag will not immediately release the button. If
-the finger is set down again within the timeout, the draging process
+the finger is set down again within the timeout, the dragging process
 continues.
 .TP 7
 .BI "Option \*qDisableWhileTyping\*q \*q" bool \*q

commit 9563334dda3c5563550fb2534b228c47216ec008
Author: Peter Hutterer <peter.hutterer@who-t.net>
Date:   Thu Aug 13 11:03:44 2015 +1000

    Use xf86OpenSerial instead of a direct open() call
    
    This will transparently handle server-side fds for us.
    
    Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
    Reviewed-by: Hans de Goede <hdegoede@redhat.com>
    Reviewed-by: Martin Pieuchot <mpi@openbsd.org>

diff --git a/src/xf86libinput.c b/src/xf86libinput.c
index 204f288..7ecc0e6 100644
--- a/src/xf86libinput.c
+++ b/src/xf86libinput.c
@@ -34,6 +34,7 @@
 #include <exevents.h>
 #include <xkbsrv.h>
 #include <xf86Xinput.h>
+#include <xf86_OSproc.h>
 #include <xserver-properties.h>
 #include <libinput.h>
 #include <linux/input.h>
@@ -923,7 +924,6 @@ open_restricted(const char *path, int flags, void *data)
 		char *device = xf86CheckStrOption(pInfo->options, "Device", NULL);
 
 		if (device != NULL && strcmp(path, device) == 0) {
-			fd = xf86CheckIntOption(pInfo->options, "fd", -1);
 			free(device);
 			break;
 		}
@@ -935,8 +935,7 @@ open_restricted(const char *path, int flags, void *data)
 		return -ENODEV;
 	}
 
-	if (fd == -1)
-		fd = open(path, flags);
+	fd = xf86OpenSerial(pInfo->options);
 	return fd < 0 ? -errno : fd;
 }
 
@@ -957,7 +956,7 @@ close_restricted(int fd, void *data)
 	}
 
 	if (!found)
-		close(fd);
+		xf86CloseSerial(fd);
 }
 
 const struct libinput_interface interface = {

commit 353c52f2bec035f04c136c8f3b28571e2a4515df
Author: Peter Hutterer <peter.hutterer@who-t.net>
Date:   Thu Aug 13 10:59:39 2015 +1000

    Revamp server fd opening
    
    The server already stores the server-fd in the options, so we only need to run
    through the list of current devices, find a match and extract that fd from the
    options. Less magic in our driver and it gives us a pInfo handle in
    open_restricted which we'll can use for xf86OpenSerial().
    
    Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
    Reviewed-by: Hans de Goede <hdegoede@redhat.com>
    Reviewed-by: Martin Pieuchot <mpi@openbsd.org>

diff --git a/src/xf86libinput.c b/src/xf86libinput.c
index 8987518..204f288 100644
--- a/src/xf86libinput.c
+++ b/src/xf86libinput.c
@@ -118,80 +118,11 @@ struct xf86libinput {
 	struct draglock draglock;
 };
 
-/*
-   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 = xnfcalloc(1, sizeof(*sfd));
-
-	sfd->fd = fd;
-	sfd->path = xnfstrdup(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)
 {
@@ -360,12 +291,6 @@ 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;
@@ -404,7 +329,6 @@ xf86libinput_off(DeviceIntPtr dev)
 	}
 
 	if (use_server_fd(pInfo)) {
-		fd_pop(&driver_context, pInfo->fd);
 		pInfo->fd = xf86SetIntOption(pInfo->options, "fd", -1);
 	} else {
 		pInfo->fd = -1;
@@ -981,13 +905,36 @@ xf86libinput_read_input(InputInfoPtr pInfo)
 	}
 }
 
+/*
+   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.
+   The server stores the fd in the options though, so we just get it from
+   there. If a device is added twice with two different fds this may give us
+   the wrong fd but why are you doing that anyway.
+ */
 static int
 open_restricted(const char *path, int flags, void *data)
 {
-	struct xf86libinput_driver *context = data;
-	int fd;
+	InputInfoPtr pInfo;
+	int fd = -1;
+
+	nt_list_for_each_entry(pInfo, xf86FirstLocalDevice(), next) {
+		char *device = xf86CheckStrOption(pInfo->options, "Device", NULL);
+
+		if (device != NULL && strcmp(path, device) == 0) {
+			fd = xf86CheckIntOption(pInfo->options, "fd", -1);
+			free(device);
+			break;
+		}
+		free(device);
+	}
+
+	if (pInfo == NULL) {
+		xf86Msg(X_ERROR, "Failed to look up path '%s'\n", path);
+		return -ENODEV;
+	}
 
-	fd = fd_get(context, path);
 	if (fd == -1)
 		fd = open(path, flags);
 	return fd < 0 ? -errno : fd;
@@ -996,9 +943,20 @@ open_restricted(const char *path, int flags, void *data)
 static void
 close_restricted(int fd, void *data)
 {
-	struct xf86libinput_driver *context = data;
+	InputInfoPtr pInfo;
+	int server_fd = -1;
+	BOOL found = FALSE;
+
+	nt_list_for_each_entry(pInfo, xf86FirstLocalDevice(), next) {
+		server_fd = xf86CheckIntOption(pInfo->options, "fd", -1);
 
-	if (fd_find(context, fd) == -1)
+		if (server_fd == fd) {
+			found = TRUE;
+			break;
+		}
+	}
+
+	if (!found)
 		close(fd);
 }
 
@@ -1523,9 +1481,6 @@ 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);
@@ -1537,8 +1492,6 @@ 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->private = driver_data;
 	driver_data->path = path;
@@ -1564,8 +1517,6 @@ 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);
 	if (driver_data->valuators_unaccelerated)

commit f139f1424936abdc43b2c8611d569b496ffa4a68
Author: Peter Hutterer <peter.hutterer@who-t.net>
Date:   Wed Aug 12 10:15:31 2015 +1000

    Add an option to disable horizontal scrolling
    
    libinput always has horizontal scrolling enabled and punts the decision when
    to scroll horizontally to the toolkit/widget. This is the better approach, but
    while we have a stack that's not ready for that, and in the X case likely
    never will be fully ready provide an option to disable horizontal scrolling.
    
    This option doesn't really disable horizontal scrolling, it merely discards
    any horizontal scroll delta. libinput will still think it's scrolling.
    
    https://bugs.freedesktop.org/show_bug.cgi?id=91589
    
    Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
    Reviewed-by: Hans de Goede <hdegoede@redhat.com>

diff --git a/include/libinput-properties.h b/include/libinput-properties.h
index ed009d5..06fad7f 100644
--- a/include/libinput-properties.h
+++ b/include/libinput-properties.h
@@ -120,4 +120,8 @@
    the target button number */
 #define LIBINPUT_PROP_DRAG_LOCK_BUTTONS "libinput Drag Lock Buttons"
 
+/* Horizontal scroll events enabled: BOOL, 1 value (0 or 1).
+ * If disabled, horizontal scroll events are discarded */
+#define LIBINPUT_PROP_HORIZ_SCROLL_ENABLED "libinput Horizonal Scroll Enabled"
+
 #endif /* _LIBINPUT_PROPERTIES_H_ */
diff --git a/man/libinput.man b/man/libinput.man
index ff7a411..3c35776 100644
--- a/man/libinput.man
+++ b/man/libinput.man
@@ -107,6 +107,12 @@ Enables a scroll method. Permitted values are
 Not all devices support all options, if an option is unsupported, the
 default scroll option for this device is used.
 .TP 7
+.BI "Option \*qHorizontalScrolling\*q" bool \*q
+Disables horizontal scrolling. When disabled, this driver will discard any
+horizontal scroll events from libinput. Note that this does not disable
+horizontal scrolling, it merely discards the horizontal axis from any scroll
+events.
+.TP 7
 .BI "Option \*qSendEventsMode\*q \*q" (disabled|enabled|disabled-on-external-mouse) \*q
 Sets the send events mode to disabled, enabled, or "disable when an external
 mouse is connected".
@@ -226,6 +232,10 @@ Either one 8-bit value specifying the meta drag lock button, or a list of
 button pairs. See section
 .B BUTTON DRAG LOCK
 for details.
+.TP 7
+.BI "libinput Horizontal Scrolling Enabled"
+1 boolean value (8 bit, 0 or 1). Indicates whether horizontal scrolling
+events are enabled or not.
 
 .SH BUTTON MAPPING
 X clients receive events with logical button numbers, where 1, 2, 3
diff --git a/src/xf86libinput.c b/src/xf86libinput.c
index 8500792..8987518 100644
--- a/src/xf86libinput.c
+++ b/src/xf86libinput.c
@@ -111,6 +111,8 @@ struct xf86libinput {
 		enum libinput_config_click_method click_method;
 
 		unsigned char btnmap[MAX_BUTTONS + 1];
+
+		BOOL horiz_scrolling_enabled;
 	} options;
 
 	struct draglock draglock;
@@ -831,6 +833,10 @@ xf86libinput_handle_axis(InputInfoPtr pInfo, struct libinput_event_pointer *even
 		}
 		valuator_mask_set_double(mask, 3, value);
 	}
+
+	if (!driver_data->options.horiz_scrolling_enabled)
+		goto out;
+
 	axis = LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL;
 	if (libinput_event_pointer_has_axis(event, axis)) {
 		if (source == LIBINPUT_POINTER_AXIS_SOURCE_WHEEL) {
@@ -842,6 +848,7 @@ xf86libinput_handle_axis(InputInfoPtr pInfo, struct libinput_event_pointer *even
 		valuator_mask_set_double(mask, 2, value);
 	}
 
+out:
 	xf86PostMotionEventM(dev, Relative, mask);
 }
 
@@ -1425,6 +1432,12 @@ xf86libinput_parse_draglock_option(InputInfoPtr pInfo,
 	free(str);
 }
 
+static inline BOOL
+xf86libinput_parse_horiz_scroll_option(InputInfoPtr pInfo)
+{
+	return xf86SetBoolOption(pInfo->options, "HorizontalScrolling", TRUE);
+}
+
 static void
 xf86libinput_parse_options(InputInfoPtr pInfo,
 			   struct xf86libinput *driver_data,
@@ -1450,8 +1463,10 @@ xf86libinput_parse_options(InputInfoPtr pInfo,
 	xf86libinput_parse_buttonmap_option(pInfo,
 					    options->btnmap,
 					    sizeof(options->btnmap));
-	if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_POINTER))
+	if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_POINTER)) {
 		xf86libinput_parse_draglock_option(pInfo, driver_data);
+		options->horiz_scrolling_enabled = xf86libinput_parse_horiz_scroll_option(pInfo);
+	}
 }
 
 static int
@@ -1647,6 +1662,7 @@ static Atom prop_disable_while_typing_default;
 
 /* driver properties */
 static Atom prop_draglock;
+static Atom prop_horiz_scroll;
 
 /* general properties */
 static Atom prop_float;
@@ -2166,6 +2182,33 @@ LibinputSetPropertyDragLockButtons(DeviceIntPtr dev,
 					       val->size, checkonly);
 }
 
+static inline int
+LibinputSetPropertyHorizScroll(DeviceIntPtr dev,
+			       Atom atom,
+			       XIPropertyValuePtr val,
+			       BOOL checkonly)
+{
+	InputInfoPtr pInfo = dev->public.devicePrivate;
+	struct xf86libinput *driver_data = pInfo->private;
+	BOOL enabled;
+
+	if (val->format != 8 || val->type != XA_INTEGER || val->size != 1)
+		return BadMatch;
+
+	enabled = *(BOOL*)val->data;
+	if (checkonly) {
+		if (enabled != 0 && enabled != 1)
+			return BadValue;
+
+		if (!xf86libinput_check_device (dev, atom))
+			return BadMatch;
+	} else {
+		driver_data->options.horiz_scrolling_enabled = enabled;
+	}
+
+	return Success;
+ }
+
 static int
 LibinputSetProperty(DeviceIntPtr dev, Atom atom, XIPropertyValuePtr val,
                  BOOL checkonly)
@@ -2205,6 +2248,8 @@ LibinputSetProperty(DeviceIntPtr dev, Atom atom, XIPropertyValuePtr val,
 		rc = LibinputSetPropertyDisableWhileTyping(dev, atom, val, checkonly);
 	else if (atom == prop_draglock)
 		rc = LibinputSetPropertyDragLockButtons(dev, atom, val, checkonly);
+	else if (atom == prop_horiz_scroll)
+		rc = LibinputSetPropertyHorizScroll(dev, atom, val, checkonly);
 	else if (atom == prop_device || atom == prop_product_id ||
 		 atom == prop_tap_default ||
 		 atom == prop_tap_drag_lock_default ||
@@ -2704,6 +2749,18 @@ LibinputInitDragLockProperty(DeviceIntPtr dev,
 }
 
 static void
+LibinputInitHorizScrollProperty(DeviceIntPtr dev,
+				struct xf86libinput *driver_data)
+{
+	BOOL enabled = driver_data->options.horiz_scrolling_enabled;
+
+	prop_horiz_scroll = LibinputMakeProperty(dev,
+						 LIBINPUT_PROP_HORIZ_SCROLL_ENABLED,
+						 XA_INTEGER, 8,
+						 1, &enabled);
+}
+
+static void
 LibinputInitProperty(DeviceIntPtr dev)
 {
 	InputInfoPtr pInfo  = dev->public.devicePrivate;
@@ -2754,4 +2811,5 @@ LibinputInitProperty(DeviceIntPtr dev)
 	XISetDevicePropertyDeletable(dev, prop_product_id, FALSE);
 
 	LibinputInitDragLockProperty(dev, driver_data);
+	LibinputInitHorizScrollProperty(dev, driver_data);
 }

commit e3a888c3ab0f4cc42943b0216852cba110c3dad2
Author: Peter Hutterer <peter.hutterer@who-t.net>
Date:   Fri Aug 7 15:19:12 2015 +1000

    Add drag lock support
    
    First, why is this here and not in libinput: drag lock should be implemented
    in the compositor (not in libinput) so it can provide feedback when it
    activates and grouped in with other accessibility features. That will work for
    Wayland but in X the compositor cannot filter button events - only the server
    and the drivers can.
    
    This patch adds mostly the same functionality that evdev provides with two
    options on how it works:
    * a single button number configures the given button to lock the next button
      pressed in a logically down state until a press+ release of that same button
      again
    * a set of button number pairs configures each button with the to-be-locked
      logical button, i.e. a pair of "1 3" will hold 3 logically down after a
      button 1 press
    
    The property and the xorg.conf options take the same configuration as the
    evdev driver (though the property has a different prefix, libinput instead of
    Evdev).
    
    The behavior difference to evdev is in how releases are handled, evdev sends
    the release on the second button press event, this implementation sends the
    release on the second release event.
    
    https://bugs.freedesktop.org/show_bug.cgi?id=85577
    
    Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
    Reviewed-by: Hans de Goede <hdegoede@redhat.com>

diff --git a/Makefile.am b/Makefile.am
index 99e6808..ef17c35 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -21,7 +21,7 @@
 
 DISTCHECK_CONFIGURE_FLAGS = --with-sdkdir='$${includedir}/xorg'
 
-SUBDIRS = src include man
+SUBDIRS = src include man test
 MAINTAINERCLEANFILES = ChangeLog INSTALL
 
 pkgconfigdir = $(libdir)/pkgconfig
diff --git a/configure.ac b/configure.ac
index c149a1b..26e0e70 100644
--- a/configure.ac
+++ b/configure.ac
@@ -71,5 +71,6 @@ AC_CONFIG_FILES([Makefile
 		 include/Makefile
 		 src/Makefile
 		 man/Makefile
+		 test/Makefile
 		 xorg-libinput.pc])
 AC_OUTPUT
diff --git a/include/libinput-properties.h b/include/libinput-properties.h
index f54cee7..ed009d5 100644
--- a/include/libinput-properties.h
+++ b/include/libinput-properties.h
@@ -114,4 +114,10 @@
 /* Disable while typing: BOOL, 1 value, read-only */
 #define LIBINPUT_PROP_DISABLE_WHILE_TYPING_DEFAULT "libinput Disable While Typing Enabled Default"
 
+/* Drag lock buttons, either:
+   CARD8, one value, the meta lock button, or
+   CARD8, n * 2 values, the drag lock pairs with n being the button and n+1
+   the target button number */
+#define LIBINPUT_PROP_DRAG_LOCK_BUTTONS "libinput Drag Lock Buttons"
+
 #endif /* _LIBINPUT_PROPERTIES_H_ */
diff --git a/man/libinput.man b/man/libinput.man
index ac546e6..ff7a411 100644
--- a/man/libinput.man
+++ b/man/libinput.man
@@ -123,6 +123,27 @@ continues.
 .BI "Option \*qDisableWhileTyping\*q \*q" bool \*q
 Indicates if the touchpad should be disabled while typing on the keyboard
 (this does not apply to modifier keys such as Ctrl or Alt).
+.TP 7
+.BI "Option \*qDragLockButtons\*q \*q" "L1 B1 L2 B2 ..." \*q
+Sets "drag lock buttons" that simulate a button logically down even when it has
+been physically released. To logically release a locked button, a second click
+of the same button is required.
+.IP
+If the option is a single button number, that button acts as the
+"meta" locking button for the next button number. See section
+.B BUTTON DRAG LOCK
+for details.
+.IP
+If the option is a list of button number pairs, the first number of each
+number pair is the lock button, the second number the logical button number
+to be locked. See section
+.B BUTTON DRAG LOCK
+for details.
+.IP
+For both meta and button pair configuration, the button numbers are
+device button numbers, i.e. the
+.B ButtonMapping
+applies after drag lock.
 .PP
 For all options, the options are only parsed if the device supports that
 configuration option. For all options, the default value is the one used by
@@ -195,11 +216,16 @@ disabled.
 .BI "libinput Disable While Typing Enabled"
 1 boolean value (8 bit, 0 or 1). Indicates if disable while typing is
 enabled or disabled.
-.TP7
 .PP
 The above properties have a
 .BI "libinput <property name> Default"
 equivalent that indicates the default value for this setting on this device.
+.TP 7
+.BI "libinput Drag Lock Buttons"
+Either one 8-bit value specifying the meta drag lock button, or a list of
+button pairs. See section
+.B BUTTON DRAG LOCK
+for details.
 
 .SH BUTTON MAPPING
 X clients receive events with logical button numbers, where 1, 2, 3
@@ -226,6 +252,24 @@ __xservername__ input driver does not use the button mapping after setup.
 Use XSetPointerMapping(__libmansuffix__) to modify the button mapping at
 runtime.
 
+.SH BUTTON DRAG LOCK
+Button drag lock holds a button logically down even when the button itself
+has been physically released since. Button drag lock comes in two modes.
+.PP
+If in "meta" mode, a meta button click activates drag lock for the next
+button press of any other button. A button click in the future will keep
+that button held logically down until a subsequent click of that same
+button. The meta button events themselves are discarded. A separate meta
+button click is required each time a drag lock should be activated for a
+button in the future.
+.PP
+If in "pairs" mode, each button can be assigned a target locking button.
+On button click, the target lock button is held logically down until the
+next click of the same button. The button events themselves are discarded
+and only the target button events are sent.
+.TP
+This feature is provided by this driver, not by libinput.
+
 .SH AUTHORS
 Peter Hutterer
 .SH "SEE ALSO"
diff --git a/src/Makefile.am b/src/Makefile.am
index 6085a9a..60703e6 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -30,8 +30,10 @@ AM_CPPFLAGS =-I$(top_srcdir)/include $(LIBINPUT_CFLAGS)
 
 @DRIVER_NAME@_drv_la_LTLIBRARIES = @DRIVER_NAME@_drv.la
 @DRIVER_NAME@_drv_la_LDFLAGS = -module -avoid-version
-@DRIVER_NAME@_drv_la_LIBADD = $(LIBINPUT_LIBS)
+@DRIVER_NAME@_drv_la_LIBADD = $(LIBINPUT_LIBS) libdraglock.la
 @DRIVER_NAME@_drv_ladir = @inputdir@
 
 @DRIVER_NAME@_drv_la_SOURCES = xf86libinput.c
 
+noinst_LTLIBRARIES = libdraglock.la
+libdraglock_la_SOURCES = draglock.c draglock.h
diff --git a/src/draglock.c b/src/draglock.c
new file mode 100644
index 0000000..b0bcac3
--- /dev/null
+++ b/src/draglock.c
@@ -0,0 +1,282 @@
+/*
+ * Copyright © 2015 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of Red Hat
+ * not be used in advertising or publicity pertaining to distribution
+ * of the software without specific, written prior permission.  Red
+ * Hat makes no representations about the suitability of this software
+ * for any purpose.  It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "draglock.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
+
+static int
+draglock_parse_config(struct draglock *dl, const char *config)
+{
+    int button = 0, target = 0;
+    const char *str = NULL;
+    char *end_str = NULL;
+    int pairs[DRAGLOCK_MAX_BUTTONS] = {0};
+
+    if (!config)
+	    return 0;
+
+    /* empty string disables drag lock */
+    if (*config == '\0') {
+	    dl->mode = DRAGLOCK_DISABLED;
+	    return 0;
+    }
+
+    /* check for a single-number string first, config is "<int>" */
+    button = strtol(config, &end_str, 10);
+    if (*end_str == '\0') {
+	    if (button < 0 || button >= DRAGLOCK_MAX_BUTTONS)
+		    return 1;
+	    /* we allow for button 0 so stacked xorg.conf.d snippets can
+	     * disable the config again */
+	    if (button == 0) {
+		    dl->mode = DRAGLOCK_DISABLED;
+		    return 0;
+	    }
+
+	    return draglock_set_meta(dl, button);
+    }
+
+    dl->mode = DRAGLOCK_DISABLED;
+
+    /* check for a set of button pairs, config is
+     * "<int> <int> <int> <int>..." */
+    str = config;
+    while (*str != '\0') {
+	    button = strtol(str, &end_str, 10);
+	    if (*end_str == '\0')
+		    return 1;
+
+	    str = end_str;
+	    target = strtol(str, &end_str, 10);
+	    if (end_str == str)
+		    return 1;
+	    if (button <= 0 || button >= DRAGLOCK_MAX_BUTTONS || target >= DRAGLOCK_MAX_BUTTONS)
+		    return 1;
+
+	    pairs[button] = target;
+	    str = end_str;
+    }
+
+    return draglock_set_pairs(dl, pairs, ARRAY_SIZE(pairs));
+}
+
+int
+draglock_init_from_string(struct draglock *dl, const char *config)
+{
+	dl->mode = DRAGLOCK_DISABLED;
+
+	dl->meta_button = 0;
+	dl->meta_state = false;
+	memset(dl->lock_pair, 0, sizeof(dl->lock_pair));
+	memset(dl->lock_state, 0, sizeof(dl->lock_state));
+
+	return draglock_parse_config(dl, config);
+}
+
+enum draglock_mode
+draglock_get_mode(const struct draglock *dl)
+{
+	return dl->mode;
+}
+
+int
+draglock_get_meta(const struct draglock *dl)
+{
+	if (dl->mode == DRAGLOCK_META)
+		return dl->meta_button;
+	return 0;
+}
+
+size_t
+draglock_get_pairs(const struct draglock *dl, int *array, size_t sz)
+{
+	unsigned int i;
+	size_t last = 0;
+
+	if (dl->mode != DRAGLOCK_PAIRS)
+		return 0;
+
+	/* size 1 array with the meta button */
+	if (dl->meta_button) {
+		*array = dl->meta_button;
+		return 1;
+	}
+
+	/* size N array with a[0] == 0, the rest ordered by button number */
+	memset(array, 0, sz * sizeof(array[0]));
+	for (i = 0; i < sz && i < ARRAY_SIZE(dl->lock_pair); i++) {
+		array[i] = dl->lock_pair[i];
+		if (array[i] != 0 && i > last)
+			last = i;
+	}
+	return last;
+}
+
+int
+draglock_set_meta(struct draglock *dl, int meta_button)
+{
+	if (meta_button < 0 || meta_button >= DRAGLOCK_MAX_BUTTONS)
+		return 1;
+
+	dl->meta_button = meta_button;
+	dl->mode = meta_button ? DRAGLOCK_META : DRAGLOCK_DISABLED;
+
+	return 0;
+}
+
+int
+draglock_set_pairs(struct draglock *dl, const int *array, size_t sz)
+{
+	unsigned int i;
+
+	if (sz == 0 || array[0] != 0)
+		return 1;
+
+	for (i = 0; i < sz; i++) {
+		if (array[i] < 0 || array[i] >= DRAGLOCK_MAX_BUTTONS)
+			return 1;
+	}
+
+	dl->mode = DRAGLOCK_DISABLED;
+	for (i = 0; i < sz; i++) {
+		dl->lock_pair[i] = array[i];
+		if (dl->lock_pair[i])
+			dl->mode = DRAGLOCK_PAIRS;
+	}
+
+	return 0;
+}
+
+static int
+draglock_filter_meta(struct draglock *dl, int *button, int *press)
+{
+	int b = *button,
+	    is_press = *press;
+
+	if (b == dl->meta_button) {
+		if (is_press)
+			dl->meta_state = true;
+		*button = 0;
+		return 0;
+	}
+
+	switch (dl->lock_state[b]) {
+	case DRAGLOCK_BUTTON_STATE_NONE:
+		if (dl->meta_state && is_press) {
+			dl->lock_state[b] = DRAGLOCK_BUTTON_STATE_DOWN_1;
+			dl->meta_state = false;
+		}
+		break;
+	case DRAGLOCK_BUTTON_STATE_DOWN_1:
+		if (!is_press) {
+			dl->lock_state[b] = DRAGLOCK_BUTTON_STATE_UP_1;
+			b = 0;
+		}
+		break;
+	case DRAGLOCK_BUTTON_STATE_UP_1:
+		if (is_press) {
+			dl->lock_state[b] = DRAGLOCK_BUTTON_STATE_DOWN_2;
+			b = 0;
+		}
+		break;
+	case DRAGLOCK_BUTTON_STATE_DOWN_2:
+		if (!is_press) {
+			dl->lock_state[b] = DRAGLOCK_BUTTON_STATE_NONE;
+		}
+		break;
+	}
+
+	*button = b;
+
+	return 0;
+}
+
+static int
+draglock_filter_pair(struct draglock *dl, int *button, int *press)
+{
+	int b = *button,
+	    is_press = *press;
+
+	if (dl->lock_pair[b] == 0)
+		return 0;
+
+	switch (dl->lock_state[b]) {
+	case DRAGLOCK_BUTTON_STATE_NONE:
+		if (is_press) {
+			dl->lock_state[b] = DRAGLOCK_BUTTON_STATE_DOWN_1;
+			b = dl->lock_pair[b];
+		}
+		break;
+	case DRAGLOCK_BUTTON_STATE_DOWN_1:
+		if (!is_press) {
+			dl->lock_state[b] = DRAGLOCK_BUTTON_STATE_UP_1;
+			b = 0;
+		}
+		break;
+	case DRAGLOCK_BUTTON_STATE_UP_1:
+		if (is_press) {
+			dl->lock_state[b] = DRAGLOCK_BUTTON_STATE_DOWN_2;
+			b = 0;
+		}
+		break;
+	case DRAGLOCK_BUTTON_STATE_DOWN_2:
+		if (!is_press) {
+			dl->lock_state[b] = DRAGLOCK_BUTTON_STATE_NONE;
+			b = dl->lock_pair[b];
+		}
+		break;
+	}
+
+	*button = b;
+
+	return 0;
+}
+
+int
+draglock_filter_button(struct draglock *dl, int *button, int *is_press)
+{
+	if (*button == 0)
+		return 0;


Reply to: