------------------------------------------------------------------------------
The profile decides the general method of pointer acceleration.
-libinput currently supports two profiles: **"adaptive"** and **"flat"**.
+libinput currently supports three profiles: **"adaptive"**, **"flat"** and
+**custom**.
- The **adaptive** profile is the default profile for all devices and takes the
current speed of the device into account when deciding on acceleration.
- The **flat** profile is simply a constant factor applied to all device deltas,
regardless of the speed of motion (see :ref:`ptraccel-profile-flat`).
+- The **custom** profile allows the user to define a custom acceleration
+ function, giving full control over accelerations behavior at different speed
+ (see :ref:`ptraccel-profile-custom`).
Most of this document describes the adaptive pointer acceleration.
Pointer acceleration for relative motion on tablet devices is a flat
acceleration, with the speed setting slowing down or speeding up the pointer
motion by a constant factor. Tablets do not allow for switchable profiles.
+
+.. _ptraccel-profile-custom:
+
+------------------------------------------------------------------------------
+The custom acceleration profile
+------------------------------------------------------------------------------
+
+libinput supports a user-defined custom acceleration profile, which can be
+adjusted for different movement types supported by a device. Movement types
+include pointer movement, scrolling, etc. but the set of supported
+movement types depends on the device.
+
+The custom pointer acceleration profile gives users full control over the
+acceleration behavior at different speeds. libinput exposes
+an acceleration function ``f(x)`` where the x axis is the device speed in
+device units per millisecond and the y axis is the pointer speed. By
+supplying the y axis values for this function, users can control the
+behavior of the device.
+
+The user should take into account the native device dpi and screen dpi in
+order to achieve the desired behavior/feel.
+
+The custom acceleration function is defined using ``n`` points which are spaced
+uniformly along the x axis, starting from 0 and continuing in constant steps.
+At least two points must be defined and there is an implementation-defined
+limit on how many points may be added.
+
+Thus the points defining the custom function are:
+``(0 * step, f[0]), (1 * step, f[1]), ..., ((n-1) * step, f[n-1])``
+where ``f`` is a list of ``n`` unitless values defining the acceleration
+factor for each velocity.
+When a velocity value does not lie exactly on those points, a linear
+interpolation of the two closest points will be calculated.
+When a velocity value is greater than the max point defined, a linear
+extrapolation of the two biggest points will be calculated.
+
+An example is the curve of ``0.0, 1.0`` with a step of ``1.0``. This curve
+is the equivalent of the flat acceleration profile with any input speed `N`
+mapped to the same pointer speed `N`. The curve `1.0, 1.0` neutralizes
+any input speed differences and results in a fixed pointer speed.
+
+Supported Movement types:
+
++---------------+---------------------------------+----------------------+
+| Movement type | Uses | supported by |
++===============+=================================+======================+
+| Fallback | Catch-all default movement type | All devices |
++---------------+---------------------------------+----------------------+
+| Motion | Used for pointer motion | All devices |
++---------------+---------------------------------+----------------------+
+
+If a user does not provide the fallback custom acceleration function, a
+flat acceleration function is used, i.e. no acceleration.
+
+The fallback acceleration may be used for different types of movements, it is
+strongly recommended that this acceleration function is a constant function.
+
+For example, a physical mouse usually has two movement types: pointer
+movement and scroll (wheel) movement. As there is no separate movement
+type for scroll yet, scroll movement is be accelerated using the Fallback
+acceleration function. Pointer movements is accelerated using the Motion
+acceleration function. If no Motion acceleration function is set, the
+Fallback acceleration function is used.
+
+When using custom acceleration profile, any calls to set the speed have no
+effect on the behavior of the custom acceleration function, but any future calls to
+get the speed will reflect the requested speed setting.
############ libfilter.a ############
src_libfilter = [
'src/filter.c',
+ 'src/filter-custom.c',
'src/filter-flat.c',
'src/filter-low-dpi.c',
'src/filter-mouse.c',
tp->accel.y_scale_coeff = (DEFAULT_MOUSE_DPI/25.4) / res_y;
tp->accel.xy_scale_coeff = 1.0 * res_x/res_y;
- if (which == LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT)
+ if (which == LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT) {
filter = create_pointer_accelerator_filter_touchpad_flat(dpi);
- else if (evdev_device_has_model_quirk(device, QUIRK_MODEL_LENOVO_X230) ||
- tp->device->model_flags & EVDEV_MODEL_LENOVO_X220_TOUCHPAD_FW81)
+ } else if (which == LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM) {
+ filter = create_custom_accelerator_filter();
+ } else if (evdev_device_has_model_quirk(device, QUIRK_MODEL_LENOVO_X230) ||
+ tp->device->model_flags & EVDEV_MODEL_LENOVO_X220_TOUCHPAD_FW81) {
filter = create_pointer_accelerator_filter_lenovo_x230(dpi, use_v_avg);
- else if (libevdev_get_id_bustype(device->evdev) == BUS_BLUETOOTH)
+ } else {
+ uint64_t eds_threshold = 0;
+ uint64_t eds_value = 0;
+
+ if (libevdev_get_id_bustype(device->evdev) == BUS_BLUETOOTH) {
+ eds_threshold = ms2us(50);
+ eds_value = ms2us(10);
+ }
filter = create_pointer_accelerator_filter_touchpad(dpi,
- ms2us(50),
- ms2us(10),
+ eds_threshold,
+ eds_value,
use_v_avg);
- else
- filter = create_pointer_accelerator_filter_touchpad(dpi, 0, 0, use_v_avg);
+ }
if (!filter)
return false;
{
struct motion_filter *filter = NULL;
- if (device->tags & EVDEV_TAG_TRACKPOINT) {
+ if (which == LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM)
+ filter = create_custom_accelerator_filter();
+ else if (device->tags & EVDEV_TAG_TRACKPOINT) {
if (which == LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT)
filter = create_pointer_accelerator_filter_trackpoint_flat(device->trackpoint_multiplier);
else
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_CUSTOM;
}
static enum libinput_config_status
return LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE;
}
+static enum libinput_config_status
+evdev_set_accel_config(struct libinput_device *libinput_device,
+ struct libinput_config_accel *accel_config)
+{
+ assert(evdev_accel_config_get_profile(libinput_device) == accel_config->profile);
+
+ struct evdev_device *dev = evdev_device(libinput_device);
+
+ if (!filter_set_accel_config(dev->pointer.filter, accel_config))
+ return LIBINPUT_CONFIG_STATUS_INVALID;
+
+ return LIBINPUT_CONFIG_STATUS_SUCCESS;
+}
+
void
evdev_device_init_pointer_acceleration(struct evdev_device *device,
struct motion_filter *filter)
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_accel_config = evdev_set_accel_config;
device->base.config.accel = &device->pointer.config;
default_speed = evdev_accel_config_get_default_speed(&device->base);
--- /dev/null
+/*
+ * 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 <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "filter.h"
+#include "filter-private.h"
+
+#define MOTION_TIMEOUT ms2us(1000)
+#define FIRST_MOTION_TIME_INTERVAL ms2us(7) /* random but good enough interval for very first event */
+
+struct custom_accel_function {
+ uint64_t last_time;
+ double step;
+ size_t npoints;
+ double points[];
+};
+
+static struct custom_accel_function *
+create_custom_accel_function(double step, const double *points, size_t npoints)
+{
+ if (npoints < LIBINPUT_ACCEL_NPOINTS_MIN ||
+ npoints > LIBINPUT_ACCEL_NPOINTS_MAX)
+ return NULL;
+
+ if (step <= 0 || step > LIBINPUT_ACCEL_STEP_MAX)
+ return NULL;
+
+ struct custom_accel_function *cf = zalloc(sizeof(*cf) + npoints * sizeof(*points));
+ cf->last_time = 0;
+ cf->step = step;
+ cf->npoints = npoints;
+ memcpy(cf->points, points, sizeof(*points) * npoints);
+
+ return cf;
+}
+
+static void
+custom_accel_function_destroy(struct custom_accel_function *cf)
+{
+ if (cf == NULL)
+ return;
+
+ free(cf);
+}
+
+static double
+custom_accel_function_calculate_speed(struct custom_accel_function *cf,
+ const struct device_float_coords *unaccelerated,
+ uint64_t time)
+{
+ /* Although most devices have a constant polling rate, and for fast
+ * movements these distances do represent the actual speed,
+ * for slow movements it is not the case.
+ *
+ * Since all devices have a finite resolution, real world events
+ * for a slow smooth movement could look like:
+ * Event 1 - (0, 1) - time 0
+ * Event 2 - (0, 0) - time 7 - filtered (zero event)
+ * Event 3 - (1, 0) - time 14
+ * Event 4 - (0, 0) - time 21 - filtered (zero event)
+ * Event 5 - (0, 0) - time 28 - filtered (zero event)
+ * Event 6 - (0, 1) - time 35
+ *
+ * Not taking the time into account would mean interpreting those events as:
+ * Move 1 unit over 7 ms
+ * Pause for 7 ms
+ * Move 1 unit over 7 ms
+ * Pause for 14 ms
+ * Move 1 unit over 7ms
+ *
+ * Where in reality this was one smooth movement without pauses,
+ * so after normalizing for time we get:
+ * Move 1 unit over 7 ms
+ * Move 1 unit over 14 ms
+ * Move 1 unit over 21ms
+ *
+ * which should give us better speed estimation.
+ */
+
+ /* calculate speed based on time passed since last event */
+ double distance = hypot(unaccelerated->x, unaccelerated->y);
+ /* handle first event in a motion */
+ if (time - cf->last_time > MOTION_TIMEOUT)
+ cf->last_time = time - FIRST_MOTION_TIME_INTERVAL;
+
+ double dt = us2ms_f(time - cf->last_time);
+ double speed = distance / dt; /* speed is in device-units per ms */
+ cf->last_time = time;
+
+ return speed;
+}
+
+static double
+custom_accel_function_profile(struct custom_accel_function *cf,
+ double speed_in)
+{
+ size_t npoints = cf->npoints;
+ double step = cf->step;
+ double *points = cf->points;
+
+ /* calculate the index of the first point used for interpolation */
+ size_t i = speed_in / step;
+
+ /* if speed is greater than custom curve's max speed,
+ use last 2 points for linear extrapolation
+ (same calculation as linear interpolation) */
+ i = min(i, npoints - 2);
+
+ /* the 2 points used for linear interpolation */
+ double x0 = step * i;
+ double x1 = step * (i + 1);
+ double y0 = points[i];
+ double y1 = points[i + 1];
+
+ /* linear interpolation */
+ double speed_out = (y0 * (x1 - speed_in) + y1 * (speed_in - x0)) / step;
+
+ /* We moved (dx, dy) device units within the last N ms. This gives us a
+ * given speed S in units/ms, that's our accel input. Our curve says map
+ * that speed S to some other speed S'.
+ *
+ * Our device delta is represented by the vector, that vector needs to
+ * be modified to represent our intended speed.
+ *
+ * Example: we moved a delta of 7 over the last 7ms. Our speed is
+ * thus 1 u/ms, our out speed is 2 u/ms because we want to double our
+ * speed (points: [0.0, 2.0]). Our delta must thus be 14 - factor of 2,
+ * or out-speed/in-speed.
+ *
+ * Example: we moved a delta of 1 over the last 7ms. Our input speed is
+ * 1/7 u/ms, our out speed is 1/7ms because we set up a flat accel
+ * curve (points: [0.0, 1.0]). Our delta must thus be 1 - factor of 1,
+ * or out-speed/in-speed.
+ *
+ * Example: we moved a delta of 1 over the last 21ms. Our input speed is
+ * 1/21 u/ms, our out speed is 1u/ms because we set up a fixed-speed
+ * curve (points: [1.0, 1.0]). Our delta must thus be 21 - factor of 21,
+ * or out-speed/in-speed.
+ *
+ * Example: we moved a delta of 21 over the last 7ms. Our input speed is
+ * 3 u/ms, our out speed is 1u/ms because we set up a fixed-speed
+ * curve (points: [1.0, 1.0]). Our delta must thus be 7 - factor of 1/3,
+ * or out-speed/in-speed.
+ */
+
+ /* calculate the acceleration factor based on the user desired speed out */
+ double accel_factor = speed_out / speed_in;
+
+ return accel_factor;
+}
+
+static struct normalized_coords
+custom_accel_function_filter(struct custom_accel_function *cf,
+ const struct device_float_coords *unaccelerated,
+ uint64_t time)
+{
+ double speed = custom_accel_function_calculate_speed(cf, unaccelerated, time);
+
+ double accel_factor = custom_accel_function_profile(cf, speed);
+
+ struct normalized_coords accelerated = {
+ .x = unaccelerated->x * accel_factor,
+ .y = unaccelerated->y * accel_factor,
+ };
+
+ return accelerated;
+}
+
+struct custom_accelerator {
+ struct motion_filter base;
+ struct {
+ struct custom_accel_function *fallback;
+ struct custom_accel_function *motion;
+ } funcs;
+};
+
+static struct custom_accel_function *
+custom_accelerator_get_custom_function(struct custom_accelerator *f,
+ enum libinput_config_accel_type accel_type)
+{
+ switch (accel_type) {
+ case LIBINPUT_ACCEL_TYPE_FALLBACK:
+ return f->funcs.fallback;
+ case LIBINPUT_ACCEL_TYPE_MOTION:
+ return f->funcs.motion ? f->funcs.motion : f->funcs.fallback;
+ }
+
+ return f->funcs.fallback;
+}
+
+static double
+custom_accelerator_profile(enum libinput_config_accel_type accel_type,
+ struct motion_filter *filter,
+ double speed_in)
+{
+ struct custom_accelerator *f = (struct custom_accelerator *)filter;
+ struct custom_accel_function *cf;
+
+ cf = custom_accelerator_get_custom_function(f, accel_type);
+
+ return custom_accel_function_profile(cf, speed_in);
+}
+
+static struct normalized_coords
+custom_accelerator_filter(enum libinput_config_accel_type accel_type,
+ struct motion_filter *filter,
+ const struct device_float_coords *unaccelerated,
+ uint64_t time)
+{
+ struct custom_accelerator *f = (struct custom_accelerator *)filter;
+ struct custom_accel_function *cf;
+
+ cf = custom_accelerator_get_custom_function(f, accel_type);
+
+ return custom_accel_function_filter(cf, unaccelerated, time);
+}
+
+static void
+custom_accelerator_restart(struct motion_filter *filter,
+ void *data,
+ uint64_t time)
+{
+ /* noop, this function has no effect in the custom interface */
+}
+
+static void
+custom_accelerator_destroy(struct motion_filter *filter)
+{
+ struct custom_accelerator *f =
+ (struct custom_accelerator *)filter;
+
+ /* destroy all custom movement functions */
+ custom_accel_function_destroy(f->funcs.fallback);
+ custom_accel_function_destroy(f->funcs.motion);
+ free(f);
+}
+
+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 bool
+custom_accelerator_set_accel_config(struct motion_filter *filter,
+ struct libinput_config_accel *config)
+{
+ struct custom_accelerator *f =
+ (struct custom_accelerator *)filter;
+
+ struct custom_accel_function *fallback = NULL,
+ *motion = NULL;
+
+ if (config->custom.fallback) {
+ fallback = create_custom_accel_function(config->custom.fallback->step,
+ config->custom.fallback->points,
+ config->custom.fallback->npoints);
+ if (!fallback)
+ goto out;
+ }
+
+ if (config->custom.motion) {
+ motion = create_custom_accel_function(config->custom.motion->step,
+ config->custom.motion->points,
+ config->custom.motion->npoints);
+ if (!motion)
+ goto out;
+ }
+
+ custom_accel_function_destroy(f->funcs.fallback);
+ custom_accel_function_destroy(f->funcs.motion);
+
+ f->funcs.fallback = fallback;
+ f->funcs.motion = motion;
+
+ return true;
+
+out:
+ custom_accel_function_destroy(fallback);
+ custom_accel_function_destroy(motion);
+
+ return false;
+}
+
+/* custom profiles and filters for the different accel types: */
+
+double
+custom_accel_profile_fallback(struct motion_filter *filter,
+ void *data,
+ double speed_in,
+ uint64_t time)
+{
+ return custom_accelerator_profile(LIBINPUT_ACCEL_TYPE_FALLBACK,
+ filter,
+ speed_in);
+}
+
+static struct normalized_coords
+custom_accelerator_filter_fallback(struct motion_filter *filter,
+ const struct device_float_coords *unaccelerated,
+ void *data,
+ uint64_t time)
+{
+ return custom_accelerator_filter(LIBINPUT_ACCEL_TYPE_FALLBACK,
+ filter,
+ unaccelerated,
+ time);
+}
+
+double
+custom_accel_profile_motion(struct motion_filter *filter,
+ void *data,
+ double speed_in,
+ uint64_t time)
+{
+ return custom_accelerator_profile(LIBINPUT_ACCEL_TYPE_MOTION,
+ filter,
+ speed_in);
+}
+
+static struct normalized_coords
+custom_accelerator_filter_motion(struct motion_filter *filter,
+ const struct device_float_coords *unaccelerated,
+ void *data,
+ uint64_t time)
+{
+ return custom_accelerator_filter(LIBINPUT_ACCEL_TYPE_MOTION,
+ filter,
+ unaccelerated,
+ time);
+}
+
+struct motion_filter_interface custom_accelerator_interface = {
+ .type = LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM,
+ .filter = custom_accelerator_filter_motion,
+ .filter_constant = custom_accelerator_filter_fallback,
+ .restart = custom_accelerator_restart,
+ .destroy = custom_accelerator_destroy,
+ .set_speed = custom_accelerator_set_speed,
+ .set_accel_config = custom_accelerator_set_accel_config,
+};
+
+struct motion_filter *
+create_custom_accelerator_filter(void)
+{
+ struct custom_accelerator *f = zalloc(sizeof(*f));
+
+ /* the unit function by default, speed in = speed out,
+ i.e. no acceleration */
+ const double default_step = 1.0;
+ const double default_points[2] = {0.0, 1.0};
+
+ /* initialize default acceleration, used as fallback */
+ f->funcs.fallback = create_custom_accel_function(default_step,
+ default_points,
+ ARRAY_LENGTH(default_points));
+ /* Don't initialize other acceleration functions. Those will be
+ initialized if the user sets their points, otherwise the fallback
+ acceleration function is used */
+
+ f->base.interface = &custom_accelerator_interface;
+
+ return &f->base;
+}
void (*destroy)(struct motion_filter *filter);
bool (*set_speed)(struct motion_filter *filter,
double speed_adjustment);
+ bool (*set_accel_config)(struct motion_filter *filter,
+ struct libinput_config_accel *accel_config);
};
struct motion_filter {
return filter->interface->type;
}
+bool
+filter_set_accel_config(struct motion_filter *filter,
+ struct libinput_config_accel *accel_config)
+{
+ assert(filter_get_type(filter) == accel_config->profile);
+
+ if (!filter->interface->set_accel_config)
+ return false;
+
+ return filter->interface->set_accel_config(filter, accel_config);
+}
+
void
trackers_init(struct pointer_trackers *trackers, int ntrackers)
{
double velocity,
uint64_t time);
+bool
+filter_set_accel_config(struct motion_filter *filter,
+ struct libinput_config_accel *accel_config);
+
/* Pointer acceleration types */
struct motion_filter *
create_pointer_accelerator_filter_flat(int dpi);
struct motion_filter *
create_pointer_accelerator_filter_tablet(int xres, int yres);
+struct motion_filter *
+create_custom_accelerator_filter(void);
+
/*
* Pointer acceleration profiles.
*/
void *data,
double velocity,
uint64_t time);
+double
+custom_accel_profile_fallback(struct motion_filter *filter,
+ void *data,
+ double speed_in,
+ uint64_t time);
+double
+custom_accel_profile_motion(struct motion_filter *filter,
+ void *data,
+ double speed_in,
+ uint64_t time);
#endif /* FILTER_H */
enum libinput_config_send_events_mode (*get_default_mode)(struct libinput_device *device);
};
+/**
+ * Custom acceleration function min number of points
+ * At least 2 points are required for linear interpolation
+ */
+#define LIBINPUT_ACCEL_NPOINTS_MIN 2
+
+/**
+ * Custom acceleration function max number of points
+ * an arbitrary limit of sample points
+ * it should be more than enough for everyone
+ */
+#define LIBINPUT_ACCEL_NPOINTS_MAX 64
+
+/**
+ * Custom acceleration function max step size
+ */
+#define LIBINPUT_ACCEL_STEP_MAX 10000
+
+struct libinput_config_accel_custom_func {
+ double step;
+ size_t npoints;
+ double points[LIBINPUT_ACCEL_NPOINTS_MAX];
+};
+
+struct libinput_config_accel {
+ enum libinput_config_accel_profile profile;
+
+ struct {
+ struct libinput_config_accel_custom_func *fallback;
+ struct libinput_config_accel_custom_func *motion;
+ } custom;
+};
+
struct libinput_device_config_accel {
int (*available)(struct libinput_device *device);
enum libinput_config_status (*set_speed)(struct libinput_device *device,
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_accel_config)(struct libinput_device *device,
+ struct libinput_config_accel *accel_config);
};
struct libinput_device_config_natural_scroll {
switch (profile) {
case LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT:
case LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE:
+ case LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM:
break;
default:
return LIBINPUT_CONFIG_STATUS_INVALID;
return device->config.accel->set_profile(device, profile);
}
+static inline struct libinput_config_accel_custom_func *
+libinput_config_accel_custom_func_create(void)
+{
+ struct libinput_config_accel_custom_func *func = zalloc(sizeof(*func));
+
+ func->step = 1.0;
+ func->npoints = 2;
+ func->points[0] = 0.0; /* default to a flat unaccelerated function */
+ func->points[1] = 1.0;
+
+ return func;
+}
+
+static inline void
+libinput_config_accel_custom_func_destroy(struct libinput_config_accel_custom_func * func)
+{
+ free(func);
+}
+
+LIBINPUT_EXPORT struct libinput_config_accel *
+libinput_config_accel_create(enum libinput_config_accel_profile profile)
+{
+ struct libinput_config_accel *config = zalloc(sizeof(*config));
+
+ config->profile = profile;
+
+ switch (profile) {
+ case LIBINPUT_CONFIG_ACCEL_PROFILE_NONE:
+ break;
+ case LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT:
+ case LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE:
+ return config;
+ case LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM:
+ config->custom.fallback = libinput_config_accel_custom_func_create();
+ return config;
+ }
+
+ free(config);
+ return NULL;
+}
+
+LIBINPUT_EXPORT void
+libinput_config_accel_destroy(struct libinput_config_accel *accel_config)
+{
+ libinput_config_accel_custom_func_destroy(accel_config->custom.fallback);
+ libinput_config_accel_custom_func_destroy(accel_config->custom.motion);
+ free(accel_config);
+}
+
+LIBINPUT_EXPORT enum libinput_config_status
+libinput_device_config_accel_apply(struct libinput_device *device,
+ struct libinput_config_accel *accel_config)
+{
+ enum libinput_config_status status;
+ status = libinput_device_config_accel_set_profile(device, accel_config->profile);
+ if (status != LIBINPUT_CONFIG_STATUS_SUCCESS)
+ return status;
+
+ switch (accel_config->profile) {
+ case LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT:
+ case LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE:
+ {
+ double speed = libinput_device_config_accel_get_default_speed(device);
+ return libinput_device_config_accel_set_speed(device, speed);
+ }
+ case LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM:
+ return device->config.accel->set_accel_config(device, accel_config);
+
+ default:
+ return LIBINPUT_CONFIG_STATUS_INVALID;
+ }
+}
+
+LIBINPUT_EXPORT enum libinput_config_status
+libinput_config_accel_set_points(struct libinput_config_accel *config,
+ enum libinput_config_accel_type accel_type,
+ double step, size_t npoints, double *points)
+{
+ if (config->profile != LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM)
+ return LIBINPUT_CONFIG_STATUS_INVALID;
+
+ switch (accel_type) {
+ case LIBINPUT_ACCEL_TYPE_FALLBACK:
+ case LIBINPUT_ACCEL_TYPE_MOTION:
+ break;
+ default:
+ return LIBINPUT_CONFIG_STATUS_INVALID;
+ }
+
+ if (step <= 0 || step > LIBINPUT_ACCEL_STEP_MAX)
+ return LIBINPUT_CONFIG_STATUS_INVALID;
+
+ if (npoints < LIBINPUT_ACCEL_NPOINTS_MIN || npoints > LIBINPUT_ACCEL_NPOINTS_MAX)
+ return LIBINPUT_CONFIG_STATUS_INVALID;
+
+ struct libinput_config_accel_custom_func *func = libinput_config_accel_custom_func_create();
+
+ func->step = step;
+ func->npoints = npoints;
+ memcpy(func->points, points, sizeof(*points) * npoints);
+
+ switch (accel_type) {
+ case LIBINPUT_ACCEL_TYPE_FALLBACK:
+ libinput_config_accel_custom_func_destroy(config->custom.fallback);
+ config->custom.fallback = func;
+ break;
+ case LIBINPUT_ACCEL_TYPE_MOTION:
+ libinput_config_accel_custom_func_destroy(config->custom.motion);
+ config->custom.motion = func;
+ break;
+ }
+
+ return LIBINPUT_CONFIG_STATUS_SUCCESS;
+}
+
LIBINPUT_EXPORT int
libinput_device_config_scroll_has_natural_scroll(struct libinput_device *device)
{
* returned value is normalized to a range of [-1, 1].
* See libinput_device_config_accel_set_speed() for details.
*
+ * If the current acceleration profile is @ref
+ * LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM, 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
*
* @return The current speed, range -1 to 1
* on the input speed. This is the default profile for most devices.
*/
LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE = (1 << 1),
+
+ /**
+ * A custom acceleration profile. Device movement acceleration depends
+ * on user defined custom acceleration functions for each movement
+ * type.
+ *
+ * @see libinput_device_config_accel_set_points
+ */
+ LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM = (1 << 2),
};
+/**
+ * @ingroup config
+ *
+ * A handle for configuration pointer acceleration.
+ *
+ * @warning Unlike other structs pointer acceleration configuration is
+ * considered transient and <b>not</b> refcounted. Calling
+ * libinput_config_accel_destroy() <b>will</b> destroy the configuration.
+ *
+ * To configure pointer acceleration, first create a config of a desired
+ * acceleration profile with libinput_config_accel_create(), then
+ * configure the profile-specific acceleration properties.
+ *
+ * In this version of libinput, this pointer acceleration configuration
+ * only provides configuration for @ref LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM.
+ *
+ * For @ref LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM use
+ * @ref libinput_config_accel_set_points.
+ *
+ * Once set up, apply the configuration to a device using
+ * libinput_device_config_accel_apply(). Once applied,
+ * destroy it with libinput_config_accel_destroy().
+ *
+ * @since 1.23
+ */
+struct libinput_config_accel;
+
+/**
+ * @ingroup config
+ *
+ * Create an acceleration configuration of a given profile.
+ *
+ * Note that in this version of libinput, only the
+ * @ref LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM profile provides configuration
+ * options. All other acceleration profiles, when applied, will merely switch
+ * the profile and reset any profile-specific options to the default values.
+ *
+ * @param profile The profile of the newly created acceleration configuration.
+ *
+ * @return The newly created acceleration configuration or NULL on error.
+ *
+ * @warning Unlike other structs pointer acceleration configuration is
+ * considered transient and <b>not</b> refcounted. Calling
+ * libinput_config_accel_destroy() <b>will</b> destroy the configuration.
+ *
+ * @see libinput_config_accel
+ * @since 1.23
+ */
+struct libinput_config_accel *
+libinput_config_accel_create(enum libinput_config_accel_profile profile);
+
+/**
+ * @ingroup config
+ *
+ * Destroy an acceleration configuration.
+ *
+ * @warning Unlike other structs pointer acceleration configuration is
+ * considered transient and <b>not</b> refcounted. Calling
+ * libinput_config_accel_destroy() <b>will</b> destroy the configuration.
+ *
+ * @param accel_config The acceleration configuration to destroy.
+ *
+ * @see libinput_config_accel
+ * @since 1.23
+ */
+void
+libinput_config_accel_destroy(struct libinput_config_accel *accel_config);
+
+/**
+ * @ingroup config
+ *
+ * Apply this pointer acceleration configuration to the device. This changes the
+ * device's pointer acceleration method to the method given in
+ * libinput_config_accel_create() and applies all other configuration settings.
+ *
+ * Once applied, call libinput_config_accel_destroy() to destroy the
+ * configuration struct.
+ *
+ * @param device The device to configure.
+ * @param accel_config The acceleration configuration.
+ *
+ * @return A config status code.
+ *
+ * @see libinput_config_accel
+ * @since 1.23
+ */
+enum libinput_config_status
+libinput_device_config_accel_apply(struct libinput_device *device,
+ struct libinput_config_accel *accel_config);
+
+/**
+ * @ingroup config
+ *
+ * Acceleration types are categories of movement by a device that may have
+ * specific acceleration functions applied. A device always supports the
+ * @ref LIBINPUT_ACCEL_TYPE_MOTION type (for regular pointer motion). Other
+ * types (e.g. scrolling) may be added in the future.
+ *
+ * The special type @ref LIBINPUT_ACCEL_TYPE_FALLBACK specifies the acceleration
+ * function to be moved for any movement produced by the device that does not
+ * have a specific acceleration type defined.
+ *
+ * Use to specify the acceleration function type in
+ * @ref libinput_config_accel_set_points
+ *
+ * Each device implements a subset of those types, see a list of supported
+ * devices for each movement type definition.
+ *
+ * @see LIBINPUT_ACCEL_ARG_TYPE
+ * @since 1.23
+ */
+enum libinput_config_accel_type {
+ /**
+ * The default acceleration type used as a fallback when other
+ * acceleration types are not provided.
+ */
+ LIBINPUT_ACCEL_TYPE_FALLBACK = 0,
+ /**
+ * Acceleration type for regular pointer movement. This
+ * type is always supported.
+ */
+ LIBINPUT_ACCEL_TYPE_MOTION,
+};
+
+/**
+ * @ingroup config
+ *
+ * Defines the acceleration function for a given movement type
+ * in an acceleration configuration with the profile
+ * @ref LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM.
+ *
+ * Movement types are specific to each device, @see libinput_config_accel_type.
+ *
+ * Each custom acceleration function is defined by ``n`` points spaced uniformly
+ * along the x-axis starting from 0 and continuing in a constant step size.
+ * There by the function is defined by the following points:
+ * (0 * step, f[0]), (1 * step, f[1]), ..., ((n - 1) * step, f[n - 1]).
+ * The x-axis represents the device-speed in device units per millisecond.
+ * The y-axis represents the pointer-speed.
+ *
+ * It is up to the user to define those values in accordance with device DPI
+ * and screen DPI.
+ *
+ * @param accel_config The acceleration configuration to modify.
+ * @param accel_type The movement type to configure a custom function for.
+ * @param step The distance between each point along the x-axis.
+ * @param npoints The number of points of the custom acceleration function.
+ * @param points The points' y-values of the custom acceleration function.
+ *
+ * @return A config status code.
+ *
+ * @see libinput_config_accel
+ * @since 1.23
+ */
+enum libinput_config_status
+libinput_config_accel_set_points(struct libinput_config_accel *accel_config,
+ enum libinput_config_accel_type accel_type,
+ double step, size_t npoints, double *points);
+
/**
* @ingroup config
*
libinput_device_config_dwtp_get_enabled;
libinput_device_config_dwtp_get_default_enabled;
} LIBINPUT_1.19;
+
+LIBINPUT_1.23 {
+ libinput_config_accel_create;
+ libinput_config_accel_destroy;
+ libinput_device_config_accel_apply;
+ libinput_config_accel_set_points;
+} LIBINPUT_1.21;
\ No newline at end of file
free (strv);
}
+/**
+ * parse a string containing a list of doubles into a double array.
+ *
+ * @param in string to parse
+ * @param separator string used to separate double in list e.g. ","
+ * @param result double array
+ * @param length length of double array
+ * @return true when parsed successfully otherwise false
+ */
+static inline double *
+double_array_from_string(const char *in,
+ const char *separator,
+ size_t *length)
+{
+ double *result = NULL;
+ *length = 0;
+
+ size_t nelem;
+ char **strv = strv_from_string(in, separator, &nelem);
+ if (!strv)
+ return result;
+
+ double *numv = zalloc(sizeof(double) * nelem);
+ for (size_t idx = 0; idx < nelem; idx++) {
+ double val;
+ if (!safe_atod(strv[idx], &val))
+ goto out;
+
+ numv[idx] = val;
+ }
+
+ result = numv;
+ numv = NULL;
+ *length = nelem;
+
+out:
+ strv_free(strv);
+ free(numv);
+ return result;
+}
+
struct key_value_str{
char *key;
char *value;
return (uint32_t)(us / 1000);
}
+static inline double
+us2ms_f(uint64_t us)
+{
+ return (double)us / 1000.0;
+}
+
static inline uint64_t
tv2us(const struct timeval *tv)
{
profiles = libinput_device_config_accel_get_profiles(device);
ck_assert(profiles & LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE);
ck_assert(profiles & LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT);
+ ck_assert(profiles & LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM);
status = libinput_device_config_accel_set_profile(device,
LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT);
ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
profile = libinput_device_config_accel_get_profile(device);
ck_assert_int_eq(profile, LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE);
+
+ status = libinput_device_config_accel_set_profile(device,
+ LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM);
+ ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
+ profile = libinput_device_config_accel_get_profile(device);
+ ck_assert_int_eq(profile, LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM);
+}
+END_TEST
+
+START_TEST(pointer_accel_config_reset_to_defaults)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput_device *device = dev->libinput_device;
+ double default_speed = libinput_device_config_accel_get_default_speed(device);
+
+ /* There are no settings for these profiles to toggle, so we expect it
+ * to simply reset to defaults */
+ enum libinput_config_accel_profile profiles[] = {
+ LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE,
+ LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT,
+ };
+
+ ARRAY_FOR_EACH(profiles, profile) {
+ ck_assert_int_eq(libinput_device_config_accel_set_speed(device, 1.0),
+ LIBINPUT_CONFIG_STATUS_SUCCESS);
+
+ ck_assert_double_eq(libinput_device_config_accel_get_speed(device), 1.0);
+
+ struct libinput_config_accel *config =
+ libinput_config_accel_create(LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE);
+ ck_assert_int_eq(libinput_device_config_accel_apply(device, config),
+ LIBINPUT_CONFIG_STATUS_SUCCESS);
+ ck_assert_double_eq(libinput_device_config_accel_get_speed(device),
+ default_speed);
+ libinput_config_accel_destroy(config);
+ }
+}
+END_TEST
+
+START_TEST(pointer_accel_config)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput_device *device = dev->libinput_device;
+ enum libinput_config_status status;
+ enum libinput_config_accel_profile profile;
+ double custom_speed[] = {0.1234, -0.567, 0.89};
+ double custom_step[] = {0.5, 0.003, 2.7};
+ double custom_npoints = 4;
+ double custom_points[3][4] = {{1.0, 2.0, 2.5, 2.6},
+ {0.1, 0.3, 0.4, 0.45},
+ {1.0, 3.0, 4.5, 4.5}};
+
+ ck_assert(libinput_device_config_accel_is_available(device));
+
+ struct libinput_config_accel *config_custom_default =
+ libinput_config_accel_create(LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM);
+ struct libinput_config_accel *config_custom_changed =
+ libinput_config_accel_create(LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM);
+
+ ck_assert_ptr_nonnull(config_custom_default);
+ ck_assert_ptr_nonnull(config_custom_changed);
+
+
+ for (size_t idx = 0; idx < ARRAY_LENGTH(custom_speed); idx++) {
+ status = libinput_config_accel_set_points(config_custom_changed,
+ LIBINPUT_ACCEL_TYPE_FALLBACK,
+ custom_step[idx],
+ custom_npoints,
+ custom_points[idx]);
+ ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
+
+ status = libinput_device_config_accel_apply(device, config_custom_changed);
+ ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
+ profile = libinput_device_config_accel_get_profile(device);
+ ck_assert_int_eq(profile, LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM);
+
+ status = libinput_device_config_accel_apply(device, config_custom_default);
+ ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
+ profile = libinput_device_config_accel_get_profile(device);
+ ck_assert_int_eq(profile, LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM);
+ }
+
+ libinput_config_accel_destroy(config_custom_default);
+ libinput_config_accel_destroy(config_custom_changed);
}
END_TEST
status = libinput_device_config_accel_set_profile(device,
LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE |LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT);
ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_INVALID);
+
+ status = libinput_device_config_accel_set_profile(device,
+ LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM |LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT);
+ ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_INVALID);
}
END_TEST
litest_add(pointer_accel_direction_change, LITEST_RELATIVE, LITEST_POINTINGSTICK);
litest_add(pointer_accel_profile_defaults, LITEST_RELATIVE, LITEST_TOUCHPAD);
litest_add(pointer_accel_profile_defaults, LITEST_TOUCHPAD, LITEST_ANY);
+ litest_add(pointer_accel_config_reset_to_defaults, LITEST_RELATIVE, LITEST_ANY);
+ litest_add(pointer_accel_config, LITEST_RELATIVE, LITEST_ANY);
litest_add(pointer_accel_profile_invalid, LITEST_RELATIVE, LITEST_ANY);
litest_add(pointer_accel_profile_noaccel, LITEST_ANY, LITEST_TOUCHPAD|LITEST_RELATIVE|LITEST_TABLET);
litest_add(pointer_accel_profile_flat_motion_relative, LITEST_RELATIVE, LITEST_TOUCHPAD);
}
END_TEST
+START_TEST(double_array_from_string_test)
+{
+ struct double_array_from_string_test {
+ const char *string;
+ const char *delim;
+ const double array[10];
+ const size_t len;
+ const bool result;
+ } tests[] = {
+ { "1 2 3", " ", { 1, 2, 3 }, 3 },
+ { "1", " ", { 1 }, 1 },
+ { "1,2.5,", ",", { 1, 2.5 }, 2 },
+ { "1.0 2", " ", { 1, 2.0 }, 2 },
+ { " 1 2", " ", { 1, 2 }, 2 },
+ { " ; 1;2 3.5 ;;4.1", "; ", { 1, 2, 3.5, 4.1 }, 4 },
+ /* special cases */
+ { "1 two", " ", { 0 }, 0 },
+ { "one two", " ", { 0 }, 0 },
+ { "one 2", " ", { 0 }, 0 },
+ { "", " ", { 0 }, 0 },
+ { " ", " ", { 0 }, 0 },
+ { " ", " ", { 0 }, 0 },
+ { "", " ", { 0 }, 0 },
+ { "oneoneone", "one", { 0 }, 0 },
+ { NULL, NULL, { 0 }, 0 }
+ };
+ struct double_array_from_string_test *t = tests;
+
+ while (t->string) {
+ size_t len;
+ double *array = double_array_from_string(t->string,
+ t->delim,
+ &len);
+ ck_assert_int_eq(len, t->len);
+
+ for (size_t idx = 0; idx < len; idx++)
+ ck_assert_double_eq(array[idx], t->array[idx]);
+
+ free(array);
+ t++;
+ }
+}
+END_TEST
+
START_TEST(strargv_test)
{
struct argv_test {
tcase_add_test(tc, safe_atou_base_8_test);
tcase_add_test(tc, safe_atod_test);
tcase_add_test(tc, strsplit_test);
+ tcase_add_test(tc, double_array_from_string_test);
tcase_add_test(tc, strargv_test);
tcase_add_test(tc, kvsplit_double_test);
tcase_add_test(tc, strjoin_test);
.B \-\-set\-scroll\-button=BTN_MIDDLE
Set the button to the given button code
.TP 8
-.B \-\-set\-profile=[adaptive|flat]
+.B \-\-set\-profile=[adaptive|flat|custom]
Set pointer acceleration profile
.TP 8
.B \-\-set\-speed=<value>
Set pointer acceleration speed. The allowed range is [-1, 1].
+This only applies to the flat or adaptive profile.
+.TP 8
+.B \-\-set\-custom\-points="<value>;...;<value>"
+Sets the n points defining a custom acceleration function
+The points are defined in a semicolon-separated list of floating point
+non-negative numbers. Defaults to "0.0;1.0".
+This only applies to the custom profile.
+.TP 8
+.B \-\-set\-custom\-step=<value>
+Sets the distance along the x-axis between each point, starting from 0.
+Defaults to 1.0.
+This only applies to the custom profile.
+.TP 8
+.B \-\-set\-custom\-type=[fallback|motion]
+Sets the type of the custom acceleration function.
+Defaults to fallback.
+This only applies to the custom profile.
.TP 8
.B \-\-set\-tap\-map=[lrm|lmr]
Set button mapping for tapping
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_CUSTOM) ? "*" : "",
+ (profiles & LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM) ? "custom" : "");
return str;
}
" touchpad ... the touchpad motion filter\n"
" x230 ... custom filter for the Lenovo x230 touchpad\n"
" trackpoint... trackpoint motion filter\n"
+ " custom ... custom motion filter, use --custom-points and --custom-step with this argument\n"
+ "--custom-points=\"<double>;...;<double>\" ... n points defining a custom acceleration function\n"
+ "--custom-step=<double> ... distance along the x-axis between each point, \n"
+ " starting from 0. defaults to 1.0\n"
"\n"
"If extra arguments are present and mode is not given, mode defaults to 'sequence'\n"
"and the arguments are interpreted as sequence of delta x coordinates\n"
const char *filter_type = "linear";
accel_profile_func_t profile = NULL;
double tp_multiplier = 1.0;
+ struct libinput_config_accel_custom_func custom_func = {
+ .step = 1.0,
+ .npoints = 2,
+ .points = {0.0, 1.0},
+ };
+ struct libinput_config_accel *accel_config =
+ libinput_config_accel_create(LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM);
enum {
OPT_HELP = 1,
OPT_SPEED,
OPT_DPI,
OPT_FILTER,
+ OPT_CUSTOM_POINTS,
+ OPT_CUSTOM_STEP,
};
while (1) {
{"speed", 1, 0, OPT_SPEED },
{"dpi", 1, 0, OPT_DPI },
{"filter", 1, 0, OPT_FILTER },
+ {"custom-points", 1, 0, OPT_CUSTOM_POINTS },
+ {"custom-step", 1, 0, OPT_CUSTOM_STEP },
{0, 0, 0, 0}
};
case OPT_FILTER:
filter_type = optarg;
break;
+ case OPT_CUSTOM_POINTS: {
+ size_t npoints;
+ double *points = double_array_from_string(optarg,
+ ";",
+ &npoints);
+ if (!points ||
+ npoints < LIBINPUT_ACCEL_NPOINTS_MIN ||
+ npoints > LIBINPUT_ACCEL_NPOINTS_MAX) {
+ fprintf(stderr,
+ "Invalid --custom-points\n"
+ "Please provide at least 2 points separated by a semicolon\n"
+ " e.g. --custom-points=\"1.0;1.5\"\n");
+ free(points);
+ return 1;
+ }
+ custom_func.npoints = npoints;
+ memcpy(custom_func.points,
+ points,
+ sizeof(*points) * npoints);
+ free(points);
+ break;
+ }
+ case OPT_CUSTOM_STEP:
+ custom_func.step = strtod(optarg, NULL);
+ break;
default:
usage();
exit(1);
filter = create_pointer_accelerator_filter_trackpoint(tp_multiplier,
use_averaging);
profile = trackpoint_accel_profile;
+ } else if (streq(filter_type, "custom")) {
+ libinput_config_accel_set_points(accel_config,
+ LIBINPUT_ACCEL_TYPE_MOTION,
+ custom_func.step,
+ custom_func.npoints,
+ custom_func.points);
+ filter = create_custom_accelerator_filter();
+ profile = custom_accel_profile_motion;
+ filter_set_accel_config(filter, accel_config);
} else {
fprintf(stderr, "Invalid filter type %s\n", filter_type);
return 1;
break;
}
+ libinput_config_accel_destroy(accel_config);
filter_destroy(filter);
return 0;
options->scroll_button_lock = -1;
options->speed = 0.0;
options->profile = LIBINPUT_CONFIG_ACCEL_PROFILE_NONE;
+ /* initialize accel args */
+ static double points[] = {0.0, 1.0};
+ options->custom_points = points;
+ options->custom_npoints = ARRAY_LENGTH(points);
+ options->custom_type = LIBINPUT_ACCEL_TYPE_FALLBACK;
+ options->custom_step = 1.0;
}
int
options->profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE;
else if (streq(optarg, "flat"))
options->profile = LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT;
+ else if (streq(optarg, "custom"))
+ options->profile = LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM;
else
return 1;
break;
"%s",
optarg);
break;
+ case OPT_CUSTOM_POINTS:
+ if (!optarg)
+ return 1;
+ options->custom_points = double_array_from_string(optarg,
+ ";",
+ &options->custom_npoints);
+ if (!options->custom_points || options->custom_npoints < 2) {
+ fprintf(stderr,
+ "Invalid --set-custom-points\n"
+ "Please provide at least 2 points separated by a semicolon\n"
+ " e.g. --set-custom-points=\"1.0;1.5\"\n");
+ return 1;
+ }
+ break;
+ case OPT_CUSTOM_STEP:
+ if (!optarg)
+ return 1;
+ options->custom_step = strtod(optarg, NULL);
+ break;
+ case OPT_CUSTOM_TYPE:
+ if (!optarg)
+ return 1;
+ if (streq(optarg, "fallback"))
+ options->custom_type = LIBINPUT_ACCEL_TYPE_FALLBACK;
+ else if (streq(optarg, "motion"))
+ options->custom_type = LIBINPUT_ACCEL_TYPE_MOTION;
+ else {
+ fprintf(stderr, "Invalid --set-custom-type\n"
+ "Valid custom types: fallback|motion\n");
+ return 1;
+ }
+ break;
}
-
return 0;
}
libinput_device_config_accel_set_profile(device,
options->profile);
}
+
+ if (options->profile == LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM) {
+ struct libinput_config_accel *config =
+ libinput_config_accel_create(LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM);
+ libinput_config_accel_set_points(config,
+ options->custom_type,
+ options->custom_step,
+ options->custom_npoints,
+ options->custom_points);
+ libinput_device_config_accel_apply(device, config);
+ libinput_config_accel_destroy(config);
+ }
}
static char*
OPT_PROFILE,
OPT_DISABLE_SENDEVENTS,
OPT_APPLY_TO,
+ OPT_CUSTOM_POINTS,
+ OPT_CUSTOM_STEP,
+ OPT_CUSTOM_TYPE,
};
#define CONFIGURATION_OPTIONS \
{ "set-profile", required_argument, 0, OPT_PROFILE }, \
{ "set-tap-map", required_argument, 0, OPT_TAP_MAP }, \
{ "set-speed", required_argument, 0, OPT_SPEED },\
- { "apply-to", required_argument, 0, OPT_APPLY_TO }
+ { "apply-to", required_argument, 0, OPT_APPLY_TO },\
+ { "set-custom-points", required_argument, 0, OPT_CUSTOM_POINTS },\
+ { "set-custom-step", required_argument, 0, OPT_CUSTOM_STEP },\
+ { "set-custom-type", required_argument, 0, OPT_CUSTOM_TYPE }
enum tools_backend {
BACKEND_NONE,
int dwtp;
enum libinput_config_accel_profile profile;
char disable_pattern[64];
+ enum libinput_config_accel_type custom_type;
+ double custom_step;
+ size_t custom_npoints;
+ double *custom_points;
};
void tools_init_options(struct tools_options *options);