[PATCH libinput 07/23] filter: move the x230 filter into a separate file

Peter Hutterer peter.hutterer at who-t.net
Thu Apr 12 06:34:33 UTC 2018


There's a fair bit of duplication of code from filter.c but it's not worth
disecting this and optimising it. The device is 5 years old now, we don't want
to touch this accel method so duplication is good here.

Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
 meson.build                |   1 +
 src/filter-touchpad-x230.c | 356 +++++++++++++++++++++++++++++++++++++++++++++
 src/filter.c               | 136 -----------------
 3 files changed, 357 insertions(+), 136 deletions(-)
 create mode 100644 src/filter-touchpad-x230.c

diff --git a/meson.build b/meson.build
index 05a45659..b60ca1ac 100644
--- a/meson.build
+++ b/meson.build
@@ -151,6 +151,7 @@ dep_libinput_util = declare_dependency(link_with : libinput_util)
 ############ libfilter.a ############
 src_libfilter = [
 		'src/filter.c',
+		'src/filter-touchpad-x230.c',
 		'src/filter.h',
 		'src/filter-private.h'
 ]
diff --git a/src/filter-touchpad-x230.c b/src/filter-touchpad-x230.c
new file mode 100644
index 00000000..be58915e
--- /dev/null
+++ b/src/filter-touchpad-x230.c
@@ -0,0 +1,356 @@
+/*
+ * Copyright © 2006-2009 Simon Thum
+ * Copyright © 2012 Jonas Ådahl
+ * Copyright © 2014-2015 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <limits.h>
+#include <math.h>
+
+#include "filter.h"
+#include "libinput-util.h"
+#include "filter-private.h"
+
+/* Trackpoint acceleration for the Lenovo x230. DO NOT TOUCH.
+ * This code is only invoked on the X230 and is quite flimsy,
+ * custom-designed to make this touchpad less terrible than the
+ * out-of-the-box experience. The x230 was released in 2013, it's
+ * not worth trying to optimize the code or de-duplicate the various
+ * copy-pastes.
+ */
+
+/*
+ * Default parameters for pointer acceleration profiles.
+ */
+
+#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 */
+
+/* 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 */
+#define X230_MAGIC_SLOWDOWN 0.4			/* unitless */
+#define X230_TP_MAGIC_LOW_RES_FACTOR 4.0	/* unitless */
+
+/*
+ * Pointer acceleration filter constants
+ */
+#define NUM_POINTER_TRACKERS	16
+
+struct pointer_accelerator_x230 {
+	struct motion_filter base;
+
+	accel_profile_func_t profile;
+
+	double velocity;	/* units/us */
+	double last_velocity;	/* units/us */
+
+	struct pointer_trackers trackers;
+
+	double threshold;	/* units/us */
+	double accel;		/* unitless factor */
+	double incline;		/* incline of the function */
+
+	int dpi;
+};
+
+/**
+ * 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_x230 *accel,
+		     void *data, double velocity, uint64_t time)
+{
+	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_x230 *accel,
+		       void *data,
+		       double velocity,
+		       double last_velocity,
+		       uint64_t time)
+{
+	double factor;
+
+	/* Use Simpson's rule to calculate the avarage acceleration between
+	 * the previous motion and the most recent. */
+	factor = acceleration_profile(accel, data, velocity, time);
+	factor += acceleration_profile(accel, data, last_velocity, time);
+	factor += 4.0 *
+		acceleration_profile(accel, data,
+				     (last_velocity + velocity) / 2,
+				     time);
+
+	factor = factor / 6.0;
+
+	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_x230 *accel,
+			      const struct device_float_coords *unaccelerated,
+			      void *data,
+			      uint64_t time)
+{
+	double velocity; /* units/us in device-native dpi*/
+	double accel_factor;
+
+	feed_trackers(&accel->trackers, unaccelerated, time);
+	velocity = calculate_velocity(&accel->trackers, 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_x230(struct motion_filter *filter,
+			const struct device_float_coords *raw,
+			void *data, uint64_t time)
+{
+	struct pointer_accelerator_x230 *accel =
+		(struct pointer_accelerator_x230 *) filter;
+	double accel_factor; /* unitless factor */
+	struct normalized_coords accelerated;
+	struct device_float_coords delta_normalized;
+	struct normalized_coords unaccelerated;
+	double velocity; /* units/us */
+
+	/* 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->trackers, &delta_normalized, time);
+	velocity = calculate_velocity(&accel->trackers, time);
+	accel_factor = calculate_acceleration(accel,
+					      data,
+					      velocity,
+					      accel->last_velocity,
+					      time);
+	accel->last_velocity = velocity;
+
+	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 device_float_coords *unaccelerated,
+				 void *data, uint64_t time)
+{
+	struct pointer_accelerator_x230 *accel =
+		(struct pointer_accelerator_x230 *) filter;
+	struct normalized_coords normalized;
+	const double factor =
+		X230_MAGIC_SLOWDOWN/X230_TP_MAGIC_LOW_RES_FACTOR;
+
+	normalized = normalize_for_dpi(unaccelerated, accel->dpi);
+	normalized.x = factor * normalized.x;
+	normalized.y = factor * normalized.y;
+
+	return normalized;
+}
+
+static void
+accelerator_restart_x230(struct motion_filter *filter,
+			 void *data,
+			 uint64_t time)
+{
+	struct pointer_accelerator_x230 *accel =
+		(struct pointer_accelerator_x230 *) filter;
+	unsigned int offset;
+	struct pointer_tracker *tracker;
+
+	for (offset = 1; offset < accel->trackers.ntrackers; offset++) {
+		tracker = tracker_by_offset(&accel->trackers, offset);
+		tracker->time = 0;
+		tracker->dir = 0;
+		tracker->delta.x = 0;
+		tracker->delta.y = 0;
+	}
+
+	tracker = tracker_by_offset(&accel->trackers, 0);
+	tracker->time = time;
+	tracker->dir = UNDEFINED_DIRECTION;
+}
+
+static void
+accelerator_destroy_x230(struct motion_filter *filter)
+{
+	struct pointer_accelerator_x230 *accel =
+		(struct pointer_accelerator_x230 *) filter;
+
+	free(accel->trackers.trackers);
+	free(accel);
+}
+
+static bool
+accelerator_set_speed_x230(struct motion_filter *filter,
+			   double speed_adjustment)
+{
+	struct pointer_accelerator_x230 *accel_filter =
+		(struct pointer_accelerator_x230 *)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 -
+					v_ms2us(0.25) * speed_adjustment;
+	if (accel_filter->threshold < MINIMUM_THRESHOLD)
+		accel_filter->threshold = MINIMUM_THRESHOLD;
+
+	/* adjust max accel factor */
+	accel_filter->accel = DEFAULT_ACCELERATION + speed_adjustment * 1.5;
+
+	/* higher speed -> faster to reach max */
+	accel_filter->incline = DEFAULT_INCLINE + speed_adjustment * 0.75;
+
+	filter->speed_adjustment = speed_adjustment;
+	return true;
+}
+
+double
+touchpad_lenovo_x230_accel_profile(struct motion_filter *filter,
+				      void *data,
+				      double speed_in, /* 1000dpi-units/µs */
+				      uint64_t time)
+{
+	/* Those touchpads presents an actual lower resolution that what is
+	 * advertised. We see some jumps from the cursor due to the big steps
+	 * in X and Y when we are receiving data.
+	 * 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 */
+	double factor; /* unitless */
+	struct pointer_accelerator_x230 *accel_filter =
+		(struct pointer_accelerator_x230 *)filter;
+
+	double f1, f2; /* unitless */
+	const double max_accel = accel_filter->accel *
+				  X230_TP_MAGIC_LOW_RES_FACTOR; /* unitless factor */
+	const double threshold = accel_filter->threshold /
+				  X230_TP_MAGIC_LOW_RES_FACTOR; /* units/us */
+	const double incline = accel_filter->incline * X230_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 explanation of what the min/max/etc. does.
+	 */
+	speed_in *= X230_MAGIC_SLOWDOWN / X230_TP_MAGIC_LOW_RES_FACTOR;
+
+	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);
+
+	return factor * X230_MAGIC_SLOWDOWN / X230_TP_MAGIC_LOW_RES_FACTOR;
+}
+
+struct motion_filter_interface accelerator_interface_x230 = {
+	.type = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE,
+	.filter = accelerator_filter_x230,
+	.filter_constant = accelerator_filter_constant_x230,
+	.restart = accelerator_restart_x230,
+	.destroy = accelerator_destroy_x230,
+	.set_speed = accelerator_set_speed_x230,
+};
+
+/* 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_x230 *filter;
+
+	filter = zalloc(sizeof *filter);
+	filter->base.interface = &accelerator_interface_x230;
+	filter->profile = touchpad_lenovo_x230_accel_profile;
+	filter->last_velocity = 0.0;
+
+	init_trackers(&filter->trackers, NUM_POINTER_TRACKERS);
+
+	filter->threshold = X230_THRESHOLD;
+	filter->accel = X230_ACCELERATION; /* unitless factor */
+	filter->incline = X230_INCLINE; /* incline of the acceleration function */
+	filter->dpi = dpi;
+
+	return &filter->base;
+}
diff --git a/src/filter.c b/src/filter.c
index d2106868..1056298a 100644
--- a/src/filter.c
+++ b/src/filter.c
@@ -109,13 +109,6 @@ filter_get_type(struct motion_filter *filter)
 #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 */
