AccelFilterInterpreter: New Mouse Pointer Acceleration Curves

We add new acceleration curves for mouse pointing. We also keep the
old curves available with a property to enable them (is settable from
inputcontrol).

Also, change how the custom curves are enabled: instead of setting
sensitivity to <1 or >5, we now have separate properties to toggle use
of custom curves. This will be settable from inputcontrol and allow
for custom curve changes to persist across logout/login.

BUG=chromium:359288
TEST=Will add regression test before checking in

Change-Id: I577547ba819f74f954de11faaa87294de10e437f
Reviewed-on: https://chromium-review.googlesource.com/209947
Reviewed-by: Andrew de los Reyes <adlr@chromium.org>
Commit-Queue: Andrew de los Reyes <adlr@chromium.org>
Tested-by: Andrew de los Reyes <adlr@chromium.org>
diff --git a/include/accel_filter_interpreter.h b/include/accel_filter_interpreter.h
index 7a9bdb8..9d8309c 100644
--- a/include/accel_filter_interpreter.h
+++ b/include/accel_filter_interpreter.h
@@ -54,25 +54,41 @@
 
   // curves for sensitivity 1..5
   CurveSegment point_curves_[kMaxAccelCurves][kMaxCurveSegs];
+  CurveSegment old_mouse_point_curves_[kMaxAccelCurves][kMaxCurveSegs];
   CurveSegment mouse_point_curves_[kMaxAccelCurves][kMaxCurveSegs];
   CurveSegment scroll_curves_[kMaxAccelCurves][kMaxCurveSegs];
 
   // Custom curves
-  CurveSegment custom_point_[kMaxCustomCurveSegs];
-  CurveSegment custom_scroll_[kMaxCustomCurveSegs];
+  CurveSegment tp_custom_point_[kMaxCustomCurveSegs];
+  CurveSegment tp_custom_scroll_[kMaxCustomCurveSegs];
+  CurveSegment mouse_custom_point_[kMaxCustomCurveSegs];
+  // Note: there is no mouse_custom_scroll_ b/c mouse wheel accel is
+  // handled in the MouseInterpreter class.
 
-  IntProperty pointer_sensitivity_;  // [1..5] or 0 for custom
-  IntProperty scroll_sensitivity_;  // [1..5] or 0 for custom
+  // These properties expose the custom curves (just above) to the
+  // property system.
+  DoubleArrayProperty tp_custom_point_prop_;
+  DoubleArrayProperty tp_custom_scroll_prop_;
+  DoubleArrayProperty mouse_custom_point_prop_;
 
-  DoubleArrayProperty custom_point_prop_;
-  DoubleArrayProperty custom_scroll_prop_;
+  // These properties enable use of custom curves (just above).
+  BoolProperty use_custom_tp_point_curve_;
+  BoolProperty use_custom_tp_scroll_curve_;
+  BoolProperty use_custom_mouse_curve_;
+
+  IntProperty pointer_sensitivity_;  // [1..5]
+  IntProperty scroll_sensitivity_;  // [1..5]
 
   DoubleProperty point_x_out_scale_;
   DoubleProperty point_y_out_scale_;
   DoubleProperty scroll_x_out_scale_;
   DoubleProperty scroll_y_out_scale_;
-  BoolProperty use_mouse_point_curves_;
-  BoolProperty use_mouse_scroll_curves_;
+  // These properties are automatically set on mice-like devices:
+  BoolProperty use_mouse_point_curves_;  // set on {touch,nontouch} mice
+  BoolProperty use_mouse_scroll_curves_;  // set on nontouch mice
+  // If use_mouse_point_curves_ is true, this is consulted to see which
+  // curves to use:
+  BoolProperty use_old_mouse_point_curves_;
 
   // Sometimes on wireless hardware (e.g. Bluetooth), patckets need to be
   // resent. This can lead to a time between packets that very large followed
diff --git a/src/accel_filter_interpreter.cc b/src/accel_filter_interpreter.cc
index 36dbd6c..f63d876 100644
--- a/src/accel_filter_interpreter.cc
+++ b/src/accel_filter_interpreter.cc
@@ -20,21 +20,32 @@
                                                Interpreter* next,
                                                Tracer* tracer)
     : FilterInterpreter(NULL, next, tracer, false),
