diff --git a/doc/pointer-acceleration.dox b/doc/pointer-acceleration.dox index c120565a..4ed2d231 100644 --- a/doc/pointer-acceleration.dox +++ b/doc/pointer-acceleration.dox @@ -157,4 +157,70 @@ Pointer acceleration for relative motion on tablet devices is a flat acceleration, with the speed seeting slowing down or speeding up the pointer motion by a constant factor. Tablets do not allow for switchable profiles. +@section ptraccel-device-speed Speed-dependent acceleration curve + +When the @ref LIBINPUT_CONFIG_ACCEL_PROFILE_DEVICE_SPEED_CURVE profile is +selected, it is the caller's responsibility to load an acceleration profile +into the device that maps the device's movement into an accelerated +movement. + +This profile maps input speed in **device units** to an acceleration +factor. libinput calculates the device's speed based on the deltas and their +timestamps and applies the factor provided by the acceleration profile to +the current delta. + +@note This profile uses data in device units, an acceleration curve loaded +by the caller only applies to that device and will not behave the same way +for other devices. + +@dot +digraph +{ + rankdir="LR"; + node [shape="box";] + subgraph cluster0 { + history[shape=record,label=" delta(t-1)| delta(t-2)| delta(t-3)| ..."]; + label = "motion history"; + } + delta[label="delta"]; + velocity[shape="ellipse"]; + factor[shape="ellipse"]; + accel[label="accelerated delta"]; + curve[label="acceleration curve"]; + + delta->velocity; + delta->factor; + factor->accel; + history->velocity; + + velocity->curve; + curve->factor; +} +@enddot + +For example, assume the current delta is (2, 4) which maps to a current +movement velocity of 10 units per microsecond. libinput looks up the custom +acceleration function for 10 which may return 3. The accelerated delta thus +becomes (6, 12). + +The profile itself is a curve defined by a number of points on the curve +mapping speed to an acceleration factor. Between each two curve points, +libinput provides linear interpolation, most acceleration profiles will thus +only need to provide a handful of curve points. The following illustration +shows the acceleration curve defined for the points (10, 0.25), (20, 0.4) +and (35, 3.0). + +@image html ptraccel-curve-example.svg "Interpolation of the acceleration curve for three defined points" + +As seen in the picture above: given an acceleration factor f(x) and a curve +defined by the caller for the points +a, b, and c where a < b < c: +- if x <= a, f(x) == f(a) +- if x >= c, f(x) == f(c) +- if a < x < b: f(x) == the the linear interpolation value between f(a) and f(b) +- if b < x < c: f(x) == the the linear interpolation value between f(b) and f(c) + +The behavior of a a curve is implementation-defined until the caller sets +curve points. + */ diff --git a/doc/svg/ptraccel-curve-example.svg b/doc/svg/ptraccel-curve-example.svg new file mode 100644 index 00000000..b098e142 --- /dev/null +++ b/doc/svg/ptraccel-curve-example.svg @@ -0,0 +1,290 @@ + + + +Gnuplot +Produced by GNUPLOT 5.0 patchlevel 6 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + 0.5 + + + + + 1 + + + + + 1.5 + + + + + 2 + + + + + 2.5 + + + + + 3 + + + + + 3.5 + + + + + 0 + + + + + 5 + + + + + 10 + + + + + 15 + + + + + 20 + + + + + 25 + + + + + 30 + + + + + 35 + + + + + 40 + + + + + + + + + acceleration factor + + + + + speed (device units/ms) + + + + + factor + + + factor + + + + + + + + + + + + + + + + + diff --git a/meson.build b/meson.build index dfbac572..c2cd0a6f 100644 --- a/meson.build +++ b/meson.build @@ -151,6 +151,7 @@ dep_libinput_util = declare_dependency(link_with : libinput_util) ############ libfilter.a ############ src_libfilter = [ 'src/filter.c', + 'src/filter-custom.c', 'src/filter-flat.c', 'src/filter-low-dpi.c', 'src/filter-mouse.c', diff --git a/src/evdev.c b/src/evdev.c index 2a36ef81..b2a3f0a1 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -943,7 +943,9 @@ evdev_init_accel(struct evdev_device *device, { struct motion_filter *filter; - if (which == LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT) + if (which == LIBINPUT_CONFIG_ACCEL_PROFILE_DEVICE_SPEED_CURVE) + filter = create_pointer_accelerator_filter_custom_device_speed(); + else if (which == LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT) filter = create_pointer_accelerator_filter_flat(device->dpi); else if (device->tags & EVDEV_TAG_TRACKPOINT) filter = create_pointer_accelerator_filter_trackpoint(device->trackpoint_range); @@ -1002,7 +1004,8 @@ evdev_accel_config_get_profiles(struct libinput_device *libinput_device) return LIBINPUT_CONFIG_ACCEL_PROFILE_NONE; return LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE | - LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT; + LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT | + LIBINPUT_CONFIG_ACCEL_PROFILE_DEVICE_SPEED_CURVE; } static enum libinput_config_status @@ -1051,6 +1054,27 @@ evdev_accel_config_get_default_profile(struct libinput_device *libinput_device) return LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE; } +static enum libinput_config_status +evdev_accel_config_set_curve_point(struct libinput_device *libinput_device, + double a, + double fa) +{ + struct evdev_device *device = evdev_device(libinput_device); + struct motion_filter *filter = device->pointer.filter; + + if (evdev_accel_config_get_profile(libinput_device) != + LIBINPUT_CONFIG_ACCEL_PROFILE_DEVICE_SPEED_CURVE) + return LIBINPUT_CONFIG_STATUS_INVALID; + + if (a < 0 || a > 50000) + return LIBINPUT_CONFIG_STATUS_INVALID; + + if (!filter_set_curve_point(filter, a, fa)) + return LIBINPUT_CONFIG_STATUS_INVALID; + + return LIBINPUT_CONFIG_STATUS_SUCCESS; +} + void evdev_device_init_pointer_acceleration(struct evdev_device *device, struct motion_filter *filter) @@ -1068,6 +1092,7 @@ evdev_device_init_pointer_acceleration(struct evdev_device *device, device->pointer.config.set_profile = evdev_accel_config_set_profile; device->pointer.config.get_profile = evdev_accel_config_get_profile; device->pointer.config.get_default_profile = evdev_accel_config_get_default_profile; + device->pointer.config.set_curve_point = evdev_accel_config_set_curve_point; device->base.config.accel = &device->pointer.config; default_speed = evdev_accel_config_get_default_speed(&device->base); diff --git a/src/filter-custom.c b/src/filter-custom.c new file mode 100644 index 00000000..7768aa2c --- /dev/null +++ b/src/filter-custom.c @@ -0,0 +1,216 @@ +/* + * Copyright © 2018 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "filter.h" +#include "libinput-util.h" +#include "filter-private.h" + +struct acceleration_curve_point { + double x, fx; +}; + +struct custom_accelerator { + struct motion_filter base; + struct acceleration_curve_point points[32]; + size_t npoints; + + double last_velocity; + struct pointer_trackers trackers; +}; + +double +custom_accel_profile(struct motion_filter *filter, + void *data, + double speed_in, /* in device units/µs */ + uint64_t time) +{ + struct custom_accelerator *f = + (struct custom_accelerator*)filter; + double fx = 1; + + speed_in *= 1000; + + if (f->npoints == 0) + return 1.0; + + if (f->points[0].x >= speed_in) + return f->points[0].fx; + + for (size_t i = 0; i < f->npoints - 1; i++) { + double a, b, fa, fb; + double k, d; + + if (f->points[i + 1].x < speed_in) + continue; + + /* + We haves points f(i), f(i+1), defining two points on the + curve. linear function in the form y = kx+d: + + y = kx + d + + y1 = kx1 + d -> d = y1 - kx1 + y2 = kx2 + d -> d = y2 - kx2 + + y1 - kx1 = y2 - kx2 + y1 - y2 = kx1 - kx2 + k = y1-y2/(x1 - x2) + + */ + a = f->points[i].x; + fa = f->points[i].fx; + b = f->points[i+1].x; + fb = f->points[i+1].fx; + + k = (fa - fb)/(a - b); + d = fa - k * a; + + fx = k * speed_in + d; + + return fx; + } + + return f->points[f->npoints - 1].fx; +} + +static struct normalized_coords +custom_accelerator_filter(struct motion_filter *filter, + const struct device_float_coords *units, + void *data, uint64_t time) +{ + struct custom_accelerator *f = + (struct custom_accelerator*)filter; + struct normalized_coords norm; + double velocity; /* units/us in device-native dpi*/ + double accel_factor; + + trackers_feed(&f->trackers, units, time); + velocity = trackers_velocity(&f->trackers, time); + accel_factor = calculate_acceleration_simpsons(filter, + custom_accel_profile, + data, + velocity, + f->last_velocity, + time); + f->last_velocity = velocity; + + norm.x = accel_factor * units->x; + norm.y = accel_factor * units->y; + + return norm; +} + +static bool +custom_accelerator_set_speed(struct motion_filter *filter, + double speed_adjustment) +{ + assert(speed_adjustment >= -1.0 && speed_adjustment <= 1.0); + + /* noop, this function has no effect in the custom interface */ + + return true; +} + +static void +custom_accelerator_destroy(struct motion_filter *filter) +{ + struct custom_accelerator *accel_filter = + (struct custom_accelerator*)filter; + + trackers_free(&accel_filter->trackers); + free(accel_filter); +} + +static bool +custom_accelerator_set_curve_point(struct motion_filter *filter, + double a, double fa) +{ + struct custom_accelerator *f = + (struct custom_accelerator*)filter; + + if (f->npoints == ARRAY_LENGTH(f->points)) + return false; + + if (a < 0 || a > 50000) + return false; + + if (f->npoints == 0) { + f->points[0].x = a; + f->points[0].fx = fa; + f->npoints = 1; + return true; + } else if (f->points[f->npoints - 1].x < a) { + f->points[f->npoints].x = a; + f->points[f->npoints].fx = fa; + f->npoints++; + return true; + } + + for (size_t i = 0; i < f->npoints; i++) { + if (f->points[i].x == a) { + f->points[i].fx = fa; + break; + } else if (f->points[i].x > a) { + f->npoints++; + for (size_t j = f->npoints - 1; j > i; j--) + f->points[j] = f->points[j-1]; + f->points[i] = (struct acceleration_curve_point){ a, fa }; + break; + } + } + + return true; +} + +struct motion_filter_interface accelerator_interface_custom = { + .type = LIBINPUT_CONFIG_ACCEL_PROFILE_DEVICE_SPEED_CURVE, + .filter = custom_accelerator_filter, + .filter_constant = NULL, + .restart = NULL, + .destroy = custom_accelerator_destroy, + .set_speed = custom_accelerator_set_speed, + .set_curve_point = custom_accelerator_set_curve_point, +}; + +struct motion_filter * +create_pointer_accelerator_filter_custom_device_speed(void) +{ + struct custom_accelerator *filter; + + filter = zalloc(sizeof *filter); + trackers_init(&filter->trackers); + + filter->base.interface = &accelerator_interface_custom; + + return &filter->base; +} diff --git a/src/filter-private.h b/src/filter-private.h index fa2fea40..71415dd9 100644 --- a/src/filter-private.h +++ b/src/filter-private.h @@ -44,6 +44,8 @@ struct motion_filter_interface { void (*destroy)(struct motion_filter *filter); bool (*set_speed)(struct motion_filter *filter, double speed_adjustment); + bool (*set_curve_point)(struct motion_filter *filter, + double a, double fa); }; struct motion_filter { diff --git a/src/filter.c b/src/filter.c index fe60c62a..15cbf44b 100644 --- a/src/filter.c +++ b/src/filter.c @@ -90,6 +90,15 @@ filter_get_type(struct motion_filter *filter) return filter->interface->type; } +bool +filter_set_curve_point(struct motion_filter *filter, double a, double fa) +{ + if (!filter->interface->set_curve_point) + return false; + + return filter->interface->set_curve_point(filter, a, fa); +} + void trackers_init(struct pointer_trackers *trackers) { diff --git a/src/filter.h b/src/filter.h index 506ab123..22d98e09 100644 --- a/src/filter.h +++ b/src/filter.h @@ -95,6 +95,9 @@ filter_set_speed(struct motion_filter *filter, double filter_get_speed(struct motion_filter *filter); +bool +filter_set_curve_point(struct motion_filter *filter, double a, double fa); + enum libinput_config_accel_profile filter_get_type(struct motion_filter *filter); @@ -104,6 +107,9 @@ typedef double (*accel_profile_func_t)(struct motion_filter *filter, uint64_t time); /* Pointer acceleration types */ +struct motion_filter * +create_pointer_accelerator_filter_custom_device_speed(void); + struct motion_filter * create_pointer_accelerator_filter_flat(int dpi); @@ -152,6 +158,12 @@ touchpad_lenovo_x230_accel_profile(struct motion_filter *filter, double speed_in, uint64_t time); double +custom_accel_profile(struct motion_filter *filter, + void *data, + double speed_in, + uint64_t time); + +double trackpoint_accel_profile(struct motion_filter *filter, void *data, double delta); diff --git a/src/libinput-private.h b/src/libinput-private.h index d50154ef..a6938ba6 100644 --- a/src/libinput-private.h +++ b/src/libinput-private.h @@ -213,6 +213,8 @@ struct libinput_device_config_accel { enum libinput_config_accel_profile); enum libinput_config_accel_profile (*get_profile)(struct libinput_device *device); enum libinput_config_accel_profile (*get_default_profile)(struct libinput_device *device); + enum libinput_config_status (*set_curve_point)(struct libinput_device *device, + double a, double fa); }; struct libinput_device_config_natural_scroll { diff --git a/src/libinput.c b/src/libinput.c index 8fb0ba92..be6fce0f 100644 --- a/src/libinput.c +++ b/src/libinput.c @@ -3673,6 +3673,19 @@ libinput_device_config_accel_get_default_speed(struct libinput_device *device) return device->config.accel->get_default_speed(device); } +LIBINPUT_EXPORT enum libinput_config_status +libinput_device_config_accel_set_curve_point( + struct libinput_device *device, + double a, double fa) +{ + if (libinput_device_config_accel_get_profile(device) != + LIBINPUT_CONFIG_ACCEL_PROFILE_DEVICE_SPEED_CURVE) { + return LIBINPUT_CONFIG_STATUS_INVALID; + } + + return device->config.accel->set_curve_point(device, a, fa); +} + LIBINPUT_EXPORT uint32_t libinput_device_config_accel_get_profiles(struct libinput_device *device) { @@ -3707,6 +3720,7 @@ libinput_device_config_accel_set_profile(struct libinput_device *device, switch (profile) { case LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT: case LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE: + case LIBINPUT_CONFIG_ACCEL_PROFILE_DEVICE_SPEED_CURVE: break; default: return LIBINPUT_CONFIG_STATUS_INVALID; diff --git a/src/libinput.h b/src/libinput.h index f1a0a2a6..fcb727ff 100644 --- a/src/libinput.h +++ b/src/libinput.h @@ -4480,6 +4480,12 @@ libinput_device_config_accel_is_available(struct libinput_device *device); * range. libinput picks the semantically closest acceleration step if the * requested value does not match a discrete setting. * + * If the current acceleration profile is @ref + * LIBINPUT_CONFIG_ACCEL_PROFILE_DEVICE_SPEED_CURVE, the behavior of the + * device will not change but future calls to + * libinput_device_config_accel_get_speed() will reflect the updated speed + * setting. + * * @param device The device to configure * @param speed The normalized speed, in a range of [-1, 1] * @@ -4528,6 +4534,44 @@ libinput_device_config_accel_get_speed(struct libinput_device *device); double libinput_device_config_accel_get_default_speed(struct libinput_device *device); +/** + * @ingroup config + * + * Sets a curve point on the custom acceleration function for this device. + * This function must be called after setting the type of the acceleration + * to @ref LIBINPUT_CONFIG_ACCEL_PROFILE_DEVICE_SPEED_CURVE and sets + * exactly one point on the device's acceleration curve. + * + * This function must be called multiple times to define a full acceleration + * curve. libinput uses linear interpolation between each defined curve + * point to calculate the appropriate factor. Any speed below or above the + * lowest or highest point defined is capped to the factor at the lowest or + * highest point, respectively. See @ref ptraccel-device-speed for a + * detailed explanation on this behavior. + * + * The behavior of the acceleration function depends on the type of the + * profile: + * - @ref LIBINPUT_CONFIG_ACCEL_PROFILE_DEVICE_SPEED_CURVE : the input data + * a is velocity in device units per millisecond, f(a) is a unitless + * factor. This factor is applied to the incoming delta so that a delta + * (x, y) is accelerated to the delta (f(a) * x, f(a) *y). The velocity + * is calculated by libinput based on the current and previous deltas and + * their timestamps. See @ref ptraccel-device-speed for details. + * + * @note libinput has a maximum limit for how many curve points may be set + * and will quietly drop curve points exceeding this limit. This limit is + * not expected to be hit by any reasonable caller. + * + * Submitting a curve point with the same value as a previous curve point + * overwrites that value. There is no facility to remove curve points, + * switch the device to a different profile and back again to reset. + * + * @return 0 on success or nonzero otherwise + */ +enum libinput_config_status +libinput_device_config_accel_set_curve_point(struct libinput_device *device, + double a, double fa); + /** * @ingroup config */ @@ -4551,6 +4595,11 @@ enum libinput_config_accel_profile { * on the input speed. This is the default profile for most devices. */ LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE = (1 << 1), + /** + * A custom user-provided profile. See + * libinput_acceleration_profile_set_curve_point() for details. + */ + LIBINPUT_CONFIG_ACCEL_PROFILE_DEVICE_SPEED_CURVE = (1 << 2), }; /** @@ -4572,6 +4621,13 @@ libinput_device_config_accel_get_profiles(struct libinput_device *device); * Set the pointer acceleration profile of this pointer device to the given * mode. * + * If the given profile is + * @ref LIBINPUT_CONFIG_ACCEL_PROFILE_DEVICE_SPEED_CURVE and it is + * different to the current profile, the acceleration curve is reset to an + * implementation-defined curve. The caller should call + * libinput_device_config_accel_set_curve_point() to + * define the curve points of the acceleration profile. + * * @param device The device to configure * @param mode The mode to set the device to. * diff --git a/src/libinput.sym b/src/libinput.sym index bb283407..f40969bf 100644 --- a/src/libinput.sym +++ b/src/libinput.sym @@ -293,3 +293,7 @@ LIBINPUT_1.7 { LIBINPUT_1.9 { libinput_device_switch_has_switch; } LIBINPUT_1.7; + +LIBINPUT_1.11 { + libinput_device_config_accel_set_curve_point; +} LIBINPUT_1.9; diff --git a/tools/libinput-debug-events.man b/tools/libinput-debug-events.man index 8a63821e..07674990 100644 --- a/tools/libinput-debug-events.man +++ b/tools/libinput-debug-events.man @@ -68,6 +68,11 @@ Enable or disable middle button emulation .B \-\-enable\-dwt|\-\-disable\-dwt Enable or disable disable-while-typing .TP 8 +.B \-\-set\-accel-curve-points="x1:y1;x2:y2" +Sets the curve points for the \fIcustom-speed\fR acceleration profile. The +set of curve points is a semicolon-separate lists of key-value pairs, each +separated by a colon. Each value is a non-negative double. +.TP 8 .B \-\-set\-click\-method=[none|clickfinger|buttonareas] Set the desired click method .TP 8 @@ -77,8 +82,9 @@ Set the desired scroll method .B \-\-set\-scroll\-button=BTN_MIDDLE Set the button to the given button code .TP 8 -.B \-\-set\-profile=[adaptive|flat] -Set pointer acceleration profile +.B \-\-set\-profile=[adaptive|flat|custom-speed] +Set pointer acceleration profile. If the \fIcustom-speed\fR profile is +selected, use \fB\-\-set-accel-curve-points\fR to specify the curve points. .TP 8 .B \-\-set\-speed= Set pointer acceleration speed. The allowed range is [-1, 1]. diff --git a/tools/libinput-list-devices.c b/tools/libinput-list-devices.c index aa225ca0..060d4b29 100644 --- a/tools/libinput-list-devices.c +++ b/tools/libinput-list-devices.c @@ -205,11 +205,13 @@ accel_profiles(struct libinput_device *device) profile = libinput_device_config_accel_get_default_profile(device); xasprintf(&str, - "%s%s %s%s", + "%s%s %s%s %s%s", (profile == LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT) ? "*" : "", (profiles & LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT) ? "flat" : "", (profile == LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE) ? "*" : "", - (profiles & LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE) ? "adaptive" : ""); + (profiles & LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE) ? "adaptive" : "", + (profile == LIBINPUT_CONFIG_ACCEL_PROFILE_DEVICE_SPEED_CURVE) ? "*" : "", + (profiles & LIBINPUT_CONFIG_ACCEL_PROFILE_DEVICE_SPEED_CURVE) ? "custom-speed" : ""); return str; } diff --git a/tools/ptraccel-debug.c b/tools/ptraccel-debug.c index 93be523f..ab7bc13e 100644 --- a/tools/ptraccel-debug.c +++ b/tools/ptraccel-debug.c @@ -161,11 +161,12 @@ print_accel_func(struct motion_filter *filter, printf("# set style data lines\n"); printf("# plot \"gnuplot.data\" using 1:2 title 'accel factor'\n"); printf("#\n"); - printf("# data: velocity(mm/s) factor velocity(units/us)\n"); + printf("# data: velocity(mm/s) factor velocity(units/us) velocity(units/ms)\n"); for (mmps = 0.0; mmps < 1000.0; mmps += 1) { double units_per_us = mmps_to_upus(mmps, dpi); + double units_per_ms = units_per_us * 1000; double result = profile(filter, NULL, units_per_us, 0 /* time */); - printf("%.8f\t%.4f\t%.8f\n", mmps, result, units_per_us); + printf("%.8f\t%.4f\t%.8f\t%.8f\n", mmps, result, units_per_us, units_per_ms); } } @@ -239,6 +240,7 @@ main(int argc, char **argv) const char *filter_type = "linear"; accel_profile_func_t profile = NULL; int tp_range_max = 20; + const char *curve_points = NULL; enum { OPT_HELP = 1, @@ -250,6 +252,7 @@ main(int argc, char **argv) OPT_DPI, OPT_FILTER, OPT_TRACKPOINT_RANGE, + OPT_CURVE_POINTS, }; while (1) { @@ -265,6 +268,7 @@ main(int argc, char **argv) {"dpi", 1, 0, OPT_DPI }, {"filter", 1, 0, OPT_FILTER }, {"trackpoint-range", 1, 0, OPT_TRACKPOINT_RANGE }, + {"curve-points", 1, 0, OPT_CURVE_POINTS }, {0, 0, 0, 0} }; @@ -325,6 +329,9 @@ main(int argc, char **argv) case OPT_TRACKPOINT_RANGE: tp_range_max = strtod(optarg, NULL); break; + case OPT_CURVE_POINTS: + curve_points = optarg; + break; default: usage(); exit(1); @@ -347,6 +354,24 @@ main(int argc, char **argv) } else if (streq(filter_type, "trackpoint")) { filter = create_pointer_accelerator_filter_trackpoint(tp_range_max); profile = NULL; /* trackpoint is special */ + } else if (streq(filter_type, "custom-speed")) { + struct key_value_double *points; + ssize_t npoints; + + filter = create_pointer_accelerator_filter_custom_device_speed(); + profile = custom_accel_profile; + + npoints = kv_double_from_string(curve_points, ";", ":", &points); + if (npoints <= 0) + return 1; + + for (ssize_t idx = 0; idx < npoints; idx++){ + filter_set_curve_point(filter, + points[idx].key, + points[idx].value); + } + + free(points); } else { fprintf(stderr, "Invalid filter type %s\n", filter_type); return 1; diff --git a/tools/shared.c b/tools/shared.c index c1ce6473..85c0d739 100644 --- a/tools/shared.c +++ b/tools/shared.c @@ -218,6 +218,17 @@ tools_parse_option(int option, "%s", optarg); break; + case OPT_CURVE_POINTS: + if (!optarg) + return 1; + + options->ncurve_points = kv_double_from_string( + optarg, + ";", ":", + &options->curve_points); + if (options->ncurve_points < 0) + return 1; + break; } return 0; @@ -386,6 +397,16 @@ tools_device_apply_config(struct libinput_device *device, libinput_device_config_send_events_set_mode(device, LIBINPUT_CONFIG_SEND_EVENTS_DISABLED); } + + if (libinput_device_config_accel_get_profile(device) == + LIBINPUT_CONFIG_ACCEL_PROFILE_DEVICE_SPEED_CURVE) { + for (ssize_t idx = 0; idx < options->ncurve_points; idx++) { + double x = options->curve_points[idx].key, + fx = options->curve_points[idx].value; + + libinput_device_config_accel_set_curve_point(device, x, fx); + } + } } static char* diff --git a/tools/shared.h b/tools/shared.h index 55e15409..dc61b5b7 100644 --- a/tools/shared.h +++ b/tools/shared.h @@ -50,6 +50,7 @@ enum configuration_options { OPT_SPEED, OPT_PROFILE, OPT_DISABLE_SENDEVENTS, + OPT_CURVE_POINTS, }; #define CONFIGURATION_OPTIONS \ @@ -73,7 +74,8 @@ enum configuration_options { { "set-scroll-button", required_argument, 0, OPT_SCROLL_BUTTON }, \ { "set-profile", required_argument, 0, OPT_PROFILE }, \ { "set-tap-map", required_argument, 0, OPT_TAP_MAP }, \ - { "set-speed", required_argument, 0, OPT_SPEED } + { "set-speed", required_argument, 0, OPT_SPEED }, \ + { "set-accel-curve-points", required_argument, 0, OPT_CURVE_POINTS } enum tools_backend { BACKEND_DEVICE, @@ -95,6 +97,9 @@ struct tools_options { int dwt; enum libinput_config_accel_profile profile; char disable_pattern[64]; + + struct key_value_double *curve_points; + ssize_t ncurve_points; }; void tools_init_options(struct tools_options *options);