Add a configuration option for natural scrolling
[platform/upstream/libinput.git] / src / filter.c
index 91e588d..defbcae 100644 (file)
  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include "config.h"
+
+#include <assert.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"
+#include "libinput-util.h"
+#include "filter-private.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);
+}
+
+bool
+filter_set_speed(struct motion_filter *filter,
+                double speed)
+{
+       return filter->interface->set_speed(filter, speed);
+}
+
+double
+filter_get_speed(struct motion_filter *filter)
+{
+       return filter->speed;
+}
+
+/*
+ * Default parameters for pointer acceleration profiles.
+ */
+
+#define DEFAULT_THRESHOLD 0.4                  /* in units/ms */
+#define DEFAULT_ACCELERATION 2.0               /* unitless factor */
+#define DEFAULT_INCLINE 1.1                    /* 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;
-};
 
-enum directions {
-       N  = 1 << 0,
-       NE = 1 << 1,
-       E  = 1 << 2,
-       SE = 1 << 3,
-       S  = 1 << 4,
-       SW = 1 << 5,
-       W  = 1 << 6,
-       NW = 1 << 7,
-       UNDEFINED_DIRECTION = 0xff
+       double threshold;       /* units/ms */
+       double accel;           /* unitless factor */
+       double incline;         /* incline of the function */
 };
 
-static int
-get_direction(int dx, int dy)
-{
-       int dir = UNDEFINED_DIRECTION;
-       int d1, d2;
-       double r;
-
-       if (abs(dx) < 2 && abs(dy) < 2) {
-               if (dx > 0 && dy > 0)
-                       dir = S | SE | E;
-               else if (dx > 0 && dy < 0)
-                       dir = N | NE | E;
-               else if (dx < 0 && dy > 0)
-                       dir = S | SW | W;
-               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 (dy > 0)
-                       dir = SE | S | SW;
-               else if (dy < 0)
-                       dir = NE | N | NW;
-       }
-       else {
-               /* Calculate r within the interval  [0 to 8)
-                *
-                * r = [0 .. 2π] where 0 is North
-                * d_f = r / 2π  ([0 .. 1))
-                * d_8 = 8 * d_f
-                */
-               r = atan2(dy, dx);
-               r = fmod(r + 2.5*M_PI, 2*M_PI);
-               r *= 4*M_1_PI;
-
-               /* Mark one or two close enough octants */
-               d1 = (int)(r + 0.9) % 8;
-               d2 = (int)(r + 0.1) % 8;
-
-               dir = (1 << d1) | (1 << d2);
-       }
-
-       return dir;
-}
-
 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;
@@ -145,7 +124,7 @@ feed_trackers(struct pointer_accelerator *accel,
        trackers[current].dx = 0.0;
        trackers[current].dy = 0.0;
        trackers[current].time = time;
-       trackers[current].dir = get_direction(dx, dy);
+       trackers[current].dir = vector_get_direction(dx, dy);
 }
 
 static struct pointer_tracker *
@@ -158,7 +137,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;
@@ -167,37 +146,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 */
@@ -212,27 +178,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;
 
@@ -247,39 +217,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;
+       return factor; /* unitless factor */
 }
 
 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);
-}
-
-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);
@@ -288,8 +237,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;
 
@@ -297,7 +244,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;
@@ -306,12 +253,35 @@ accelerator_destroy(struct weston_motion_filter *filter)
        free(accel);
 }
 
-struct weston_motion_filter_interface accelerator_interface = {
+static bool
+accelerator_set_speed(struct motion_filter *filter,
+                     double speed)
+{
+       struct pointer_accelerator *accel_filter =
+               (struct pointer_accelerator *)filter;
+
+       assert(speed >= -1.0 && speed <= 1.0);
+
+       /* delay when accel kicks in */
+       accel_filter->threshold = DEFAULT_THRESHOLD - speed/6.0;
+
+       /* adjust max accel factor */
+       accel_filter->accel = DEFAULT_ACCELERATION + speed;
+
+       /* higher speed -> faster to reach max */
+       accel_filter->incline = DEFAULT_INCLINE + speed/2.0;
+
+       filter->speed = speed;
+       return true;
+}
+
+struct motion_filter_interface accelerator_interface = {
        accelerator_filter,
-       accelerator_destroy
+       accelerator_destroy,
+       accelerator_set_speed,
 };
 
-struct weston_motion_filter *
+struct motion_filter *
 create_pointer_accelator_filter(accel_profile_func_t profile)
 {
        struct pointer_accelerator *filter;
@@ -321,7 +291,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;
@@ -332,5 +301,37 @@ create_pointer_accelator_filter(accel_profile_func_t profile)
                calloc(NUM_POINTER_TRACKERS, sizeof *filter->trackers);
        filter->cur_tracker = 0;
 
+       filter->threshold = DEFAULT_THRESHOLD;
+       filter->accel = DEFAULT_ACCELERATION;
+       filter->incline = DEFAULT_INCLINE;
+
        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_linear(struct motion_filter *filter,
+                            void *data,
+                            double speed_in,
+                            uint64_t time)
+{
+       struct pointer_accelerator *accel_filter =
+               (struct pointer_accelerator *)filter;
+
+       double s1, s2;
+       const double max_accel = accel_filter->accel; /* unitless factor */
+       const double threshold = accel_filter->threshold; /* units/ms */
+       const double incline = accel_filter->incline;
+
+       s1 = min(1, speed_in * 5);
+       s2 = 1 + (speed_in - threshold) * incline;
+
+       return min(max_accel, s2 > 1 ? s2 : s1);
+}