Commit e3a888c3 authored by Peter Hutterer's avatar Peter Hutterer

Add drag lock support

First, why is this here and not in libinput: drag lock should be implemented
in the compositor (not in libinput) so it can provide feedback when it
activates and grouped in with other accessibility features. That will work for
Wayland but in X the compositor cannot filter button events - only the server
and the drivers can.

This patch adds mostly the same functionality that evdev provides with two
options on how it works:
* a single button number configures the given button to lock the next button
  pressed in a logically down state until a press+ release of that same button
  again
* a set of button number pairs configures each button with the to-be-locked
  logical button, i.e. a pair of "1 3" will hold 3 logically down after a
  button 1 press

The property and the xorg.conf options take the same configuration as the
evdev driver (though the property has a different prefix, libinput instead of
Evdev).

The behavior difference to evdev is in how releases are handled, evdev sends
the release on the second button press event, this implementation sends the
release on the second release event.

https://bugs.freedesktop.org/show_bug.cgi?id=85577Signed-off-by: Peter Hutterer's avatarPeter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: default avatarHans de Goede <hdegoede@redhat.com>
parent cd61ddb0
......@@ -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
......
......@@ -71,5 +71,6 @@ AC_CONFIG_FILES([Makefile
include/Makefile
src/Makefile
man/Makefile
test/Makefile
xorg-libinput.pc])
AC_OUTPUT
......@@ -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_ */
......@@ -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"
......
......@@ -30,8 +30,10 @@ AM_CPPFLAGS =-I$(top_srcdir)/include $(LIBINPUT_CFLAGS)
@DRIVER_NAME@_drv_la_LTLIBRARIES = @DRIVER_NAME@_drv.la
@DRIVER_NAME@_drv_la_LDFLAGS = -module -avoid-version
@DRIVER_NAME@_drv_la_LIBADD = $(LIBINPUT_LIBS)
@DRIVER_NAME@_drv_la_LIBADD = $(LIBINPUT_LIBS) libdraglock.la
@DRIVER_NAME@_drv_ladir = @inputdir@
@DRIVER_NAME@_drv_la_SOURCES = xf86libinput.c
noinst_LTLIBRARIES = libdraglock.la
libdraglock_la_SOURCES = draglock.c draglock.h
/*
* 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;
}
/*
* 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 */
/*
* 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 ||
......@@ -2563,6 +2672,37 @@ LibinputInitDisableWhileTypingProperty(DeviceIntPtr dev,
1, &dwt);
}
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