From 348401a5de950314faadfdd69d160cdeda1362fa Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 23 Jun 2015 12:45:16 +1000 Subject: [PATCH libinput 9/9] filter: add a custom low-dpi acceleration 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 --- 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 b98b47a..1edf93f 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1823,6 +1823,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) { @@ -1914,9 +1927,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 04dd2b9..35449f5 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