+      // Hack: cast tp_custom_point_/mouse_custom_point_/tp_custom_scroll_
+      // to float arrays.
+      tp_custom_point_prop_(prop_reg, "Pointer Accel Curve",
+                            reinterpret_cast<double*>(&tp_custom_point_),
+                            sizeof(tp_custom_point_) / sizeof(double)),
+      tp_custom_scroll_prop_(prop_reg, "Scroll Accel Curve",
+                             reinterpret_cast<double*>(&tp_custom_scroll_),
+                             sizeof(tp_custom_scroll_) / sizeof(double)),
+      mouse_custom_point_prop_(prop_reg, "Mouse Pointer Accel Curve",
+                               reinterpret_cast<double*>(&mouse_custom_point_),
+                               sizeof(mouse_custom_point_) / sizeof(double)),
+      use_custom_tp_point_curve_(
+          prop_reg, "Use Custom Touchpad Pointer Accel Curve", 0),
+      use_custom_tp_scroll_curve_(
+          prop_reg, "Use Custom Touchpad Scroll Accel Curve", 0),
+      use_custom_mouse_curve_(
+          prop_reg, "Use Custom Mouse Pointer Accel Curve", 0),
       pointer_sensitivity_(prop_reg, "Pointer Sensitivity", 3),
       scroll_sensitivity_(prop_reg, "Scroll Sensitivity", 3),
-      // Hack: cast custom_point_/custom_scroll_ to float arrays.
-      custom_point_prop_(prop_reg, "Pointer Accel Curve",
-                         reinterpret_cast<double*>(&custom_point_),
-                         sizeof(custom_point_) / sizeof(double)),
-      custom_scroll_prop_(prop_reg, "Scroll Accel Curve",
-                          reinterpret_cast<double*>(&custom_scroll_),
-                          sizeof(custom_scroll_) / sizeof(double)),
       point_x_out_scale_(prop_reg, "Point X Out Scale", 1.0),
       point_y_out_scale_(prop_reg, "Point Y Out Scale", 1.0),
       scroll_x_out_scale_(prop_reg, "Scroll X Out Scale", 3.0),
       scroll_y_out_scale_(prop_reg, "Scroll Y Out Scale", 3.0),
       use_mouse_point_curves_(prop_reg, "Mouse Accel Curves", 0),
       use_mouse_scroll_curves_(prop_reg, "Mouse Scroll Curves", 0),
+      use_old_mouse_point_curves_(prop_reg, "Old Mouse Accel Curves", 0),
       min_reasonable_dt_(prop_reg, "Accel Min dt", 0.003),
       max_reasonable_dt_(prop_reg, "Accel Max dt", 0.050),
       last_reasonable_dt_(0.05),
@@ -70,26 +81,42 @@
     point_curves_[i][2] = CurveSegment(INFINITY, 0, slope, icept);
   }
 
-  const float mouse_speed_straight_cutoff[] = { 5.0, 5.0, 5.0, 8.0, 8.0 };
-  const float mouse_speed_accel[] = { 1.0, 1.4, 1.8, 2.0, 2.2 };
+  const float old_mouse_speed_straight_cutoff[] = { 5.0, 5.0, 5.0, 8.0, 8.0 };
+  const float old_mouse_speed_accel[] = { 1.0, 1.4, 1.8, 2.0, 2.2 };
 
   for (size_t i = 0; i < kMaxAccelCurves; ++i) {
     const float kParabolaA = 1.3;
     const float kParabolaB = 0.2;
-    const float cutoff_x = mouse_speed_straight_cutoff[i];
+    const float cutoff_x = old_mouse_speed_straight_cutoff[i];
     const float cutoff_y =
         kParabolaA * cutoff_x * cutoff_x + kParabolaB * cutoff_x;
     const float line_m = 2.0 * kParabolaA * cutoff_x + kParabolaB;
     const float line_b = cutoff_y - cutoff_x * line_m;
-    const float kOutMult = mouse_speed_accel[i];
+    const float kOutMult = old_mouse_speed_accel[i];
 
-    mouse_point_curves_[i][0] =
+    old_mouse_point_curves_[i][0] =
         CurveSegment(cutoff_x * 25.4, kParabolaA * kOutMult / 25.4,
                      kParabolaB * kOutMult, 0.0);
-    mouse_point_curves_[i][1] = CurveSegment(INFINITY, 0.0, line_m * kOutMult,
+    old_mouse_point_curves_[i][1] = CurveSegment(INFINITY, 0.0, line_m * kOutMult,
                                              line_b * kOutMult * 25.4);
   }
 
+  // These values were determined empirically through user studies:
+  const float kMouseMultiplierA = 0.0311;
+  const float kMouseMultiplierB = 3.26;
+  const float kMouseCutoff = 195.0;
+  const float kMultipliers[] = { 1.2, 1.4, 1.6, 1.8, 2.0 };
+  for (size_t i = 0; i < kMaxAccelCurves; ++i) {
+    float mouse_a = kMouseMultiplierA * kMultipliers[i] * kMultipliers[i];
+    float mouse_b = kMouseMultiplierB * kMultipliers[i];
+    float cutoff = kMouseCutoff / kMultipliers[i];
+    float second_slope =
+        (2.0 * kMouseMultiplierA * kMouseCutoff + kMouseMultiplierB) *
+        kMultipliers[i];
+    mouse_point_curves_[i][0] = CurveSegment(cutoff, mouse_a, mouse_b, 0.0);
+    mouse_point_curves_[i][1] = CurveSegment(INFINITY, 0.0, second_slope, -1182);
+  }
+
   const float scroll_divisors[] = { 0.0, // unused
                                     150, 75.0, 70.0, 65.0 };  // used
   // Our scrolling curves are the following.
@@ -162,14 +189,22 @@
         scale_out_x = dx = &copy.details.swipe.dx;
         scale_out_y = dy = &copy.details.swipe.dy;
       }
