Signed-off-by: Peter Hutterer <peter.hutte...@who-t.net> --- meson.build | 1 + src/filter-trackpoint.c | 309 ++++++++++++++++++++++++++++++++++++++++++++++++ src/filter.c | 263 ----------------------------------------- 3 files changed, 310 insertions(+), 263 deletions(-) create mode 100644 src/filter-trackpoint.c
diff --git a/meson.build b/meson.build index cd7af9fd..ecc92fd5 100644 --- a/meson.build +++ b/meson.build @@ -153,6 +153,7 @@ src_libfilter = [ 'src/filter.c', 'src/filter-touchpad-x230.c', 'src/filter-tablet.c', + 'src/filter-trackpoint.c', 'src/filter.h', 'src/filter-private.h' ] diff --git a/src/filter-trackpoint.c b/src/filter-trackpoint.c new file mode 100644 index 00000000..ad77ed88 --- /dev/null +++ b/src/filter-trackpoint.c @@ -0,0 +1,309 @@ +/* + * 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 */ +#define TRACKPOINT_DEFAULT_MAX_ACCEL 2.0 /* in units/us */ +#define TRACKPOINT_DEFAULT_MAX_DELTA 60 +/* As measured on a Lenovo T440 at kernel-default sensitivity 128 */ +#define TRACKPOINT_DEFAULT_RANGE 20 /* max value */ + +struct tablet_accelerator_flat { + struct motion_filter base; + + double factor; + int xres, yres; + double xres_scale, /* 1000dpi : tablet res */ + yres_scale; /* 1000dpi : tablet res */ +}; + +struct trackpoint_accelerator { + struct motion_filter base; + + struct device_float_coords history[4]; + size_t history_size; + + double scale_factor; + double max_accel; + double max_delta; + + double incline; /* incline of the function */ + double offset; /* offset of the function */ +}; + +double +trackpoint_accel_profile(struct motion_filter *filter, + void *data, + double delta) +{ + struct trackpoint_accelerator *accel_filter = + (struct trackpoint_accelerator *)filter; + const double max_accel = accel_filter->max_accel; + double factor; + + delta = fabs(delta); + + /* This is almost the equivalent of the xserver acceleration + at sensitivity 128 and speed 0.0 */ + factor = delta * accel_filter->incline + accel_filter->offset; + factor = min(factor, max_accel); + + return factor; +} + +/** + * Average the deltas, they are messy and can provide sequences like 7, 7, + * 9, 8, 14, 7, 9, 8 ... The outliers cause unpredictable jumps, so average + * them out. + */ +static inline struct device_float_coords +trackpoint_average_delta(struct trackpoint_accelerator *filter, + const struct device_float_coords *unaccelerated) +{ + size_t i; + struct device_float_coords avg = {0}; + + memmove(&filter->history[1], + &filter->history[0], + sizeof(*filter->history) * (filter->history_size - 1)); + filter->history[0] = *unaccelerated; + + for (i = 0; i < filter->history_size; i++) { + avg.x += filter->history[i].x; + avg.y += filter->history[i].y; + } + avg.x /= filter->history_size; + avg.y /= filter->history_size; + + return avg; +} + +/** + * Undo any system-wide magic scaling, so we're behaving the same regardless + * of the trackpoint hardware. This way we can apply our profile independent + * of any other configuration that messes with things. + */ +static inline struct device_float_coords +trackpoint_normalize_deltas(const struct trackpoint_accelerator *accel_filter, + const struct device_float_coords *delta) +{ + struct device_float_coords scaled = *delta; + + scaled.x *= accel_filter->scale_factor; + scaled.y *= accel_filter->scale_factor; + + return scaled; +} + +/** + * We set a max delta per event, to avoid extreme jumps once we exceed the + * expected pressure. Trackpoint hardware is inconsistent once the pressure + * gets high, so we can expect sequences like 30, 40, 35, 55, etc. This may + * be caused by difficulty keeping up high consistent pressures or just + * measuring errors in the hardware. Either way, we cap to a max delta so + * once we hit the high pressures, movement is capped and consistent. + */ +static inline struct normalized_coords +trackpoint_clip_to_max_delta(const struct trackpoint_accelerator *accel_filter, + struct normalized_coords coords) +{ + const double max_delta = accel_filter->max_delta; + + if (abs(coords.x) > max_delta) + coords.x = copysign(max_delta, coords.x); + if (abs(coords.y) > max_delta) + coords.y = copysign(max_delta, coords.y); + + return coords; +} + +static struct normalized_coords +trackpoint_accelerator_filter(struct motion_filter *filter, + const struct device_float_coords *unaccelerated, + void *data, uint64_t time) +{ + struct trackpoint_accelerator *accel_filter = + (struct trackpoint_accelerator *)filter; + struct device_float_coords scaled; + struct device_float_coords avg; + struct normalized_coords coords; + double f; + double delta; + + scaled = trackpoint_normalize_deltas(accel_filter, unaccelerated); + avg = trackpoint_average_delta(accel_filter, &scaled); + + delta = hypot(avg.x, avg.y); + + f = trackpoint_accel_profile(filter, data, delta); + + coords.x = avg.x * f; + coords.y = avg.y * f; + + coords = trackpoint_clip_to_max_delta(accel_filter, coords); + + return coords; +} + +static struct normalized_coords +trackpoint_accelerator_filter_noop(struct motion_filter *filter, + const struct device_float_coords *unaccelerated, + void *data, uint64_t time) +{ + + struct trackpoint_accelerator *accel_filter = + (struct trackpoint_accelerator *)filter; + struct device_float_coords scaled; + struct device_float_coords avg; + struct normalized_coords coords; + + scaled = trackpoint_normalize_deltas(accel_filter, unaccelerated); + avg = trackpoint_average_delta(accel_filter, &scaled); + + coords.x = avg.x; + coords.y = avg.y; + + coords = trackpoint_clip_to_max_delta(accel_filter, coords); + + return coords; +} + +static bool +trackpoint_accelerator_set_speed(struct motion_filter *filter, + double speed_adjustment) +{ + struct trackpoint_accelerator *accel_filter = + (struct trackpoint_accelerator*)filter; + double incline, offset, max; + + assert(speed_adjustment >= -1.0 && speed_adjustment <= 1.0); + + /* Helloooo, magic numbers. + + These numbers were obtained by finding an acceleration curve that + provides precision at slow speeds but still provides a good + acceleration at higher pressure - and a quick ramp-up to that + acceleration. + + Trackpoints have built-in acceleration curves already, so we + don't put a new function on top, we merely scale the output from + those curves (re-calculating the pressure values from the + firmware-defined curve and applying a new curve is unreliable). + + For that basic scaling, we assume a constant factor f based on + the speed setting together with a maximum factor m (for this + speed setting). Delta acceleration is thus: + factor = max(m, f) + accelerated_delta = delta * factor; + + Trial and error showed a couple of pairs that work well for the + various speed settings (Lenovo T440, sensitivity 128): + + -1.0: f = 0.3, m = 1 + -0.5: f = 0.6, m = 2 + 0.0: f = 1.0, m = 6 + 0.5: f = 1.4, m = 8 + 1.0: f = 1.9, m = 15 + + Note: if f >= 2.0, some pixels are unaddressable + + Those pairs were fed into the linear/exponential regression tool + at http://www.xuru.org/rt/LR.asp and show two functions that map + speed settings to the respective f and m. + Given a speed setting s in [-1.0, 1.0] + f(s) = 0.8 * s + 1.04 + m(s) = 4.6 * e**(1.2 * s) + These are close enough to the tested pairs. + */ + + max = 4.6 * pow(M_E, 1.2 * speed_adjustment); + incline = 0.8 * speed_adjustment + 1.04; + offset = 0; + + accel_filter->max_accel = max; + accel_filter->incline = incline; + accel_filter->offset = offset; + filter->speed_adjustment = speed_adjustment; + + return true; +} + +static void +trackpoint_accelerator_destroy(struct motion_filter *filter) +{ + struct trackpoint_accelerator *accel_filter = + (struct trackpoint_accelerator *)filter; + + free(accel_filter); +} + +struct motion_filter_interface accelerator_interface_trackpoint = { + .type = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE, + .filter = trackpoint_accelerator_filter, + .filter_constant = trackpoint_accelerator_filter_noop, + .restart = NULL, + .destroy = trackpoint_accelerator_destroy, + .set_speed = trackpoint_accelerator_set_speed, +}; + +struct motion_filter * +create_pointer_accelerator_filter_trackpoint(int max_hw_delta) +{ + struct trackpoint_accelerator *filter; + + /* Trackpoints are special. They don't have a movement speed like a + * mouse or a finger, instead they send a constant stream of events + * based on the pressure applied. + * + * Physical ranges on a trackpoint are the max values for relative + * deltas, but these are highly device-specific. + * + */ + + filter = zalloc(sizeof *filter); + if (!filter) + return NULL; + + filter->history_size = ARRAY_LENGTH(filter->history); + filter->scale_factor = 1.0 * TRACKPOINT_DEFAULT_RANGE / max_hw_delta; + filter->max_accel = TRACKPOINT_DEFAULT_MAX_ACCEL; + filter->max_delta = TRACKPOINT_DEFAULT_MAX_DELTA; + + filter->base.interface = &accelerator_interface_trackpoint; + + return &filter->base; +} diff --git a/src/filter.c b/src/filter.c index 204ace90..ff3f7dd1 100644 --- a/src/filter.c +++ b/src/filter.c @@ -109,12 +109,6 @@ filter_get_type(struct motion_filter *filter) #define TOUCHPAD_ACCELERATION 9.0 /* unitless factor */ #define TOUCHPAD_INCLINE 0.011 /* unitless factor */ -/* Trackpoint acceleration */ -#define TRACKPOINT_DEFAULT_MAX_ACCEL 2.0 /* in units/us */ -#define TRACKPOINT_DEFAULT_MAX_DELTA 60 -/* As measured on a Lenovo T440 at kernel-default sensitivity 128 */ -#define TRACKPOINT_DEFAULT_RANGE 20 /* max value */ - /* * Pointer acceleration filter constants */ @@ -147,20 +141,6 @@ struct pointer_accelerator_flat { int dpi; }; -struct trackpoint_accelerator { - struct motion_filter base; - - struct device_float_coords history[4]; - size_t history_size; - - double scale_factor; - double max_accel; - double max_delta; - - double incline; /* incline of the function */ - double offset; /* offset of the function */ -}; - void init_trackers(struct pointer_trackers *trackers, size_t ntrackers) @@ -913,249 +893,6 @@ create_pointer_accelerator_filter_touchpad(int dpi, return &filter->base; } -double -trackpoint_accel_profile(struct motion_filter *filter, - void *data, - double delta) -{ - struct trackpoint_accelerator *accel_filter = - (struct trackpoint_accelerator *)filter; - const double max_accel = accel_filter->max_accel; - double factor; - - delta = fabs(delta); - - /* This is almost the equivalent of the xserver acceleration - at sensitivity 128 and speed 0.0 */ - factor = delta * accel_filter->incline + accel_filter->offset; - factor = min(factor, max_accel); - - return factor; -} - -/** - * Average the deltas, they are messy and can provide sequences like 7, 7, - * 9, 8, 14, 7, 9, 8 ... The outliers cause unpredictable jumps, so average - * them out. - */ -static inline struct device_float_coords -trackpoint_average_delta(struct trackpoint_accelerator *filter, - const struct device_float_coords *unaccelerated) -{ - size_t i; - struct device_float_coords avg = {0}; - - memmove(&filter->history[1], - &filter->history[0], - sizeof(*filter->history) * (filter->history_size - 1)); - filter->history[0] = *unaccelerated; - - for (i = 0; i < filter->history_size; i++) { - avg.x += filter->history[i].x; - avg.y += filter->history[i].y; - } - avg.x /= filter->history_size; - avg.y /= filter->history_size; - - return avg; -} - -/** - * Undo any system-wide magic scaling, so we're behaving the same regardless - * of the trackpoint hardware. This way we can apply our profile independent - * of any other configuration that messes with things. - */ -static inline struct device_float_coords -trackpoint_normalize_deltas(const struct trackpoint_accelerator *accel_filter, - const struct device_float_coords *delta) -{ - struct device_float_coords scaled = *delta; - - scaled.x *= accel_filter->scale_factor; - scaled.y *= accel_filter->scale_factor; - - return scaled; -} - -/** - * We set a max delta per event, to avoid extreme jumps once we exceed the - * expected pressure. Trackpoint hardware is inconsistent once the pressure - * gets high, so we can expect sequences like 30, 40, 35, 55, etc. This may - * be caused by difficulty keeping up high consistent pressures or just - * measuring errors in the hardware. Either way, we cap to a max delta so - * once we hit the high pressures, movement is capped and consistent. - */ -static inline struct normalized_coords -trackpoint_clip_to_max_delta(const struct trackpoint_accelerator *accel_filter, - struct normalized_coords coords) -{ - const double max_delta = accel_filter->max_delta; - - if (abs(coords.x) > max_delta) - coords.x = copysign(max_delta, coords.x); - if (abs(coords.y) > max_delta) - coords.y = copysign(max_delta, coords.y); - - return coords; -} - -static struct normalized_coords -trackpoint_accelerator_filter(struct motion_filter *filter, - const struct device_float_coords *unaccelerated, - void *data, uint64_t time) -{ - struct trackpoint_accelerator *accel_filter = - (struct trackpoint_accelerator *)filter; - struct device_float_coords scaled; - struct device_float_coords avg; - struct normalized_coords coords; - double f; - double delta; - - scaled = trackpoint_normalize_deltas(accel_filter, unaccelerated); - avg = trackpoint_average_delta(accel_filter, &scaled); - - delta = hypot(avg.x, avg.y); - - f = trackpoint_accel_profile(filter, data, delta); - - coords.x = avg.x * f; - coords.y = avg.y * f; - - coords = trackpoint_clip_to_max_delta(accel_filter, coords); - - return coords; -} - -static struct normalized_coords -trackpoint_accelerator_filter_noop(struct motion_filter *filter, - const struct device_float_coords *unaccelerated, - void *data, uint64_t time) -{ - - struct trackpoint_accelerator *accel_filter = - (struct trackpoint_accelerator *)filter; - struct device_float_coords scaled; - struct device_float_coords avg; - struct normalized_coords coords; - - scaled = trackpoint_normalize_deltas(accel_filter, unaccelerated); - avg = trackpoint_average_delta(accel_filter, &scaled); - - coords.x = avg.x; - coords.y = avg.y; - - coords = trackpoint_clip_to_max_delta(accel_filter, coords); - - return coords; -} - -static bool -trackpoint_accelerator_set_speed(struct motion_filter *filter, - double speed_adjustment) -{ - struct trackpoint_accelerator *accel_filter = - (struct trackpoint_accelerator*)filter; - double incline, offset, max; - - assert(speed_adjustment >= -1.0 && speed_adjustment <= 1.0); - - /* Helloooo, magic numbers. - - These numbers were obtained by finding an acceleration curve that - provides precision at slow speeds but still provides a good - acceleration at higher pressure - and a quick ramp-up to that - acceleration. - - Trackpoints have built-in acceleration curves already, so we - don't put a new function on top, we merely scale the output from - those curves (re-calculating the pressure values from the - firmware-defined curve and applying a new curve is unreliable). - - For that basic scaling, we assume a constant factor f based on - the speed setting together with a maximum factor m (for this - speed setting). Delta acceleration is thus: - factor = max(m, f) - accelerated_delta = delta * factor; - - Trial and error showed a couple of pairs that work well for the - various speed settings (Lenovo T440, sensitivity 128): - - -1.0: f = 0.3, m = 1 - -0.5: f = 0.6, m = 2 - 0.0: f = 1.0, m = 6 - 0.5: f = 1.4, m = 8 - 1.0: f = 1.9, m = 15 - - Note: if f >= 2.0, some pixels are unaddressable - - Those pairs were fed into the linear/exponential regression tool - at http://www.xuru.org/rt/LR.asp and show two functions that map - speed settings to the respective f and m. - Given a speed setting s in [-1.0, 1.0] - f(s) = 0.8 * s + 1.04 - m(s) = 4.6 * e**(1.2 * s) - These are close enough to the tested pairs. - */ - - max = 4.6 * pow(M_E, 1.2 * speed_adjustment); - incline = 0.8 * speed_adjustment + 1.04; - offset = 0; - - accel_filter->max_accel = max; - accel_filter->incline = incline; - accel_filter->offset = offset; - filter->speed_adjustment = speed_adjustment; - - return true; -} - -static void -trackpoint_accelerator_destroy(struct motion_filter *filter) -{ - struct trackpoint_accelerator *accel_filter = - (struct trackpoint_accelerator *)filter; - - free(accel_filter); -} - -struct motion_filter_interface accelerator_interface_trackpoint = { - .type = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE, - .filter = trackpoint_accelerator_filter, - .filter_constant = trackpoint_accelerator_filter_noop, - .restart = NULL, - .destroy = trackpoint_accelerator_destroy, - .set_speed = trackpoint_accelerator_set_speed, -}; - -struct motion_filter * -create_pointer_accelerator_filter_trackpoint(int max_hw_delta) -{ - struct trackpoint_accelerator *filter; - - /* Trackpoints are special. They don't have a movement speed like a - * mouse or a finger, instead they send a constant stream of events - * based on the pressure applied. - * - * Physical ranges on a trackpoint are the max values for relative - * deltas, but these are highly device-specific. - * - */ - - filter = zalloc(sizeof *filter); - if (!filter) - return NULL; - - filter->history_size = ARRAY_LENGTH(filter->history); - filter->scale_factor = 1.0 * TRACKPOINT_DEFAULT_RANGE / max_hw_delta; - filter->max_accel = TRACKPOINT_DEFAULT_MAX_ACCEL; - filter->max_delta = TRACKPOINT_DEFAULT_MAX_DELTA; - - filter->base.interface = &accelerator_interface_trackpoint; - - return &filter->base; -} - static struct normalized_coords accelerator_filter_flat(struct motion_filter *filter, const struct device_float_coords *unaccelerated, -- 2.14.3 _______________________________________________ wayland-devel mailing list wayland-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/wayland-devel