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, JankyScrollTest); 21 FRIEND_TEST(MouseInterpreterTest, WheelTickReportingHighResTest); 22 FRIEND_TEST(MouseInterpreterTest, WheelTickReportingLowResTest); 23 public: 24 MouseInterpreter(PropRegistry* prop_reg, Tracer* tracer); ~MouseInterpreter()25 virtual ~MouseInterpreter() {}; 26 27 protected: 28 virtual void SyncInterpretImpl(HardwareState* hwstate, stime_t* timeout); 29 // These functions interpret mouse events, which include button clicking and 30 // mouse movement. This function needs two consecutive HardwareState. If no 31 // mouse events are presented, result object is not modified. Scroll wheel 32 // events are not interpreted as they are handled differently for normal 33 // mice and multi-touch mice (ignored for multi-touch mice and accelerated 34 // for normal mice). 35 void InterpretMouseButtonEvent(const HardwareState& prev_state, 36 const HardwareState& hwstate); 37 38 void InterpretMouseMotionEvent(const HardwareState& prev_state, 39 const HardwareState& hwstate); 40 // Check for scroll wheel events and produce scroll gestures. 41 void InterpretScrollWheelEvent(const HardwareState& hwstate, 42 bool is_vertical); 43 bool EmulateScrollWheel(const HardwareState& hwstate); 44 private: 45 struct WheelRecord { WheelRecordWheelRecord46 WheelRecord(float v, stime_t t): value(v), timestamp(t) {} WheelRecordWheelRecord47 WheelRecord(): value(0), timestamp(0) {} 48 float value; 49 stime_t timestamp; 50 }; 51 52 // Accelerate mouse scroll offsets so that it is larger when the user scroll 53 // the mouse wheel faster. 54 double ComputeScrollAccelFactor(double input_speed); 55 56 Gesture CreateWheelGesture(stime_t start, stime_t end, float dx, float dy, 57 int tick_120ths_dx, int tick_120ths_dy); 58 59 HardwareState prev_state_; 60 61 // Records last scroll wheel event. 62 WheelRecord last_wheel_, last_hwheel_; 63 64 // Accumulators to measure scroll distance while doing scroll wheel emulation 65 double wheel_emulation_accu_x_; 66 double wheel_emulation_accu_y_; 67 68 // True while wheel emulation is locked in. 69 bool wheel_emulation_active_; 70 71 // f_approximated = a0 + a1*v + a2*v^2 + a3*v^3 + a4*v^4 72 double scroll_accel_curve_[5]; 73 74 // Reverse wheel scrolling. 75 BoolProperty reverse_scrolling_; 76 77 // Enable high-resolution scrolling. 78 BoolProperty hi_res_scrolling_; 79 80 // We use normal CDF to simulate scroll wheel acceleration curve. Use the 81 // following method to generate the coefficients of a degree-4 polynomial 82 // regression for a specific normal cdf in Python. 83 // 84 // Note: x for wheel value, v for velocity, y for scroll pixels (offset), 85 // and v = x / dt. 86 // 87 // The offset is computed as x * f(v) where f() outputs the acceleration 88 // factor for the given input speed. The formula allows us to produce similar 89 // offsets regardless of the mouse scrolling resolution. Since we want y to 90 // follow the normal CDF, we need to attenuate the case where x >= 1. This can 91 // happen when the user scrolls really fast, e.g., more than 1 unit within 8ms 92 // for a common, low-resolution mouse. 93 // 94 // In reality, v ranges from 1 to 120+ for an Apple Mighty Mouse, use range 95 // greater than that to minimize approximation error at the end points. 96 // In our case, the range is [-50, 200]. 97 // 98 // Python (3) code: 99 // import numpy as np 100 // from scipy.stats import norm 101 // v = np.arange(-50, 201) 102 // f = (580 * norm.cdf(v, 100, 40) + 20) / np.maximum(v / 125.0, 1) 103 // coeff = np.flip(np.polyfit(v, f, 4), 0) 104 // Adjust the scroll acceleration curve 105 DoubleArrayProperty scroll_accel_curve_prop_; 106 107 // when x is 177, the polynomial curve gives 450, the max pixels to scroll. 108 DoubleProperty scroll_max_allowed_input_speed_; 109 110 // Force scroll wheel emulation for any devices 111 BoolProperty force_scroll_wheel_emulation_; 112 113 // Multiplication factor to translate cursor motion into scrolling 114 DoubleProperty scroll_wheel_emulation_speed_; 115 116 // Movement distance after which to start scroll wheel emulation [in mm] 117 DoubleProperty scroll_wheel_emulation_thresh_; 118 119 // Whether to output GestureMouseWheel or GestureScroll structs from scrolls. 120 // TODO(chromium:1077644): remove once Chrome is migrated to the new structs. 121 BoolProperty output_mouse_wheel_gestures_; 122 }; 123 124 } // namespace gestures 125 126 #endif // GESTURES_MOUSE_INTERPRETER_H_ 127