[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