From f04ef9f985773a509cd4a5f998548b025c8825be Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 15 Dec 2016 09:22:23 +1000 Subject: [PATCH 01/13] filter: store the raw dpi value in the filter Currently unused, will be used in the future. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/filter.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/filter.c b/src/filter.c index 4b15c307..eecf4ca8 100644 --- a/src/filter.c +++ b/src/filter.c @@ -154,6 +154,7 @@ struct pointer_accelerator { double incline; /* incline of the function */ double dpi_factor; + int dpi; }; struct pointer_accelerator_flat { @@ -161,6 +162,7 @@ struct pointer_accelerator_flat { double factor; double dpi_factor; + int dpi; }; struct tablet_accelerator_flat { @@ -775,6 +777,7 @@ create_default_filter(int dpi) filter->incline = DEFAULT_INCLINE; filter->dpi_factor = dpi/(double)DEFAULT_MOUSE_DPI; + filter->dpi = dpi; return filter; } @@ -877,6 +880,7 @@ create_pointer_accelerator_filter_lenovo_x230(int dpi) filter->incline = X230_INCLINE; /* incline of the acceleration function */ filter->dpi_factor = 1; /* unused for this accel method */ + filter->dpi = dpi; return &filter->base; } @@ -904,6 +908,7 @@ create_pointer_accelerator_filter_trackpoint(int dpi) filter->threshold = DEFAULT_THRESHOLD; filter->accel = DEFAULT_ACCELERATION; filter->incline = DEFAULT_INCLINE; + filter->dpi = dpi; return &filter->base; } @@ -980,6 +985,7 @@ create_pointer_accelerator_filter_flat(int dpi) filter->base.interface = &accelerator_interface_flat; filter->dpi_factor = dpi/(double)DEFAULT_MOUSE_DPI; + filter->dpi = dpi; return &filter->base; } From a2dbd4f2d78971c8eb80789485dda58c39579417 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 15 Dec 2016 10:51:16 +1000 Subject: [PATCH 02/13] filter: drop the dpi_factor in favor of direct calculation This was badly since the factor was the ratio of "dpi:default dpi" Most devices don't need it, so storing it in all filters event though we only use it for some devices is confusing. Now that we have the dpi stored directlyconfusing. Now that we have the dpi stored directly we might as well use that. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/filter.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/filter.c b/src/filter.c index eecf4ca8..06f07af1 100644 --- a/src/filter.c +++ b/src/filter.c @@ -153,7 +153,6 @@ struct pointer_accelerator { double accel; /* unitless factor */ double incline; /* incline of the function */ - double dpi_factor; int dpi; }; @@ -161,7 +160,6 @@ struct pointer_accelerator_flat { struct motion_filter base; double factor; - double dpi_factor; int dpi; }; @@ -382,7 +380,7 @@ accelerator_filter_low_dpi(struct motion_filter *filter, double accel_value; /* unitless factor */ struct normalized_coords accelerated; struct normalized_coords unnormalized; - double dpi_factor = accel->dpi_factor; + double dpi_factor = accel->dpi/(double)DEFAULT_MOUSE_DPI; /* For low-dpi mice, use device units, everything else uses 1000dpi normalized */ @@ -411,7 +409,7 @@ accelerator_filter_trackpoint(struct motion_filter *filter, double accel_value; /* unitless factor */ struct normalized_coords accelerated; struct normalized_coords unnormalized; - double dpi_factor = accel->dpi_factor; + double dpi_factor = accel->dpi/(double)DEFAULT_MOUSE_DPI; /* trackpoints with a dpi factor have a const accel set, remove that * and restore device units. The accel profile takes const accel @@ -568,8 +566,8 @@ pointer_accel_profile_linear_low_dpi(struct motion_filter *filter, double max_accel = accel_filter->accel; /* unitless factor */ double threshold = accel_filter->threshold; /* units/us */ const double incline = accel_filter->incline; + double dpi_factor = accel_filter->dpi/(double)DEFAULT_MOUSE_DPI; double factor; /* unitless */ - double dpi_factor = accel_filter->dpi_factor; /* dpi_factor is always < 1.0, increase max_accel, reduce the threshold so it kicks in earlier */ @@ -727,8 +725,8 @@ trackpoint_accel_profile(struct motion_filter *filter, double max_accel = accel_filter->accel; /* unitless factor */ double threshold = accel_filter->threshold; /* units/ms */ const double incline = accel_filter->incline; + double dpi_factor = accel_filter->dpi/(double)DEFAULT_MOUSE_DPI; double factor; - double dpi_factor = accel_filter->dpi_factor; /* dpi_factor is always < 1.0, increase max_accel, reduce the threshold so it kicks in earlier */ @@ -775,8 +773,6 @@ create_default_filter(int dpi) filter->threshold = DEFAULT_THRESHOLD; filter->accel = DEFAULT_ACCELERATION; filter->incline = DEFAULT_INCLINE; - - filter->dpi_factor = dpi/(double)DEFAULT_MOUSE_DPI; filter->dpi = dpi; return filter; @@ -878,8 +874,6 @@ create_pointer_accelerator_filter_lenovo_x230(int dpi) filter->threshold = X230_THRESHOLD; filter->accel = X230_ACCELERATION; /* unitless factor */ filter->incline = X230_INCLINE; /* incline of the acceleration function */ - - filter->dpi_factor = 1; /* unused for this accel method */ filter->dpi = dpi; return &filter->base; @@ -923,11 +917,12 @@ accelerator_filter_flat(struct motion_filter *filter, double factor; /* unitless factor */ struct normalized_coords accelerated; struct normalized_coords unnormalized; + double dpi_factor = accel_filter->dpi/(double)DEFAULT_MOUSE_DPI; /* You want flat acceleration, you get flat acceleration for the * device */ - unnormalized.x = unaccelerated->x * accel_filter->dpi_factor; - unnormalized.y = unaccelerated->y * accel_filter->dpi_factor; + unnormalized.x = unaccelerated->x * dpi_factor; + unnormalized.y = unaccelerated->y * dpi_factor; factor = accel_filter->factor; accelerated.x = factor * unnormalized.x; @@ -984,7 +979,6 @@ create_pointer_accelerator_filter_flat(int dpi) return NULL; filter->base.interface = &accelerator_interface_flat; - filter->dpi_factor = dpi/(double)DEFAULT_MOUSE_DPI; filter->dpi = dpi; return &filter->base; From 8e2d3b9049af06c98baa41173626912e456c9c08 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 14 Nov 2016 16:47:26 +1000 Subject: [PATCH 03/13] filter: duplicate pointer accel for touchpads This duplicates the code so we can change it for touchpads without affecting mice. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/filter.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 98 insertions(+), 5 deletions(-) diff --git a/src/filter.c b/src/filter.c index 06f07af1..6f06ab98 100644 --- a/src/filter.c +++ b/src/filter.c @@ -117,6 +117,12 @@ filter_get_type(struct motion_filter *filter) #define DEFAULT_ACCELERATION 2.0 /* unitless factor */ #define DEFAULT_INCLINE 1.1 /* unitless factor */ +/* Touchpad acceleration */ +#define TOUCHPAD_DEFAULT_THRESHOLD v_ms2us(0.4) +#define TOUCHPAD_MINIMUM_THRESHOLD v_ms2us(0.2) +#define TOUCHPAD_ACCELERATION 2.0 /* unitless factor */ +#define TOUCHPAD_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 */ @@ -470,6 +476,34 @@ accelerator_filter_constant_x230(struct motion_filter *filter, return normalized; } +static bool +touchpad_accelerator_set_speed(struct motion_filter *filter, + double speed_adjustment) +{ + struct pointer_accelerator *accel_filter = + (struct pointer_accelerator *)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 = TOUCHPAD_DEFAULT_THRESHOLD - + v_ms2us(0.25) * speed_adjustment; + if (accel_filter->threshold < TOUCHPAD_MINIMUM_THRESHOLD) + accel_filter->threshold = TOUCHPAD_MINIMUM_THRESHOLD; + + /* adjust max accel factor */ + accel_filter->accel = TOUCHPAD_ACCELERATION + speed_adjustment * 1.5; + + /* higher speed -> faster to reach max */ + accel_filter->incline = TOUCHPAD_INCLINE + speed_adjustment * 0.75; + + filter->speed_adjustment = speed_adjustment; + return true; +} + static struct normalized_coords touchpad_constant_filter(struct motion_filter *filter, const struct normalized_coords *unaccelerated, @@ -662,15 +696,74 @@ pointer_accel_profile_linear(struct motion_filter *filter, double touchpad_accel_profile_linear(struct motion_filter *filter, - void *data, - double speed_in, /* units/us */ - uint64_t time) + void *data, + double speed_in, /* 1000-dpi normalized */ + uint64_t time) { + struct pointer_accelerator *accel_filter = + (struct pointer_accelerator *)filter; + const double max_accel = accel_filter->accel; /* unitless factor */ + const double threshold = accel_filter->threshold; /* units/us */ + const double incline = accel_filter->incline; double factor; /* unitless */ speed_in *= TP_MAGIC_SLOWDOWN; - factor = pointer_accel_profile_linear(filter, data, speed_in, time); + /* + Our acceleration function calculates a factor to accelerate input + deltas with. The function is a double incline with a plateau, + with a rough shape like this: + + accel + factor + ^ + | / + | _____/ + | / + |/ + +-------------> speed in + + The two inclines are linear functions in the form + y = ax + b + where y is speed_out + x is speed_in + a is the incline of acceleration + b is minimum acceleration factor + + for speeds up to 0.07 u/ms, we decelerate, down to 30% of input + speed. + hence 1 = a * 0.07 + 0.3 + 0.3 = a * 0.07 => a := 10 + deceleration function is thus: + y = 10x + 0.3 + + Note: + * 0.07u/ms as threshold is a result of trial-and-error and + has no other intrinsic meaning. + * 0.3 is chosen simply because it is above the Nyquist frequency + for subpixel motion within a pixel. + */ + if (v_us2ms(speed_in) < 0.07) { + factor = 10 * v_us2ms(speed_in) + 0.3; + /* up to the threshold, we keep factor 1, i.e. 1:1 movement */ + } else if (speed_in < threshold) { + factor = 1; + } else { + /* Acceleration function above the threshold: + y = ax' + b + where T is threshold + x is speed_in + x' is speed + and + y(T) == 1 + hence 1 = ax' + 1 + => x' := (x - T) + */ + factor = incline * v_us2ms(speed_in - threshold) + 1; + } + + /* Cap at the maximum acceleration factor */ + factor = min(max_accel, factor); return factor * TP_MAGIC_SLOWDOWN; } @@ -823,7 +916,7 @@ struct motion_filter_interface accelerator_interface_touchpad = { .filter_constant = touchpad_constant_filter, .restart = accelerator_restart, .destroy = accelerator_destroy, - .set_speed = accelerator_set_speed, + .set_speed = touchpad_accelerator_set_speed, }; struct motion_filter * From db9cfc9c5c91a530a9c694d118c316e5a6cf9ec2 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 15 Dec 2016 12:45:38 +1000 Subject: [PATCH 04/13] touchpad: init the device's dpi correctly This has no real effect just yet because we don't use a touchpad's dpi anywhere in the touchpad code. Only the acceleration code wants it but all touchpads use the same acceleration method, and that one doesn't care about the dpi. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 26b65de9..a5ab4c2e 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -2257,6 +2257,10 @@ tp_init(struct tp_dispatch *tp, EV_ABS, ABS_MT_DISTANCE); + /* Set the dpi to that of the x axis, because that's what we normalize + to when needed*/ + device->dpi = device->abs.absinfo_x->resolution * 25.4; + tp_init_hysteresis(tp); if (!tp_init_accel(tp)) From 187c38f660d6dd02cd361fafe554d14684a728ae Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 15 Dec 2016 09:26:42 +1000 Subject: [PATCH 05/13] Add device_float_get_direction With some upcoming changes we need this function for device float coordinates as well. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/libinput-private.h | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/src/libinput-private.h b/src/libinput-private.h index 46258802..f80efdc9 100644 --- a/src/libinput-private.h +++ b/src/libinput-private.h @@ -701,28 +701,28 @@ enum directions { }; static inline uint32_t -normalized_get_direction(struct normalized_coords norm) +xy_get_direction(double x, double y) { uint32_t dir = UNDEFINED_DIRECTION; int d1, d2; double r; - if (fabs(norm.x) < 2.0 && fabs(norm.y) < 2.0) { - if (norm.x > 0.0 && norm.y > 0.0) + if (fabs(x) < 2.0 && fabs(y) < 2.0) { + if (x > 0.0 && y > 0.0) dir = S | SE | E; - else if (norm.x > 0.0 && norm.y < 0.0) + else if (x > 0.0 && y < 0.0) dir = N | NE | E; - else if (norm.x < 0.0 && norm.y > 0.0) + else if (x < 0.0 && y > 0.0) dir = S | SW | W; - else if (norm.x < 0.0 && norm.y < 0.0) + else if (x < 0.0 && y < 0.0) dir = N | NW | W; - else if (norm.x > 0.0) + else if (x > 0.0) dir = NE | E | SE; - else if (norm.x < 0.0) + else if (x < 0.0) dir = NW | W | SW; - else if (norm.y > 0.0) + else if (y > 0.0) dir = SE | S | SW; - else if (norm.y < 0.0) + else if (y < 0.0) dir = NE | N | NW; } else { /* Calculate r within the interval [0 to 8) @@ -731,7 +731,7 @@ normalized_get_direction(struct normalized_coords norm) * d_f = r / 2π ([0 .. 1)) * d_8 = 8 * d_f */ - r = atan2(norm.y, norm.x); + r = atan2(y, x); r = fmod(r + 2.5*M_PI, 2*M_PI); r *= 4*M_1_PI; @@ -745,4 +745,19 @@ normalized_get_direction(struct normalized_coords norm) return dir; } +static inline uint32_t +normalized_get_direction(struct normalized_coords norm) +{ + return xy_get_direction(norm.x, norm.y); +} + +/** + * Get the direction for the given set of coordinates. + * assumption: coordinates are normalized to one axis resolution. + */ +static inline uint32_t +device_float_get_direction(struct device_float_coords coords) +{ + return xy_get_direction(coords.x, coords.y); +} #endif /* LIBINPUT_PRIVATE_H */ From bdd4264d6150f4a6248eec7e1fbf74a2837ac77f Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 15 Dec 2016 08:36:22 +1000 Subject: [PATCH 06/13] filter: change the filter functions to take raw device coordinates We used to normalize all deltas to equivalents of a 1000dpi mouse before passing it into the acceleration functions. This has a bunch of drawbacks, not least that we already have to un-normalize back into device units for a few devices already (trackpoints, tablet, low-dpi mice). Switch the filter code over to use device units, relying on the dpi set earlier during filter creation to convert to normalized. To make things easy, the output of the filter code is still normalized data, i.e. data ready to be handed to the libinput caller. No effective functional changes. For touchpads, we still send normalized coordinates (for now, anyway). For the various filter methods, we either drop the places where we unnormalized before or we normalize where needed. Two possible changes: for trackpoints and low-dpi mice we had a max dpi factor of 1.0 before - now we don't anymore. This was only the case if a low-dpi mouse had more than 1000dpi (never true) or a trackpoint had a const accel lower than 1.0 (yeah, whatever). Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad.c | 16 ++- src/evdev-tablet.c | 7 +- src/evdev.c | 2 +- src/filter-private.h | 4 +- src/filter.c | 227 ++++++++++++++++++++++++++++------------ src/filter.h | 25 ++++- src/libinput-private.h | 6 ++ tools/ptraccel-debug.c | 21 ++-- 8 files changed, 223 insertions(+), 85 deletions(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index a5ab4c2e..20ba06f8 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -51,11 +51,17 @@ tp_filter_motion(struct tp_dispatch *tp, const struct normalized_coords *unaccelerated, uint64_t time) { + struct device_float_coords raw; + if (normalized_is_zero(*unaccelerated)) return *unaccelerated; + /* Temporary solution only: convert back to raw coordinates, but + * make sure we're on the same resolution for both axes */ + raw = tp_unnormalize_for_xaxis(tp, *unaccelerated); + return filter_dispatch(tp->device->pointer.filter, - unaccelerated, tp, time); + &raw, tp, time); } struct normalized_coords @@ -63,11 +69,17 @@ tp_filter_motion_unaccelerated(struct tp_dispatch *tp, const struct normalized_coords *unaccelerated, uint64_t time) { + struct device_float_coords raw; + if (normalized_is_zero(*unaccelerated)) return *unaccelerated; + /* Temporary solution only: convert back to raw coordinates, but + * make sure we're on the same resolution for both axes */ + raw = tp_unnormalize_for_xaxis(tp, *unaccelerated); + return filter_dispatch_constant(tp->device->pointer.filter, - unaccelerated, tp, time); + &raw, tp, time); } static inline void diff --git a/src/evdev-tablet.c b/src/evdev-tablet.c index b840e724..90d3b44c 100644 --- a/src/evdev-tablet.c +++ b/src/evdev-tablet.c @@ -392,13 +392,14 @@ tool_process_delta(struct libinput_tablet_tool *tool, const struct device_coords *delta, uint64_t time) { - struct normalized_coords accel; + const struct normalized_coords zero = { 0.0, 0.0 }; + struct device_float_coords accel; accel.x = 1.0 * delta->x; accel.y = 1.0 * delta->y; - if (normalized_is_zero(accel)) - return accel; + if (device_float_is_zero(accel)) + return zero; return filter_dispatch(device->pointer.filter, &accel, diff --git a/src/evdev.c b/src/evdev.c index 5a3850ce..b4de1997 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -419,7 +419,7 @@ fallback_flush_relative_motion(struct fallback_dispatch *dispatch, if (device->pointer.filter) { /* Apply pointer acceleration. */ accel = filter_dispatch(device->pointer.filter, - &unaccel, + &raw, device, time); } else { diff --git a/src/filter-private.h b/src/filter-private.h index 637125a0..6ccbf97f 100644 --- a/src/filter-private.h +++ b/src/filter-private.h @@ -32,11 +32,11 @@ struct motion_filter_interface { enum libinput_config_accel_profile type; struct normalized_coords (*filter)( struct motion_filter *filter, - const struct normalized_coords *unaccelerated, + const struct device_float_coords *unaccelerated, void *data, uint64_t time); struct normalized_coords (*filter_constant)( struct motion_filter *filter, - const struct normalized_coords *unaccelerated, + const struct device_float_coords *unaccelerated, void *data, uint64_t time); void (*restart)(struct motion_filter *filter, void *data, diff --git a/src/filter.c b/src/filter.c index 6f06ab98..5da81368 100644 --- a/src/filter.c +++ b/src/filter.c @@ -56,9 +56,20 @@ v_ms2us(double units_per_ms) return units_per_ms/1000.0; } +static inline struct normalized_coords +normalize_for_dpi(const struct device_float_coords *coords, int dpi) +{ + struct normalized_coords norm; + + norm.x = coords->x * DEFAULT_MOUSE_DPI/dpi; + norm.y = coords->y * DEFAULT_MOUSE_DPI/dpi; + + return norm; +} + struct normalized_coords filter_dispatch(struct motion_filter *filter, - const struct normalized_coords *unaccelerated, + const struct device_float_coords *unaccelerated, void *data, uint64_t time) { return filter->interface->filter(filter, unaccelerated, data, time); @@ -66,7 +77,7 @@ filter_dispatch(struct motion_filter *filter, struct normalized_coords filter_dispatch_constant(struct motion_filter *filter, - const struct normalized_coords *unaccelerated, + const struct device_float_coords *unaccelerated, void *data, uint64_t time) { return filter->interface->filter_constant(filter, unaccelerated, data, time); @@ -180,7 +191,7 @@ struct tablet_accelerator_flat { static void feed_trackers(struct pointer_accelerator *accel, - const struct normalized_coords *delta, + const struct device_float_coords *delta, uint64_t time) { int i, current; @@ -197,7 +208,7 @@ feed_trackers(struct pointer_accelerator *accel, trackers[current].delta.x = 0.0; trackers[current].delta.y = 0.0; trackers[current].time = time; - trackers[current].dir = normalized_get_direction(*delta); + trackers[current].dir = device_float_get_direction(*delta); } static struct pointer_tracker * @@ -296,6 +307,16 @@ calculate_velocity(struct pointer_accelerator *accel, uint64_t time) return result; /* units/us */ } +/** + * 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 *accel, void *data, double velocity, uint64_t time) @@ -303,6 +324,18 @@ acceleration_profile(struct pointer_accelerator *accel, 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 *accel, void *data, @@ -326,13 +359,23 @@ calculate_acceleration(struct pointer_accelerator *accel, 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 *accel, - const struct normalized_coords *unaccelerated, + const struct device_float_coords *unaccelerated, void *data, uint64_t time) { - double velocity; /* units/us */ + double velocity; /* units/us in device-native dpi*/ double accel_factor; feed_trackers(accel, unaccelerated, time); @@ -347,9 +390,21 @@ calculate_acceleration_factor(struct pointer_accelerator *accel, return accel_factor; } +/** + * Generic filter that calculates the acceleration factor and applies it to + * the coordinates. + * + * @param filter 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 An accelerated tuple of coordinates representing normalized + * motion + */ static struct normalized_coords accelerator_filter(struct motion_filter *filter, - const struct normalized_coords *unaccelerated, + const struct device_float_coords *unaccelerated, void *data, uint64_t time) { struct pointer_accelerator *accel = @@ -368,85 +423,120 @@ accelerator_filter(struct motion_filter *filter, return accelerated; } +/** + * Generic filter that does nothing beyond converting from the device's + * native dpi into normalized coordinates. + * + * @param filter 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 An accelerated tuple of coordinates representing normalized + * motion + */ static struct normalized_coords accelerator_filter_noop(struct motion_filter *filter, - const struct normalized_coords *unaccelerated, + const struct device_float_coords *unaccelerated, void *data, uint64_t time) { - return *unaccelerated; + struct pointer_accelerator *accel = + (struct pointer_accelerator *) filter; + + return normalize_for_dpi(unaccelerated, accel->dpi); } +/** + * Low-dpi filter that handles events from devices with less than the + * default dpi. + * + * @param filter 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 An accelerated tuple of coordinates representing normalized + * motion + */ static struct normalized_coords accelerator_filter_low_dpi(struct motion_filter *filter, - const struct normalized_coords *unaccelerated, + const struct device_float_coords *unaccelerated, void *data, uint64_t time) { struct pointer_accelerator *accel = (struct pointer_accelerator *) filter; double accel_value; /* unitless factor */ struct normalized_coords accelerated; - struct normalized_coords unnormalized; - double dpi_factor = accel->dpi/(double)DEFAULT_MOUSE_DPI; - - /* For low-dpi mice, use device units, everything else uses - 1000dpi normalized */ - dpi_factor = min(1.0, dpi_factor); - unnormalized.x = unaccelerated->x * dpi_factor; - unnormalized.y = unaccelerated->y * dpi_factor; + /* Input is already in device-native DPI, nothing else needed */ accel_value = calculate_acceleration_factor(accel, - &unnormalized, + unaccelerated, data, time); - - accelerated.x = accel_value * unnormalized.x; - accelerated.y = accel_value * unnormalized.y; + accelerated.x = accel_value * unaccelerated->x; + accelerated.y = accel_value * unaccelerated->y; return accelerated; } +/** + * Custom filter that applies the trackpoint's constant acceleration, if any. + * + * @param filter 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 An accelerated tuple of coordinates representing normalized + * motion + */ static struct normalized_coords accelerator_filter_trackpoint(struct motion_filter *filter, - const struct normalized_coords *unaccelerated, + const struct device_float_coords *unaccelerated, void *data, uint64_t time) { struct pointer_accelerator *accel = (struct pointer_accelerator *) filter; double accel_value; /* unitless factor */ struct normalized_coords accelerated; - struct normalized_coords unnormalized; - double dpi_factor = accel->dpi/(double)DEFAULT_MOUSE_DPI; - - /* trackpoints with a dpi factor have a const accel set, remove that - * and restore device units. The accel profile takes const accel - * into account */ - dpi_factor = min(1.0, dpi_factor); - unnormalized.x = unaccelerated->x * dpi_factor; - unnormalized.y = unaccelerated->y * dpi_factor; + /* Nothing special to do here, data is already in device dpi */ accel_value = calculate_acceleration_factor(accel, - &unnormalized, + unaccelerated, data, time); - accelerated.x = accel_value * unnormalized.x; - accelerated.y = accel_value * unnormalized.y; + accelerated.x = accel_value * unaccelerated->x; + accelerated.y = accel_value * unaccelerated->y; return accelerated; } static struct normalized_coords accelerator_filter_x230(struct motion_filter *filter, - const struct normalized_coords *unaccelerated, + 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 */ - feed_trackers(accel, unaccelerated, time); + /* 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, &delta_normalized, time); velocity = calculate_velocity(accel, time); accel_factor = calculate_acceleration(accel, data, @@ -455,23 +545,26 @@ accelerator_filter_x230(struct motion_filter *filter, time); accel->last_velocity = velocity; - accelerated.x = accel_factor * unaccelerated->x; - accelerated.y = accel_factor * unaccelerated->y; + 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 normalized_coords *unaccelerated, + 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.x = factor * unaccelerated->x; - normalized.y = factor * unaccelerated->y; + normalized = normalize_for_dpi(unaccelerated, accel->dpi); + normalized.x = factor * normalized.x; + normalized.y = factor * normalized.y; return normalized; } @@ -506,13 +599,16 @@ touchpad_accelerator_set_speed(struct motion_filter *filter, static struct normalized_coords touchpad_constant_filter(struct motion_filter *filter, - const struct normalized_coords *unaccelerated, + const struct device_float_coords *unaccelerated, void *data, uint64_t time) { + struct pointer_accelerator *accel = + (struct pointer_accelerator *)filter; struct normalized_coords normalized; - normalized.x = TP_MAGIC_SLOWDOWN * unaccelerated->x; - normalized.y = TP_MAGIC_SLOWDOWN * unaccelerated->y; + normalized = normalize_for_dpi(unaccelerated, accel->dpi); + normalized.x = TP_MAGIC_SLOWDOWN * normalized.x; + normalized.y = TP_MAGIC_SLOWDOWN * normalized.y; return normalized; } @@ -624,7 +720,7 @@ pointer_accel_profile_linear_low_dpi(struct motion_filter *filter, double pointer_accel_profile_linear(struct motion_filter *filter, void *data, - double speed_in, /* 1000-dpi normalized */ + double speed_in, /* in device units (units/µs) */ uint64_t time) { struct pointer_accelerator *accel_filter = @@ -634,6 +730,9 @@ pointer_accel_profile_linear(struct motion_filter *filter, const double incline = accel_filter->incline; double factor; /* unitless */ + /* Normalize to 1000dpi, because the rest below relies on that */ + speed_in = speed_in * DEFAULT_MOUSE_DPI/accel_filter->dpi; + /* Our acceleration function calculates a factor to accelerate input deltas with. The function is a double incline with a plateau, @@ -697,7 +796,7 @@ pointer_accel_profile_linear(struct motion_filter *filter, double touchpad_accel_profile_linear(struct motion_filter *filter, void *data, - double speed_in, /* 1000-dpi normalized */ + double speed_in, /* in device units/µs */ uint64_t time) { struct pointer_accelerator *accel_filter = @@ -707,6 +806,9 @@ touchpad_accel_profile_linear(struct motion_filter *filter, const double incline = accel_filter->incline; double factor; /* unitless */ + /* Normalize to 1000dpi, because the rest below relies on that */ + speed_in = speed_in * DEFAULT_MOUSE_DPI/accel_filter->dpi; + speed_in *= TP_MAGIC_SLOWDOWN; /* @@ -771,7 +873,7 @@ touchpad_accel_profile_linear(struct motion_filter *filter, double touchpad_lenovo_x230_accel_profile(struct motion_filter *filter, void *data, - double speed_in, + double speed_in, /* 1000dpi-units/µs */ uint64_t time) { /* Those touchpads presents an actual lower resolution that what is @@ -1002,24 +1104,19 @@ create_pointer_accelerator_filter_trackpoint(int dpi) static struct normalized_coords accelerator_filter_flat(struct motion_filter *filter, - const struct normalized_coords *unaccelerated, + const struct device_float_coords *unaccelerated, void *data, uint64_t time) { struct pointer_accelerator_flat *accel_filter = (struct pointer_accelerator_flat *)filter; double factor; /* unitless factor */ struct normalized_coords accelerated; - struct normalized_coords unnormalized; - double dpi_factor = accel_filter->dpi/(double)DEFAULT_MOUSE_DPI; /* You want flat acceleration, you get flat acceleration for the * device */ - unnormalized.x = unaccelerated->x * dpi_factor; - unnormalized.y = unaccelerated->y * dpi_factor; factor = accel_filter->factor; - - accelerated.x = factor * unnormalized.x; - accelerated.y = factor * unnormalized.y; + accelerated.x = factor * unaccelerated->x; + accelerated.y = factor * unaccelerated->y; return accelerated; } @@ -1079,19 +1176,17 @@ create_pointer_accelerator_filter_flat(int dpi) static inline struct normalized_coords tablet_accelerator_filter_flat_mouse(struct tablet_accelerator_flat *filter, - const struct normalized_coords *units) + const struct device_float_coords *units) { struct normalized_coords accelerated; /* - The input for and output of accel methods is usually a delta in - 1000dpi equivalents. Tablets are high res (Intuos 4 is 5080 dpi) - and unmodified deltas are way too high. Slow it down to the - equivalent of a 1000dpi mouse. The ratio of that is: + Tablets are high res (Intuos 4 is 5080 dpi) and unmodified deltas + are way too high. Slow it down to the equivalent of a 1000dpi + mouse. The ratio of that is: ratio = 1000/(resolution_per_mm * 25.4) i.e. on the Intuos4 it's a ratio of ~1/5. - */ accelerated.x = units->x * filter->xres_scale; @@ -1105,12 +1200,12 @@ tablet_accelerator_filter_flat_mouse(struct tablet_accelerator_flat *filter, static struct normalized_coords tablet_accelerator_filter_flat_pen(struct tablet_accelerator_flat *filter, - const struct normalized_coords *units) + const struct device_float_coords *units) { struct normalized_coords accelerated; - /* Tablet input is in device units, output is supposed to be in logical - * pixels roughly equivalent to a mouse/touchpad. + /* Tablet input is in device units, output is supposed to be in + * logical pixels roughly equivalent to a mouse/touchpad. * * This is a magical constant found by trial and error. On a 96dpi * screen 0.4mm of movement correspond to 1px logical pixel which @@ -1130,7 +1225,7 @@ tablet_accelerator_filter_flat_pen(struct tablet_accelerator_flat *filter, static struct normalized_coords tablet_accelerator_filter_flat(struct motion_filter *filter, - const struct normalized_coords *units, + const struct device_float_coords *units, void *data, uint64_t time) { struct tablet_accelerator_flat *accel_filter = diff --git a/src/filter.h b/src/filter.h index c1b43a5d..e24c20d4 100644 --- a/src/filter.h +++ b/src/filter.h @@ -41,11 +41,24 @@ struct motion_filter; * * This is a superset of filter_dispatch_constant() * + * @param filter The device's motion filter + * @param unaccelerated The unaccelerated delta in the device's dpi + * resolution as specified during filter creation. If a device has uneven + * resolution for x and y, one axis needs to be scaled to match the + * originally provided resolution. + * @param data Custom data + * @param time The time of the delta + * + * @return A set of normalized coordinates that can be used for pixel + * movement. The normalized coordiantes are scaled to the default dpi range, + * i.e. regardless of the resolution of the underlying device, the returned + * values always reflect a 1000dpi mouse. + * * @see filter_dispatch_constant */ struct normalized_coords filter_dispatch(struct motion_filter *filter, - const struct normalized_coords *unaccelerated, + const struct device_float_coords *unaccelerated, void *data, uint64_t time); /** @@ -54,11 +67,19 @@ filter_dispatch(struct motion_filter *filter, * Takes a set of unaccelerated deltas and applies any constant filters to * it but does not accelerate the delta in the conventional sense. * + * @param filter The device's motion filter + * @param unaccelerated The unaccelerated delta in the device's dpi + * resolution as specified during filter creation. If a device has uneven + * resolution for x and y, one axis needs to be scaled to match the + * originally provided resolution. + * @param data Custom data + * @param time The time of the delta + * * @see filter_dispatch */ struct normalized_coords filter_dispatch_constant(struct motion_filter *filter, - const struct normalized_coords *unaccelerated, + const struct device_float_coords *unaccelerated, void *data, uint64_t time); void diff --git a/src/libinput-private.h b/src/libinput-private.h index f80efdc9..0c75b3aa 100644 --- a/src/libinput-private.h +++ b/src/libinput-private.h @@ -676,6 +676,12 @@ device_float_average(struct device_float_coords a, struct device_float_coords b) return average; } +static inline bool +device_float_is_zero(struct device_float_coords coords) +{ + return coords.x == 0.0 && coords.y == 0.0; +} + static inline double normalized_length(struct normalized_coords norm) { diff --git a/tools/ptraccel-debug.c b/tools/ptraccel-debug.c index 84a1221c..4ecb7e89 100644 --- a/tools/ptraccel-debug.c +++ b/tools/ptraccel-debug.c @@ -38,7 +38,8 @@ static void print_ptraccel_deltas(struct motion_filter *filter, double step) { - struct normalized_coords motion; + struct device_float_coords motion; + struct normalized_coords accel; uint64_t time = 0; double i; @@ -55,9 +56,9 @@ print_ptraccel_deltas(struct motion_filter *filter, double step) motion.y = 0; time += us(12500); /* pretend 80Hz data */ - motion = filter_dispatch(filter, &motion, NULL, time); + accel = filter_dispatch(filter, &motion, NULL, time); - printf("%.2f %.3f\n", i, motion.x); + printf("%.2f %.3f\n", i, accel.x); } } @@ -67,7 +68,8 @@ print_ptraccel_movement(struct motion_filter *filter, double max_dx, double step) { - struct normalized_coords motion; + struct device_float_coords motion; + struct normalized_coords accel; uint64_t time = 0; double dx; int i; @@ -98,9 +100,9 @@ print_ptraccel_movement(struct motion_filter *filter, motion.y = 0; time += us(12500); /* pretend 80Hz data */ - motion = filter_dispatch(filter, &motion, NULL, time); + accel = filter_dispatch(filter, &motion, NULL, time); - printf("%d %.3f %.3f\n", i, motion.x, dx); + printf("%d %.3f %.3f\n", i, accel.x, dx); if (dx < max_dx) dx += step; @@ -112,7 +114,8 @@ print_ptraccel_sequence(struct motion_filter *filter, int nevents, double *deltas) { - struct normalized_coords motion; + struct device_float_coords motion; + struct normalized_coords accel; uint64_t time = 0; double *dx; int i; @@ -132,9 +135,9 @@ print_ptraccel_sequence(struct motion_filter *filter, motion.y = 0; time += us(12500); /* pretend 80Hz data */ - motion = filter_dispatch(filter, &motion, NULL, time); + accel = filter_dispatch(filter, &motion, NULL, time); - printf("%d %.3f %.3f\n", i, motion.x, *dx); + printf("%d %.3f %.3f\n", i, accel.x, *dx); } } From 91d287228f15f73718c59abbd02bb2bab9ea6e6b Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 15 Dec 2016 11:26:52 +1000 Subject: [PATCH 07/13] filter: drop the now-generic trackpoint and low-dpi filter functions The profile is what is still special about those two, the filter itself does the same as the default filter (calculate velocity, calculate accel factor, apply to delta). Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/filter.c | 80 +++++----------------------------------------------- 1 file changed, 7 insertions(+), 73 deletions(-) diff --git a/src/filter.c b/src/filter.c index 5da81368..abbfb48e 100644 --- a/src/filter.c +++ b/src/filter.c @@ -403,9 +403,9 @@ calculate_acceleration_factor(struct pointer_accelerator *accel, * motion */ static struct normalized_coords -accelerator_filter(struct motion_filter *filter, - const struct device_float_coords *unaccelerated, - void *data, uint64_t time) +accelerator_filter_generic(struct motion_filter *filter, + const struct device_float_coords *unaccelerated, + void *data, uint64_t time) { struct pointer_accelerator *accel = (struct pointer_accelerator *) filter; @@ -446,72 +446,6 @@ accelerator_filter_noop(struct motion_filter *filter, return normalize_for_dpi(unaccelerated, accel->dpi); } -/** - * Low-dpi filter that handles events from devices with less than the - * default dpi. - * - * @param filter 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 An accelerated tuple of coordinates representing normalized - * motion - */ -static struct normalized_coords -accelerator_filter_low_dpi(struct motion_filter *filter, - const struct device_float_coords *unaccelerated, - void *data, uint64_t time) -{ - struct pointer_accelerator *accel = - (struct pointer_accelerator *) filter; - double accel_value; /* unitless factor */ - struct normalized_coords accelerated; - - /* Input is already in device-native DPI, nothing else needed */ - accel_value = calculate_acceleration_factor(accel, - unaccelerated, - data, - time); - accelerated.x = accel_value * unaccelerated->x; - accelerated.y = accel_value * unaccelerated->y; - - return accelerated; -} - -/** - * Custom filter that applies the trackpoint's constant acceleration, if any. - * - * @param filter 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 An accelerated tuple of coordinates representing normalized - * motion - */ -static struct normalized_coords -accelerator_filter_trackpoint(struct motion_filter *filter, - const struct device_float_coords *unaccelerated, - void *data, uint64_t time) -{ - struct pointer_accelerator *accel = - (struct pointer_accelerator *) filter; - double accel_value; /* unitless factor */ - struct normalized_coords accelerated; - - /* Nothing special to do here, data is already in device dpi */ - accel_value = calculate_acceleration_factor(accel, - unaccelerated, - data, - time); - - accelerated.x = accel_value * unaccelerated->x; - accelerated.y = accel_value * unaccelerated->y; - - return accelerated; -} - static struct normalized_coords accelerator_filter_x230(struct motion_filter *filter, const struct device_float_coords *raw, @@ -943,7 +877,7 @@ trackpoint_accel_profile(struct motion_filter *filter, struct motion_filter_interface accelerator_interface = { .type = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE, - .filter = accelerator_filter, + .filter = accelerator_filter_generic, .filter_constant = accelerator_filter_noop, .restart = accelerator_restart, .destroy = accelerator_destroy, @@ -990,7 +924,7 @@ create_pointer_accelerator_filter_linear(int dpi) struct motion_filter_interface accelerator_interface_low_dpi = { .type = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE, - .filter = accelerator_filter_low_dpi, + .filter = accelerator_filter_generic, .filter_constant = accelerator_filter_noop, .restart = accelerator_restart, .destroy = accelerator_destroy, @@ -1014,7 +948,7 @@ create_pointer_accelerator_filter_linear_low_dpi(int dpi) struct motion_filter_interface accelerator_interface_touchpad = { .type = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE, - .filter = accelerator_filter, + .filter = accelerator_filter_generic, .filter_constant = touchpad_constant_filter, .restart = accelerator_restart, .destroy = accelerator_destroy, @@ -1076,7 +1010,7 @@ create_pointer_accelerator_filter_lenovo_x230(int dpi) struct motion_filter_interface accelerator_interface_trackpoint = { .type = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE, - .filter = accelerator_filter_trackpoint, + .filter = accelerator_filter_generic, .filter_constant = accelerator_filter_noop, .restart = accelerator_restart, .destroy = accelerator_destroy, From 4c4b5de641e86456be3c7c4cb519b252139bc48c Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 15 Dec 2016 14:15:22 +1000 Subject: [PATCH 08/13] filter: change touchpad accel code to use mm/s That's something human brains can map to because mapping a touchpad to equivalent units of a 1000dpi mouse requires a lot of mental acrobatics. And I'm getting older and my physio told me acrobatics is more something for the youngens, possibly those on my lawn listening to terrible music, etc. The various numbers are converted either times 25.4/1000 or times 1000/25.4, depending on the usage. Somewhere I made a mistake or a rounding error or something, so the acceleration curve is not exactly the same, but it's close enough that it shouldn't matter. The difference shows up in a gnuplot of the curve but it may not even perceivable anyway. And these values will be overhauled soon anyway, so meh. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/filter.c | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/src/filter.c b/src/filter.c index abbfb48e..f4baa997 100644 --- a/src/filter.c +++ b/src/filter.c @@ -49,6 +49,12 @@ v_us2ms(double units_per_us) return units_per_us * 1000.0; } +static inline double +v_us2s(double units_per_us) +{ + return units_per_us * 1000000.0; +} + /* Convert speed/velocity from units/ms to units/us */ static inline double v_ms2us(double units_per_ms) @@ -129,10 +135,10 @@ filter_get_type(struct motion_filter *filter) #define DEFAULT_INCLINE 1.1 /* unitless factor */ /* Touchpad acceleration */ -#define TOUCHPAD_DEFAULT_THRESHOLD v_ms2us(0.4) -#define TOUCHPAD_MINIMUM_THRESHOLD v_ms2us(0.2) +#define TOUCHPAD_DEFAULT_THRESHOLD 15.75 /* mm/s */ +#define TOUCHPAD_MINIMUM_THRESHOLD 7.87 /* mm/s */ #define TOUCHPAD_ACCELERATION 2.0 /* unitless factor */ -#define TOUCHPAD_INCLINE 1.1 /* unitless factor */ +#define TOUCHPAD_INCLINE 0.02794 /* unitless factor */ /* for the Lenovo x230 custom accel. do not touch */ #define X230_THRESHOLD v_ms2us(0.4) /* in units/us */ @@ -740,8 +746,8 @@ touchpad_accel_profile_linear(struct motion_filter *filter, const double incline = accel_filter->incline; double factor; /* unitless */ - /* Normalize to 1000dpi, because the rest below relies on that */ - speed_in = speed_in * DEFAULT_MOUSE_DPI/accel_filter->dpi; + /* Convert to mm/s because that's something one can understand */ + speed_in = v_us2s(speed_in) * 25.4/accel_filter->dpi; speed_in *= TP_MAGIC_SLOWDOWN; @@ -766,21 +772,22 @@ touchpad_accel_profile_linear(struct motion_filter *filter, a is the incline of acceleration b is minimum acceleration factor - for speeds up to 0.07 u/ms, we decelerate, down to 30% of input - speed. - hence 1 = a * 0.07 + 0.3 - 0.3 = a * 0.07 => a := 10 + for speeds up to the lower threshold, we decelerate, down to 30% + of input speed. + hence 1 = a * 2.756 + 0.3 + 0.7 = a * 2.756 => a := 0.254 deceleration function is thus: - y = 10x + 0.3 + y = 0.254x + 0.3 Note: - * 0.07u/ms as threshold is a result of trial-and-error and + * The minimum threshold is a result of trial-and-error and has no other intrinsic meaning. * 0.3 is chosen simply because it is above the Nyquist frequency for subpixel motion within a pixel. */ - if (v_us2ms(speed_in) < 0.07) { - factor = 10 * v_us2ms(speed_in) + 0.3; + if (speed_in < 2.756) { + const double incline = 0.254; + factor = incline * speed_in + 0.3; /* up to the threshold, we keep factor 1, i.e. 1:1 movement */ } else if (speed_in < threshold) { factor = 1; @@ -795,7 +802,7 @@ touchpad_accel_profile_linear(struct motion_filter *filter, hence 1 = ax' + 1 => x' := (x - T) */ - factor = incline * v_us2ms(speed_in - threshold) + 1; + factor = incline * (speed_in - threshold) + 1; } /* Cap at the maximum acceleration factor */ From 84ab969c2656e4debdc71b1158e889677059f817 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 15 Dec 2016 16:23:57 +1000 Subject: [PATCH 09/13] filter: work the touchpad magic slowdown into the various parameters We have everything separate from the mouse now, so having a magic slowdown isn't needed, we can work this into our parameters. So the acceleration function now uses everything adjusted, but the factor is still multiplied by the slowdown in the end. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/filter.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/filter.c b/src/filter.c index f4baa997..7865c7ec 100644 --- a/src/filter.c +++ b/src/filter.c @@ -135,10 +135,10 @@ filter_get_type(struct motion_filter *filter) #define DEFAULT_INCLINE 1.1 /* unitless factor */ /* Touchpad acceleration */ -#define TOUCHPAD_DEFAULT_THRESHOLD 15.75 /* mm/s */ -#define TOUCHPAD_MINIMUM_THRESHOLD 7.87 /* mm/s */ +#define TOUCHPAD_DEFAULT_THRESHOLD 40 /* mm/s */ +#define TOUCHPAD_MINIMUM_THRESHOLD 20 /* mm/s */ #define TOUCHPAD_ACCELERATION 2.0 /* unitless factor */ -#define TOUCHPAD_INCLINE 0.02794 /* 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 */ @@ -749,8 +749,6 @@ touchpad_accel_profile_linear(struct motion_filter *filter, /* Convert to mm/s because that's something one can understand */ speed_in = v_us2s(speed_in) * 25.4/accel_filter->dpi; - speed_in *= TP_MAGIC_SLOWDOWN; - /* Our acceleration function calculates a factor to accelerate input deltas with. The function is a double incline with a plateau, @@ -774,10 +772,10 @@ touchpad_accel_profile_linear(struct motion_filter *filter, for speeds up to the lower threshold, we decelerate, down to 30% of input speed. - hence 1 = a * 2.756 + 0.3 - 0.7 = a * 2.756 => a := 0.254 + hence 1 = a * 7 + 0.3 + 0.7 = a * 7 => a := 0.1 deceleration function is thus: - y = 0.254x + 0.3 + y = 0.1x + 0.3 Note: * The minimum threshold is a result of trial-and-error and @@ -785,9 +783,8 @@ touchpad_accel_profile_linear(struct motion_filter *filter, * 0.3 is chosen simply because it is above the Nyquist frequency for subpixel motion within a pixel. */ - if (speed_in < 2.756) { - const double incline = 0.254; - factor = incline * speed_in + 0.3; + if (speed_in < 7.0) { + factor = 0.1 * speed_in + 0.3; /* up to the threshold, we keep factor 1, i.e. 1:1 movement */ } else if (speed_in < threshold) { factor = 1; From ec63ecd48599277ca9c30d6f906a96c5fdfd6a01 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 16 Dec 2016 08:22:54 +1000 Subject: [PATCH 10/13] tools: switch the ptraccel-debug printf to use mm/s Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- tools/ptraccel-debug.c | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/tools/ptraccel-debug.c b/tools/ptraccel-debug.c index 4ecb7e89..4fcb45f4 100644 --- a/tools/ptraccel-debug.c +++ b/tools/ptraccel-debug.c @@ -141,19 +141,31 @@ print_ptraccel_sequence(struct motion_filter *filter, } } -static void -print_accel_func(struct motion_filter *filter, accel_profile_func_t profile) +/* mm/s → units/µs */ +static inline double +mmps_to_upus(double mmps, int dpi) { - double vel; + return mmps * (dpi/25.4) / 1e6; +} + +static void +print_accel_func(struct motion_filter *filter, + accel_profile_func_t profile, + int dpi) +{ + double mmps; printf("# gnuplot:\n"); - printf("# set xlabel \"speed\"\n"); + printf("# set xlabel \"speed (mm/s)\"\n"); printf("# set ylabel \"raw accel factor\"\n"); printf("# set style data lines\n"); - printf("# plot \"gnuplot.data\" using 1:2\n"); - for (vel = 0.0; vel < 0.004; vel += 0.0000001) { - double result = profile(filter, NULL, vel, 0 /* time */); - printf("%.8f\t%.4f\n", vel, result); + printf("# plot \"gnuplot.data\" using 1:2 title 'accel factor'\n"); + printf("#\n"); + printf("# data: velocity(mm/s) factor velocity(units/us)\n"); + for (mmps = 0.0; mmps < 300.0; mmps += 1) { + double units_per_us = mmps_to_upus(mmps, dpi); + double result = profile(filter, NULL, units_per_us, 0 /* time */); + printf("%.8f\t%.4f\t%.8f\n", mmps, result, units_per_us); } } @@ -338,7 +350,7 @@ main(int argc, char **argv) } if (print_accel) - print_accel_func(filter, profile); + print_accel_func(filter, profile, dpi); else if (print_delta) print_ptraccel_deltas(filter, step); else if (print_motion) From ff31427e802897415b520aa6474d32f20978224c Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 14 Dec 2016 19:40:18 +1000 Subject: [PATCH 11/13] filter: revamp the touchpad's acceleration code The previous code had three main issues: * acceleration kicked in too early, so even slow movements were accelerated * acceleration kicked in too quickly, there was only a very narrow window where we would have less than the max acceleration factor * the max accel factor was too low for fast movements, so they still fell short of expectations This patch revamps most of the acceleration though it keeps the basic shape of the acceleration curve. * The threshold is increased significantly so that faster movement still map to the finger movement. Acceleration doesn't kick in until we get to something that's really fast like a flick. * The incline is dropped, so acceleration kicks in slower than before, i.e. the difference between the first speed that is accelerated and the speed that reaches the maximum is higher than before. * The maximum acceleration is increased so ever faster movements get ever faster. The max is effectively out of reach now, if you move fast enough to hit this speed, your cursor will end up on the moon anyway. A couple of other changes apply now too, specifically: * The incline remains the same regardless of the speed * The max accel factor remains the same regardless of the speed The caculated factor changes with the speed set so that the base speed changes with the desired speed. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/filter.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/filter.c b/src/filter.c index 7865c7ec..ab503cff 100644 --- a/src/filter.c +++ b/src/filter.c @@ -135,9 +135,9 @@ filter_get_type(struct motion_filter *filter) #define DEFAULT_INCLINE 1.1 /* unitless factor */ /* Touchpad acceleration */ -#define TOUCHPAD_DEFAULT_THRESHOLD 40 /* mm/s */ -#define TOUCHPAD_MINIMUM_THRESHOLD 20 /* mm/s */ -#define TOUCHPAD_ACCELERATION 2.0 /* unitless factor */ +#define TOUCHPAD_DEFAULT_THRESHOLD 254 /* mm/s */ +#define TOUCHPAD_THRESHOLD_RANGE 184 /* mm/s */ +#define TOUCHPAD_ACCELERATION 9.0 /* unitless factor */ #define TOUCHPAD_INCLINE 0.011 /* unitless factor */ /* for the Lenovo x230 custom accel. do not touch */ @@ -521,19 +521,13 @@ touchpad_accelerator_set_speed(struct motion_filter *filter, /* 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 */ + /* adjust when accel kicks in */ accel_filter->threshold = TOUCHPAD_DEFAULT_THRESHOLD - - v_ms2us(0.25) * speed_adjustment; - if (accel_filter->threshold < TOUCHPAD_MINIMUM_THRESHOLD) - accel_filter->threshold = TOUCHPAD_MINIMUM_THRESHOLD; - - /* adjust max accel factor */ - accel_filter->accel = TOUCHPAD_ACCELERATION + speed_adjustment * 1.5; - - /* higher speed -> faster to reach max */ - accel_filter->incline = TOUCHPAD_INCLINE + speed_adjustment * 0.75; - + TOUCHPAD_THRESHOLD_RANGE * speed_adjustment; + accel_filter->accel = TOUCHPAD_ACCELERATION; + accel_filter->incline = TOUCHPAD_INCLINE; filter->speed_adjustment = speed_adjustment; + return true; } @@ -805,6 +799,9 @@ touchpad_accel_profile_linear(struct motion_filter *filter, /* Cap at the maximum acceleration factor */ factor = min(max_accel, factor); + /* Scale everything depending on the acceleration set */ + factor *= 1 + 0.5 * filter->speed_adjustment; + return factor * TP_MAGIC_SLOWDOWN; } From 7e5062f63224d4c73de263db6d1ff2c02d2a2398 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 19 Dec 2016 12:18:42 +1000 Subject: [PATCH 12/13] doc: update the touchpad pointer acceleration svg Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- doc/pointer-acceleration.dox | 9 +- doc/svg/ptraccel-touchpad.svg | 1981 +++++++-------------------------- tools/make-ptraccel-graphs.sh | 16 +- 3 files changed, 391 insertions(+), 1615 deletions(-) diff --git a/doc/pointer-acceleration.dox b/doc/pointer-acceleration.dox index 502bc7b5..f6237c6b 100644 --- a/doc/pointer-acceleration.dox +++ b/doc/pointer-acceleration.dox @@ -96,10 +96,11 @@ applied sooner and with a stronger acceleration factor. @section ptraccel-touchpad Pointer acceleration on touchpads -Touchpad pointer acceleration uses the @ref ptraccel-linear profile, with a -constant deceleration factor applied. The user expectation of how much a -pointer should move in response to finger movement is different to that of a -mouse device, hence the constant deceleration factor. +Touchpad pointer acceleration uses the same approach as the @ref +ptraccel-linear profile, with a constant deceleration factor applied. The +user expectation of how much a pointer should move in response to finger +movement is different to that of a mouse device, hence the constant +deceleration factor. @image html ptraccel-touchpad.svg "Pointer acceleration curve for touchpads" diff --git a/doc/svg/ptraccel-touchpad.svg b/doc/svg/ptraccel-touchpad.svg index 1befa1e5..b6fa4658 100644 --- a/doc/svg/ptraccel-touchpad.svg +++ b/doc/svg/ptraccel-touchpad.svg @@ -9,7 +9,7 @@ > Gnuplot -Produced by GNUPLOT 5.0 patchlevel 0 +Produced by GNUPLOT 5.0 patchlevel 5 @@ -44,73 +44,103 @@ - + 0 - - 0.5 + + 0.2 - + + 0.4 + + + + + 0.6 + + + + + 0.8 + + + + 1 - - 1.5 + + 1.2 - + + 1.4 + + + + + 1.6 + + + + + 1.8 + + + + 2 - - 2.5 - - - - - 3 - - - - + 0 - - 0.0005 + + 50 - - 0.001 + + 100 - - 0.0015 + + 150 - - 0.002 + + 200 - - 0.0025 + + 250 - - 0.003 + + 300 + + + + + 350 + + + + + 400 @@ -118,1597 +148,340 @@ - + accel factor - - speed in units/us + + speed in mm/s - linear (mouse) + -1 - - linear (mouse) + + -1 - + - touchpad + -0.5 - - touchpad + + -0.5 - + - + -0.2 + + + -0.2 + + + + + + 0 + + + 0 + + + + + + 0.2 + + + 0.2 + + + + + + 0.5 + + + 0.5 + + + + + + 1 + + + 1 + + + + + + diff --git a/tools/make-ptraccel-graphs.sh b/tools/make-ptraccel-graphs.sh index 901baf9c..870ea925 100755 --- a/tools/make-ptraccel-graphs.sh +++ b/tools/make-ptraccel-graphs.sh @@ -44,18 +44,20 @@ plot "$outfile-200.gnuplot" using 1:2 title "200dpi", \ EOF outfile="ptraccel-touchpad" -$tool --mode=accel --dpi=1000 --filter=linear > $outfile-mouse.gnuplot -$tool --mode=accel --dpi=1000 --filter=touchpad > $outfile-touchpad.gnuplot $gnuplot < $outfile-$speed.gnuplot + echo "\"$outfile-$speed.gnuplot\" using 1:2 title '$speed', \\" + done +) EOF outfile="ptraccel-trackpoint" From f4ff619cf709406842d6256dcf44bb14f496165e Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 14 Dec 2016 19:51:13 +1000 Subject: [PATCH 13/13] filter: tweak the magic slowdown Could be confirmation bias, but it feels better. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/filter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/filter.c b/src/filter.c index ab503cff..d7a15158 100644 --- a/src/filter.c +++ b/src/filter.c @@ -40,7 +40,7 @@ * technically correct but subjectively wrong, we expect a touchpad to be a * lot slower than a mouse. Apply a magic factor to slow down all movements */ -#define TP_MAGIC_SLOWDOWN 0.4 /* unitless factor */ +#define TP_MAGIC_SLOWDOWN 0.37 /* unitless factor */ /* Convert speed/velocity from units/us to units/ms */ static inline double