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