There's a fair bit of duplication of code from filter.c but it's not worth disecting this and optimising it. The device is 5 years old now, we don't want to touch this accel method so duplication is good here.
Signed-off-by: Peter Hutterer <peter.hutte...@who-t.net> --- meson.build | 1 + src/filter-touchpad-x230.c | 356 +++++++++++++++++++++++++++++++++++++++++++++ src/filter.c | 136 ----------------- 3 files changed, 357 insertions(+), 136 deletions(-) create mode 100644 src/filter-touchpad-x230.c diff --git a/meson.build b/meson.build index 05a45659..b60ca1ac 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-touchpad-x230.c', 'src/filter.h', 'src/filter-private.h' ] diff --git a/src/filter-touchpad-x230.c b/src/filter-touchpad-x230.c new file mode 100644 index 00000000..be58915e --- /dev/null +++ b/src/filter-touchpad-x230.c @@ -0,0 +1,356 @@ +/* + * Copyright © 2006-2009 Simon Thum + * Copyright © 2012 Jonas Ådahl + * Copyright © 2014-2015 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 <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <limits.h> +#include <math.h> + +#include "filter.h" +#include "libinput-util.h" +#include "filter-private.h" + +/* Trackpoint acceleration for the Lenovo x230. DO NOT TOUCH. + * This code is only invoked on the X230 and is quite flimsy, + * custom-designed to make this touchpad less terrible than the + * out-of-the-box experience. The x230 was released in 2013, it's + * not worth trying to optimize the code or de-duplicate the various + * copy-pastes. + */ + +/* + * Default parameters for pointer acceleration profiles. + */ + +#define DEFAULT_THRESHOLD v_ms2us(0.4) /* in units/us */ +#define MINIMUM_THRESHOLD v_ms2us(0.2) /* in units/us */ +#define DEFAULT_ACCELERATION 2.0 /* unitless factor */ +#define DEFAULT_INCLINE 1.1 /* unitless factor */ + +/* for the Lenovo x230 custom accel. do not touch */ +#define X230_THRESHOLD v_ms2us(0.4) /* in units/us */ +#define X230_ACCELERATION 2.0 /* unitless factor */ +#define X230_INCLINE 1.1 /* unitless factor */ +#define X230_MAGIC_SLOWDOWN 0.4 /* unitless */ +#define X230_TP_MAGIC_LOW_RES_FACTOR 4.0 /* unitless */ + +/* + * Pointer acceleration filter constants + */ +#define NUM_POINTER_TRACKERS 16 + +struct pointer_accelerator_x230 { + struct motion_filter base; + + accel_profile_func_t profile; + + double velocity; /* units/us */ + double last_velocity; /* units/us */ + + struct pointer_trackers trackers; + + double threshold; /* units/us */ + double accel; /* unitless factor */ + double incline; /* incline of the function */ + + int dpi; +}; + +/** + * Apply the acceleration profile to the given velocity. + * + * @param accel The acceleration filter + * @param data Caller-specific data + * @param velocity Velocity in device-units per µs + * @param time Current time in µs + * + * @return A unitless acceleration factor, to be applied to the delta + */ +static double +acceleration_profile(struct pointer_accelerator_x230 *accel, + void *data, double velocity, uint64_t time) +{ + return accel->profile(&accel->base, data, velocity, time); +} + +/** + * Calculate the acceleration factor for our current velocity, averaging + * between our current and the most recent velocity to smoothen out changes. + * + * @param accel The acceleration filter + * @param data Caller-specific data + * @param velocity Velocity in device-units per µs + * @param last_velocity Previous velocity in device-units per µs + * @param time Current time in µs + * + * @return A unitless acceleration factor, to be applied to the delta + */ +static double +calculate_acceleration(struct pointer_accelerator_x230 *accel, + void *data, + double velocity, + double last_velocity, + uint64_t time) +{ + double factor; + + /* Use Simpson's rule to calculate the avarage acceleration between + * the previous motion and the most recent. */ + factor = acceleration_profile(accel, data, velocity, time); + factor += acceleration_profile(accel, data, last_velocity, time); + factor += 4.0 * + acceleration_profile(accel, data, + (last_velocity + velocity) / 2, + time); + + factor = factor / 6.0; + + return factor; /* unitless factor */ +} + +/** + * Calculate the acceleration factor for the given delta with the timestamp. + * + * @param accel The acceleration filter + * @param unaccelerated The raw delta in the device's dpi + * @param data Caller-specific data + * @param time Current time in µs + * + * @return A unitless acceleration factor, to be applied to the delta + */ +static inline double +calculate_acceleration_factor(struct pointer_accelerator_x230 *accel, + const struct device_float_coords *unaccelerated, + void *data, + uint64_t time) +{ + double velocity; /* units/us in device-native dpi*/ + double accel_factor; + + feed_trackers(&accel->trackers, unaccelerated, time); + velocity = calculate_velocity(&accel->trackers, time); + accel_factor = calculate_acceleration(accel, + data, + velocity, + accel->last_velocity, + time); + accel->last_velocity = velocity; + + return accel_factor; +} + +static struct normalized_coords +accelerator_filter_x230(struct motion_filter *filter, + const struct device_float_coords *raw, + void *data, uint64_t time) +{ + struct pointer_accelerator_x230 *accel = + (struct pointer_accelerator_x230 *) filter; + double accel_factor; /* unitless factor */ + struct normalized_coords accelerated; + struct device_float_coords delta_normalized; + struct normalized_coords unaccelerated; + double velocity; /* units/us */ + + /* This filter is a "do not touch me" filter. So the hack here is + * just to replicate the old behavior before filters switched to + * device-native dpi: + * 1) convert from device-native to 1000dpi normalized + * 2) run all calculation on 1000dpi-normalized data + * 3) apply accel factor no normalized data + */ + unaccelerated = normalize_for_dpi(raw, accel->dpi); + delta_normalized.x = unaccelerated.x; + delta_normalized.y = unaccelerated.y; + + feed_trackers(&accel->trackers, &delta_normalized, time); + velocity = calculate_velocity(&accel->trackers, time); + accel_factor = calculate_acceleration(accel, + data, + velocity, + accel->last_velocity, + time); + accel->last_velocity = velocity; + + accelerated.x = accel_factor * delta_normalized.x; + accelerated.y = accel_factor * delta_normalized.y; + + return accelerated; +} + +static struct normalized_coords +accelerator_filter_constant_x230(struct motion_filter *filter, + const struct device_float_coords *unaccelerated, + void *data, uint64_t time) +{ + struct pointer_accelerator_x230 *accel = + (struct pointer_accelerator_x230 *) filter; + struct normalized_coords normalized; + const double factor = + X230_MAGIC_SLOWDOWN/X230_TP_MAGIC_LOW_RES_FACTOR; + + normalized = normalize_for_dpi(unaccelerated, accel->dpi); + normalized.x = factor * normalized.x; + normalized.y = factor * normalized.y; + + return normalized; +} + +static void +accelerator_restart_x230(struct motion_filter *filter, + void *data, + uint64_t time) +{ + struct pointer_accelerator_x230 *accel = + (struct pointer_accelerator_x230 *) filter; + unsigned int offset; + struct pointer_tracker *tracker; + + for (offset = 1; offset < accel->trackers.ntrackers; offset++) { + tracker = tracker_by_offset(&accel->trackers, offset); + tracker->time = 0; + tracker->dir = 0; + tracker->delta.x = 0; + tracker->delta.y = 0; + } + + tracker = tracker_by_offset(&accel->trackers, 0); + tracker->time = time; + tracker->dir = UNDEFINED_DIRECTION; +} + +static void +accelerator_destroy_x230(struct motion_filter *filter) +{ + struct pointer_accelerator_x230 *accel = + (struct pointer_accelerator_x230 *) filter; + + free(accel->trackers.trackers); + free(accel); +} + +static bool +accelerator_set_speed_x230(struct motion_filter *filter, + double speed_adjustment) +{ + struct pointer_accelerator_x230 *accel_filter = + (struct pointer_accelerator_x230 *)filter; + + assert(speed_adjustment >= -1.0 && speed_adjustment <= 1.0); + + /* Note: the numbers below are nothing but trial-and-error magic, + don't read more into them other than "they mostly worked ok" */ + + /* delay when accel kicks in */ + accel_filter->threshold = DEFAULT_THRESHOLD - + v_ms2us(0.25) * speed_adjustment; + if (accel_filter->threshold < MINIMUM_THRESHOLD) + accel_filter->threshold = MINIMUM_THRESHOLD; + + /* adjust max accel factor */ + accel_filter->accel = DEFAULT_ACCELERATION + speed_adjustment * 1.5; + + /* higher speed -> faster to reach max */ + accel_filter->incline = DEFAULT_INCLINE + speed_adjustment * 0.75; + + filter->speed_adjustment = speed_adjustment; + return true; +} + +double +touchpad_lenovo_x230_accel_profile(struct motion_filter *filter, + void *data, + double speed_in, /* 1000dpi-units/µs */ + uint64_t time) +{ + /* Those touchpads presents an actual lower resolution that what is + * advertised. We see some jumps from the cursor due to the big steps + * in X and Y when we are receiving data. + * Apply a factor to minimize those jumps at low speed, and try + * keeping the same feeling as regular touchpads at high speed. + * It still feels slower but it is usable at least */ + double factor; /* unitless */ + struct pointer_accelerator_x230 *accel_filter = + (struct pointer_accelerator_x230 *)filter; + + double f1, f2; /* unitless */ + const double max_accel = accel_filter->accel * + X230_TP_MAGIC_LOW_RES_FACTOR; /* unitless factor */ + const double threshold = accel_filter->threshold / + X230_TP_MAGIC_LOW_RES_FACTOR; /* units/us */ + const double incline = accel_filter->incline * X230_TP_MAGIC_LOW_RES_FACTOR; + + /* Note: the magic values in this function are obtained by + * trial-and-error. No other meaning should be interpreted. + * The calculation is a compressed form of + * pointer_accel_profile_linear(), look at the git history of that + * function for an explanation of what the min/max/etc. does. + */ + speed_in *= X230_MAGIC_SLOWDOWN / X230_TP_MAGIC_LOW_RES_FACTOR; + + f1 = min(1, v_us2ms(speed_in) * 5); + f2 = 1 + (v_us2ms(speed_in) - v_us2ms(threshold)) * incline; + + factor = min(max_accel, f2 > 1 ? f2 : f1); + + return factor * X230_MAGIC_SLOWDOWN / X230_TP_MAGIC_LOW_RES_FACTOR; +} + +struct motion_filter_interface accelerator_interface_x230 = { + .type = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE, + .filter = accelerator_filter_x230, + .filter_constant = accelerator_filter_constant_x230, + .restart = accelerator_restart_x230, + .destroy = accelerator_destroy_x230, + .set_speed = accelerator_set_speed_x230, +}; + +/* The Lenovo x230 has a bad touchpad. This accel method has been + * trial-and-error'd, any changes to it will require re-testing everything. + * Don't touch this. + */ +struct motion_filter * +create_pointer_accelerator_filter_lenovo_x230(int dpi) +{ + struct pointer_accelerator_x230 *filter; + + filter = zalloc(sizeof *filter); + filter->base.interface = &accelerator_interface_x230; + filter->profile = touchpad_lenovo_x230_accel_profile; + filter->last_velocity = 0.0; + + init_trackers(&filter->trackers, NUM_POINTER_TRACKERS); + + filter->threshold = X230_THRESHOLD; + filter->accel = X230_ACCELERATION; /* unitless factor */ + filter->incline = X230_INCLINE; /* incline of the acceleration function */ + filter->dpi = dpi; + + return &filter->base; +} diff --git a/src/filter.c b/src/filter.c index d2106868..1056298a 100644 --- a/src/filter.c +++ b/src/filter.c @@ -109,13 +109,6 @@ filter_get_type(struct motion_filter *filter) #define TOUCHPAD_ACCELERATION 9.0 /* unitless factor */ #define TOUCHPAD_INCLINE 0.011 /* unitless factor */ -/* for the Lenovo x230 custom accel. do not touch */ -#define X230_THRESHOLD v_ms2us(0.4) /* in units/us */ -#define X230_ACCELERATION 2.0 /* unitless factor */ -#define X230_INCLINE 1.1 /* unitless factor */ -#define X230_MAGIC_SLOWDOWN 0.4 /* unitless */ -#define X230_TP_MAGIC_LOW_RES_FACTOR 4.0 /* unitless */ - /* Trackpoint acceleration */ #define TRACKPOINT_DEFAULT_MAX_ACCEL 2.0 /* in units/us */ #define TRACKPOINT_DEFAULT_MAX_DELTA 60 @@ -547,63 +540,6 @@ accelerator_filter_noop(struct motion_filter *filter, return normalize_for_dpi(unaccelerated, accel->dpi); } -static struct normalized_coords -accelerator_filter_x230(struct motion_filter *filter, - const struct device_float_coords *raw, - void *data, uint64_t time) -{ - struct pointer_accelerator *accel = - (struct pointer_accelerator *) filter; - double accel_factor; /* unitless factor */ - struct normalized_coords accelerated; - struct device_float_coords delta_normalized; - struct normalized_coords unaccelerated; - double velocity; /* units/us */ - - /* This filter is a "do not touch me" filter. So the hack here is - * just to replicate the old behavior before filters switched to - * device-native dpi: - * 1) convert from device-native to 1000dpi normalized - * 2) run all calculation on 1000dpi-normalized data - * 3) apply accel factor no normalized data - */ - unaccelerated = normalize_for_dpi(raw, accel->dpi); - delta_normalized.x = unaccelerated.x; - delta_normalized.y = unaccelerated.y; - - feed_trackers(&accel->trackers, &delta_normalized, time); - velocity = calculate_velocity(&accel->trackers, time); - accel_factor = calculate_acceleration(accel, - data, - velocity, - accel->last_velocity, - time); - accel->last_velocity = velocity; - - accelerated.x = accel_factor * delta_normalized.x; - accelerated.y = accel_factor * delta_normalized.y; - - return accelerated; -} - -static struct normalized_coords -accelerator_filter_constant_x230(struct motion_filter *filter, - const struct device_float_coords *unaccelerated, - void *data, uint64_t time) -{ - struct pointer_accelerator *accel = - (struct pointer_accelerator *) filter; - struct normalized_coords normalized; - const double factor = - X230_MAGIC_SLOWDOWN/X230_TP_MAGIC_LOW_RES_FACTOR; - - normalized = normalize_for_dpi(unaccelerated, accel->dpi); - normalized.x = factor * normalized.x; - normalized.y = factor * normalized.y; - - return normalized; -} - static bool touchpad_accelerator_set_speed(struct motion_filter *filter, double speed_adjustment) @@ -888,45 +824,6 @@ touchpad_accel_profile_linear(struct motion_filter *filter, return factor * TP_MAGIC_SLOWDOWN; } -double -touchpad_lenovo_x230_accel_profile(struct motion_filter *filter, - void *data, - double speed_in, /* 1000dpi-units/µs */ - uint64_t time) -{ - /* Those touchpads presents an actual lower resolution that what is - * advertised. We see some jumps from the cursor due to the big steps - * in X and Y when we are receiving data. - * Apply a factor to minimize those jumps at low speed, and try - * keeping the same feeling as regular touchpads at high speed. - * It still feels slower but it is usable at least */ - double factor; /* unitless */ - struct pointer_accelerator *accel_filter = - (struct pointer_accelerator *)filter; - - double f1, f2; /* unitless */ - const double max_accel = accel_filter->accel * - X230_TP_MAGIC_LOW_RES_FACTOR; /* unitless factor */ - const double threshold = accel_filter->threshold / - X230_TP_MAGIC_LOW_RES_FACTOR; /* units/us */ - const double incline = accel_filter->incline * X230_TP_MAGIC_LOW_RES_FACTOR; - - /* Note: the magic values in this function are obtained by - * trial-and-error. No other meaning should be interpreted. - * The calculation is a compressed form of - * pointer_accel_profile_linear(), look at the git history of that - * function for an explanation of what the min/max/etc. does. - */ - speed_in *= X230_MAGIC_SLOWDOWN / X230_TP_MAGIC_LOW_RES_FACTOR; - - f1 = min(1, v_us2ms(speed_in) * 5); - f2 = 1 + (v_us2ms(speed_in) - v_us2ms(threshold)) * incline; - - factor = min(max_accel, f2 > 1 ? f2 : f1); - - return factor * X230_MAGIC_SLOWDOWN / X230_TP_MAGIC_LOW_RES_FACTOR; -} - struct motion_filter_interface accelerator_interface = { .type = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE, .filter = accelerator_filter_pre_normalized, @@ -1025,39 +922,6 @@ create_pointer_accelerator_filter_touchpad(int dpi, return &filter->base; } -struct motion_filter_interface accelerator_interface_x230 = { - .type = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE, - .filter = accelerator_filter_x230, - .filter_constant = accelerator_filter_constant_x230, - .restart = accelerator_restart, - .destroy = accelerator_destroy, - .set_speed = accelerator_set_speed, -}; - -/* The Lenovo x230 has a bad touchpad. This accel method has been - * trial-and-error'd, any changes to it will require re-testing everything. - * Don't touch this. - */ -struct motion_filter * -create_pointer_accelerator_filter_lenovo_x230(int dpi) -{ - struct pointer_accelerator *filter; - - filter = zalloc(sizeof *filter); - filter->base.interface = &accelerator_interface_x230; - filter->profile = touchpad_lenovo_x230_accel_profile; - filter->last_velocity = 0.0; - - init_trackers(&filter->trackers, NUM_POINTER_TRACKERS); - - filter->threshold = X230_THRESHOLD; - filter->accel = X230_ACCELERATION; /* unitless factor */ - filter->incline = X230_INCLINE; /* incline of the acceleration function */ - filter->dpi = dpi; - - return &filter->base; -} - double trackpoint_accel_profile(struct motion_filter *filter, void *data, -- 2.14.3 _______________________________________________ wayland-devel mailing list wayland-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/wayland-devel