xref: /aosp_15_r20/external/libchrome-gestures/include/mouse_interpreter.h (revision aed3e5085e770be5b69ce25295ecf6ddf906af95)
1*aed3e508SAndroid Build Coastguard Worker // Copyright 2012 The ChromiumOS Authors
2*aed3e508SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*aed3e508SAndroid Build Coastguard Worker // found in the LICENSE file.
4*aed3e508SAndroid Build Coastguard Worker 
5*aed3e508SAndroid Build Coastguard Worker #include <gtest/gtest.h>  // For FRIEND_TEST
6*aed3e508SAndroid Build Coastguard Worker 
7*aed3e508SAndroid Build Coastguard Worker #include "include/gestures.h"
8*aed3e508SAndroid Build Coastguard Worker #include "include/interpreter.h"
9*aed3e508SAndroid Build Coastguard Worker #include "include/prop_registry.h"
10*aed3e508SAndroid Build Coastguard Worker #include "include/tracer.h"
11*aed3e508SAndroid Build Coastguard Worker 
12*aed3e508SAndroid Build Coastguard Worker #ifndef GESTURES_MOUSE_INTERPRETER_H_
13*aed3e508SAndroid Build Coastguard Worker #define GESTURES_MOUSE_INTERPRETER_H_
14*aed3e508SAndroid Build Coastguard Worker 
15*aed3e508SAndroid Build Coastguard Worker namespace gestures {
16*aed3e508SAndroid Build Coastguard Worker 
17*aed3e508SAndroid Build Coastguard Worker class MouseInterpreter : public Interpreter, public PropertyDelegate {
18*aed3e508SAndroid Build Coastguard Worker   FRIEND_TEST(MouseInterpreterTest, SimpleTest);
19*aed3e508SAndroid Build Coastguard Worker   FRIEND_TEST(MouseInterpreterTest, HighResolutionVerticalScrollTest);
20*aed3e508SAndroid Build Coastguard Worker   FRIEND_TEST(MouseInterpreterTest, ScrollAccelerationOnAndOffTest);
21*aed3e508SAndroid Build Coastguard Worker   FRIEND_TEST(MouseInterpreterTest, JankyScrollTest);
22*aed3e508SAndroid Build Coastguard Worker   FRIEND_TEST(MouseInterpreterTest, WheelTickReportingHighResTest);
23*aed3e508SAndroid Build Coastguard Worker   FRIEND_TEST(MouseInterpreterTest, WheelTickReportingLowResTest);
24*aed3e508SAndroid Build Coastguard Worker   FRIEND_TEST(MouseInterpreterTest, EmulateScrollWheelTest);
25*aed3e508SAndroid Build Coastguard Worker  public:
26*aed3e508SAndroid Build Coastguard Worker   MouseInterpreter(PropRegistry* prop_reg, Tracer* tracer);
~MouseInterpreter()27*aed3e508SAndroid Build Coastguard Worker   virtual ~MouseInterpreter() {};
28*aed3e508SAndroid Build Coastguard Worker 
29*aed3e508SAndroid Build Coastguard Worker  protected:
30*aed3e508SAndroid Build Coastguard Worker   virtual void SyncInterpretImpl(HardwareState& hwstate, stime_t* timeout);
31*aed3e508SAndroid Build Coastguard Worker   // These functions interpret mouse events, which include button clicking and
32*aed3e508SAndroid Build Coastguard Worker   // mouse movement. This function needs two consecutive HardwareState. If no
33*aed3e508SAndroid Build Coastguard Worker   // mouse events are presented, result object is not modified. Scroll wheel
34*aed3e508SAndroid Build Coastguard Worker   // events are not interpreted as they are handled differently for normal
35*aed3e508SAndroid Build Coastguard Worker   // mice and multi-touch mice (ignored for multi-touch mice and accelerated
36*aed3e508SAndroid Build Coastguard Worker   // for normal mice).
37*aed3e508SAndroid Build Coastguard Worker   void InterpretMouseButtonEvent(const HardwareState& prev_state,
38*aed3e508SAndroid Build Coastguard Worker                                  const HardwareState& hwstate);
39*aed3e508SAndroid Build Coastguard Worker 
40*aed3e508SAndroid Build Coastguard Worker   void InterpretMouseMotionEvent(const HardwareState& prev_state,
41*aed3e508SAndroid Build Coastguard Worker                                  const HardwareState& hwstate);
42*aed3e508SAndroid Build Coastguard Worker   // Check for scroll wheel events and produce scroll gestures.
43*aed3e508SAndroid Build Coastguard Worker   void InterpretScrollWheelEvent(const HardwareState& hwstate,
44*aed3e508SAndroid Build Coastguard Worker                                  bool is_vertical);
45*aed3e508SAndroid Build Coastguard Worker   bool EmulateScrollWheel(const HardwareState& hwstate);
46*aed3e508SAndroid Build Coastguard Worker  private:
47*aed3e508SAndroid Build Coastguard Worker   struct WheelRecord {
WheelRecordWheelRecord48*aed3e508SAndroid Build Coastguard Worker     WheelRecord(float v, stime_t t): change(v), timestamp(t) {}
WheelRecordWheelRecord49*aed3e508SAndroid Build Coastguard Worker     WheelRecord(): change(0), timestamp(0) {}
50*aed3e508SAndroid Build Coastguard Worker     float change;
51*aed3e508SAndroid Build Coastguard Worker     stime_t timestamp;
52*aed3e508SAndroid Build Coastguard Worker   };
53*aed3e508SAndroid Build Coastguard Worker 
54*aed3e508SAndroid Build Coastguard Worker   // Accelerate mouse scroll offsets so that it is larger when the user scroll
55*aed3e508SAndroid Build Coastguard Worker   // the mouse wheel faster.
56*aed3e508SAndroid Build Coastguard Worker   double ComputeScrollAccelFactor(double input_speed);
57*aed3e508SAndroid Build Coastguard Worker 
58*aed3e508SAndroid Build Coastguard Worker   Gesture CreateWheelGesture(stime_t start, stime_t end, float dx, float dy,
59*aed3e508SAndroid Build Coastguard Worker                              int tick_120ths_dx, int tick_120ths_dy);
60*aed3e508SAndroid Build Coastguard Worker 
61*aed3e508SAndroid Build Coastguard Worker   HardwareState prev_state_;
62*aed3e508SAndroid Build Coastguard Worker 
63*aed3e508SAndroid Build Coastguard Worker   // Records last scroll wheel events.
64*aed3e508SAndroid Build Coastguard Worker   std::vector<WheelRecord> last_vertical_wheels_, last_horizontal_wheels_;
65*aed3e508SAndroid Build Coastguard Worker 
66*aed3e508SAndroid Build Coastguard Worker   // Accumulators to measure scroll distance while doing scroll wheel emulation
67*aed3e508SAndroid Build Coastguard Worker   double wheel_emulation_accu_x_;
68*aed3e508SAndroid Build Coastguard Worker   double wheel_emulation_accu_y_;
69*aed3e508SAndroid Build Coastguard Worker 
70*aed3e508SAndroid Build Coastguard Worker   // True while wheel emulation is locked in.
71*aed3e508SAndroid Build Coastguard Worker   bool wheel_emulation_active_;
72*aed3e508SAndroid Build Coastguard Worker 
73*aed3e508SAndroid Build Coastguard Worker   // f_approximated = a0 + a1*v + a2*v^2 + a3*v^3 + a4*v^4
74*aed3e508SAndroid Build Coastguard Worker   double scroll_accel_curve_[5];
75*aed3e508SAndroid Build Coastguard Worker 
76*aed3e508SAndroid Build Coastguard Worker   // Reverse wheel scrolling.
77*aed3e508SAndroid Build Coastguard Worker   BoolProperty reverse_scrolling_;
78*aed3e508SAndroid Build Coastguard Worker 
79*aed3e508SAndroid Build Coastguard Worker   // Mouse scroll acceleration.
80*aed3e508SAndroid Build Coastguard Worker   BoolProperty scroll_acceleration_;
81*aed3e508SAndroid Build Coastguard Worker 
82*aed3e508SAndroid Build Coastguard Worker   // Mouse scroll sensitivity 1..5.
83*aed3e508SAndroid Build Coastguard Worker   IntProperty scroll_sensitivity_;
84*aed3e508SAndroid Build Coastguard Worker 
85*aed3e508SAndroid Build Coastguard Worker   // Enable high-resolution scrolling.
86*aed3e508SAndroid Build Coastguard Worker   BoolProperty hi_res_scrolling_;
87*aed3e508SAndroid Build Coastguard Worker 
88*aed3e508SAndroid Build Coastguard Worker   // When calculating scroll velocity for the purpose of acceleration, we
89*aed3e508SAndroid Build Coastguard Worker   // use the average of this many events in the same direction. This is to avoid
90*aed3e508SAndroid Build Coastguard Worker   // over-accelerating if we receive batched events with timestamps that are
91*aed3e508SAndroid Build Coastguard Worker   // artificially close. If we don't have enough events, we won't accelerate at
92*aed3e508SAndroid Build Coastguard Worker   // all.
93*aed3e508SAndroid Build Coastguard Worker   IntProperty scroll_velocity_buffer_size_;
94*aed3e508SAndroid Build Coastguard Worker 
95*aed3e508SAndroid Build Coastguard Worker   // We use normal CDF to simulate scroll wheel acceleration curve. Use the
96*aed3e508SAndroid Build Coastguard Worker   // following method to generate the coefficients of a degree-4 polynomial
97*aed3e508SAndroid Build Coastguard Worker   // regression for a specific normal cdf in Python.
98*aed3e508SAndroid Build Coastguard Worker   //
99*aed3e508SAndroid Build Coastguard Worker   // Note: x for wheel value, v for velocity, y for scroll pixels (offset),
100*aed3e508SAndroid Build Coastguard Worker   // and v = x / dt.
101*aed3e508SAndroid Build Coastguard Worker   //
102*aed3e508SAndroid Build Coastguard Worker   // The offset is computed as x * f(v) where f() outputs the acceleration
103*aed3e508SAndroid Build Coastguard Worker   // factor for the given input speed. The formula allows us to produce similar
104*aed3e508SAndroid Build Coastguard Worker   // offsets regardless of the mouse scrolling resolution. Since we want y to
105*aed3e508SAndroid Build Coastguard Worker   // follow the normal CDF, we need to attenuate the case where x >= 1. This can
106*aed3e508SAndroid Build Coastguard Worker   // happen when the user scrolls really fast, e.g., more than 1 unit within 8ms
107*aed3e508SAndroid Build Coastguard Worker   // for a common, low-resolution mouse.
108*aed3e508SAndroid Build Coastguard Worker   //
109*aed3e508SAndroid Build Coastguard Worker   // In reality, v ranges from 1 to 120+ for an Apple Mighty Mouse, use range
110*aed3e508SAndroid Build Coastguard Worker   // greater than that to minimize approximation error at the end points.
111*aed3e508SAndroid Build Coastguard Worker   // In our case, the range is [-50, 200].
112*aed3e508SAndroid Build Coastguard Worker   //
113*aed3e508SAndroid Build Coastguard Worker   // Python (3) code:
114*aed3e508SAndroid Build Coastguard Worker   // import numpy as np
115*aed3e508SAndroid Build Coastguard Worker   // from scipy.stats import norm
116*aed3e508SAndroid Build Coastguard Worker   // v = np.arange(-50, 201)
117*aed3e508SAndroid Build Coastguard Worker   // f = (580 * norm.cdf(v, 100, 40) + 20) / np.maximum(v / 125.0, 1)
118*aed3e508SAndroid Build Coastguard Worker   // coeff = np.flip(np.polyfit(v, f, 4), 0)
119*aed3e508SAndroid Build Coastguard Worker   // Adjust the scroll acceleration curve
120*aed3e508SAndroid Build Coastguard Worker   DoubleArrayProperty scroll_accel_curve_prop_;
121*aed3e508SAndroid Build Coastguard Worker 
122*aed3e508SAndroid Build Coastguard Worker   // when x is 177, the polynomial curve gives 450, the max pixels to scroll.
123*aed3e508SAndroid Build Coastguard Worker   DoubleProperty scroll_max_allowed_input_speed_;
124*aed3e508SAndroid Build Coastguard Worker 
125*aed3e508SAndroid Build Coastguard Worker   // Force scroll wheel emulation for any devices
126*aed3e508SAndroid Build Coastguard Worker   BoolProperty force_scroll_wheel_emulation_;
127*aed3e508SAndroid Build Coastguard Worker 
128*aed3e508SAndroid Build Coastguard Worker   // Multiplication factor to translate cursor motion into scrolling
129*aed3e508SAndroid Build Coastguard Worker   DoubleProperty scroll_wheel_emulation_speed_;
130*aed3e508SAndroid Build Coastguard Worker 
131*aed3e508SAndroid Build Coastguard Worker   // Movement distance after which to start scroll wheel emulation [in mm]
132*aed3e508SAndroid Build Coastguard Worker   DoubleProperty scroll_wheel_emulation_thresh_;
133*aed3e508SAndroid Build Coastguard Worker 
134*aed3e508SAndroid Build Coastguard Worker   // Whether to output GestureMouseWheel or GestureScroll structs from scrolls.
135*aed3e508SAndroid Build Coastguard Worker   // TODO(chromium:1077644): remove once Chrome is migrated to the new structs.
136*aed3e508SAndroid Build Coastguard Worker   BoolProperty output_mouse_wheel_gestures_;
137*aed3e508SAndroid Build Coastguard Worker };
138*aed3e508SAndroid Build Coastguard Worker 
139*aed3e508SAndroid Build Coastguard Worker }  // namespace gestures
140*aed3e508SAndroid Build Coastguard Worker 
141*aed3e508SAndroid Build Coastguard Worker #endif  // GESTURES_MOUSE_INTERPRETER_H_
142