[PATCH libinput 5/5] filter: add a custom low-dpi acceleration

Peter Hutterer peter.hutterer at who-t.net
Thu Jun 25 21:08:13 PDT 2015


Motion normalization does not work well for devices below the default 1000dpi
rate. A 400dpi mouse's minimum movement generates a 2.5 normalized motion,
causing it to skip pixels at low speeds even when unaccelerated.

Likewise, we don't want 1000dpi mice to be normalized to a 400dpi mouse, it
feels sluggish even at higher acceleration speeds.
Instead, add a custom acceleration method for lower-dpi mice. At low-speeds,
one device unit results in a one-pixel movement. Depending on the DPI factor,
the acceleration kicks in earlier and goes to higher acceleration so faster
movements with a low-dpi mouse feel approximately the same as the same
movement on a higher-dpi mouse.

https://bugzilla.redhat.com/show_bug.cgi?id=1231304

Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
 src/evdev.c  | 17 ++++++++++++++---
 src/filter.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++------
 src/filter.h |  5 +++++
 3 files changed, 69 insertions(+), 9 deletions(-)

diff --git a/src/evdev.c b/src/evdev.c
index 62e6b83..4c147b3 100644
--- a/src/evdev.c
+++ b/src/evdev.c
@@ -1828,6 +1828,19 @@ evdev_configure_mt_device(struct evdev_device *device)
 	return 0;
 }
 
+static inline int
+evdev_init_accel(struct evdev_device *device)
+{
+	accel_profile_func_t profile;
+
+	if (device->dpi < DEFAULT_MOUSE_DPI)
+		profile = pointer_accel_profile_linear_low_dpi;
+	else
+		profile = pointer_accel_profile_linear;
+
+	return evdev_device_init_pointer_acceleration(device, profile);
+}
+
 static int
 evdev_configure_device(struct evdev_device *device)
 {
@@ -1923,9 +1936,7 @@ evdev_configure_device(struct evdev_device *device)
 	    udev_tags & EVDEV_UDEV_TAG_POINTINGSTICK) {
 		if (libevdev_has_event_code(evdev, EV_REL, REL_X) &&
 		    libevdev_has_event_code(evdev, EV_REL, REL_Y) &&
-		    evdev_device_init_pointer_acceleration(
-					device,
-					pointer_accel_profile_linear) == -1)
+		    evdev_init_accel(device) == -1)
 			return -1;
 
 		device->seat_caps |= EVDEV_DEVICE_POINTER;
diff --git a/src/filter.c b/src/filter.c
index b1fd67f..b47360e 100644
--- a/src/filter.c
+++ b/src/filter.c
@@ -264,8 +264,16 @@ accelerator_filter(struct motion_filter *filter,
 	double velocity; /* units/ms */
 	double accel_value; /* unitless factor */
 	struct normalized_coords accelerated;
+	struct normalized_coords unnormalized;
+	double dpi_factor = accel->dpi_factor;
 
-	feed_trackers(accel, unaccelerated, time);
+	/* 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;
+
+	feed_trackers(accel, &unnormalized, time);
 	velocity = calculate_velocity(accel, time);
 	accel_value = calculate_acceleration(accel,
 					     data,
@@ -273,10 +281,10 @@ accelerator_filter(struct motion_filter *filter,
 					     accel->last_velocity,
 					     time);
 
-	accelerated.x = accel_value * unaccelerated->x;
-	accelerated.y = accel_value * unaccelerated->y;
+	accelerated.x = accel_value * unnormalized.x;
+	accelerated.y = accel_value * unnormalized.y;
 
-	accel->last = *unaccelerated;
+	accel->last = unnormalized;
 
 	accel->last_velocity = velocity;
 
@@ -372,15 +380,51 @@ create_pointer_accelerator_filter(accel_profile_func_t profile,
 	filter->accel = DEFAULT_ACCELERATION;
 	filter->incline = DEFAULT_INCLINE;
 
-	filter->dpi = dpi;
+	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.
+ * The threshold/max accel depends on the DPI, the smaller the DPI the
+ * earlier we accelerate and the higher the maximum acceleration is. Result:
+ * at low speeds we get pixel-precision, at high speeds we get approx. the
+ * same movement as a high-dpi mouse.
+ *
+ * Note: data fed to this function is in device units, not normalized.
+ */
+double
+pointer_accel_profile_linear_low_dpi(struct motion_filter *filter,
+				     void *data,
+				     double speed_in, /* in device units */
+				     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 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);
+	s2 = 1 + (speed_in - threshold * dpi_factor) * incline;
+
+	factor = min(max_accel, s2 > 1 ? s2 : s1);
+
+	return factor;
+}
+
 double
 pointer_accel_profile_linear(struct motion_filter *filter,
 			     void *data,
-			     double speed_in,
+			     double speed_in, /* 1000-dpi normalized */
 			     uint64_t time)
 {
 	struct pointer_accelerator *accel_filter =
diff --git a/src/filter.h b/src/filter.h
index 64a8b50..617fab1 100644
--- a/src/filter.h
+++ b/src/filter.h
@@ -66,6 +66,11 @@ create_pointer_accelerator_filter(accel_profile_func_t filter,
  */
 
 double
+pointer_accel_profile_linear_low_dpi(struct motion_filter *filter,
+				     void *data,
+				     double speed_in,
+				     uint64_t time);
+double
 pointer_accel_profile_linear(struct motion_filter *filter,
 			     void *data,
 			     double speed_in,
-- 
2.4.3



More information about the wayland-devel mailing list