-      if (pointer_sensitivity_.val_ >= 1 && pointer_sensitivity_.val_ <= 5) {
-        if (use_mouse_point_curves_.val_)
-          segs = mouse_point_curves_[pointer_sensitivity_.val_ - 1];
-        else
-          segs = point_curves_[pointer_sensitivity_.val_ - 1];
-      } else {
-        segs = custom_point_;
+      if (use_mouse_point_curves_.val_ && use_custom_mouse_curve_.val_) {
+        segs = mouse_custom_point_;
         max_segs = kMaxCustomCurveSegs;
+      } else if (!use_mouse_point_curves_.val_ &&
+                 use_custom_tp_point_curve_.val_) {
+        segs = tp_custom_point_;
+        max_segs = kMaxCustomCurveSegs;
+      } else {
+        if (use_mouse_point_curves_.val_) {
+          if (use_old_mouse_point_curves_.val_)
+            segs = old_mouse_point_curves_[pointer_sensitivity_.val_ - 1];
+          else
+            segs = mouse_point_curves_[pointer_sensitivity_.val_ - 1];
+        } else {
+          segs = point_curves_[pointer_sensitivity_.val_ - 1];
+        }
       }
       x_scale = point_x_out_scale_.val_;
       y_scale = point_y_out_scale_.val_;
@@ -196,10 +231,10 @@
         ProduceGesture(gs);
         return;
       }
-      if (scroll_sensitivity_.val_ >= 1 && scroll_sensitivity_.val_ <= 5) {
+      if (!use_custom_tp_scroll_curve_.val_) {
         segs = scroll_curves_[scroll_sensitivity_.val_ - 1];
       } else {
-        segs = custom_scroll_;
+        segs = tp_custom_scroll_;
         max_segs = kMaxCustomCurveSegs;
       }
       x_scale = scroll_x_out_scale_.val_;
diff --git a/src/accel_filter_interpreter_unittest.cc b/src/accel_filter_interpreter_unittest.cc
index b03c6a3..186d27e 100644
--- a/src/accel_filter_interpreter_unittest.cc
+++ b/src/accel_filter_interpreter_unittest.cc
@@ -249,21 +249,22 @@
   accel_interpreter.min_reasonable_dt_.val_ = 0.0;
   accel_interpreter.max_reasonable_dt_.val_ = INFINITY;
 
-  accel_interpreter.pointer_sensitivity_.val_ = 0;  // custom sensitivity
-  accel_interpreter.scroll_sensitivity_.val_ = 0;  // custom sensitivity
-  accel_interpreter.custom_point_[0] =
+  // custom sensitivity
+  accel_interpreter.use_custom_tp_point_curve_.val_ = 1;
+  accel_interpreter.use_custom_tp_scroll_curve_.val_ = 1;
+  accel_interpreter.tp_custom_point_[0] =
       AccelFilterInterpreter::CurveSegment(2.0, 0.0, 0.5, 0.0);
-  accel_interpreter.custom_point_[1] =
+  accel_interpreter.tp_custom_point_[1] =
       AccelFilterInterpreter::CurveSegment(3.0, 0.0, 2.0, -3.0);
-  accel_interpreter.custom_point_[2] =
+  accel_interpreter.tp_custom_point_[2] =
       AccelFilterInterpreter::CurveSegment(INFINITY, 0.0, 0.0, 3.0);
-  accel_interpreter.custom_scroll_[0] =
+  accel_interpreter.tp_custom_scroll_[0] =
       AccelFilterInterpreter::CurveSegment(0.5, 0.0, 2.0, 0.0);
-  accel_interpreter.custom_scroll_[1] =
+  accel_interpreter.tp_custom_scroll_[1] =
       AccelFilterInterpreter::CurveSegment(1.0, 0.0, 2.0, 0.0);
-  accel_interpreter.custom_scroll_[2] =
+  accel_interpreter.tp_custom_scroll_[2] =
       AccelFilterInterpreter::CurveSegment(2.0, 0.0, 0.0, 2.0);
-  accel_interpreter.custom_scroll_[3] =
+  accel_interpreter.tp_custom_scroll_[3] =
       AccelFilterInterpreter::CurveSegment(INFINITY, 0.0, 2.0, -2.0);
 
   float move_in[]  = { 1.0, 2.5, 3.5, 5.0 };