touchpad: don't feed 0/0 deltas into the accel filters
[platform/upstream/libinput.git] / src / filter.c
index 72ef9be..5b0b877 100644 (file)
  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <stdlib.h>
+#include "config.h"
+
 #include <stdio.h>
+#include <stdlib.h>
 #include <stdint.h>
 #include <limits.h>
 #include <math.h>
 
-#include <wayland-util.h>
-
-#include "compositor.h"
 #include "filter.h"
 
 void
-weston_filter_dispatch(struct weston_motion_filter *filter,
-                      struct weston_motion_params *motion,
-                      void *data, uint32_t time)
+filter_dispatch(struct motion_filter *filter,
+               struct motion_params *motion,
+               void *data, uint64_t time)
 {
        filter->interface->filter(filter, motion, data, time);
 }
 
+void
+filter_destroy(struct motion_filter *filter)
+{
+       if (!filter)
+               return;
+
+       filter->interface->destroy(filter);
+}
+
+/*
+ * Default parameters for pointer acceleration profiles.
+ */
+
+#define DEFAULT_THRESHOLD 0.4                  /* in units/ms */
+#define DEFAULT_ACCELERATION 2.0               /* unitless factor */
+
 /*
- * Pointer acceleration filter
+ * Pointer acceleration filter constants
  */
 
-#define MAX_VELOCITY_DIFF      1.0
+#define MAX_VELOCITY_DIFF      1.0 /* units/ms */
 #define MOTION_TIMEOUT         300 /* (ms) */
 #define NUM_POINTER_TRACKERS   16
 
 struct pointer_tracker {
-       double dx;
-       double dy;
-       uint32_t time;
+       double dx;      /* delta to most recent event, in device units */
+       double dy;      /* delta to most recent event, in device units */
+       uint64_t time;  /* ms */
        int dir;
 };
 
 struct pointer_accelerator;
 struct pointer_accelerator {
-       struct weston_motion_filter base;
+       struct motion_filter base;
 
        accel_profile_func_t profile;
 
-       double velocity;
-       double last_velocity;
-       int last_dx;
-       int last_dy;
+       double velocity;        /* units/ms */
+       double last_velocity;   /* units/ms */
+       int last_dx;            /* device units */
+       int last_dy;            /* device units */
 
        struct pointer_tracker *trackers;
        int cur_tracker;
@@ -98,15 +113,14 @@ get_direction(int dx, int dy)
                else if (dx < 0 && dy < 0)
                        dir = N | NW | W;
                else if (dx > 0)
-                       dir = NW | W | SW;
-               else if (dx < 0)
                        dir = NE | E | SE;
+               else if (dx < 0)
+                       dir = NW | W | SW;
                else if (dy > 0)
                        dir = SE | S | SW;
                else if (dy < 0)
                        dir = NE | N | NW;
-       }
-       else {
+       } else {
                /* Calculate r within the interval  [0 to 8)
                 *
                 * r = [0 .. 2π] where 0 is North
@@ -130,7 +144,7 @@ get_direction(int dx, int dy)
 static void
 feed_trackers(struct pointer_accelerator *accel,
              double dx, double dy,
-             uint32_t time)
+             uint64_t time)
 {
        int i, current;
        struct pointer_tracker *trackers = accel->trackers;
@@ -159,7 +173,7 @@ tracker_by_offset(struct pointer_accelerator *accel, unsigned int offset)
 }
 
 static double
-calculate_tracker_velocity(struct pointer_tracker *tracker, uint32_t time)
+calculate_tracker_velocity(struct pointer_tracker *tracker, uint64_t time)
 {
        int dx;
        int dy;
@@ -168,37 +182,24 @@ calculate_tracker_velocity(struct pointer_tracker *tracker, uint32_t time)
        dx = tracker->dx;
        dy = tracker->dy;
        distance = sqrt(dx*dx + dy*dy);
-       return distance / (double)(time - tracker->time);
+       return distance / (double)(time - tracker->time); /* units/ms */
 }
 
 static double
