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/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index b1666ea7..f437c2df 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 @@ -2266,6 +2278,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)) 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 4b15c307..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 @@ -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) @@ -56,9 +62,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 +83,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); @@ -117,6 +134,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 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 */ #define X230_THRESHOLD v_ms2us(0.4) /* in units/us */ #define X230_ACCELERATION 2.0 /* unitless factor */ @@ -153,14 +176,14 @@ struct pointer_accelerator { double accel; /* unitless factor */ double incline; /* incline of the function */ - double dpi_factor; + int dpi; }; struct pointer_accelerator_flat { struct motion_filter base; double factor; - double dpi_factor; + int dpi; }; struct tablet_accelerator_flat { @@ -174,7 +197,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; @@ -191,7 +214,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 * @@ -290,6 +313,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) @@ -297,6 +330,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, @@ -320,13 +365,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); @@ -341,10 +396,22 @@ 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, - 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; @@ -362,85 +429,54 @@ 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; -} - -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; - /* 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; - - 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_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; + return normalize_for_dpi(unaccelerated, accel->dpi); } 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, @@ -449,36 +485,64 @@ 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; } +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" */ + + /* adjust when accel kicks in */ + accel_filter->threshold = TOUCHPAD_DEFAULT_THRESHOLD - + TOUCHPAD_THRESHOLD_RANGE * speed_adjustment; + accel_filter->accel = TOUCHPAD_ACCELERATION; + accel_filter->incline = TOUCHPAD_INCLINE; + filter->speed_adjustment = speed_adjustment; + + return true; +} + 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; } @@ -566,8 +630,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 */ @@ -590,7 +654,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 = @@ -600,6 +664,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, @@ -662,15 +729,78 @@ 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, /* in device units/µs */ + 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; + /* Convert to mm/s because that's something one can understand */ + speed_in = v_us2s(speed_in) * 25.4/accel_filter->dpi; - 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 the lower threshold, we decelerate, down to 30% + of input speed. + hence 1 = a * 7 + 0.3 + 0.7 = a * 7 => a := 0.1 + deceleration function is thus: + y = 0.1x + 0.3 + + Note: + * 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 (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; + } 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 * (speed_in - threshold) + 1; + } + + /* 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; } @@ -678,7 +808,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 @@ -725,8 +855,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 */ @@ -748,7 +878,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, @@ -773,8 +903,7 @@ 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; } @@ -796,7 +925,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, @@ -820,11 +949,11 @@ 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, - .set_speed = accelerator_set_speed, + .set_speed = touchpad_accelerator_set_speed, }; struct motion_filter * @@ -875,15 +1004,14 @@ 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; } 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, @@ -904,29 +1032,26 @@ 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; } 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; /* 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; 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; } @@ -979,26 +1104,24 @@ 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; } 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; @@ -1012,12 +1135,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 @@ -1037,7 +1160,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 46258802..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) { @@ -701,28 +707,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 +737,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 +751,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 */ 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" diff --git a/tools/ptraccel-debug.c b/tools/ptraccel-debug.c index 84a1221c..4fcb45f4 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,25 +135,37 @@ 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); } } -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); } } @@ -335,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)