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

Hans de Goede hdegoede at redhat.com
Wed Aug 12 01:43:02 PDT 2015


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 ?

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>

Regards,

Hans



> ---
>   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