[PATCH xf86-input-libinput 1/2] Add drag lock support

Peter Hutterer peter.hutterer at who-t.net
Wed Aug 12 14:54:26 PDT 2015


On Wed, Aug 12, 2015 at 10:43:02AM +0200, Hans de Goede wrote:
> Hi,
> 
> On 12-08-15 03:28, Peter Hutterer wrote:
> >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.
> >
> >Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
> 
> I had to think a bit about why you are not doing this in libinput, I assume that
> is because for wayland you want the compositor to do this so that some form of
> visual feedback can be provided, correct ?

yep, sorry, I thought I had that added (and the link to the bug report
https://bugs.freedesktop.org/show_bug.cgi?id=85577). amended this now and
added the requested paragraph.

> 
> Assuming that that is your reasoning, please at a paragraph explaining that to
> the commit message. Otherwise this looks good and is:
> 
> Reviewed-by: Hans de Goede <hdegoede at redhat.com>

thanks.

Cheers,
   Peter

> 
> >---
> >  Makefile.am                   |   2 +-
> >  configure.ac                  |   1 +
> >  include/libinput-properties.h |   6 +
> >  man/libinput.man              |  46 +++-
> >  src/Makefile.am               |   4 +-
> >  src/draglock.c                | 282 ++++++++++++++++++++++
> >  src/draglock.h                | 159 +++++++++++++
> >  src/xf86libinput.c            | 146 +++++++++++-
> >  test/.gitignore               |   1 +
> >  test/Makefile.am              |  13 +
> >  test/test-draglock.c          | 540 ++++++++++++++++++++++++++++++++++++++++++
> >  11 files changed, 1195 insertions(+), 5 deletions(-)
> >  create mode 100644 src/draglock.c
> >  create mode 100644 src/draglock.h
> >  create mode 100644 test/.gitignore
> >  create mode 100644 test/Makefile.am
> >  create mode 100644 test/test-draglock.c
> >
> >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 at _drv_la_LTLIBRARIES = @DRIVER_NAME at _drv.la
> >  @DRIVER_NAME at _drv_la_LDFLAGS = -module -avoid-version
> >- at DRIVER_NAME@_drv_la_LIBADD = $(LIBINPUT_LIBS)
> >+ at DRIVER_NAME@_drv_la_LIBADD = $(LIBINPUT_LIBS) libdraglock.la
> >  @DRIVER_NAME at _drv_ladir = @inputdir@
> >
> >  @DRIVER_NAME at _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;
> >+
> >+	switch(dl->mode) {
> >+	case DRAGLOCK_DISABLED:
> >+		return 0;
> >+	case DRAGLOCK_META:
> >+		return draglock_filter_meta(dl, button, is_press);
> >+	case DRAGLOCK_PAIRS:
> >+		return draglock_filter_pair(dl, button, is_press);
> >+	default:
> >+		abort();
> >+		break;
> >+	}
> >+
> >+	return 0;
> >+}
> >diff --git a/src/draglock.h b/src/draglock.h
> >new file mode 100644
> >index 0000000..acc1314
> >--- /dev/null
> >+++ b/src/draglock.h
> >@@ -0,0 +1,159 @@
> >+/*
> >+ * 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
> >+
> >+#ifndef DRAGLOCK_H
> >+#define DRAGLOCK_H 1
> >+
> >+#include <stdbool.h>
> >+#include <stdlib.h>
> >+
> >+/* 32 buttons are enough for everybody™
> >+ * Note that this is the limit of physical buttons as well as the highest
> >+ * allowed target button.
> >+ */
> >+#define DRAGLOCK_MAX_BUTTONS 32
> >+
> >+enum draglock_mode
> >+{
> >+	DRAGLOCK_DISABLED,
> >+	DRAGLOCK_META,
> >+	DRAGLOCK_PAIRS
> >+};
> >+
> >+enum draglock_button_state
> >+{
> >+	DRAGLOCK_BUTTON_STATE_NONE,
> >+	DRAGLOCK_BUTTON_STATE_DOWN_1,
> >+	DRAGLOCK_BUTTON_STATE_UP_1,
> >+	DRAGLOCK_BUTTON_STATE_DOWN_2,
> >+};
> >+
> >+struct draglock
> >+{
> >+	enum draglock_mode mode;
> >+	int meta_button;			/* meta key to lock any button */
> >+	bool meta_state;			/* meta_button state */
> >+	unsigned int lock_pair[DRAGLOCK_MAX_BUTTONS + 1];/* specify a meta/lock pair */
> >+	enum draglock_button_state lock_state[DRAGLOCK_MAX_BUTTONS + 1];	/* state of any locked buttons */
> >+};
> >+
> >+/**
> >+ * Initialize the draglock struct based on the config string. The string is
> >+ * either a single number to configure DRAGLOCK_META mode or a list of
> >+ * number pairs, with pair[0] as button and pair[1] as target lock number to
> >+ * configure DRAGLOCK_PAIRS mode.
> >+ *
> >+ * If config is NULL, the empty string, "0" or an even-numbered list of 0,
> >+ * the drag lock mode is DRAGLOCK_DISABLED.
> >+ *
> >+ * @return 0 on success or nonzero on error
> >+ */
> >+int
> >+draglock_init_from_string(struct draglock *dl, const char *config);
> >+
> >+/**
> >+ * Get the current drag lock mode.
> >+ *
> >+ * If the mode is DRAGLOCK_META, a meta button click will cause the next
> >+ * subsequent button click to be held logically down until the release of
> >+ * the second button click of that same button. Events from the meta button
> >+ * are always discarded.
> >+ *
> >+ * If the mode is DRAGLOCK_PAIRS, any button may be configured with a
> >+ * 'target' button number. A click of that button causes the target button
> >+ * to be held logically down until the release of the second button click.
> >+ */
> >+enum draglock_mode
> >+draglock_get_mode(const struct draglock *dl);
> >+
> >+/**
> >+ * @return the meta button number or 0 if the current mode is not
> >+ * DRAGLOCK_META.
> >+ */
> >+int
> >+draglock_get_meta(const struct draglock *dl);
> >+
> >+/**
> >+ * Get the drag lock button mapping pairs. The array is filled with the
> >+ * button number as index and the mapped target button number as value, i.e.
> >+ * array[3] == 8 means button 3 will draglock button 8.
> >+ *
> >+ * A value of 0 indicates draglock is disabled for that button.
> >+ *
> >+ * @note Button numbers start at 1, array[0] is always 0.
> >+ *
> >+ * @param[in|out] array Caller-allocated array to hold the button mappings.
> >+ * @param[in] sz Maximum number of elements in array
> >+ *
> >+ * @return The number of valid elements in array or 0 if the current mode is
> >+ * not DRAGLOCK_PAIRS
> >+ */
> >+size_t
> >+draglock_get_pairs(const struct draglock *dl, int *array, size_t sz);
> >+
> >+/**
> >+ * Set the drag lock config to the DRAGLOCK_META mode, with the given
> >+ * button as meta button.
> >+ *
> >+ * If the button is 0 the mode becomes DRAGLOCK_DISABLED.
> >+ *
> >+ * @return 0 on success, nonzero otherwise
> >+ */
> >+int
> >+draglock_set_meta(struct draglock *dl, int meta_button);
> >+
> >+/**
> >+ * Set the drag lock config to the DRAGLOCK_PAIRS mode. The array
> >+ * must be filled with the button number as index and the mapped target
> >+ * button number as value, i.e.
> >+ * array[3] == 8 means button 3 will draglock button 8.
> >+ *
> >+ * A value of 0 indicates draglock is disabled for that button. If all
> >+ * buttons are 0, the mode becomes DRAGLOCK_DISABLED.
> >+ *
> >+ * @note Button numbers start at 1, array[0] is always 0.
> >+ *
> >+ * @return 0 on successor nonzero otherwise
> >+ */
> >+int
> >+draglock_set_pairs(struct draglock *dl, const int *array, size_t sz);
> >+
> >+/**
> >+ * Process the given button event through the drag lock state machine.
> >+ * If the event is to be discarded by the caller, button is set to 0.
> >+ * Otherwise, button is set to the button event to process and is_press is
> >+ * set to the button state to process.
> >+ *
> >+ * @param[in|out] button The button number to process
> >+ * @param[in|out] is_press nonzero for press, zero for release
> >+ *
> >+ * @return 0 on success or 1 on error
> >+ */
> >+int
> >+draglock_filter_button(struct draglock *dl, int *button, int *is_press);
> >+
> >+#endif /* DRAGLOCK_H */
> >diff --git a/src/xf86libinput.c b/src/xf86libinput.c
> >index 041aedf..8500792 100644
> >--- a/src/xf86libinput.c
> >+++ b/src/xf86libinput.c
> >@@ -1,5 +1,5 @@
> >  /*
> >- * Copyright © 2013 Red Hat, Inc.
> >+ * Copyright © 2013-2015 Red Hat, Inc.
> >   *
> >   * Permission to use, copy, modify, distribute, and sell this software
> >   * and its documentation for any purpose is hereby granted without
> >@@ -40,6 +40,7 @@
> >
> >  #include <X11/Xatom.h>
> >
> >+#include "draglock.h"
> >  #include "libinput-properties.h"
> >
> >  #ifndef XI86_SERVER_FD
> >@@ -111,6 +112,8 @@ struct xf86libinput {
> >
> >  		unsigned char btnmap[MAX_BUTTONS + 1];
> >  	} options;
> >+
> >+	struct draglock draglock;
> >  };
> >
> >  /*
> >@@ -769,12 +772,18 @@ static void
> >  xf86libinput_handle_button(InputInfoPtr pInfo, struct libinput_event_pointer *event)
> >  {
> >  	DeviceIntPtr dev = pInfo->dev;
> >+	struct xf86libinput *driver_data = pInfo->private;
> >  	int button;
> >  	int is_press;
> >
> >  	button = btn_linux2xorg(libinput_event_pointer_get_button(event));
> >  	is_press = (libinput_event_pointer_get_button_state(event) == LIBINPUT_BUTTON_STATE_PRESSED);
> >-	xf86PostButtonEvent(dev, Relative, button, is_press, 0, 0);
> >+
> >+	if (draglock_get_mode(&driver_data->draglock) != DRAGLOCK_DISABLED)
> >+		draglock_filter_button(&driver_data->draglock, &button, &is_press);
> >+
> >+	if (button)
> >+		xf86PostButtonEvent(dev, Relative, button, is_press, 0, 0);
> >  }
> >
> >  static void
> >@@ -1402,6 +1411,20 @@ xf86libinput_parse_buttonmap_option(InputInfoPtr pInfo,
> >  	free(mapping);
> >  }
> >
> >+static inline void
> >+xf86libinput_parse_draglock_option(InputInfoPtr pInfo,
> >+				   struct xf86libinput *driver_data)
> >+{
> >+	char *str;
> >+
> >+	str = xf86CheckStrOption(pInfo->options, "DragLockButtons",NULL);
> >+	if (draglock_init_from_string(&driver_data->draglock, str) != 0)
> >+		xf86IDrvMsg(pInfo, X_ERROR,
> >+			    "Invalid DragLockButtons option: \"%s\"\n",
> >+			    str);
> >+	free(str);
> >+}
> >+
> >  static void
> >  xf86libinput_parse_options(InputInfoPtr pInfo,
> >  			   struct xf86libinput *driver_data,
> >@@ -1427,6 +1450,8 @@ 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))
> >+		xf86libinput_parse_draglock_option(pInfo, driver_data);
> >  }
> >
> >  static int
> >@@ -1620,6 +1645,9 @@ static Atom prop_middle_emulation_default;
> >  static Atom prop_disable_while_typing;
> >  static Atom prop_disable_while_typing_default;
> >
> >+/* driver properties */
> >+static Atom prop_draglock;
> >+
> >  /* general properties */
> >  static Atom prop_float;
> >  static Atom prop_device;
> >@@ -2059,6 +2087,85 @@ LibinputSetPropertyDisableWhileTyping(DeviceIntPtr dev,
> >  	return Success;
> >  }
> >
> >+static inline int
> >+prop_draglock_set_meta(struct xf86libinput *driver_data,
> >+		       const BYTE *values,
> >+		       int len,
> >+		       BOOL checkonly)
> >+{
> >+	struct draglock *dl,
> >+			dummy; /* for checkonly */
> >+	int meta;
> >+
> >+	if (len > 1)
> >+		return BadImplementation; /* should not happen */
> >+
> >+	dl = (checkonly) ? &dummy : &driver_data->draglock;
> >+	meta = len > 0 ? values[0] : 0;
> >+
> >+	return draglock_set_meta(dl, meta) == 0 ? Success: BadValue;
> >+}
> >+
> >+static inline int
> >+prop_draglock_set_pairs(struct xf86libinput *driver_data,
> >+			const BYTE* pairs,
> >+			int len,
> >+			BOOL checkonly)
> >+{
> >+	struct draglock *dl,
> >+			dummy; /* for checkonly */
> >+	int data[MAX_BUTTONS + 1] = {0};
> >+	int i;
> >+	int highest = 0;
> >+
> >+	if (len >= ARRAY_SIZE(data))
> >+		return BadMatch;
> >+
> >+	if (len < 2 || len % 2)
> >+		return BadImplementation; /* should not happen */
> >+
> >+	dl = (checkonly) ? &dummy : &driver_data->draglock;
> >+
> >+	for (i = 0; i < len; i += 2) {
> >+		if (pairs[i] > MAX_BUTTONS)
> >+			return BadValue;
> >+
> >+		data[pairs[i]] = pairs[i+1];
> >+		highest = max(highest, pairs[i]);
> >+	}
> >+
> >+	return draglock_set_pairs(dl, data, highest + 1) == 0 ? Success : BadValue;
> >+}
> >+
> >+static inline int
> >+LibinputSetPropertyDragLockButtons(DeviceIntPtr dev,
> >+				   Atom atom,
> >+				   XIPropertyValuePtr val,
> >+				   BOOL checkonly)
> >+{
> >+	InputInfoPtr pInfo = dev->public.devicePrivate;
> >+	struct xf86libinput *driver_data = pInfo->private;
> >+
> >+	if (val->format != 8 || val->type != XA_INTEGER)
> >+		return BadMatch;
> >+
> >+	/* either a single value, or pairs of values */
> >+	if (val->size > 1 && val->size % 2)
> >+		return BadMatch;
> >+
> >+	if (!xf86libinput_check_device(dev, atom))
> >+		return BadMatch;
> >+
> >+	if (val->size <= 1)
> >+		return prop_draglock_set_meta(driver_data,
> >+					      (BYTE*)val->data,
> >+					      val->size, checkonly);
> >+	else
> >+		return prop_draglock_set_pairs(driver_data,
> >+					       (BYTE*)val->data,
> >+					       val->size, checkonly);
> >+}
> >+
> >  static int
> >  LibinputSetProperty(DeviceIntPtr dev, Atom atom, XIPropertyValuePtr val,
> >                   BOOL checkonly)
> >@@ -2096,6 +2203,8 @@ LibinputSetProperty(DeviceIntPtr dev, Atom atom, XIPropertyValuePtr val,
> >  		rc = LibinputSetPropertyMiddleEmulation(dev, atom, val, checkonly);
> >  	else if (atom == prop_disable_while_typing)
> >  		rc = LibinputSetPropertyDisableWhileTyping(dev, atom, val, checkonly);
> >+	else if (atom == prop_draglock)
> >+		rc = LibinputSetPropertyDragLockButtons(dev, atom, val, checkonly);
> >  	else if (atom == prop_device || atom == prop_product_id ||
> >  		 atom == prop_tap_default ||
> >  		 atom == prop_tap_drag_lock_default ||
> >@@ -2564,6 +2673,37 @@ LibinputInitDisableWhileTypingProperty(DeviceIntPtr dev,
> >  }
> >
> >  static void
> >+LibinputInitDragLockProperty(DeviceIntPtr dev,
> >+			     struct xf86libinput *driver_data)
> >+{
> >+	size_t sz;
> >+	int dl_values[MAX_BUTTONS + 1];
> >+
> >+	if (!libinput_device_has_capability(driver_data->device,
> >+					    LIBINPUT_DEVICE_CAP_POINTER))
> >+		return;
> >+
> >+	switch (draglock_get_mode(&driver_data->draglock)) {
> >+	case DRAGLOCK_DISABLED:
> >+		sz = 0; /* will be an empty property */
> >+		break;
> >+	case DRAGLOCK_META:
> >+		dl_values[0] = draglock_get_meta(&driver_data->draglock);
> >+		sz = 1;
> >+		break;
> >+	case DRAGLOCK_PAIRS:
> >+		sz = draglock_get_pairs(&driver_data->draglock,
> >+					dl_values, sizeof(dl_values));
> >+		break;
> >+	}
> >+
> >+	prop_draglock = LibinputMakeProperty(dev,
> >+					     LIBINPUT_PROP_DRAG_LOCK_BUTTONS,
> >+					     XA_INTEGER, 8,
> >+					     sz, dl_values);
> >+}
> >+
> >+static void
> >  LibinputInitProperty(DeviceIntPtr dev)
> >  {
> >  	InputInfoPtr pInfo  = dev->public.devicePrivate;
> >@@ -2612,4 +2752,6 @@ LibinputInitProperty(DeviceIntPtr dev)
> >  		return;
> >
> >  	XISetDevicePropertyDeletable(dev, prop_product_id, FALSE);
> >+
> >+	LibinputInitDragLockProperty(dev, driver_data);
> >  }
> >diff --git a/test/.gitignore b/test/.gitignore
> >new file mode 100644
> >index 0000000..48a46e3
> >--- /dev/null
> >+++ b/test/.gitignore
> >@@ -0,0 +1 @@
> >+test-draglock
> >diff --git a/test/Makefile.am b/test/Makefile.am
> >new file mode 100644
> >index 0000000..6f94abe
> >--- /dev/null
> >+++ b/test/Makefile.am
> >@@ -0,0 +1,13 @@
> >+AM_CPPFLAGS = $(XORG_CFLAGS) \
> >+	      $(CWARNFLAGS) \
> >+	      -I$(top_srcdir)/include \
> >+	      -I$(top_srcdir)/src
> >+
> >+tests = test-draglock
> >+
> >+noinst_PROGRAMS = $(tests)
> >+
> >+test_draglock_SOURCES = test-draglock.c
> >+test_draglock_LDADD = ../src/libdraglock.la
> >+
> >+TESTS = $(tests)
> >diff --git a/test/test-draglock.c b/test/test-draglock.c
> >new file mode 100644
> >index 0000000..96ef5bb
> >--- /dev/null
> >+++ b/test/test-draglock.c
> >@@ -0,0 +1,540 @@
> >+/*
> >+ * Copyright © 2013-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.
> >+ */
> >+
> >+#include "draglock.h"
> >+
> >+#include <assert.h>
> >+#include <string.h>
> >+
> >+static void
> >+test_config_empty(void)
> >+{
> >+	struct draglock dl;
> >+	int rc;
> >+
> >+	rc = draglock_init_from_string(&dl, NULL);
> >+	assert(dl.mode == DRAGLOCK_DISABLED);
> >+	assert(rc == 0);
> >+}
> >+
> >+static void
> >+test_config_invalid(void)
> >+{
> >+	struct draglock dl;
> >+	int rc;
> >+
> >+	/* no trailing space */
> >+	rc = draglock_init_from_string(&dl, "1 ");
> >+	assert(rc != 0);
> >+	assert(dl.mode == DRAGLOCK_DISABLED);
> >+
> >+	rc = draglock_init_from_string(&dl, "256");
> >+	assert(rc != 0);
> >+	assert(dl.mode == DRAGLOCK_DISABLED);
> >+
> >+	rc = draglock_init_from_string(&dl, "-1");
> >+	assert(rc != 0);
> >+	assert(dl.mode == DRAGLOCK_DISABLED);
> >+
> >+	rc = draglock_init_from_string(&dl, "1 2 3");
> >+	assert(rc != 0);
> >+	assert(dl.mode == DRAGLOCK_DISABLED);
> >+
> >+	rc = draglock_init_from_string(&dl, "0 2");
> >+	assert(rc != 0);
> >+	assert(dl.mode == DRAGLOCK_DISABLED);
> >+
> >+	rc = draglock_init_from_string(&dl, "0 0");
> >+	assert(rc != 0);
> >+	assert(dl.mode == DRAGLOCK_DISABLED);
> >+}
> >+
> >+static void
> >+test_config_disable(void)
> >+{
> >+	struct draglock dl;
> >+	int rc;
> >+
> >+	rc = draglock_init_from_string(&dl, "");
> >+	assert(rc == 0);
> >+	assert(dl.mode == DRAGLOCK_DISABLED);
> >+
> >+	rc = draglock_init_from_string(&dl, "0");
> >+	assert(rc == 0);
> >+	assert(dl.mode == DRAGLOCK_DISABLED);
> >+}
> >+
> >+static void
> >+test_config_meta_button(void)
> >+{
> >+	struct draglock dl;
> >+	int rc;
> >+
> >+	rc = draglock_init_from_string(&dl, "1");
> >+	assert(rc == 0);
> >+	assert(dl.mode == DRAGLOCK_META);
> >+	assert(dl.meta_button == 1);
> >+
> >+	rc = draglock_init_from_string(&dl, "2");
> >+	assert(rc == 0);
> >+	assert(dl.mode == DRAGLOCK_META);
> >+	assert(dl.meta_button == 2);
> >+
> >+	rc = draglock_init_from_string(&dl, "10");
> >+	assert(rc == 0);
> >+	assert(dl.mode == DRAGLOCK_META);
> >+	assert(dl.meta_button == 10);
> >+}
> >+
> >+static void
> >+test_config_button_pairs(void)
> >+{
> >+	struct draglock dl;
> >+	int rc;
> >+
> >+	rc = draglock_init_from_string(&dl, "1 1");
> >+	assert(rc == 0);
> >+	assert(dl.mode == DRAGLOCK_PAIRS);
> >+
> >+	rc = draglock_init_from_string(&dl, "1 2 3 4 5 6 7 8");
> >+	assert(rc == 0);
> >+	assert(dl.mode == DRAGLOCK_PAIRS);
> >+
> >+	rc = draglock_init_from_string(&dl, "1 2 3 4 5 0 7 8");
> >+	assert(rc == 0);
> >+	assert(dl.mode == DRAGLOCK_PAIRS);
> >+
> >+	/* all disabled */
> >+	rc = draglock_init_from_string(&dl, "1 0 3 0 5 0 7 0");
> >+	assert(rc == 0);
> >+	assert(dl.mode == DRAGLOCK_DISABLED);
> >+}
> >+
> >+static void
> >+test_config_get(void)
> >+{
> >+	struct draglock dl;
> >+	int rc;
> >+	const int sz = 32;
> >+	int map[sz];
> >+
> >+	draglock_init_from_string(&dl, "");
> >+	rc = draglock_get_meta(&dl);
> >+	assert(rc == 0);
> >+	rc = draglock_get_pairs(&dl, map, sz);
> >+	assert(rc == 0);
> >+
> >+	draglock_init_from_string(&dl, "8");
> >+	rc = draglock_get_meta(&dl);
> >+	assert(rc == 8);
> >+	rc = draglock_get_pairs(&dl, map, sz);
> >+	assert(rc == 0);
> >+
> >+	draglock_init_from_string(&dl, "1 2 3 4 5 6");
> >+	rc = draglock_get_meta(&dl);
> >+	assert(rc == 0);
> >+	rc = draglock_get_pairs(&dl, map, sz);
> >+	assert(rc == 5);
> >+	assert(map[0] == 0);
> >+	assert(map[1] == 2);
> >+	assert(map[2] == 0);
> >+	assert(map[3] == 4);
> >+	assert(map[4] == 0);
> >+	assert(map[5] == 6);
> >+}
> >+
> >+static void
> >+test_set_meta(void)
> >+{
> >+	struct draglock dl;
> >+	int rc;
> >+
> >+	draglock_init_from_string(&dl, "");
> >+
> >+	rc = draglock_set_meta(&dl, 0);
> >+	assert(rc == 0);
> >+	assert(dl.mode == DRAGLOCK_DISABLED);
> >+
> >+	rc = draglock_set_meta(&dl, 1);
> >+	assert(rc == 0);
> >+	assert(dl.mode == DRAGLOCK_META);
> >+
> >+	rc = draglock_set_meta(&dl, -1);
> >+	assert(rc == 1);
> >+	rc = draglock_set_meta(&dl, 32);
> >+	assert(rc == 1);
> >+}
> >+
> >+static void
> >+test_set_pairs(void)
> >+{
> >+	struct draglock dl;
> >+	int rc;
> >+	const int sz = 32;
> >+	int map[sz];
> >+
> >+	draglock_init_from_string(&dl, "");
> >+	memset(map, 0, sizeof(map));
> >+
> >+	rc = draglock_set_pairs(&dl, map, sz);
> >+	assert(rc == 0);
> >+	assert(dl.mode == DRAGLOCK_DISABLED);
> >+
> >+	rc = draglock_set_pairs(&dl, map, 1);
> >+	assert(rc == 0);
> >+	assert(dl.mode == DRAGLOCK_DISABLED);
> >+
> >+	map[0] = 1;
> >+	rc = draglock_set_pairs(&dl, map, 1);
> >+	assert(rc == 1);
> >+
> >+	map[0] = 0;
> >+	map[1] = 2;
> >+	rc = draglock_set_pairs(&dl, map, sz);
> >+	assert(rc == 0);
> >+	assert(dl.mode == DRAGLOCK_PAIRS);
> >+
> >+	map[0] = 0;
> >+	map[1] = 0;
> >+	map[10] = 8;
> >+	rc = draglock_set_pairs(&dl, map, sz);
> >+	assert(rc == 0);
> >+	assert(dl.mode == DRAGLOCK_PAIRS);
> >+}
> >+
> >+static void
> >+test_filter_meta_passthrough(void)
> >+{
> >+	struct draglock dl;
> >+	int rc;
> >+	int button, press;
> >+	int i;
> >+
> >+	rc = draglock_init_from_string(&dl, "10");
> >+
> >+	for (i = 0; i < 10; i++) {
> >+		button = i;
> >+		press = 1;
> >+
> >+		rc = draglock_filter_button(&dl, &button, &press);
> >+		assert(rc == 0);
> >+		assert(button == i);
> >+		assert(press == 1);
> >+
> >+		press = 1;
> >+		rc = draglock_filter_button(&dl, &button, &press);
> >+		assert(rc == 0);
> >+		assert(button == i);
> >+		assert(press == 1);
> >+	}
> >+}
> >+
> >+static void
> >+test_filter_meta_click_meta_only(void)
> >+{
> >+	struct draglock dl;
> >+	int rc;
> >+	int button, press;
> >+
> >+	rc = draglock_init_from_string(&dl, "10");
> >+
> >+	button = 10;
> >+	press = 1;
> >+
> >+	rc = draglock_filter_button(&dl, &button, &press);
> >+	assert(rc == 0);
> >+	assert(button == 0);
> >+
> >+	button = 10;
> >+	press = 0;
> >+	rc = draglock_filter_button(&dl, &button, &press);
> >+	assert(rc == 0);
> >+	assert(button == 0);
> >+}
> >+
> >+static void
> >+test_filter_meta(void)
> >+{
> >+	struct draglock dl;
> >+	int rc;
> >+	int button, press;
> >+	int i;
> >+
> >+	rc = draglock_init_from_string(&dl, "10");
> >+
> >+	for (i = 1; i < 10; i++) {
> >+		/* meta down */
> >+		button = 10;
> >+		press = 1;
> >+		rc = draglock_filter_button(&dl, &button, &press);
> >+		assert(rc == 0);
> >+		assert(button == 0);
> >+
> >+		/* meta up */
> >+		button = 10;
> >+		press = 0;
> >+		rc = draglock_filter_button(&dl, &button, &press);
> >+		assert(rc == 0);
> >+		assert(button == 0);
> >+
> >+		/* button down -> passthrough */
> >+		button = i;
> >+		press = 1;
> >+		rc = draglock_filter_button(&dl, &button, &press);
> >+		assert(rc == 0);
> >+		assert(button == i);
> >+
> >+		/* button up -> eaten */
> >+		button = i;
> >+		press = 0;
> >+		rc = draglock_filter_button(&dl, &button, &press);
> >+		assert(rc == 0);
> >+		assert(button == 0);
> >+
> >+		/* button down -> eaten */
> >+		button = i;
> >+		press = 1;
> >+		rc = draglock_filter_button(&dl, &button, &press);
> >+		assert(rc == 0);
> >+		assert(button == 0);
> >+
> >+		/* button up -> passthrough */
> >+		button = i;
> >+		press = 0;
> >+		rc = draglock_filter_button(&dl, &button, &press);
> >+		assert(rc == 0);
> >+		assert(button == i);
> >+		assert(press == 0);
> >+	}
> >+}
> >+
> >+static void
> >+test_filter_meta_extra_click(void)
> >+{
> >+	struct draglock dl;
> >+	int rc;
> >+	int button, press;
> >+	int i;
> >+
> >+	rc = draglock_init_from_string(&dl, "10");
> >+
> >+	for (i = 1; i < 10; i++) {
> >+		/* meta down */
> >+		button = 10;
> >+		press = 1;
> >+		rc = draglock_filter_button(&dl, &button, &press);
> >+		assert(rc == 0);
> >+		assert(button == 0);
> >+
> >+		/* meta up */
> >+		button = 10;
> >+		press = 0;
> >+		rc = draglock_filter_button(&dl, &button, &press);
> >+		assert(rc == 0);
> >+		assert(button == 0);
> >+
> >+		/* button down -> passthrough */
> >+		button = i;
> >+		press = 1;
> >+		rc = draglock_filter_button(&dl, &button, &press);
> >+		assert(rc == 0);
> >+		assert(button == i);
> >+
> >+		/* button up -> eaten */
> >+		button = i;
> >+		press = 0;
> >+		rc = draglock_filter_button(&dl, &button, &press);
> >+		assert(rc == 0);
> >+		assert(button == 0);
> >+
> >+		/* meta down */
> >+		button = 10;
> >+		press = 1;
> >+		rc = draglock_filter_button(&dl, &button, &press);
> >+		assert(rc == 0);
> >+		assert(button == 0);
> >+
> >+		/* meta up */
> >+		button = 10;
> >+		press = 0;
> >+		rc = draglock_filter_button(&dl, &button, &press);
> >+		assert(rc == 0);
> >+		assert(button == 0);
> >+
> >+		/* button down -> eaten */
> >+		button = i;
> >+		press = 1;
> >+		rc = draglock_filter_button(&dl, &button, &press);
> >+		assert(rc == 0);
> >+		assert(button == 0);
> >+
> >+		/* button up -> passthrough */
> >+		button = i;
> >+		press = 0;
> >+		rc = draglock_filter_button(&dl, &button, &press);
> >+		assert(rc == 0);
> >+		assert(button == i);
> >+		assert(press == 0);
> >+	}
> >+}
> >+
> >+static void
> >+test_filter_meta_interleaved(void)
> >+{
> >+	struct draglock dl;
> >+	int rc;
> >+	int button, press;
> >+	int i;
> >+
> >+	rc = draglock_init_from_string(&dl, "10");
> >+
> >+	for (i = 1; i < 10; i++) {
> >+		/* meta down */
> >+		button = 10;
> >+		press = 1;
> >+		rc = draglock_filter_button(&dl, &button, &press);
> >+		assert(rc == 0);
> >+		assert(button == 0);
> >+
> >+		/* meta up */
> >+		button = 10;
> >+		press = 0;
> >+		rc = draglock_filter_button(&dl, &button, &press);
> >+		assert(rc == 0);
> >+		assert(button == 0);
> >+
> >+		/* button down -> passthrough */
> >+		button = i;
> >+		press = 1;
> >+		rc = draglock_filter_button(&dl, &button, &press);
> >+		assert(rc == 0);
> >+		assert(button == i);
> >+
> >+		/* button up -> eaten */
> >+		button = i;
> >+		press = 0;
> >+		rc = draglock_filter_button(&dl, &button, &press);
> >+		assert(rc == 0);
> >+		assert(button == 0);
> >+	}
> >+
> >+	for (i = 0; i < 10; i++) {
> >+		/* button down -> eaten */
> >+		button = i;
> >+		press = 1;
> >+		rc = draglock_filter_button(&dl, &button, &press);
> >+		assert(rc == 0);
> >+		assert(button == 0);
> >+
> >+		/* button up -> passthrough */
> >+		button = i;
> >+		press = 0;
> >+		rc = draglock_filter_button(&dl, &button, &press);
> >+		assert(rc == 0);
> >+		assert(button == i);
> >+		assert(press == 0);
> >+	}
> >+}
> >+
> >+static void
> >+test_filter_pairs(void)
> >+{
> >+	struct draglock dl;
> >+	int rc;
> >+	int button, press;
> >+	int i;
> >+
> >+	rc = draglock_init_from_string(&dl, "1 11 2 0 3 13 4 0 5 15 6 0 7 17 8 0 9 19");
> >+
> >+	for (i = 1; i < 10; i++) {
> >+		button = i;
> >+		press = 1;
> >+
> >+		rc = draglock_filter_button(&dl, &button, &press);
> >+		assert(rc == 0);
> >+		if (i % 2)
> >+			assert(button == i + 10);
> >+		else
> >+			assert(button == i);
> >+		assert(press == 1);
> >+
> >+		button = i;
> >+		press = 0;
> >+		rc = draglock_filter_button(&dl, &button, &press);
> >+		assert(rc == 0);
> >+		if (i % 2) {
> >+			assert(button == 0);
> >+		} else {
> >+			assert(button == i);
> >+			assert(press == 0);
> >+		}
> >+	}
> >+
> >+	for (i = 1; i < 10; i++) {
> >+		button = i;
> >+		press = 1;
> >+
> >+		rc = draglock_filter_button(&dl, &button, &press);
> >+		assert(rc == 0);
> >+		if (i % 2) {
> >+			assert(button == 0);
> >+		} else {
> >+			assert(button == i);
> >+			assert(press == 1);
> >+		}
> >+
> >+		button = i;
> >+		press = 0;
> >+		rc = draglock_filter_button(&dl, &button, &press);
> >+		assert(rc == 0);
> >+		if (i % 2)
> >+			assert(button == i + 10);
> >+		else
> >+			assert(button == i);
> >+		assert(press == 0);
> >+	}
> >+}
> >+
> >+int
> >+main(int argc, char **argv)
> >+{
> >+	test_config_empty();
> >+	test_config_invalid();
> >+	test_config_disable();
> >+	test_config_meta_button();
> >+	test_config_button_pairs();
> >+
> >+	test_config_get();
> >+	test_set_meta();
> >+	test_set_pairs();
> >+
> >+	test_filter_meta_passthrough();
> >+	test_filter_meta_click_meta_only();
> >+	test_filter_meta();
> >+	test_filter_meta_extra_click();
> >+	test_filter_meta_interleaved();
> >+
> >+	test_filter_pairs();
> >+
> >+	return 0;
> >+}
> >


More information about the xorg-devel mailing list