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