-calculate_velocity(struct pointer_accelerator *accel, uint32_t time)
+calculate_velocity(struct pointer_accelerator *accel, uint64_t time)
 {
        struct pointer_tracker *tracker;
        double velocity;
        double result = 0.0;
-       double initial_velocity;
+       double initial_velocity = 0.0;
        double velocity_diff;
        unsigned int offset;
 
        unsigned int dir = tracker_by_offset(accel, 0)->dir;
 
-       /* Find first velocity */
-       for (offset = 1; offset < NUM_POINTER_TRACKERS; offset++) {
-               tracker = tracker_by_offset(accel, offset);
-
-               if (time <= tracker->time)
-                       continue;
-
-               result = initial_velocity =
-                       calculate_tracker_velocity(tracker, time);
-               if (initial_velocity > 0.0)
-                       break;
-       }
-
        /* Find least recent vector within a timelimit, maximum velocity diff
         * and direction threshold. */
-       for (; offset < NUM_POINTER_TRACKERS; offset++) {
+       for (offset = 1; offset < NUM_POINTER_TRACKERS; offset++) {
                tracker = tracker_by_offset(accel, offset);
 
                /* Stop if too far away in time */
@@ -213,27 +214,31 @@ calculate_velocity(struct pointer_accelerator *accel, uint32_t time)
 
                velocity = calculate_tracker_velocity(tracker, time);
 
-               /* Stop if velocity differs too much from initial */
-               velocity_diff = fabs(initial_velocity - velocity);
-               if (velocity_diff > MAX_VELOCITY_DIFF)
-                       break;
+               if (initial_velocity == 0.0) {
+                       result = initial_velocity = velocity;
+               } else {
+                       /* Stop if velocity differs too much from initial */
+                       velocity_diff = fabs(initial_velocity - velocity);
+                       if (velocity_diff > MAX_VELOCITY_DIFF)
+                               break;
 
-               result = velocity;
+                       result = velocity;
+               }
        }
 
-       return result;
+       return result; /* units/ms */
 }
 
 static double
 acceleration_profile(struct pointer_accelerator *accel,
-                    void *data, double velocity, uint32_t time)
+                    void *data, double velocity, uint64_t time)
 {
        return accel->profile(&accel->base, data, velocity, time);
 }
 
 static double
 calculate_acceleration(struct pointer_accelerator *accel,
-                      void *data, double velocity, uint32_t time)
+                      void *data, double velocity, uint64_t time)
 {
        double factor;
 
@@ -248,39 +253,18 @@ calculate_acceleration(struct pointer_accelerator *accel,
 
        factor = factor / 6.0;
 
-       return factor;
-}
-
-static double
-soften_delta(double last_delta, double delta)
-{
-       if (delta < -1.0 || delta > 1.0) {
-               if (delta > last_delta)
-                       return delta - 0.5;
-               else if (delta < last_delta)
-                       return delta + 0.5;
-       }
-
-       return delta;
-}
-
-static void
-apply_softening(struct pointer_accelerator *accel,
-               struct weston_motion_params *motion)
-{
-       motion->dx = soften_delta(accel->last_dx, motion->dx);
-       motion->dy = soften_delta(accel->last_dy, motion->dy);
+       return factor; /* unitless factor */
 }
 
 static void
-accelerator_filter(struct weston_motion_filter *filter,
-                  struct weston_motion_params *motion,
-                  void *data, uint32_t time)
+accelerator_filter(struct motion_filter *filter,
+                  struct motion_params *motion,
+                  void *data, uint64_t time)
 {
        struct pointer_accelerator *accel =
                (struct pointer_accelerator *) filter;
-       double velocity;
-       double accel_value;
+       double velocity; /* units/ms */
+       double accel_value; /* unitless factor */
 
        feed_trackers(accel, motion->dx, motion->dy, time);
        velocity = calculate_velocity(accel, time);
@@ -289,8 +273,6 @@ accelerator_filter(struct weston_motion_filter *filter,
        motion->dx = accel_value * motion->dx;
        motion->dy = accel_value * motion->dy;
 
-       apply_softening(accel, motion);
-
        accel->last_dx = motion->dx;
        accel->last_dy = motion->dy;
 
@@ -298,7 +280,7 @@ accelerator_filter(struct weston_motion_filter *filter,
 }
 
 static void
-accelerator_destroy(struct weston_motion_filter *filter)
+accelerator_destroy(struct motion_filter *filter)
 {
        struct pointer_accelerator *accel =
                (struct pointer_accelerator *) filter;
@@ -307,12 +289,12 @@ accelerator_destroy(struct weston_motion_filter *filter)
        free(accel);
 }
 
-struct weston_motion_filter_interface accelerator_interface = {
+struct motion_filter_interface accelerator_interface = {
        accelerator_filter,
        accelerator_destroy
 };
 
-struct weston_motion_filter *
+struct motion_filter *
 create_pointer_accelator_filter(accel_profile_func_t profile)
 {
        struct pointer_accelerator *filter;
@@ -322,7 +304,6 @@ create_pointer_accelator_filter(accel_profile_func_t profile)
                return NULL;
 
        filter->base.interface = &accelerator_interface;
-       wl_list_init(&filter->base.link);
 
        filter->profile = profile;
        filter->last_velocity = 0.0;
@@ -335,3 +316,44 @@ create_pointer_accelator_filter(accel_profile_func_t profile)
 
        return &filter->base;
 }
+
+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, /* units/ms */
+                                   uint64_t time)
+{
+       double threshold = DEFAULT_THRESHOLD; /* units/ms */
+       double accel = DEFAULT_ACCELERATION; /* unitless factor */
+       double smooth_accel_coefficient; /* unitless factor */
+       double factor; /* unitless factor */
+
+       if (threshold < 0.1)
+               threshold = 0.1;
+       if (accel < 1.0)
+               accel = 1.0;
+
+
+       if (velocity < (threshold / 2.0))
+               return calc_penumbral_gradient(0.5 + velocity / threshold) * 2.0 - 1.0;
+
+       if (velocity <= threshold)
+               return 1.0;
+
+       factor = velocity/threshold;
+       if (factor >= accel)
+               return accel;
+
+       /* factor is between 1.0 and accel, scale this to 0.0 - 1.0 */
+       factor = (factor - 1.0) / (accel - 1.0);
+       smooth_accel_coefficient = calc_penumbral_gradient(factor);
+       return 1.0 + (smooth_accel_coefficient * (accel - 1.0));
+}