[PATCH libinput] filter: replace the trackpoint accel with a velocity-based one

Peter Hutterer peter.hutterer at who-t.net
Fri Jun 29 05:36:57 UTC 2018


This gets rid of the trackpoint range propery that we've been parsing until
now and instead just opts for a basic curve with some deceleration for low
pressure. The speed range is taken from the touchpad and should be wide enough
for most trackpoints that fall within the expected range.

Trackpoints like the new ALPS ones need to be configured through a hwdb (this
part is currently missing).

Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---

I admit it, I'm out of ideas. This one provides a decent acceleration
curve at sensitivity 200 on the trackpoint here. It's still too slow at the
128 range but increasing the speed helps but it obviously can't make the
sensitivity the same way.

No idea how this works for ALPS trackpoints, I don't have one accessible to
me.

Any tests are appreciated.

This is tracked in issue 34
https://gitlab.freedesktop.org/libinput/libinput/issues/34

 src/filter-trackpoint.c | 250 +++++++++++-----------------------------
 1 file changed, 69 insertions(+), 181 deletions(-)

diff --git a/src/filter-trackpoint.c b/src/filter-trackpoint.c
index 760f0e6e..5e07851d 100644
--- a/src/filter-trackpoint.c
+++ b/src/filter-trackpoint.c
@@ -1,7 +1,7 @@
 /*
  * Copyright © 2006-2009 Simon Thum
  * Copyright © 2012 Jonas Ådahl
- * Copyright © 2014-2015 Red Hat, Inc.
+ * Copyright © 2014-2018 Red Hat, Inc.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -36,121 +36,38 @@
 #include "libinput-util.h"
 #include "filter-private.h"
 
-/* Trackpoint acceleration */
-#define TRACKPOINT_DEFAULT_MAX_ACCEL 2.0	/* in units/us */
-#define TRACKPOINT_DEFAULT_MAX_DELTA 120
-/* As measured on a Lenovo T440 at kernel-default sensitivity 128 */
-#define TRACKPOINT_DEFAULT_RANGE 20		/* max value */
-
-struct tablet_accelerator_flat {
-	struct motion_filter base;
-
-	double factor;
-	int xres, yres;
-	double xres_scale, /* 1000dpi : tablet res */
-	       yres_scale; /* 1000dpi : tablet res */
-};
-
 struct trackpoint_accelerator {
 	struct motion_filter base;
 
-	struct device_float_coords history[4];
-	size_t history_size;
-
-	double scale_factor;
-	double max_accel;
-	double max_delta;
-
-	double incline; /* incline of the function */
-	double offset; /* offset of the function */
+	struct pointer_trackers trackers;
+	double speed_factor;
 };
 
 double
 trackpoint_accel_profile(struct motion_filter *filter,
 			 void *data,
-			 double delta)
+			 double velocity)
 {
 	struct trackpoint_accelerator *accel_filter =
 		(struct trackpoint_accelerator *)filter;
-	const double max_accel = accel_filter->max_accel;
 	double factor;
 
-	delta = fabs(delta);
+	velocity = v_us2ms(velocity); /* make it units/ms */
 
-	/* This is almost the equivalent of the xserver acceleration
-	   at sensitivity 128 and speed 0.0 */
-	factor = delta * accel_filter->incline + accel_filter->offset;
-	factor = min(factor, max_accel);
+	/* Just a nice-enough curve that provides fluid factor conversion
+	 * from the minimum speed up to the real maximum. Generated by
+	 * https://www.mycurvefit.com/ with input data
+	 * 0    0.3
+	 * 0.1  1
+	 * 0.4  3
+	 * 0.6  4
+	 */
+	factor = 10.06254 + (0.3 - 10.06254)/(1 + pow(velocity/0.9205459, 1.15363));
 
+	factor *= accel_filter->speed_factor;
 	return factor;
 }
 
-/**
- * Average the deltas, they are messy and can provide sequences like 7, 7,
- * 9, 8, 14, 7, 9, 8 ... The outliers cause unpredictable jumps, so average
- * them out.
- */
-static inline struct device_float_coords
-trackpoint_average_delta(struct trackpoint_accelerator *filter,
-			 const struct device_float_coords *unaccelerated)
-{
-	size_t i;
-	struct device_float_coords avg = {0};
-
-	memmove(&filter->history[1],
-		&filter->history[0],
-		sizeof(*filter->history) * (filter->history_size - 1));
-	filter->history[0] = *unaccelerated;
-
-	for (i = 0; i < filter->history_size; i++) {
-		avg.x += filter->history[i].x;
-		avg.y += filter->history[i].y;
-	}
-	avg.x /= filter->history_size;
-	avg.y /= filter->history_size;
-
-	return avg;
-}
-
-/**
- * Undo any system-wide magic scaling, so we're behaving the same regardless
- * of the trackpoint hardware. This way we can apply our profile independent
- * of any other configuration that messes with things.
- */
-static inline struct device_float_coords
-trackpoint_normalize_deltas(const struct trackpoint_accelerator *accel_filter,
-			    const struct device_float_coords *delta)
-{
-	struct device_float_coords scaled = *delta;
-
-	scaled.x *= accel_filter->scale_factor;
-	scaled.y *= accel_filter->scale_factor;
-
-	return scaled;
-}
-
-/**
- * We set a max delta per event, to avoid extreme jumps once we exceed the
- * expected pressure. Trackpoint hardware is inconsistent once the pressure
- * gets high, so we can expect sequences like 30, 40, 35, 55, etc. This may
- * be caused by difficulty keeping up high consistent pressures or just
- * measuring errors in the hardware. Either way, we cap to a max delta so
- * once we hit the high pressures, movement is capped and consistent.
- */
-static inline struct normalized_coords
-trackpoint_clip_to_max_delta(const struct trackpoint_accelerator *accel_filter,
-			     struct normalized_coords coords)
-{
-	const double max_delta = accel_filter->max_delta;
-
-	if (abs(coords.x) > max_delta)
-		coords.x = copysign(max_delta, coords.x);
-	if (abs(coords.y) > max_delta)
-		coords.y = copysign(max_delta, coords.y);
-
-	return coords;
-}
-
 static struct normalized_coords
 trackpoint_accelerator_filter(struct motion_filter *filter,
 			      const struct device_float_coords *unaccelerated,
@@ -158,23 +75,16 @@ trackpoint_accelerator_filter(struct motion_filter *filter,
 {
 	struct trackpoint_accelerator *accel_filter =
 		(struct trackpoint_accelerator *)filter;
-	struct device_float_coords scaled;
-	struct device_float_coords avg;
 	struct normalized_coords coords;
 	double f;
-	double delta;
+	double velocity;
 
-	scaled = trackpoint_normalize_deltas(accel_filter, unaccelerated);
-	avg = trackpoint_average_delta(accel_filter, &scaled);
+	trackers_feed(&accel_filter->trackers, unaccelerated, time);
+	velocity = trackers_velocity(&accel_filter->trackers, time);
 
-	delta = hypot(avg.x, avg.y);
-
-	f = trackpoint_accel_profile(filter, data, delta);
-
-	coords.x = avg.x * f;
-	coords.y = avg.y * f;
-
-	coords = trackpoint_clip_to_max_delta(accel_filter, coords);
+	f = trackpoint_accel_profile(filter, data, velocity);
+	coords.x = unaccelerated->x * f;
+	coords.y = unaccelerated->y * f;
 
 	return coords;
 }
@@ -185,89 +95,72 @@ trackpoint_accelerator_filter_noop(struct motion_filter *filter,
 				   void *data, uint64_t time)
 {
 
-	struct trackpoint_accelerator *accel_filter =
-		(struct trackpoint_accelerator *)filter;
-	struct device_float_coords scaled;
-	struct device_float_coords avg;
 	struct normalized_coords coords;
 
-	scaled = trackpoint_normalize_deltas(accel_filter, unaccelerated);
-	avg = trackpoint_average_delta(accel_filter, &scaled);
-
-	coords.x = avg.x;
-	coords.y = avg.y;
-
-	coords = trackpoint_clip_to_max_delta(accel_filter, coords);
+	coords.x = unaccelerated->x;
+	coords.y = unaccelerated->y;
 
 	return coords;
 }
 
+/* Maps the [-1, 1] speed setting into a constant acceleration
+ * range. This isn't a linear scale, we keep 0 as the 'optimized'
+ * mid-point and scale down to 0 for setting -1 and up to 5 for
+ * setting 1. On the premise that if you want a faster cursor, it
+ * doesn't matter as much whether you have 0.56789 or 0.56790,
+ * but for lower settings it does because you may lose movements.
+ * *shrug*.
+ *
+ * Magic numbers calculated by MyCurveFit.com, data points were
+ *  0.0 0.0
+ *  0.1 0.1 (because we need 4 points)
+ *  1   1
+ *  2   5
+ *
+ *  This curve fits nicely into the range necessary.
+ */
+static inline double
+speed_factor(double s)
+{
+	s += 1; /* map to [0, 2] */
+	return 435837.2 + (0.04762636 - 435837.2)/(1 + pow(s/240.4549,
+							   2.377168));
+}
+
 static bool
 trackpoint_accelerator_set_speed(struct motion_filter *filter,
 				 double speed_adjustment)
 {
 	struct trackpoint_accelerator *accel_filter =
 		(struct trackpoint_accelerator*)filter;
-	double incline, offset, max;
 
 	assert(speed_adjustment >= -1.0 && speed_adjustment <= 1.0);
 
-	/* Helloooo, magic numbers.
-
-	   These numbers were obtained by finding an acceleration curve that
-	   provides precision at slow speeds but still provides a good
-	   acceleration at higher pressure - and a quick ramp-up to that
-	   acceleration.
-
-	   Trackpoints have built-in acceleration curves already, so we
-	   don't put a new function on top, we merely scale the output from
-	   those curves (re-calculating the pressure values from the
-	   firmware-defined curve and applying a new curve is unreliable).
-
-	   For that basic scaling, we assume a constant factor f based on
-	   the speed setting together with a maximum factor m (for this
-	   speed setting). Delta acceleration is thus:
-	      factor = max(m, f)
-	      accelerated_delta = delta * factor;
-
-	   Trial and error showed a couple of pairs that work well for the
-	   various speed settings (Lenovo T440, sensitivity 128):
-
-	       -1.0: f = 0.3, m = 1
-	       -0.5: f = 0.6, m = 2
-	        0.0: f = 1.0, m = 6
-	        0.5: f = 1.4, m = 8
-	        1.0: f = 1.9, m = 15
-
-	   Note: if f >= 2.0, some pixels are unaddressable
-
-	   Those pairs were fed into the linear/exponential regression tool
-	   at http://www.xuru.org/rt/LR.asp and show two functions that map
-	   speed settings to the respective f and m.
-	   Given a speed setting s in [-1.0, 1.0]
-		   f(s) = 0.8 * s + 1.04
-		   m(s) = 4.6 * e**(1.2 * s)
-	   These are close enough to the tested pairs.
-	*/
-
-	max = 4.6 * pow(M_E, 1.2 * speed_adjustment);
-	incline = 0.8 * speed_adjustment + 1.04;
-	offset = 0;
-
-	accel_filter->max_accel = max;
-	accel_filter->incline = incline;
-	accel_filter->offset = offset;
 	filter->speed_adjustment = speed_adjustment;
+	accel_filter->speed_factor = speed_factor(speed_adjustment);
+
 
 	return true;
 }
 
+static void
+trackpoint_accelerator_restart(struct motion_filter *filter,
+			       void *data,
+			       uint64_t time)
+{
+	struct trackpoint_accelerator *accel =
+		(struct trackpoint_accelerator *) filter;
+
+	trackers_reset(&accel->trackers, time);
+}
+
 static void
 trackpoint_accelerator_destroy(struct motion_filter *filter)
 {
 	struct trackpoint_accelerator *accel_filter =
 		(struct trackpoint_accelerator *)filter;
 
+	trackers_free(&accel_filter->trackers);
 	free(accel_filter);
 }
 
@@ -275,7 +168,7 @@ struct motion_filter_interface accelerator_interface_trackpoint = {
 	.type = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE,
 	.filter = trackpoint_accelerator_filter,
 	.filter_constant = trackpoint_accelerator_filter_noop,
-	.restart = NULL,
+	.restart = trackpoint_accelerator_restart,
 	.destroy = trackpoint_accelerator_destroy,
 	.set_speed = trackpoint_accelerator_set_speed,
 };
@@ -286,8 +179,8 @@ create_pointer_accelerator_filter_trackpoint(int max_hw_delta)
 	struct trackpoint_accelerator *filter;
 
 	/* Trackpoints are special. They don't have a movement speed like a
-	 * mouse or a finger, instead they send a constant stream of events
-	 * based on the pressure applied.
+	 * mouse or a finger, instead they send a stream of events based on
+	 * the pressure applied.
 	 *
 	 * Physical ranges on a trackpoint are the max values for relative
 	 * deltas, but these are highly device-specific.
@@ -298,17 +191,12 @@ create_pointer_accelerator_filter_trackpoint(int max_hw_delta)
 	if (!filter)
 		return NULL;
 
-	filter->history_size = ARRAY_LENGTH(filter->history);
-	filter->max_accel = TRACKPOINT_DEFAULT_MAX_ACCEL;
-	filter->max_delta = TRACKPOINT_DEFAULT_MAX_DELTA;
-
-	filter->scale_factor = 1.0 * TRACKPOINT_DEFAULT_RANGE / max_hw_delta;
-
-	/* Crop to a maximum 1.0 for the scale factor, otherwise we scale up
-	 * events from low-res trackpoints when really we should just take
-	 * those as-is.
+	/* FIXME: should figure out something here to deal with the
+	 * trackpoint range/max hw delta. Or we just make it a literal
+	 * "magic" number and live with it.
 	 */
-	filter->scale_factor = min(1.0, filter->scale_factor);
+
+	trackers_init(&filter->trackers);
 
 	filter->base.interface = &accelerator_interface_trackpoint;
 
-- 
2.17.1



More information about the wayland-devel mailing list