-#define X230_INCLINE 1.1			/* unitless factor */
-#define X230_MAGIC_SLOWDOWN 0.4			/* unitless */
-#define X230_TP_MAGIC_LOW_RES_FACTOR 4.0	/* unitless */
-
 /* Trackpoint acceleration */
 #define TRACKPOINT_DEFAULT_MAX_ACCEL 2.0	/* in units/us */
 #define TRACKPOINT_DEFAULT_MAX_DELTA 60
@@ -547,63 +540,6 @@ accelerator_filter_noop(struct motion_filter *filter,
 	return normalize_for_dpi(unaccelerated, accel->dpi);
 }
 
-static struct normalized_coords
-accelerator_filter_x230(struct motion_filter *filter,
-			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 */
-
-	/* 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->trackers, &delta_normalized, time);
-	velocity = calculate_velocity(&accel->trackers, time);
-	accel_factor = calculate_acceleration(accel,
-					      data,
-					      velocity,
-					      accel->last_velocity,
-					      time);
-	accel->last_velocity = velocity;
-
-	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 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 = 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)
@@ -888,45 +824,6 @@ touchpad_accel_profile_linear(struct motion_filter *filter,
 	return factor * TP_MAGIC_SLOWDOWN;
 }
 
-double
-touchpad_lenovo_x230_accel_profile(struct motion_filter *filter,
-				      void *data,
-				      double speed_in, /* 1000dpi-units/µs */
-				      uint64_t time)
-{
-	/* Those touchpads presents an actual lower resolution that what is
-	 * advertised. We see some jumps from the cursor due to the big steps
-	 * in X and Y when we are receiving data.
-	 * 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 */
-	double factor; /* unitless */
-	struct pointer_accelerator *accel_filter =
-		(struct pointer_accelerator *)filter;
-
-	double f1, f2; /* unitless */
-	const double max_accel = accel_filter->accel *
-				  X230_TP_MAGIC_LOW_RES_FACTOR; /* unitless factor */
-	const double threshold = accel_filter->threshold /
-				  X230_TP_MAGIC_LOW_RES_FACTOR; /* units/us */
-	const double incline = accel_filter->incline * X230_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 explanation of what the min/max/etc. does.
-	 */
-	speed_in *= X230_MAGIC_SLOWDOWN / X230_TP_MAGIC_LOW_RES_FACTOR;
-
-	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);
-
-	return factor * X230_MAGIC_SLOWDOWN / X230_TP_MAGIC_LOW_RES_FACTOR;
-}
-
 struct motion_filter_interface accelerator_interface = {
 	.type = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE,
 	.filter = accelerator_filter_pre_normalized,
@@ -1025,39 +922,6 @@ create_pointer_accelerator_filter_touchpad(int dpi,
 	return &filter->base;
 }
 
-struct motion_filter_interface accelerator_interface_x230 = {
-	.type = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE,
-	.filter = accelerator_filter_x230,
-	.filter_constant = accelerator_filter_constant_x230,
-	.restart = accelerator_restart,
-	.destroy = accelerator_destroy,
-	.set_speed = 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 = zalloc(sizeof *filter);
-	filter->base.interface = &accelerator_interface_x230;
-	filter->profile = touchpad_lenovo_x230_accel_profile;
-	filter->last_velocity = 0.0;
-
-	init_trackers(&filter->trackers, NUM_POINTER_TRACKERS);
-
-	filter->threshold = X230_THRESHOLD;
-	filter->accel = X230_ACCELERATION; /* unitless factor */
-	filter->incline = X230_INCLINE; /* incline of the acceleration function */
-	filter->dpi = dpi;
-
-	return &filter->base;
-}
-
 double
 trackpoint_accel_profile(struct motion_filter *filter,
 			 void *data,
-- 
2.14.3



More information about the wayland-devel mailing list