From cbff01daf1c128b6fce395210737d667d0d8f631 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 4 Aug 2015 15:45:53 +1000 Subject: [PATCH 01/16] Revert "filter: move the pointer acceleration profiles back to units/ms" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 8a6825f1602aa9d9c4b29a83d296f55f68b316e0. Aside from introducing bugs, this doesn't really help with anything, it adds a requirement to rename everything to make clear where we're using µs and where we're using ms and that just clutters up the code. Signed-off-by: Peter Hutterer Acked-by: Jonas Ådahl --- src/filter.c | 54 +++++++++++++++++++++--------------------- tools/ptraccel-debug.c | 4 ++-- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/filter.c b/src/filter.c index e11d58a9..b01b68ab 100644 --- a/src/filter.c +++ b/src/filter.c @@ -77,8 +77,8 @@ filter_get_speed(struct motion_filter *filter) * Default parameters for pointer acceleration profiles. */ -#define DEFAULT_THRESHOLD 0.4 /* in units/ms */ -#define MINIMUM_THRESHOLD 0.2 /* in units/ms */ +#define DEFAULT_THRESHOLD 0.0004 /* in units/us */ +#define MINIMUM_THRESHOLD 0.0002 /* in units/us */ #define DEFAULT_ACCELERATION 2.0 /* unitless factor */ #define DEFAULT_INCLINE 1.1 /* unitless factor */ @@ -86,7 +86,7 @@ filter_get_speed(struct motion_filter *filter) * Pointer acceleration filter constants */ -#define MAX_VELOCITY_DIFF 1 /* units/ms */ +#define MAX_VELOCITY_DIFF 0.001 /* units/us */ #define MOTION_TIMEOUT ms2us(1000) #define NUM_POINTER_TRACKERS 16 @@ -102,14 +102,14 @@ struct pointer_accelerator { accel_profile_func_t profile; - double velocity; /* units/ms */ - double last_velocity; /* units/ms */ + double velocity; /* units/us */ + double last_velocity; /* units/us */ struct normalized_coords last; struct pointer_tracker *trackers; int cur_tracker; - double threshold; /* units/ms */ + double threshold; /* units/us */ double accel; /* unitless factor */ double incline; /* incline of the function */ @@ -151,7 +151,7 @@ static double calculate_tracker_velocity(struct pointer_tracker *tracker, uint64_t time) { double tdelta = time - tracker->time + 1; - return normalized_length(tracker->delta) / tdelta * 1000.0; /* units/ms */ + return normalized_length(tracker->delta) / tdelta; /* units/us */ } static inline double @@ -221,7 +221,7 @@ calculate_velocity(struct pointer_accelerator *accel, uint64_t time) } } - return result; /* units/ms */ + return result; /* units/us */ } static double @@ -257,11 +257,11 @@ calculate_acceleration(struct pointer_accelerator *accel, static struct normalized_coords accelerator_filter(struct motion_filter *filter, const struct normalized_coords *unaccelerated, - void *data, uint64_t time /* in us */) + void *data, uint64_t time) { struct pointer_accelerator *accel = (struct pointer_accelerator *) filter; - double velocity; /* units/ms */ + double velocity; /* units/us */ double accel_value; /* unitless factor */ struct normalized_coords accelerated; struct normalized_coords unnormalized; @@ -334,7 +334,7 @@ accelerator_set_speed(struct motion_filter *filter, assert(speed >= -1.0 && speed <= 1.0); /* delay when accel kicks in */ - accel_filter->threshold = DEFAULT_THRESHOLD - speed / 4.0; + accel_filter->threshold = DEFAULT_THRESHOLD - speed / 4000.0; if (accel_filter->threshold < MINIMUM_THRESHOLD) accel_filter->threshold = MINIMUM_THRESHOLD; @@ -398,23 +398,23 @@ create_pointer_accelerator_filter(accel_profile_func_t profile, double pointer_accel_profile_linear_low_dpi(struct motion_filter *filter, void *data, - double speed_in, /* in device units (units/ms) */ - uint64_t time /* in us */) + double speed_in, /* in device units (units/us) */ + uint64_t time) { struct pointer_accelerator *accel_filter = (struct pointer_accelerator *)filter; double s1, s2; double max_accel = accel_filter->accel; /* unitless factor */ - const double threshold = accel_filter->threshold; /* units/ms */ + const double threshold = accel_filter->threshold; /* units/us */ const double incline = accel_filter->incline; double factor; double dpi_factor = accel_filter->dpi_factor; max_accel /= dpi_factor; - s1 = min(1, 0.3 + speed_in * 10.0); - s2 = 1 + (speed_in - threshold * dpi_factor) * incline; + s1 = min(1, 0.3 + speed_in * 10000.0); + s2 = 1 + (speed_in * 1000.0 - threshold * dpi_factor * 1000.0) * incline; factor = min(max_accel, s2 > 1 ? s2 : s1); @@ -425,19 +425,19 @@ double pointer_accel_profile_linear(struct motion_filter *filter, void *data, double speed_in, /* 1000-dpi normalized */ - uint64_t time /* in us */) + uint64_t time) { struct pointer_accelerator *accel_filter = (struct pointer_accelerator *)filter; double s1, s2; const double max_accel = accel_filter->accel; /* unitless factor */ - const double threshold = accel_filter->threshold; /* units/ms */ + const double threshold = accel_filter->threshold; /* units/us */ const double incline = accel_filter->incline; double factor; - s1 = min(1, 0.3 + speed_in * 10); - s2 = 1 + (speed_in - threshold) * incline; + s1 = min(1, 0.3 + speed_in * 10 * 1000.0); + s2 = 1 + (speed_in * 1000.0 - threshold * 1000.0) * incline; factor = min(max_accel, s2 > 1 ? s2 : s1); @@ -446,9 +446,9 @@ pointer_accel_profile_linear(struct motion_filter *filter, double touchpad_accel_profile_linear(struct motion_filter *filter, - void *data, - double speed_in, - uint64_t time /* in us */) + void *data, + double speed_in, + uint64_t time) { /* Once normalized, touchpads see the same acceleration as mice. that is technically correct but @@ -469,7 +469,7 @@ double touchpad_lenovo_x230_accel_profile(struct motion_filter *filter, void *data, double speed_in, - uint64_t time /* in us */) + uint64_t time) { /* Keep the magic factor from touchpad_accel_profile_linear. */ const double TP_MAGIC_SLOWDOWN = 0.4; @@ -489,13 +489,13 @@ touchpad_lenovo_x230_accel_profile(struct motion_filter *filter, const double max_accel = accel_filter->accel * TP_MAGIC_LOW_RES_FACTOR; /* unitless factor */ const double threshold = accel_filter->threshold / - TP_MAGIC_LOW_RES_FACTOR; /* units/ms */ + TP_MAGIC_LOW_RES_FACTOR; /* units/us */ const double incline = accel_filter->incline * TP_MAGIC_LOW_RES_FACTOR; speed_in *= TP_MAGIC_SLOWDOWN / TP_MAGIC_LOW_RES_FACTOR; - s1 = min(1, speed_in * 5); - s2 = 1 + (speed_in - threshold) * incline; + s1 = min(1, speed_in * 5 * 1000.0); + s2 = 1 + (speed_in * 1000.0 - threshold * 1000.0) * incline; speed_out = min(max_accel, s2 > 1 ? s2 : s1); diff --git a/tools/ptraccel-debug.c b/tools/ptraccel-debug.c index 1496763b..b0867db3 100644 --- a/tools/ptraccel-debug.c +++ b/tools/ptraccel-debug.c @@ -147,12 +147,12 @@ print_accel_func(struct motion_filter *filter) 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 < 3.0; vel += .0001) { + for (vel = 0.0; vel < 0.003; vel += 0.0000001) { double result = pointer_accel_profile_linear(filter, NULL, vel, 0 /* time */); - printf("%.4f\t%.4f\n", vel, result); + printf("%.8f\t%.4f\n", vel, result); } } From c0a1d22fead992a2fa7fe7b6f9bf35de118052e9 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 4 Aug 2015 15:47:39 +1000 Subject: [PATCH 02/16] filter: drop superfluous struct declaration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Peter Hutterer Reviewed-by: Jonas Ådahl --- src/filter.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/filter.c b/src/filter.c index b01b68ab..e615deb1 100644 --- a/src/filter.c +++ b/src/filter.c @@ -96,7 +96,6 @@ struct pointer_tracker { int dir; }; -struct pointer_accelerator; struct pointer_accelerator { struct motion_filter base; From b750c7d69fb45f755390e46c5e82637cdb34ff37 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 4 Aug 2015 15:48:40 +1000 Subject: [PATCH 03/16] filter: rename speed to speed_adjustment where it's in the [-1,1] range To avoid confusion with the other speed in units/time Signed-off-by: Peter Hutterer --- src/filter-private.h | 4 ++-- src/filter.c | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/filter-private.h b/src/filter-private.h index 0e7afa1f..f5e8b7f7 100644 --- a/src/filter-private.h +++ b/src/filter-private.h @@ -38,11 +38,11 @@ struct motion_filter_interface { uint64_t time); void (*destroy)(struct motion_filter *filter); bool (*set_speed)(struct motion_filter *filter, - double speed); + double speed_adjustment); }; struct motion_filter { - double speed; /* normalized [-1, 1] */ + double speed_adjustment; /* normalized [-1, 1] */ struct motion_filter_interface *interface; }; diff --git a/src/filter.c b/src/filter.c index e615deb1..400c52a8 100644 --- a/src/filter.c +++ b/src/filter.c @@ -62,15 +62,15 @@ filter_destroy(struct motion_filter *filter) bool filter_set_speed(struct motion_filter *filter, - double speed) + double speed_adjustment) { - return filter->interface->set_speed(filter, speed); + return filter->interface->set_speed(filter, speed_adjustment); } double filter_get_speed(struct motion_filter *filter) { - return filter->speed; + return filter->speed_adjustment; } /* @@ -325,25 +325,25 @@ accelerator_destroy(struct motion_filter *filter) static bool accelerator_set_speed(struct motion_filter *filter, - double speed) + double speed_adjustment) { struct pointer_accelerator *accel_filter = (struct pointer_accelerator *)filter; - assert(speed >= -1.0 && speed <= 1.0); + assert(speed_adjustment >= -1.0 && speed_adjustment <= 1.0); /* delay when accel kicks in */ - accel_filter->threshold = DEFAULT_THRESHOLD - speed / 4000.0; + accel_filter->threshold = DEFAULT_THRESHOLD - speed_adjustment / 4000.0; if (accel_filter->threshold < MINIMUM_THRESHOLD) accel_filter->threshold = MINIMUM_THRESHOLD; /* adjust max accel factor */ - accel_filter->accel = DEFAULT_ACCELERATION + speed * 1.5; + accel_filter->accel = DEFAULT_ACCELERATION + speed_adjustment * 1.5; /* higher speed -> faster to reach max */ - accel_filter->incline = DEFAULT_INCLINE + speed * 0.75; + accel_filter->incline = DEFAULT_INCLINE + speed_adjustment * 0.75; - filter->speed = speed; + filter->speed_adjustment = speed_adjustment; return true; } From 7db8884ff5e5d41d06cd374c218ef18223587b24 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 4 Aug 2015 16:01:20 +1000 Subject: [PATCH 04/16] filter: rename speed_out to "factor" for the touchpad profiles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The return value of a profile is a unitless factor, not a speed. Same applies for s1/s2, these are factors, not speeds. Signed-off-by: Peter Hutterer Reviewed-by: Jonas Ådahl --- src/filter.c | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/filter.c b/src/filter.c index 400c52a8..9aaf1f42 100644 --- a/src/filter.c +++ b/src/filter.c @@ -403,19 +403,19 @@ pointer_accel_profile_linear_low_dpi(struct motion_filter *filter, struct pointer_accelerator *accel_filter = (struct pointer_accelerator *)filter; - double s1, s2; + double f1, f2; /* unitless */ double max_accel = accel_filter->accel; /* unitless factor */ const double threshold = accel_filter->threshold; /* units/us */ const double incline = accel_filter->incline; - double factor; + double factor; /* unitless */ double dpi_factor = accel_filter->dpi_factor; max_accel /= dpi_factor; - s1 = min(1, 0.3 + speed_in * 10000.0); - s2 = 1 + (speed_in * 1000.0 - threshold * dpi_factor * 1000.0) * incline; + f1 = min(1, 0.3 + speed_in * 10000.0); + f2 = 1 + (speed_in * 1000.0 - threshold * dpi_factor * 1000.0) * incline; - factor = min(max_accel, s2 > 1 ? s2 : s1); + factor = min(max_accel, f2 > 1 ? f2 : f1); return factor; } @@ -429,16 +429,16 @@ pointer_accel_profile_linear(struct motion_filter *filter, struct pointer_accelerator *accel_filter = (struct pointer_accelerator *)filter; - double s1, s2; + double f1, f2; /* unitless */ 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; + double factor; /* unitless */ - s1 = min(1, 0.3 + speed_in * 10 * 1000.0); - s2 = 1 + (speed_in * 1000.0 - threshold * 1000.0) * incline; + f1 = min(1, 0.3 + speed_in * 10 * 1000.0); + f2 = 1 + (speed_in * 1000.0 - threshold * 1000.0) * incline; - factor = min(max_accel, s2 > 1 ? s2 : s1); + factor = min(max_accel, f2 > 1 ? f2 : f1); return factor; } @@ -446,7 +446,7 @@ pointer_accel_profile_linear(struct motion_filter *filter, double touchpad_accel_profile_linear(struct motion_filter *filter, void *data, - double speed_in, + double speed_in, /* units/us */ uint64_t time) { /* Once normalized, touchpads see the same @@ -454,14 +454,14 @@ touchpad_accel_profile_linear(struct motion_filter *filter, subjectively wrong, we expect a touchpad to be a lot slower than a mouse. Apply a magic factor here and proceed as normal. */ - const double TP_MAGIC_SLOWDOWN = 0.4; - double speed_out; + const double TP_MAGIC_SLOWDOWN = 0.4; /* unitless */ + double factor; /* unitless */ speed_in *= TP_MAGIC_SLOWDOWN; - speed_out = pointer_accel_profile_linear(filter, data, speed_in, time); + factor = pointer_accel_profile_linear(filter, data, speed_in, time); - return speed_out * TP_MAGIC_SLOWDOWN; + return factor * TP_MAGIC_SLOWDOWN; } double @@ -471,7 +471,7 @@ touchpad_lenovo_x230_accel_profile(struct motion_filter *filter, uint64_t time) { /* Keep the magic factor from touchpad_accel_profile_linear. */ - const double TP_MAGIC_SLOWDOWN = 0.4; + const double TP_MAGIC_SLOWDOWN = 0.4; /* unitless */ /* Those touchpads presents an actual lower resolution that what is * advertised. We see some jumps from the cursor due to the big steps @@ -479,12 +479,12 @@ touchpad_lenovo_x230_accel_profile(struct motion_filter *filter, * 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 */ - const double TP_MAGIC_LOW_RES_FACTOR = 4.0; - double speed_out; + const double TP_MAGIC_LOW_RES_FACTOR = 4.0; /* unitless */ + double factor; /* unitless */ struct pointer_accelerator *accel_filter = (struct pointer_accelerator *)filter; - double s1, s2; + double f1, f2; /* unitless */ const double max_accel = accel_filter->accel * TP_MAGIC_LOW_RES_FACTOR; /* unitless factor */ const double threshold = accel_filter->threshold / @@ -493,10 +493,10 @@ touchpad_lenovo_x230_accel_profile(struct motion_filter *filter, speed_in *= TP_MAGIC_SLOWDOWN / TP_MAGIC_LOW_RES_FACTOR; - s1 = min(1, speed_in * 5 * 1000.0); - s2 = 1 + (speed_in * 1000.0 - threshold * 1000.0) * incline; + f1 = min(1, speed_in * 5 * 1000.0); + f2 = 1 + (speed_in * 1000.0 - threshold * 1000.0) * incline; - speed_out = min(max_accel, s2 > 1 ? s2 : s1); + factor = min(max_accel, f2 > 1 ? f2 : f1); - return speed_out * TP_MAGIC_SLOWDOWN / TP_MAGIC_LOW_RES_FACTOR; + return factor * TP_MAGIC_SLOWDOWN / TP_MAGIC_LOW_RES_FACTOR; } From 1cbcc641a06e49d676263093e088c5c25c47cb4d Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 4 Aug 2015 16:04:06 +1000 Subject: [PATCH 05/16] filter: add two helper functions to convert between speeds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This makes it more obvious where we're using units/us and units/ms as input variable and what the output is. Clutters up the code, but still better than dealing with us/ms differently per function, and still better than carrying all the 1000.0 multiplications/divisions manually. Signed-off-by: Peter Hutterer Reviewed-by: Jonas Ådahl --- src/filter.c | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/src/filter.c b/src/filter.c index 9aaf1f42..425bc795 100644 --- a/src/filter.c +++ b/src/filter.c @@ -36,6 +36,20 @@ #include "libinput-util.h" #include "filter-private.h" +/* Convert speed/velocity from units/us to units/ms */ +static inline double +v_us2ms(double units_per_us) +{ + return units_per_us * 1000.0; +} + +/* Convert speed/velocity from units/ms to units/us */ +static inline double +v_ms2us(double units_per_ms) +{ + return units_per_ms/1000.0; +} + struct normalized_coords filter_dispatch(struct motion_filter *filter, const struct normalized_coords *unaccelerated, @@ -77,8 +91,8 @@ filter_get_speed(struct motion_filter *filter) * Default parameters for pointer acceleration profiles. */ -#define DEFAULT_THRESHOLD 0.0004 /* in units/us */ -#define MINIMUM_THRESHOLD 0.0002 /* in units/us */ +#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 */ @@ -86,7 +100,7 @@ filter_get_speed(struct motion_filter *filter) * Pointer acceleration filter constants */ -#define MAX_VELOCITY_DIFF 0.001 /* units/us */ +#define MAX_VELOCITY_DIFF v_ms2us(1) /* units/us */ #define MOTION_TIMEOUT ms2us(1000) #define NUM_POINTER_TRACKERS 16 @@ -332,8 +346,12 @@ accelerator_set_speed(struct motion_filter *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 - speed_adjustment / 4000.0; + accel_filter->threshold = DEFAULT_THRESHOLD - + v_ms2us(0.25) * speed_adjustment; if (accel_filter->threshold < MINIMUM_THRESHOLD) accel_filter->threshold = MINIMUM_THRESHOLD; @@ -412,8 +430,8 @@ pointer_accel_profile_linear_low_dpi(struct motion_filter *filter, max_accel /= dpi_factor; - f1 = min(1, 0.3 + speed_in * 10000.0); - f2 = 1 + (speed_in * 1000.0 - threshold * dpi_factor * 1000.0) * incline; + f1 = min(1, 0.3 + v_us2ms(speed_in) * 10.0); + f2 = 1 + (v_us2ms(speed_in) - v_us2ms(threshold) * dpi_factor) * incline; factor = min(max_accel, f2 > 1 ? f2 : f1); @@ -435,8 +453,8 @@ pointer_accel_profile_linear(struct motion_filter *filter, const double incline = accel_filter->incline; double factor; /* unitless */ - f1 = min(1, 0.3 + speed_in * 10 * 1000.0); - f2 = 1 + (speed_in * 1000.0 - threshold * 1000.0) * incline; + f1 = min(1, 0.3 + v_us2ms(speed_in) * 10); + f2 = 1 + (v_us2ms(speed_in) - v_us2ms(threshold)) * incline; factor = min(max_accel, f2 > 1 ? f2 : f1); @@ -493,8 +511,8 @@ touchpad_lenovo_x230_accel_profile(struct motion_filter *filter, speed_in *= TP_MAGIC_SLOWDOWN / TP_MAGIC_LOW_RES_FACTOR; - f1 = min(1, speed_in * 5 * 1000.0); - f2 = 1 + (speed_in * 1000.0 - threshold * 1000.0) * incline; + 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); From 723dccfd939d151ad460e166c7a73a25ea7c0fc5 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 5 Aug 2015 13:39:33 +1000 Subject: [PATCH 06/16] filter: explain the acceleration function in detail MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit And switch to a code-flow that's a bit more self-explanatory than the current min/max combinations. Signed-off-by: Peter Hutterer Reviewed-by: Jonas Ådahl --- src/filter.c | 83 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 73 insertions(+), 10 deletions(-) diff --git a/src/filter.c b/src/filter.c index 425bc795..467eb225 100644 --- a/src/filter.c +++ b/src/filter.c @@ -421,19 +421,26 @@ pointer_accel_profile_linear_low_dpi(struct motion_filter *filter, struct pointer_accelerator *accel_filter = (struct pointer_accelerator *)filter; - double f1, f2; /* unitless */ double max_accel = accel_filter->accel; /* unitless factor */ - const double threshold = accel_filter->threshold; /* units/us */ + double threshold = accel_filter->threshold; /* units/us */ const double incline = accel_filter->incline; 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 */ max_accel /= dpi_factor; + threshold *= dpi_factor; - f1 = min(1, 0.3 + v_us2ms(speed_in) * 10.0); - f2 = 1 + (v_us2ms(speed_in) - v_us2ms(threshold) * dpi_factor) * incline; + /* see pointer_accel_profile_linear for a long description */ + if (v_us2ms(speed_in) < 0.07) + factor = 10 * v_us2ms(speed_in) + 0.3; + else if (speed_in < threshold) + factor = 1; + else + factor = incline * v_us2ms(speed_in - threshold) + 1; - factor = min(max_accel, f2 > 1 ? f2 : f1); + factor = min(max_accel, factor); return factor; } @@ -446,17 +453,67 @@ pointer_accel_profile_linear(struct motion_filter *filter, { struct pointer_accelerator *accel_filter = (struct pointer_accelerator *)filter; - - double f1, f2; /* unitless */ 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 */ - f1 = min(1, 0.3 + v_us2ms(speed_in) * 10); - f2 = 1 + (v_us2ms(speed_in) - v_us2ms(threshold)) * incline; + /* + 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: - factor = min(max_accel, f2 > 1 ? f2 : f1); + 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.00 + 0.3 => 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; } @@ -509,6 +566,12 @@ touchpad_lenovo_x230_accel_profile(struct motion_filter *filter, TP_MAGIC_LOW_RES_FACTOR; /* units/us */ const double incline = accel_filter->incline * 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 explaination of what the min/max/etc. does. + */ speed_in *= TP_MAGIC_SLOWDOWN / TP_MAGIC_LOW_RES_FACTOR; f1 = min(1, v_us2ms(speed_in) * 5); From 5ec449f7dce7df4ac685449bc152a2e92289cb53 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 28 Jul 2015 15:39:19 +1000 Subject: [PATCH 07/16] filter: drop accel->last, write-only value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Peter Hutterer Reviewed-by: Jonas Ådahl --- src/filter.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/filter.c b/src/filter.c index 467eb225..4c00dbb8 100644 --- a/src/filter.c +++ b/src/filter.c @@ -117,7 +117,6 @@ struct pointer_accelerator { double velocity; /* units/us */ double last_velocity; /* units/us */ - struct normalized_coords last; struct pointer_tracker *trackers; int cur_tracker; @@ -297,8 +296,6 @@ accelerator_filter(struct motion_filter *filter, accelerated.x = accel_value * unnormalized.x; accelerated.y = accel_value * unnormalized.y; - accel->last = unnormalized; - accel->last_velocity = velocity; return accelerated; @@ -386,8 +383,6 @@ create_pointer_accelerator_filter(accel_profile_func_t profile, filter->profile = profile; filter->last_velocity = 0.0; - filter->last.x = 0; - filter->last.y = 0; filter->trackers = calloc(NUM_POINTER_TRACKERS, sizeof *filter->trackers); From 4cdcf5cc381c6cfbe552b642e56e1c6a68263876 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 28 Jul 2015 14:45:51 +1000 Subject: [PATCH 08/16] filter: move create_pointer_accelerator_filter down in the file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No functional changes, just moving code in preparation for filter patches Signed-off-by: Peter Hutterer Reviewed-by: Jonas Ådahl --- src/filter.c | 70 ++++++++++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/src/filter.c b/src/filter.c index 4c00dbb8..38d0d367 100644 --- a/src/filter.c +++ b/src/filter.c @@ -362,41 +362,6 @@ accelerator_set_speed(struct motion_filter *filter, return true; } -struct motion_filter_interface accelerator_interface = { - accelerator_filter, - accelerator_restart, - accelerator_destroy, - accelerator_set_speed, -}; - -struct motion_filter * -create_pointer_accelerator_filter(accel_profile_func_t profile, - int dpi) -{ - struct pointer_accelerator *filter; - - filter = zalloc(sizeof *filter); - if (filter == NULL) - return NULL; - - filter->base.interface = &accelerator_interface; - - filter->profile = profile; - filter->last_velocity = 0.0; - - filter->trackers = - calloc(NUM_POINTER_TRACKERS, sizeof *filter->trackers); - filter->cur_tracker = 0; - - filter->threshold = DEFAULT_THRESHOLD; - filter->accel = DEFAULT_ACCELERATION; - filter->incline = DEFAULT_INCLINE; - - filter->dpi_factor = dpi/(double)DEFAULT_MOUSE_DPI; - - return &filter->base; -} - /** * Custom acceleration function for mice < 1000dpi. * At slow motion, a single device unit causes a one-pixel movement. @@ -576,3 +541,38 @@ touchpad_lenovo_x230_accel_profile(struct motion_filter *filter, return factor * TP_MAGIC_SLOWDOWN / TP_MAGIC_LOW_RES_FACTOR; } + +struct motion_filter_interface accelerator_interface = { + accelerator_filter, + accelerator_restart, + accelerator_destroy, + accelerator_set_speed, +}; + +struct motion_filter * +create_pointer_accelerator_filter(accel_profile_func_t profile, + int dpi) +{ + struct pointer_accelerator *filter; + + filter = zalloc(sizeof *filter); + if (filter == NULL) + return NULL; + + filter->base.interface = &accelerator_interface; + + filter->profile = profile; + filter->last_velocity = 0.0; + + filter->trackers = + calloc(NUM_POINTER_TRACKERS, sizeof *filter->trackers); + filter->cur_tracker = 0; + + filter->threshold = DEFAULT_THRESHOLD; + filter->accel = DEFAULT_ACCELERATION; + filter->incline = DEFAULT_INCLINE; + + filter->dpi_factor = dpi/(double)DEFAULT_MOUSE_DPI; + + return &filter->base; +} From 4c48b46d15497fec9fa16cbf39f83bc3c090db6f Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 28 Jul 2015 15:06:13 +1000 Subject: [PATCH 09/16] filter: revamp to create device-specific filters, rather than accel functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous approach to pointer acceleration was to initialize the same motion filter behavior but a different acceleration profile depending on the hardware (the profile converts a speed to a multiplier for input deltas). To be more flexible for hardware-specifics, change this into a set of specific pointer acceleration init functions. This patch has no effective functional changes, they're still all the same. The acceleration functions are kept for direct access by the ptraccel-debug tool. Signed-off-by: Peter Hutterer Reviewed-by: Jonas Ådahl --- src/evdev-mt-touchpad.c | 10 +++---- src/evdev.c | 18 ++++++------ src/evdev.h | 2 +- src/filter.c | 62 ++++++++++++++++++++++++++++++++++++++--- src/filter.h | 14 ++++++++-- tools/ptraccel-debug.c | 3 +- 6 files changed, 86 insertions(+), 23 deletions(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index a683d9a4..f48aa26d 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -1510,7 +1510,7 @@ static int tp_init_accel(struct tp_dispatch *tp, double diagonal) { int res_x, res_y; - accel_profile_func_t profile; + struct motion_filter *filter; res_x = tp->device->abs.absinfo_x->resolution; res_y = tp->device->abs.absinfo_y->resolution; @@ -1526,14 +1526,14 @@ tp_init_accel(struct tp_dispatch *tp, double diagonal) tp->accel.y_scale_coeff = (DEFAULT_MOUSE_DPI/25.4) / res_y; if (tp->device->model_flags & EVDEV_MODEL_LENOVO_X230) - profile = touchpad_lenovo_x230_accel_profile; + filter = create_pointer_accelerator_filter_lenovo_x230(tp->device->dpi); else - profile = touchpad_accel_profile_linear; + filter = create_pointer_accelerator_filter_touchpad(tp->device->dpi); - if (evdev_device_init_pointer_acceleration(tp->device, profile) == -1) + if (!filter) return -1; - return 0; + return evdev_device_init_pointer_acceleration(tp->device, filter); } static uint32_t diff --git a/src/evdev.c b/src/evdev.c index 17c26043..bb317248 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1408,12 +1408,9 @@ evdev_accel_config_get_default_speed(struct libinput_device *device) int evdev_device_init_pointer_acceleration(struct evdev_device *device, - accel_profile_func_t profile) + struct motion_filter *filter) { - device->pointer.filter = create_pointer_accelerator_filter(profile, - device->dpi); - if (!device->pointer.filter) - return -1; + device->pointer.filter = filter; device->pointer.config.available = evdev_accel_config_available; device->pointer.config.set_speed = evdev_accel_config_set_speed; @@ -1862,14 +1859,17 @@ evdev_configure_mt_device(struct evdev_device *device) static inline int evdev_init_accel(struct evdev_device *device) { - accel_profile_func_t profile; + struct motion_filter *filter; if (device->dpi < DEFAULT_MOUSE_DPI) - profile = pointer_accel_profile_linear_low_dpi; + filter = create_pointer_accelerator_filter_linear_low_dpi(device->dpi); else - profile = pointer_accel_profile_linear; + filter = create_pointer_accelerator_filter_linear(device->dpi); - return evdev_device_init_pointer_acceleration(device, profile); + if (!filter) + return -1; + + return evdev_device_init_pointer_acceleration(device, filter); } static int diff --git a/src/evdev.h b/src/evdev.h index be5df0d0..42404fc2 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -280,7 +280,7 @@ evdev_device_create(struct libinput_seat *seat, int evdev_device_init_pointer_acceleration(struct evdev_device *device, - accel_profile_func_t profile); + struct motion_filter *filter); struct evdev_dispatch * evdev_touchpad_create(struct evdev_device *device); diff --git a/src/filter.c b/src/filter.c index 38d0d367..b94647d2 100644 --- a/src/filter.c +++ b/src/filter.c @@ -549,9 +549,8 @@ struct motion_filter_interface accelerator_interface = { accelerator_set_speed, }; -struct motion_filter * -create_pointer_accelerator_filter(accel_profile_func_t profile, - int dpi) +static struct pointer_accelerator * +create_default_filter(int dpi) { struct pointer_accelerator *filter; @@ -561,7 +560,6 @@ create_pointer_accelerator_filter(accel_profile_func_t profile, filter->base.interface = &accelerator_interface; - filter->profile = profile; filter->last_velocity = 0.0; filter->trackers = @@ -574,5 +572,61 @@ create_pointer_accelerator_filter(accel_profile_func_t profile, filter->dpi_factor = dpi/(double)DEFAULT_MOUSE_DPI; + return filter; +} + +struct motion_filter * +create_pointer_accelerator_filter_linear(int dpi) +{ + struct pointer_accelerator *filter; + + filter = create_default_filter(dpi); + if (!filter) + return NULL; + + filter->profile = pointer_accel_profile_linear; + + return &filter->base; +} + +struct motion_filter * +create_pointer_accelerator_filter_linear_low_dpi(int dpi) +{ + struct pointer_accelerator *filter; + + filter = create_default_filter(dpi); + if (!filter) + return NULL; + + filter->profile = pointer_accel_profile_linear_low_dpi; + + return &filter->base; +} + +struct motion_filter * +create_pointer_accelerator_filter_touchpad(int dpi) +{ + struct pointer_accelerator *filter; + + filter = create_default_filter(dpi); + if (!filter) + return NULL; + + filter->profile = touchpad_accel_profile_linear; + + return &filter->base; +} + +struct motion_filter * +create_pointer_accelerator_filter_lenovo_x230(int dpi) +{ + struct pointer_accelerator *filter; + + filter = create_default_filter(dpi); + if (!filter) + return NULL; + + filter->profile = touchpad_lenovo_x230_accel_profile; + return &filter->base; } diff --git a/src/filter.h b/src/filter.h index 617fab1f..76fc1476 100644 --- a/src/filter.h +++ b/src/filter.h @@ -57,9 +57,19 @@ typedef double (*accel_profile_func_t)(struct motion_filter *filter, double velocity, uint64_t time); +/* Pointer acceleration types */ + struct motion_filter * -create_pointer_accelerator_filter(accel_profile_func_t filter, - int dpi); +create_pointer_accelerator_filter_linear(int dpi); + +struct motion_filter * +create_pointer_accelerator_filter_linear_low_dpi(int dpi); + +struct motion_filter * +create_pointer_accelerator_filter_touchpad(int dpi); + +struct motion_filter * +create_pointer_accelerator_filter_lenovo_x230(int dpi); /* * Pointer acceleration profiles. diff --git a/tools/ptraccel-debug.c b/tools/ptraccel-debug.c index b0867db3..077da59d 100644 --- a/tools/ptraccel-debug.c +++ b/tools/ptraccel-debug.c @@ -272,8 +272,7 @@ main(int argc, char **argv) } } - filter = create_pointer_accelerator_filter(pointer_accel_profile_linear, - dpi); + filter = create_pointer_accelerator_filter_linear(dpi); assert(filter != NULL); filter_set_speed(filter, speed); From f2663e2b255e71aac6045c7a6bd3f7679fb16078 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 28 Jul 2015 15:46:33 +1000 Subject: [PATCH 10/16] filter: split calculating the accel factor into a helper function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No functional changes. Signed-off-by: Peter Hutterer Reviewed-by: Jonas Ådahl --- src/filter.c | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/src/filter.c b/src/filter.c index b94647d2..f3e0fb32 100644 --- a/src/filter.c +++ b/src/filter.c @@ -266,6 +266,27 @@ calculate_acceleration(struct pointer_accelerator *accel, return factor; /* unitless factor */ } +static inline double +calculate_acceleration_factor(struct pointer_accelerator *accel, + const struct normalized_coords *unaccelerated, + void *data, + uint64_t time) +{ + double velocity; /* units/us */ + double accel_factor; + + feed_trackers(accel, unaccelerated, time); + velocity = calculate_velocity(accel, 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(struct motion_filter *filter, const struct normalized_coords *unaccelerated, @@ -273,7 +294,6 @@ accelerator_filter(struct motion_filter *filter, { struct pointer_accelerator *accel = (struct pointer_accelerator *) filter; - double velocity; /* units/us */ double accel_value; /* unitless factor */ struct normalized_coords accelerated; struct normalized_coords unnormalized; @@ -285,19 +305,14 @@ accelerator_filter(struct motion_filter *filter, unnormalized.x = unaccelerated->x * dpi_factor; unnormalized.y = unaccelerated->y * dpi_factor; - feed_trackers(accel, &unnormalized, time); - velocity = calculate_velocity(accel, time); - accel_value = calculate_acceleration(accel, - data, - velocity, - accel->last_velocity, - time); + accel_value = calculate_acceleration_factor(accel, + &unnormalized, + data, + time); accelerated.x = accel_value * unnormalized.x; accelerated.y = accel_value * unnormalized.y; - accel->last_velocity = velocity; - return accelerated; } From ef3f234345a459cda1dd24ce43d6034038e8e4a3 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 28 Jul 2015 15:49:22 +1000 Subject: [PATCH 11/16] filter: split out handling of the low-dpi accel method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Peter Hutterer Reviewed-by: Jonas Ådahl --- src/filter.c | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/filter.c b/src/filter.c index f3e0fb32..369d7370 100644 --- a/src/filter.c +++ b/src/filter.c @@ -296,6 +296,27 @@ accelerator_filter(struct motion_filter *filter, (struct pointer_accelerator *) filter; double accel_value; /* unitless factor */ struct normalized_coords accelerated; + + 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_low_dpi(struct motion_filter *filter, + const struct normalized_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_factor; @@ -573,8 +594,6 @@ create_default_filter(int dpi) if (filter == NULL) return NULL; - filter->base.interface = &accelerator_interface; - filter->last_velocity = 0.0; filter->trackers = @@ -599,11 +618,19 @@ create_pointer_accelerator_filter_linear(int dpi) if (!filter) return NULL; + filter->base.interface = &accelerator_interface; filter->profile = pointer_accel_profile_linear; return &filter->base; } +struct motion_filter_interface accelerator_interface_low_dpi = { + accelerator_filter_low_dpi, + accelerator_restart, + accelerator_destroy, + accelerator_set_speed, +}; + struct motion_filter * create_pointer_accelerator_filter_linear_low_dpi(int dpi) { @@ -613,6 +640,7 @@ create_pointer_accelerator_filter_linear_low_dpi(int dpi) if (!filter) return NULL; + filter->base.interface = &accelerator_interface_low_dpi; filter->profile = pointer_accel_profile_linear_low_dpi; return &filter->base; @@ -627,6 +655,7 @@ create_pointer_accelerator_filter_touchpad(int dpi) if (!filter) return NULL; + filter->base.interface = &accelerator_interface; filter->profile = touchpad_accel_profile_linear; return &filter->base; @@ -641,6 +670,7 @@ create_pointer_accelerator_filter_lenovo_x230(int dpi) if (!filter) return NULL; + filter->base.interface = &accelerator_interface; filter->profile = touchpad_lenovo_x230_accel_profile; return &filter->base; From e1be2f54cad745b4c6d8764f3352c1be50729985 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 28 Jul 2015 15:53:23 +1000 Subject: [PATCH 12/16] filter: duplicate the code for the Lenovo x230 accel method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is "once-tested, don't touch it again" code. The quirks on the touchpad means we'd have to find that specific device again and re-test everything if we change anything elsewhere in the code. So duplicate it properly, so that we don't have to touch it again. Signed-off-by: Peter Hutterer Reviewed-by: Jonas Ådahl --- src/filter.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 3 deletions(-) diff --git a/src/filter.c b/src/filter.c index 369d7370..0c3e1de2 100644 --- a/src/filter.c +++ b/src/filter.c @@ -96,6 +96,11 @@ filter_get_speed(struct motion_filter *filter) #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 */ + /* * Pointer acceleration filter constants */ @@ -337,6 +342,32 @@ accelerator_filter_low_dpi(struct motion_filter *filter, return accelerated; } +static struct normalized_coords +accelerator_filter_x230(struct motion_filter *filter, + const struct normalized_coords *unaccelerated, + void *data, uint64_t time) +{ + struct pointer_accelerator *accel = + (struct pointer_accelerator *) filter; + double accel_factor; /* unitless factor */ + struct normalized_coords accelerated; + double velocity; /* units/us */ + + feed_trackers(accel, unaccelerated, time); + velocity = calculate_velocity(accel, time); + accel_factor = calculate_acceleration(accel, + data, + velocity, + accel->last_velocity, + time); + accel->last_velocity = velocity; + + accelerated.x = accel_factor * unaccelerated->x; + accelerated.y = accel_factor * unaccelerated->y; + + return accelerated; +} + static void accelerator_restart(struct motion_filter *filter, void *data, @@ -661,17 +692,39 @@ create_pointer_accelerator_filter_touchpad(int dpi) return &filter->base; } +struct motion_filter_interface accelerator_interface_x230 = { + accelerator_filter_x230, + accelerator_restart, + accelerator_destroy, + 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 = create_default_filter(dpi); - if (!filter) + filter = zalloc(sizeof *filter); + if (filter == NULL) return NULL; - filter->base.interface = &accelerator_interface; + filter->base.interface = &accelerator_interface_x230; filter->profile = touchpad_lenovo_x230_accel_profile; + filter->last_velocity = 0.0; + + filter->trackers = + calloc(NUM_POINTER_TRACKERS, sizeof *filter->trackers); + filter->cur_tracker = 0; + + 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 */ return &filter->base; } From 4c1c572b19dec80b5d08125272f2f635ada2bd57 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 28 Jul 2015 16:02:05 +1000 Subject: [PATCH 13/16] filter: split trackpoint acceleration out MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is step one to fixing trackpoint acceleration, separating it from the other acceleration code. No functional changes yet, it still uses the low-dpi accel method. https://bugs.freedesktop.org/show_bug.cgi?id=91369 Signed-off-by: Peter Hutterer Reviewed-by: Jonas Ådahl --- src/evdev.c | 4 ++- src/filter.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/filter.h | 8 +++++ 3 files changed, 98 insertions(+), 1 deletion(-) diff --git a/src/evdev.c b/src/evdev.c index bb317248..22d51dcb 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1861,7 +1861,9 @@ evdev_init_accel(struct evdev_device *device) { struct motion_filter *filter; - if (device->dpi < DEFAULT_MOUSE_DPI) + if (device->tags & EVDEV_TAG_TRACKPOINT) + filter = create_pointer_accelerator_filter_trackpoint(device->dpi); + else if (device->dpi < DEFAULT_MOUSE_DPI) filter = create_pointer_accelerator_filter_linear_low_dpi(device->dpi); else filter = create_pointer_accelerator_filter_linear(device->dpi); diff --git a/src/filter.c b/src/filter.c index 0c3e1de2..7054fafa 100644 --- a/src/filter.c +++ b/src/filter.c @@ -342,6 +342,36 @@ accelerator_filter_low_dpi(struct motion_filter *filter, return accelerated; } +static struct normalized_coords +accelerator_filter_trackpoint(struct motion_filter *filter, + const struct normalized_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_factor; + + /* 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; + + accel_value = calculate_acceleration_factor(accel, + &unnormalized, + data, + time); + + accelerated.x = accel_value * unnormalized.x; + accelerated.y = accel_value * unnormalized.y; + + return accelerated; +} + static struct normalized_coords accelerator_filter_x230(struct motion_filter *filter, const struct normalized_coords *unaccelerated, @@ -609,6 +639,38 @@ touchpad_lenovo_x230_accel_profile(struct motion_filter *filter, return factor * TP_MAGIC_SLOWDOWN / TP_MAGIC_LOW_RES_FACTOR; } +double +trackpoint_accel_profile(struct motion_filter *filter, + void *data, + double speed_in, /* 1000-dpi normalized */ + uint64_t time) +{ + struct pointer_accelerator *accel_filter = + (struct pointer_accelerator *)filter; + double max_accel = accel_filter->accel; /* unitless factor */ + double threshold = accel_filter->threshold; /* units/ms */ + const double incline = accel_filter->incline; + 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 */ + max_accel /= dpi_factor; + threshold *= dpi_factor; + + /* see pointer_accel_profile_linear for a long description */ + if (v_us2ms(speed_in) < 0.07) + factor = 10 * v_us2ms(speed_in) + 0.3; + else if (speed_in < threshold) + factor = 1; + else + factor = incline * v_us2ms(speed_in - threshold) + 1; + + factor = min(max_accel, factor); + + return factor; +} + struct motion_filter_interface accelerator_interface = { accelerator_filter, accelerator_restart, @@ -728,3 +790,28 @@ create_pointer_accelerator_filter_lenovo_x230(int dpi) return &filter->base; } + +struct motion_filter_interface accelerator_interface_trackpoint = { + accelerator_filter_trackpoint, + accelerator_restart, + accelerator_destroy, + accelerator_set_speed, +}; + +struct motion_filter * +create_pointer_accelerator_filter_trackpoint(int dpi) +{ + struct pointer_accelerator *filter; + + filter = create_default_filter(dpi); + if (!filter) + return NULL; + + filter->base.interface = &accelerator_interface_trackpoint; + filter->profile = trackpoint_accel_profile; + filter->threshold = DEFAULT_THRESHOLD; + filter->accel = DEFAULT_ACCELERATION; + filter->incline = DEFAULT_INCLINE; + + return &filter->base; +} diff --git a/src/filter.h b/src/filter.h index 76fc1476..fd36da49 100644 --- a/src/filter.h +++ b/src/filter.h @@ -71,6 +71,9 @@ create_pointer_accelerator_filter_touchpad(int dpi); struct motion_filter * create_pointer_accelerator_filter_lenovo_x230(int dpi); +struct motion_filter * +create_pointer_accelerator_filter_trackpoint(int dpi); + /* * Pointer acceleration profiles. */ @@ -95,4 +98,9 @@ touchpad_lenovo_x230_accel_profile(struct motion_filter *filter, void *data, double speed_in, uint64_t time); +double +trackpoint_accel_profile(struct motion_filter *filter, + void *data, + double speed_in, + uint64_t time); #endif /* FILTER_H */ From e1849b3e58a3ddcb1b8e288e4d9dc2ab89fac9cf Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 31 Jul 2015 12:29:23 +1000 Subject: [PATCH 14/16] tools: allow filter-switching in ptraccel-debug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Peter Hutterer Reviewed-by: Jonas Ådahl --- tools/ptraccel-debug.c | 43 +++++++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/tools/ptraccel-debug.c b/tools/ptraccel-debug.c index 077da59d..aa887fec 100644 --- a/tools/ptraccel-debug.c +++ b/tools/ptraccel-debug.c @@ -138,7 +138,7 @@ print_ptraccel_sequence(struct motion_filter *filter, } static void -print_accel_func(struct motion_filter *filter) +print_accel_func(struct motion_filter *filter, accel_profile_func_t profile) { double vel; @@ -148,10 +148,7 @@ print_accel_func(struct motion_filter *filter) printf("# set style data lines\n"); printf("# plot \"gnuplot.data\" using 1:2\n"); for (vel = 0.0; vel < 0.003; vel += 0.0000001) { - double result = pointer_accel_profile_linear(filter, - NULL, - vel, - 0 /* time */); + double result = profile(filter, NULL, vel, 0 /* time */); printf("%.8f\t%.4f\n", vel, result); } } @@ -171,6 +168,12 @@ usage(void) "--steps= ... in motion and delta modes only. Increase dx by step each round\n" "--speed= ... accel speed [-1, 1], default 0\n" "--dpi= ... device resolution in DPI (default: 1000)\n" + "--filter= \n" + " linear ... the default motion filter\n" + " low-dpi ... low-dpi filter, use --dpi with this argument\n" + " touchpad ... the touchpad motion filter\n" + " x230 ... custom filter for the Lenovo x230 touchpad\n" + " trackpoint... trackpoint motion filter\n" "\n" "If extra arguments are present and mode is not given, mode defaults to 'sequence'\n" "and the arguments are interpreted as sequence of delta x coordinates\n" @@ -195,6 +198,8 @@ main(int argc, char **argv) double custom_deltas[1024]; double speed = 0.0; int dpi = 1000; + const char *filter_type = "linear"; + accel_profile_func_t profile = NULL; enum { OPT_MODE = 1, @@ -203,6 +208,7 @@ main(int argc, char **argv) OPT_STEP, OPT_SPEED, OPT_DPI, + OPT_FILTER, }; while (1) { @@ -215,6 +221,7 @@ main(int argc, char **argv) {"step", 1, 0, OPT_STEP }, {"speed", 1, 0, OPT_SPEED }, {"dpi", 1, 0, OPT_DPI }, + {"filter", 1, 0, OPT_FILTER}, {0, 0, 0, 0} }; @@ -265,6 +272,9 @@ main(int argc, char **argv) case OPT_DPI: dpi = strtod(optarg, NULL); break; + case OPT_FILTER: + filter_type = optarg; + break; default: usage(); exit(1); @@ -272,7 +282,26 @@ main(int argc, char **argv) } } - filter = create_pointer_accelerator_filter_linear(dpi); + if (streq(filter_type, "linear")) { + filter = create_pointer_accelerator_filter_linear(dpi); + profile = pointer_accel_profile_linear; + } else if (streq(filter_type, "low-dpi")) { + filter = create_pointer_accelerator_filter_linear_low_dpi(dpi); + profile = pointer_accel_profile_linear_low_dpi; + } else if (streq(filter_type, "touchpad")) { + filter = create_pointer_accelerator_filter_touchpad(dpi); + profile = touchpad_accel_profile_linear; + } else if (streq(filter_type, "x230")) { + filter = create_pointer_accelerator_filter_lenovo_x230(dpi); + profile = touchpad_lenovo_x230_accel_profile; + } else if (streq(filter_type, "trackpoint")) { + filter = create_pointer_accelerator_filter_trackpoint(dpi); + profile = trackpoint_accel_profile; + } else { + fprintf(stderr, "Invalid filter type %s\n", filter_type); + return 1; + } + assert(filter != NULL); filter_set_speed(filter, speed); @@ -296,7 +325,7 @@ main(int argc, char **argv) } if (print_accel) - print_accel_func(filter); + print_accel_func(filter, profile); else if (print_delta) print_ptraccel_deltas(filter, step); else if (print_motion) From 9424fb6f99d9032a73130e6f380eb11be1988025 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 29 Jul 2015 15:05:52 +1000 Subject: [PATCH 15/16] doc: add pointer acceleration documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Peter Hutterer Reviewed-by: Jonas Ådahl --- doc/Makefile.am | 5 + doc/page-hierarchy.dox | 1 + doc/pointer-acceleration.dox | 110 + doc/svg/ptraccel-linear.svg | 5486 +++++++++++++++++++++++++++++++ doc/svg/ptraccel-low-dpi.svg | 3748 +++++++++++++++++++++ doc/svg/ptraccel-touchpad.svg | 1723 ++++++++++ doc/svg/ptraccel-trackpoint.svg | 3689 +++++++++++++++++++++ 7 files changed, 14762 insertions(+) create mode 100644 doc/pointer-acceleration.dox create mode 100644 doc/svg/ptraccel-linear.svg create mode 100644 doc/svg/ptraccel-low-dpi.svg create mode 100644 doc/svg/ptraccel-touchpad.svg create mode 100644 doc/svg/ptraccel-trackpoint.svg diff --git a/doc/Makefile.am b/doc/Makefile.am index c9dccfda..a8d41822 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -18,6 +18,7 @@ header_files = \ $(srcdir)/normalization-of-relative-motion.dox \ $(srcdir)/palm-detection.dox \ $(srcdir)/page-hierarchy.dox \ + $(srcdir)/pointer-acceleration.dox \ $(srcdir)/reporting-bugs.dox \ $(srcdir)/scrolling.dox \ $(srcdir)/seats.dox \ @@ -39,6 +40,10 @@ diagram_files = \ $(srcdir)/svg/edge-scrolling.svg \ $(srcdir)/svg/palm-detection.svg \ $(srcdir)/svg/pinch-gestures.svg \ + $(srcdir)/svg/ptraccel-linear.svg \ + $(srcdir)/svg/ptraccel-low-dpi.svg \ + $(srcdir)/svg/ptraccel-touchpad.svg \ + $(srcdir)/svg/ptraccel-trackpoint.svg \ $(srcdir)/svg/swipe-gestures.svg \ $(srcdir)/svg/tap-n-drag.svg \ $(srcdir)/svg/thumb-detection.svg \ diff --git a/doc/page-hierarchy.dox b/doc/page-hierarchy.dox index 1e82535f..3fdb1f72 100644 --- a/doc/page-hierarchy.dox +++ b/doc/page-hierarchy.dox @@ -31,5 +31,6 @@ - @subpage test-suite - @subpage tools +- @subpage pointer-acceleration */ diff --git a/doc/pointer-acceleration.dox b/doc/pointer-acceleration.dox new file mode 100644 index 00000000..a372e154 --- /dev/null +++ b/doc/pointer-acceleration.dox @@ -0,0 +1,110 @@ +/** +@page pointer-acceleration Pointer acceleration + +libinput uses device-specific pointer acceleration methods, with the default +being the @ref ptraccel-linear. The methods share common properties, such as +@ref ptraccel-velocity. + +This page explains the high-level concepts used in the code. It aims to +provide an overview for developers and is not necessarily useful for +users. + +@section ptraccel-velocity Velocity calculation + +The device's speed of movement is measured across multiple input events +through so-called "trackers". Each event prepends a the tracker item, each +subsequent tracker contains the delta of that item to the current position, +the timestamp of the event that created it and the cardinal direction of the +movement at the time. If a device moves into the same direction, the +velocity is calculated across multiple trackers. For example, if a device +moves steadily for 10 events to the left, the velocity is calculated across +all 10 events. + +Whenever the movement changes direction or significantly changes speed, the +velocity is calculated from the direction/speed change only. For example, if +a device moves steadily for 8 events to the left and then 2 events to the +right, the velocity is only that of the last 2 events. + +An extra time limit prevents events that are too old to factor into the +velocity calculation. For example, if a device moves steadily for 5 events +to the left, then pauses, then moves again for 5 events to the left, only +the last 5 events are used for velocity calculation. + +The velocity is then used to calculate the acceleration factor + +@section ptraccel-factor Acceleration factor + +The acceleration factor is the final outcome of the pointer acceleration +calculations. It is a unitless factor that is applied to the current delta, +a factor of 2 doubles the delta (i.e. speeds up the movement), a factor of +less than 1 reduces the delta (i.e. slows the movement). + +Any factor less than 1 requires the user to move the device further to move +the visible pointer. This is called deceleration and enables high precision +target selection through subpixel movements. libinput's current maximum +deceleration factor is 0.3 (i.e. slow down to 30% of the pointer speed). + +A factor higher than 1 moves the pointer further than the physical device +moves. This is acceleration and allows a user to cross the screen quickly +but effectively skips pixels. libinput's current maximum acceleration factor +is 3.5. + +@section ptraccel-linear Linear pointer acceleration + +The linear pointer acceleration method is the default for most pointer +devices. It provides deceleration at very slow movements, a 1:1 mapping for +regular movements and a linear increase to the maximum acceleration factor +for fast movements. + +Linear pointer acceleration applies to devices with above 1000dpi resolution +and after @ref motion_normalization is applied. + +@image html ptraccel-linear.svg "Linear pointer acceleration" + +The image above shows the linear pointer acceleration settings at various +speeds. The line for 0.0 is the default acceleration curve, speed settings +above 0.0 accelerate sooner, faster and to a higher maximum acceleration. +Speed settings below 0 delay when acceleration kicks in, how soon the +maximum acceleration is reached and the maximum acceleration factor. + +Extremely low speed settings provide no acceleration and additionally +decelerate all movement by a constant factor. + +@section ptraccel-low-dpi Pointer acceleration for low-dpi devices + +Low-dpi devices are those with a physical resolution of less than 1000 dots +per inch (dpi). The pointer acceleration is adjusted to provide roughly the +same feel for all devices at normal to high speeds. At slow speeds, the +pointer acceleration works on device-units rather than normalized +coordinates (see @ref motion_normalization). + +@image html ptraccel-low-dpi.svg "Pointer acceleration for low-dpi devices" + +The image above shows the default pointer acceleration curve for a speed of +0.0 at different DPI settings. A device with low DPI has the acceleration +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. + +@image html ptraccel-touchpad.svg "Pointer acceleration curve for touchpads" + +The image above shows the touchpad acceleration profile in comparison to the +@ref ptraccel-linear. The shape of the curve is identical but vertically squashed. + +@section ptraccel-trackpoint Pointer acceleration on trackpoints + +Trackpoint pointer acceleration uses the @ref ptraccel-low-dpi profile, with a +constant deceleration factor taking the place of the DPI settings. + +@image html ptraccel-trackpoint.svg "Pointer acceleration curves for trackpoints" + +The image above shows the trackpoint acceleration profile in comparison to the +@ref ptraccel-linear. The constant acceleration factor, usually applied by +udev, shapes the acceleration profile. + +*/ diff --git a/doc/svg/ptraccel-linear.svg b/doc/svg/ptraccel-linear.svg new file mode 100644 index 00000000..cb575f21 --- /dev/null +++ b/doc/svg/ptraccel-linear.svg @@ -0,0 +1,5486 @@ + + + + +Gnuplot +Produced by GNUPLOT 5.0 patchlevel 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + 0.5 + + + + + 1 + + + + + 1.5 + + + + + 2 + + + + + 2.5 + + + + + 3 + + + + + 0 + + + + + 0.0005 + + + + + 0.001 + + + + + 0.0015 + + + + + 0.002 + + + + + 0.0025 + + + + + 0.003 + + + + + + + + + accel factor + + + + + speed in units/us + + + + + -1.0 + + + -1.0 + + + + + + -0.75 + + + -0.75 + + + + + + -0.5 + + + -0.5 + + + + + + -0.25 + + + -0.25 + + + + + + 0.0 + + + 0.0 + + + + + + 0.5 + + + 0.5 + + + + + + 1.0 + + + 1.0 + + + + + + + + + + + + + + + + + + diff --git a/doc/svg/ptraccel-low-dpi.svg b/doc/svg/ptraccel-low-dpi.svg new file mode 100644 index 00000000..e31eaa1a --- /dev/null +++ b/doc/svg/ptraccel-low-dpi.svg @@ -0,0 +1,3748 @@ + + + + +Gnuplot +Produced by GNUPLOT 5.0 patchlevel 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + 1 + + + + + 2 + + + + + 3 + + + + + 4 + + + + + 5 + + + + + 0 + + + + + 0.0005 + + + + + 0.001 + + + + + 0.0015 + + + + + 0.002 + + + + + 0.0025 + + + + + 0.003 + + + + + + + + + accel factor + + + + + speed in units/us + + + + + 200dpi + + + 200dpi + + + + + + 400dpi + + + 400dpi + + + + + + 800dpi + + + 800dpi + + + + + + 1000dpi + + + 1000dpi + + + + + + + + + + + + + + + + + + diff --git a/doc/svg/ptraccel-touchpad.svg b/doc/svg/ptraccel-touchpad.svg new file mode 100644 index 00000000..1befa1e5 --- /dev/null +++ b/doc/svg/ptraccel-touchpad.svg @@ -0,0 +1,1723 @@ + + + + +Gnuplot +Produced by GNUPLOT 5.0 patchlevel 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + 0.5 + + + + + 1 + + + + + 1.5 + + + + + 2 + + + + + 2.5 + + + + + 3 + + + + + 0 + + + + + 0.0005 + + + + + 0.001 + + + + + 0.0015 + + + + + 0.002 + + + + + 0.0025 + + + + + 0.003 + + + + + + + + + accel factor + + + + + speed in units/us + + + + + linear (mouse) + + + linear (mouse) + + + + + + touchpad + + + touchpad + + + + + + + + + + + + + + + + + + diff --git a/doc/svg/ptraccel-trackpoint.svg b/doc/svg/ptraccel-trackpoint.svg new file mode 100644 index 00000000..79589c24 --- /dev/null +++ b/doc/svg/ptraccel-trackpoint.svg @@ -0,0 +1,3689 @@ + + + + +Gnuplot +Produced by GNUPLOT 5.0 patchlevel 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + 1 + + + + + 2 + + + + + 3 + + + + + 4 + + + + + 5 + + + + + 0 + + + + + 0.0005 + + + + + 0.001 + + + + + 0.0015 + + + + + 0.002 + + + + + 0.0025 + + + + + 0.003 + + + + + + + + + accel factor + + + + + speed in units/us + + + + + linear (mouse) + + + linear (mouse) + + + + + + const accel 1 + + + const accel 1 + + + + + + const accel 2 + + + const accel 2 + + + + + + const accel 3 + + + const accel 3 + + + + + + + + + + + + + + + + + + From cd63a06153cbb6ec95fb221b99a463a1f7a8a1d6 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 5 Aug 2015 14:47:59 +1000 Subject: [PATCH 16/16] tools: add shell script for making the pointer acceleration graphs in the docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Peter Hutterer Reviewed-by: Jonas Ådahl --- tools/Makefile.am | 2 + tools/make-ptraccel-graphs.sh | 79 +++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100755 tools/make-ptraccel-graphs.sh diff --git a/tools/Makefile.am b/tools/Makefile.am index 68e60cbb..8f72db99 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -39,3 +39,5 @@ event_gui_LDADD = ../src/libinput.la libshared.la $(CAIRO_LIBS) $(GTK_LIBS) $(LI event_gui_CFLAGS = $(CAIRO_CFLAGS) $(GTK_CFLAGS) $(LIBUDEV_CFLAGS) event_gui_LDFLAGS = -no-install endif + +EXTRA_DIST = make-ptraccel-graphs.sh diff --git a/tools/make-ptraccel-graphs.sh b/tools/make-ptraccel-graphs.sh new file mode 100755 index 00000000..901baf9c --- /dev/null +++ b/tools/make-ptraccel-graphs.sh @@ -0,0 +1,79 @@ +#!/bin/bash + +tool=`dirname $0`/ptraccel-debug +gnuplot=/usr/bin/gnuplot + +outfile="ptraccel-linear" +for speed in -1 -0.75 -0.5 -0.25 0 0.5 1; do + $tool --mode=accel --dpi=1000 --filter=linear --speed=$speed > $outfile-$speed.gnuplot +done +$gnuplot < $outfile-$dpi.gnuplot +done + +$gnuplot < $outfile-mouse.gnuplot +$tool --mode=accel --dpi=1000 --filter=touchpad > $outfile-touchpad.gnuplot +$gnuplot < $outfile-mouse.gnuplot +for constaccel in 1 2 3; do + dpi=$((1000/$constaccel)) + $tool --mode=accel --dpi=$dpi --filter=trackpoint > $outfile-trackpoint-$constaccel.gnuplot +done +$gnuplot <