Commit d84e0035 authored by Peter Hutterer's avatar Peter Hutterer

Implement the custom acceleration curve options

One new property, and the existing accel profile gets extended to keep one
extra value. The new property libinput Accel Curve Points is a list of pairs
of points to be added to the acceleration curve.

libinput only supports adding points to the curve so we simply declare the
behavior as undefined when the curve is set multiple times. Also helps to
identify those that bother to read the man page before playing with random
driver values.
Signed-off-by: Peter Hutterer's avatarPeter Hutterer <peter.hutterer@who-t.net>
parent 6c75acfc
......@@ -47,6 +47,23 @@ XORG_DEFAULT_OPTIONS
PKG_CHECK_MODULES(XORG, [xorg-server >= 1.10] xproto [inputproto >= 2.2])
PKG_CHECK_MODULES(LIBINPUT, [libinput >= 1.4.901])
AC_MSG_CHECKING([if libinput_device_config_accel_set_curve_point is available])
OLD_LIBS=$LIBS
OLD_CFLAGS=$CFLAGS
LIBS="$LIBS $LIBINPUT_LIBS"
CFLAGS="$CFLAGS $LIBINPUT_CFLAGS"
AC_LINK_IFELSE(
[AC_LANG_PROGRAM([[#include <libinput.h>]],
[[libinput_device_config_accel_set_curve_point(NULL, 0, 0)]])],
[AC_MSG_RESULT([yes])
AC_DEFINE(HAVE_LIBINPUT_CUSTOM_ACCEL_CURVE, [1],
[libinput_device_config_accel_set_curve_point() is available])
[libinput_have_custom_accel_curve=yes]],
[AC_MSG_RESULT([no])
[libinput_have_custom_accel_curve=no]])
LIBS=$OLD_LIBS
CFLAGS=$OLD_CFLAGS
# Define a configure option for an alternate input module directory
AC_ARG_WITH(xorg-module-dir,
AC_HELP_STRING([--with-xorg-module-dir=DIR],
......
......@@ -73,6 +73,14 @@
only one is enabled at a time at max */
#define LIBINPUT_PROP_ACCEL_PROFILE_ENABLED "libinput Accel Profile Enabled"
/* Pointer accel custom curve: FLOAT, n values for n>=0 && n%2 == 0, where
each pair of values are the x/y coordinates of the acceleration curve.
The exact behavior depends on the acceleration profile.
This property should be considered write-only, the value of the property may
not reflect the actual curve points used in libinput. */
#define LIBINPUT_PROP_ACCEL_CURVE_POINTS "libinput Accel Curve Points"
/* Natural scrolling: BOOL, 1 value */
#define LIBINPUT_PROP_NATURAL_SCROLL "libinput Natural Scrolling Enabled"
......
......@@ -45,11 +45,19 @@ are supported:
Sets the pointer acceleration profile to the given profile. Permitted values
are
.BI adaptive,
.BI flat.
.BI flat,
.BI device-speed-curve.
Not all devices support this option or all profiles. If a profile is
unsupported, the default profile for this device is used. For a description
on the profiles and their behavior, see the libinput documentation.
.TP 7
.BI "Option \*qAccelCurvePoints\*q \*q" string \*q
A string of space-separated pairs of floats, each pair value separated by a
colon, i.e. the string looks like this "A:B C:D". These pairs represent the
x/y value of a point on the acceleration curve. See
.B CUSTOM POINTER ACCELERATION CURVES
for details.
.TP 7
.BI "Option \*qAccelSpeed\*q \*q" float \*q
Sets the pointer acceleration speed within the range [-1, 1]
.TP 7
......@@ -204,16 +212,26 @@ on the device. The following properties are provided by the
driver.
.TP 7
.BI "libinput Accel Profiles Available"
2 boolean values (8 bit, 0 or 1), in order "adaptive", "flat".
Indicates which acceleration profiles are available on this device.
3 boolean values (8 bit, 0 or 1), in order "adaptive", "flat",
"device-speed-curve". Indicates which acceleration profiles are available on
this device.
.TP 7
.BI "libinput Accel Profile Enabled"
2 boolean values (8 bit, 0 or 1), in order "adaptive", "flat".
Indicates which acceleration profile is currently enabled on this device.
3 boolean values (8 bit, 0 or 1), in order "adaptive", "flat",
"device-speed-curve". Indicates which acceleration profile is currently
enabled on this device.
.TP 7
.BI "libinput Accel Speed"
1 32-bit float value, defines the pointer speed. Value range -1, 1
.TP 7
.BI "libinput Accel Curve Points"
n 32-bit float values, n is a multiple of 2, each pair represents one point
on the acceleration curve. If the acceleration profile is
\fBdevice-speed-curve\fR, each value x is the device speed in units/ms and each
value x+1 is the acceleration factor to apply. This property should only be
written once per profile change, see section
.B CUSTOM POINTER ACCELERATION CURVES
.TP 7
.BI "libinput Button Scrolling Button"
1 32-bit value. Sets the button number to use for button scrolling. This
setting is independent of the scroll method, to enable button scrolling the
......@@ -381,6 +399,31 @@ it takes left-handed-ness into account.
.TP
This feature is provided by this driver, not by libinput.
.SH CUSTOM POINTER ACCELERATION CURVES
Some profiles require the acceleration curve to be provided
by the user. These curve points must be provided as pairs in the option or
the property and each curve point defines the x/y value of one point on the
curve. libinput does linear interpolation between the defined points. See
the libinput documentation for details.
.PP
If the profile is
.B device-speed-curve,
the points represent the mapping of device speed (in device units/ms) to an
acceleration factor, e.g. the values (50, 2) map a device speed of 50
units/ms to an acceleration factor of 2. The current delta is then
multiplied by that factor.
.PP
To apply a custom acceleration curve, first set the profile to
.B device-speed-curve,
then set the curve points. All curve points must be set with one property
update. Once set, adding, removing or otherwise modifying the property
values results in undefined behavior.
.PP
To reset the curve, change to a different acceleration profile, then change
back to
.B device-speed-curve
before applying the new set of curve points.
.SH BUGS
This driver does not work with \fBOption \*qDevice\*q\fR set to an event
node in \fI/dev/input/by-id\fR and \fI/dev/input/by-path\fR. This can be
......
......@@ -122,6 +122,11 @@ struct xf86libinput_tablet_tool {
struct libinput_tablet_tool *tool;
};
struct curve_point {
struct xorg_list node;
float x, fx;
} curve_point;
struct xf86libinput {
InputInfoPtr pInfo;
char *path;
......@@ -173,6 +178,8 @@ struct xf86libinput {
struct ratio {
int x, y;
} area;
struct xorg_list curve_points;
} options;
struct draglock draglock;
......@@ -547,6 +554,11 @@ LibinputApplyConfigAccel(DeviceIntPtr dev,
case LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT:
profile = "flat";
break;
#if HAVE_LIBINPUT_CUSTOM_ACCEL_CURVE
case LIBINPUT_CONFIG_ACCEL_PROFILE_DEVICE_SPEED_CURVE:
profile = "device-speed-curve";
break;
#endif
default:
profile = "unknown";
break;
......@@ -763,6 +775,30 @@ LibinputApplyConfigRotation(DeviceIntPtr dev,
driver_data->options.rotation_angle);
}
static void
LibinputApplyConfigAccelCurvePoints(DeviceIntPtr dev,
struct xf86libinput *driver_data,
struct libinput_device *device)
{
#if HAVE_LIBINPUT_CUSTOM_ACCEL_CURVE
struct curve_point *p;
if (!subdevice_has_capabilities(dev, CAP_POINTER))
return;
if (!libinput_device_config_accel_is_available(device))
return;
if (libinput_device_config_accel_get_profile(device) !=
LIBINPUT_CONFIG_ACCEL_PROFILE_DEVICE_SPEED_CURVE)
return;
xorg_list_for_each_entry(p, &driver_data->options.curve_points, node) {
libinput_device_config_accel_set_curve_point(device, p->x, p->fx);
}
#endif
}
static inline void
LibinputApplyConfig(DeviceIntPtr dev)
{
......@@ -781,6 +817,7 @@ LibinputApplyConfig(DeviceIntPtr dev)
LibinputApplyConfigMiddleEmulation(dev, driver_data, device);
LibinputApplyConfigDisableWhileTyping(dev, driver_data, device);
LibinputApplyConfigRotation(dev, driver_data, device);
LibinputApplyConfigAccelCurvePoints(dev, driver_data, device);
}
static int
......@@ -2592,6 +2629,10 @@ xf86libinput_parse_accel_profile_option(InputInfoPtr pInfo,
profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE;
else if (strncasecmp(str, "flat", 4) == 0)
profile = LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT;
#if HAVE_LIBINPUT_CUSTOM_ACCEL_CURVE
else if (strncasecmp(str, "device-speed-curve", 18) == 0)
profile = LIBINPUT_CONFIG_ACCEL_PROFILE_DEVICE_SPEED_CURVE;
#endif
else {
xf86IDrvMsg(pInfo, X_ERROR,
"Unknown accel profile '%s'. Using default.\n",
......@@ -2709,6 +2750,57 @@ xf86libinput_parse_calibration_option(InputInfoPtr pInfo,
free(str);
}
static inline void
xf86libinput_parse_accel_curve_points_options(InputInfoPtr pInfo,
struct libinput_device *device,
struct xorg_list *points)
{
#if HAVE_LIBINPUT_CUSTOM_ACCEL_CURVE
uint32_t supported;
char *str, *s;
xorg_list_init(points);
if (!libinput_device_config_accel_is_available(device))
return;
supported = libinput_device_config_accel_get_profiles(device);
if ((supported & LIBINPUT_CONFIG_ACCEL_PROFILE_DEVICE_SPEED_CURVE) == 0)
return;
str = xf86SetStrOption(pInfo->options, "AccelCurvePoints", NULL);
if (!str)
return;
s = str;
while (s) {
char *token = strtok_r(s, " ", &s);
struct curve_point *p;
int nparsed;
if (!token)
break;
p = calloc(1, sizeof(*p));
if (!p)
break;
nparsed = sscanf(token, "%f:%f", &p->x, &p->fx);
if (nparsed != 2) {
xf86IDrvMsg(pInfo, X_ERROR,
"Failed to parse curve points: %s\n", str);
free(p);
break;
}
xorg_list_append(&p->node, points);
libinput_device_config_accel_set_curve_point(device, p->x, p->fx);
}
free(str);
#endif
}
static inline BOOL
xf86libinput_parse_lefthanded_option(InputInfoPtr pInfo,
struct libinput_device *device)
......@@ -3080,6 +3172,8 @@ xf86libinput_parse_options(InputInfoPtr pInfo,
options->disable_while_typing = xf86libinput_parse_disablewhiletyping_option(pInfo, device);
options->rotation_angle = xf86libinput_parse_rotation_angle_option(pInfo, device);
xf86libinput_parse_calibration_option(pInfo, device, driver_data->options.matrix);
xf86libinput_parse_accel_curve_points_options(pInfo, device,
&driver_data->options.curve_points);
/* non-libinput options */
xf86libinput_parse_buttonmap_option(pInfo,
......@@ -3556,6 +3650,7 @@ static Atom prop_draglock;
static Atom prop_horiz_scroll;
static Atom prop_pressurecurve;
static Atom prop_area_ratio;
static Atom prop_accel_curve_points;
/* general properties */
static Atom prop_float;
......@@ -3882,7 +3977,7 @@ LibinputSetPropertyAccelProfile(DeviceIntPtr dev,
BOOL* data;
uint32_t profiles = 0;
if (val->format != 8 || val->size != 2 || val->type != XA_INTEGER)
if (val->format != 8 || val->size < 2 || val->size > 3 || val->type != XA_INTEGER)
return BadMatch;
data = (BOOL*)val->data;
......@@ -3891,6 +3986,10 @@ LibinputSetPropertyAccelProfile(DeviceIntPtr dev,
profiles |= LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE;
if (data[1])
profiles |= LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT;
#if HAVE_LIBINPUT_CUSTOM_ACCEL_CURVE
if (val->size > 2 && data[2])
profiles |= LIBINPUT_CONFIG_ACCEL_PROFILE_DEVICE_SPEED_CURVE;
#endif
if (checkonly) {
uint32_t supported;
......@@ -4456,6 +4555,64 @@ LibinputSetPropertyAreaRatio(DeviceIntPtr dev,
return Success;
}
static inline int
LibinputSetPropertyAccelCurvePoints(DeviceIntPtr dev,
Atom atom,
XIPropertyValuePtr val,
BOOL checkonly)
{
#if HAVE_LIBINPUT_CUSTOM_ACCEL_CURVE
InputInfoPtr pInfo = dev->public.devicePrivate;
struct xf86libinput *driver_data = pInfo->private;
struct libinput_device *device = driver_data->shared_device->device;
float *vals;
if (val->format != 32 || val->size % 2 != 0 || val->type != prop_float)
return BadMatch;
vals = val->data;
if (val->size >= 128) /* 128 curve points is enough for everybody */
return BadMatch;
if (checkonly) {
uint32_t supported;
supported = libinput_device_config_accel_get_profiles(device);
if ((supported & LIBINPUT_CONFIG_ACCEL_PROFILE_DEVICE_SPEED_CURVE) == 0)
return BadMatch;
for (size_t i = 0; i < val->size; i++) {
if (vals[i] < 0.0)
return BadValue;
}
} else {
struct curve_point *p, *tmp;
xorg_list_for_each_entry_safe(p, tmp,
&driver_data->options.curve_points,
node) {
xorg_list_del(&p->node);
free(p);
}
xorg_list_init(&driver_data->options.curve_points);
for (size_t i = 0; i < val->size; i += 2) {
p = calloc(1, sizeof(*p));
if (!p)
break;
p->x = vals[i];
p->fx = vals[i+1];
xorg_list_append(&p->node,
&driver_data->options.curve_points);
}
}
#endif
return Success;
}
static int
LibinputSetProperty(DeviceIntPtr dev, Atom atom, XIPropertyValuePtr val,
BOOL checkonly)
......@@ -4512,6 +4669,8 @@ LibinputSetProperty(DeviceIntPtr dev, Atom atom, XIPropertyValuePtr val,
rc = LibinputSetPropertyPressureCurve(dev, atom, val, checkonly);
else if (atom == prop_area_ratio)
rc = LibinputSetPropertyAreaRatio(dev, atom, val, checkonly);
else if (atom == prop_accel_curve_points)
rc = LibinputSetPropertyAccelCurvePoints(dev, atom, val, checkonly);
else if (atom == prop_device || atom == prop_product_id ||
atom == prop_tap_default ||
atom == prop_tap_drag_default ||
......@@ -4750,7 +4909,7 @@ LibinputInitAccelProperty(DeviceIntPtr dev,
float speed = driver_data->options.speed;
uint32_t profile_mask;
enum libinput_config_accel_profile profile;
BOOL profiles[2] = {FALSE};
BOOL profiles[3] = {FALSE};
if (!subdevice_has_capabilities(dev, CAP_POINTER))
return;
......@@ -4780,6 +4939,10 @@ LibinputInitAccelProperty(DeviceIntPtr dev,
profiles[0] = TRUE;
if (profile_mask & LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE)
profiles[1] = TRUE;
#if HAVE_LIBINPUT_CUSTOM_ACCEL_CURVE
if (profile_mask & LIBINPUT_CONFIG_ACCEL_PROFILE_DEVICE_SPEED_CURVE)
profiles[2] = TRUE;
#endif
prop_accel_profiles_available = LibinputMakeProperty(dev,
LIBINPUT_PROP_ACCEL_PROFILES_AVAILABLE,
......@@ -4799,6 +4962,11 @@ LibinputInitAccelProperty(DeviceIntPtr dev,
case LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT:
profiles[1] = TRUE;
break;
#if HAVE_LIBINPUT_CUSTOM_ACCEL_CURVE
case LIBINPUT_CONFIG_ACCEL_PROFILE_DEVICE_SPEED_CURVE:
profiles[2] = TRUE;
break;
#endif
default:
break;
}
......@@ -4821,6 +4989,11 @@ LibinputInitAccelProperty(DeviceIntPtr dev,
case LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT:
profiles[1] = TRUE;
break;
#if HAVE_LIBINPUT_CUSTOM_ACCEL_CURVE
case LIBINPUT_CONFIG_ACCEL_PROFILE_DEVICE_SPEED_CURVE:
profiles[2] = TRUE;
break;
#endif
default:
break;
}
......@@ -5423,6 +5596,55 @@ LibinputInitTabletAreaRatioProperty(DeviceIntPtr dev,
2, data);
}
static void
LibinputInitAccelCurvePointsProperty(DeviceIntPtr dev,
struct xf86libinput *driver_data,
struct libinput_device *device)
{
#if HAVE_LIBINPUT_CUSTOM_ACCEL_CURVE
struct curve_point *p;
uint32_t supported;
float *data = NULL;
int npoints, idx;
if (!subdevice_has_capabilities(dev, CAP_POINTER))
return;
if (!libinput_device_config_accel_is_available(device))
return;
supported = libinput_device_config_accel_get_profiles(device);
if ((supported & LIBINPUT_CONFIG_ACCEL_PROFILE_DEVICE_SPEED_CURVE) == 0)
return;
npoints = 0;
xorg_list_for_each_entry(p, &driver_data->options.curve_points, node)
npoints++;
if (npoints > 0) {
npoints = min(npoints, 128);
data = alloca(npoints * 2 * sizeof *data);
idx = 0;
xorg_list_for_each_entry(p, &driver_data->options.curve_points, node) {
data[idx++] = p->x;
data[idx++] = p->fx;
if (idx >= npoints)
break;
}
}
prop_accel_curve_points = LibinputMakeProperty(dev,
LIBINPUT_PROP_ACCEL_CURVE_POINTS,
prop_float, 32,
npoints, data);
if (!prop_accel_curve_points)
return;
#endif
}
static void
LibinputInitProperty(DeviceIntPtr dev)
{
......@@ -5481,4 +5703,5 @@ LibinputInitProperty(DeviceIntPtr dev)
LibinputInitHorizScrollProperty(dev, driver_data);
LibinputInitPressureCurveProperty(dev, driver_data);
LibinputInitTabletAreaRatioProperty(dev, driver_data);
LibinputInitAccelCurvePointsProperty(dev, driver_data, device);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment