Add basic mouse pointer acceleration
authorJonas Ådahl <jadahl@gmail.com>
Sun, 18 May 2014 17:20:39 +0000 (19:20 +0200)
committerJonas Ådahl <jadahl@gmail.com>
Thu, 29 May 2014 11:06:32 +0000 (13:06 +0200)
This patch reimplements the simple smooth pointer acceleration profile
from X.org xserver. The algorithm is identical to the classic profile
with a non-zero pointer acceleration threshold.

When support for changable parameters is in place, to get a pointer
acceleration the same as the default classic profile of X.org a
polynomial acceleration profile should be used for when the threshold
parameter is zero.

Signed-off-by: Jonas Ådahl <jadahl@gmail.com>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
src/evdev.c
src/evdev.h
src/filter.c
src/filter.h

index 08a18fd82cd4487127b0c0b08526ffa36bbcd922..d23e349a690f4be7d460e2afb628cf80e9bb940e 100644 (file)
 #include <mtdev-plumbing.h>
 #include <assert.h>
 #include <time.h>
+#include <math.h>
 
 #include "libinput.h"
 #include "evdev.h"
+#include "filter.h"
 #include "libinput-private.h"
 
 #define DEFAULT_AXIS_STEP_DISTANCE li_fixed_from_int(10)
@@ -112,8 +114,10 @@ evdev_device_transform_y(struct evdev_device *device,
 static void
 evdev_flush_pending_event(struct evdev_device *device, uint64_t time)
 {
+       struct motion_params motion;
        int32_t cx, cy;
        li_fixed_t x, y;
+       li_fixed_t dx, dy;
        int slot;
        int seat_slot;
        struct libinput_device *base = &device->base;
@@ -125,12 +129,20 @@ evdev_flush_pending_event(struct evdev_device *device, uint64_t time)
        case EVDEV_NONE:
                return;
        case EVDEV_RELATIVE_MOTION:
-               pointer_notify_motion(base,
-                                     time,
-                                     device->rel.dx,
-                                     device->rel.dy);
+               motion.dx = li_fixed_to_double(device->rel.dx);
+               motion.dy = li_fixed_to_double(device->rel.dy);
                device->rel.dx = 0;
                device->rel.dy = 0;
+
+               /* Apply pointer acceleration. */
+               filter_dispatch(device->pointer.filter, &motion, device, time);
+
+               dx = li_fixed_from_double(motion.dx);
+               dy = li_fixed_from_double(motion.dy);
+               if (dx == 0 && dy == 0)
+                       break;
+
+               pointer_notify_motion(base, time, dx, dy);
                break;
        case EVDEV_ABSOLUTE_MT_DOWN:
                if (!(device->seat_caps & EVDEV_DEVICE_TOUCH))
@@ -566,6 +578,18 @@ evdev_device_dispatch(void *data)
        }
 }
 
+static int
+configure_pointer_acceleration(struct evdev_device *device)
+{
+       device->pointer.filter =
+               create_pointer_accelator_filter(
+                       pointer_accel_profile_smooth_simple);
+       if (!device->pointer.filter)
+               return -1;
+
+       return 0;
+}
+
 static int
 evdev_configure_device(struct evdev_device *device)
 {
@@ -676,7 +700,11 @@ evdev_configure_device(struct evdev_device *device)
                has_keyboard = 1;
 
        if ((has_abs || has_rel) && has_button) {
+               if (configure_pointer_acceleration(device) == -1)
+                       return -1;
+
                device->seat_caps |= EVDEV_DEVICE_POINTER;
+
                log_info("input device '%s', %s is a pointer caps =%s%s%s\n",
                         device->devname, device->devnode,
                         has_abs ? " absolute-motion" : "",
@@ -849,6 +877,7 @@ evdev_device_destroy(struct evdev_device *device)
        if (dispatch)
                dispatch->interface->destroy(dispatch);
 
+       motion_filter_destroy(device->pointer.filter);
        libinput_seat_unref(device->base.seat);
        libevdev_free(device->evdev);
        free(device->mt.slots);
index bb88074edc56b064ec59c0dc70d2ba5aa67a465b..c9717b56bdfd1df8bc68e94a0ee9085eb9ed2231 100644 (file)
@@ -90,6 +90,10 @@ struct evdev_device {
        enum evdev_device_seat_capability seat_caps;
 
        int is_mt;
+
+       struct {
+               struct motion_filter *filter;
+       } pointer;
 };
 
 #define EVDEV_UNHANDLED_DEVICE ((struct evdev_device *) 1)
index 22c3ed8ad85121d5f4ed97b3926df5cf9b64241b..45f060f923f0fa6e55a7d0e8c820f8a6c70486d9 100644 (file)
@@ -22,6 +22,7 @@
 
 #include "config.h"
 
+#include <stdio.h>
 #include <stdlib.h>
 #include <stdint.h>
 #include <limits.h>
@@ -38,7 +39,15 @@ filter_dispatch(struct motion_filter *filter,
 }
 
 /*
- * Pointer acceleration filter
+ * Default parameters for pointer acceleration profiles.
+ */
+
+#define DEFAULT_CONSTANT_ACCELERATION 10.0
+#define DEFAULT_THRESHOLD 4.0
+#define DEFAULT_ACCELERATION 2.0
+
+/*
+ * Pointer acceleration filter constants
  */
 
 #define MAX_VELOCITY_DIFF      1.0
@@ -340,3 +349,39 @@ motion_filter_destroy(struct motion_filter *filter)
 
        filter->interface->destroy(filter);
 }
+
+static inline double
+calc_penumbral_gradient(double x)
+{
+       x *= 2.0;
+       x -= 1.0;
+       return 0.5 + (x * sqrt(1.0 - x * x) + asin(x)) / M_PI;
+}
+
+double
+pointer_accel_profile_smooth_simple(struct motion_filter *filter,
+                                   void *data,
+                                   double velocity,
+                                   uint64_t time)
+{
+       double threshold = DEFAULT_THRESHOLD;
+       double accel = DEFAULT_ACCELERATION;
+       double smooth_accel_coefficient;
+
+       velocity *= DEFAULT_CONSTANT_ACCELERATION;
+
+       if (velocity < 1.0)
+               return calc_penumbral_gradient(0.5 + velocity * 0.5) * 2.0 - 1.0;
+       if (threshold < 1.0)
+               threshold = 1.0;
+       if (velocity <= threshold)
+               return 1;
+       velocity /= threshold;
+       if (velocity >= accel) {
+               return accel;
+       } else {
+               smooth_accel_coefficient =
+                       calc_penumbral_gradient(velocity / accel);
+               return 1.0 + (smooth_accel_coefficient * (accel - 1.0));
+       }
+}
index ada4f9306d31e3bfa2dbd3fd6960f17859fe730a..c0219eeb33828f95e28ed597d82d5b5157d4dbbc 100644 (file)
@@ -62,4 +62,18 @@ create_pointer_accelator_filter(accel_profile_func_t filter);
 void
 motion_filter_destroy(struct motion_filter *filter);
 
+/*
+ * Pointer acceleration profiles.
+ */
+
+/*
+ * Profile similar which is similar to nonaccelerated but with a smooth
+ * transition between accelerated and non-accelerated.
+ */
+double
+pointer_accel_profile_smooth_simple(struct motion_filter *filter,
+                                   void *data,
+                                   double velocity,
+                                   uint64_t time);
+
 #endif /* FILTER_H */