| // Copyright 2012 The ChromiumOS Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <gtest/gtest.h> // For FRIEND_TEST |
| |
| #include "include/gestures.h" |
| #include "include/interpreter.h" |
| #include "include/prop_registry.h" |
| #include "include/tracer.h" |
| |
| #ifndef GESTURES_MOUSE_INTERPRETER_H_ |
| #define GESTURES_MOUSE_INTERPRETER_H_ |
| |
| namespace gestures { |
| |
| class MouseInterpreter : public Interpreter, public PropertyDelegate { |
| FRIEND_TEST(MouseInterpreterTest, SimpleTest); |
| FRIEND_TEST(MouseInterpreterTest, HighResolutionVerticalScrollTest); |
| FRIEND_TEST(MouseInterpreterTest, ScrollAccelerationOnAndOffTest); |
| FRIEND_TEST(MouseInterpreterTest, JankyScrollTest); |
| FRIEND_TEST(MouseInterpreterTest, WheelTickReportingHighResTest); |
| FRIEND_TEST(MouseInterpreterTest, WheelTickReportingLowResTest); |
| FRIEND_TEST(MouseInterpreterTest, EmulateScrollWheelTest); |
| public: |
| MouseInterpreter(PropRegistry* prop_reg, Tracer* tracer); |
| virtual ~MouseInterpreter() {}; |
| |
| protected: |
| virtual void SyncInterpretImpl(HardwareState& hwstate, stime_t* timeout); |
| // These functions interpret mouse events, which include button clicking and |
| // mouse movement. This function needs two consecutive HardwareState. If no |
| // mouse events are presented, result object is not modified. Scroll wheel |
| // events are not interpreted as they are handled differently for normal |
| // mice and multi-touch mice (ignored for multi-touch mice and accelerated |
| // for normal mice). |
| void InterpretMouseButtonEvent(const HardwareState& prev_state, |
| const HardwareState& hwstate); |
| |
| void InterpretMouseMotionEvent(const HardwareState& prev_state, |
| const HardwareState& hwstate); |
| // Check for scroll wheel events and produce scroll gestures. |
| void InterpretScrollWheelEvent(const HardwareState& hwstate, |
| bool is_vertical); |
| bool EmulateScrollWheel(const HardwareState& hwstate); |
| private: |
| struct WheelRecord { |
| WheelRecord(float v, stime_t t): change(v), timestamp(t) {} |
| WheelRecord(): change(0), timestamp(0) {} |
| float change; |
| stime_t timestamp; |
| }; |
| |
| // Accelerate mouse scroll offsets so that it is larger when the user scroll |
| // the mouse wheel faster. |
| double ComputeScrollAccelFactor(double input_speed); |
| |
| Gesture CreateWheelGesture(stime_t start, stime_t end, float dx, float dy, |
| int tick_120ths_dx, int tick_120ths_dy); |
| |
| HardwareState prev_state_; |
| |
| // Records last scroll wheel events. |
| std::vector<WheelRecord> last_vertical_wheels_, last_horizontal_wheels_; |
| |
| // Accumulators to measure scroll distance while doing scroll wheel emulation |
| double wheel_emulation_accu_x_; |
| double wheel_emulation_accu_y_; |
| |
| // True while wheel emulation is locked in. |
| bool wheel_emulation_active_; |
| |
| // f_approximated = a0 + a1*v + a2*v^2 + a3*v^3 + a4*v^4 |
| double scroll_accel_curve_[5]; |
| |
| // Reverse wheel scrolling. |
| BoolProperty reverse_scrolling_; |
| |
| // Mouse scroll acceleration. |
| BoolProperty scroll_acceleration_; |
| |
| // Mouse scroll sensitivity 1..5. |
| IntProperty scroll_sensitivity_; |
| |
| // Enable high-resolution scrolling. |
| BoolProperty hi_res_scrolling_; |
| |
| // When calculating scroll velocity for the purpose of acceleration, we |
| // use the average of this many events in the same direction. This is to avoid |
| // over-accelerating if we receive batched events with timestamps that are |
| // artificially close. If we don't have enough events, we won't accelerate at |
| // all. |
| IntProperty scroll_velocity_buffer_size_; |
| |
| // We use normal CDF to simulate scroll wheel acceleration curve. Use the |
| // following method to generate the coefficients of a degree-4 polynomial |
| // regression for a specific normal cdf in Python. |
| // |
| // Note: x for wheel value, v for velocity, y for scroll pixels (offset), |
| // and v = x / dt. |
| // |
| // The offset is computed as x * f(v) where f() outputs the acceleration |
| // factor for the given input speed. The formula allows us to produce similar |
| // offsets regardless of the mouse scrolling resolution. Since we want y to |
| // follow the normal CDF, we need to attenuate the case where x >= 1. This can |
| // happen when the user scrolls really fast, e.g., more than 1 unit within 8ms |
| // for a common, low-resolution mouse. |
| // |
| // In reality, v ranges from 1 to 120+ for an Apple Mighty Mouse, use range |
| // greater than that to minimize approximation error at the end points. |
| // In our case, the range is [-50, 200]. |
| // |
| // Python (3) code: |
| // import numpy as np |
| // from scipy.stats import norm |
| // v = np.arange(-50, 201) |
| // f = (580 * norm.cdf(v, 100, 40) + 20) / np.maximum(v / 125.0, 1) |
| // coeff = np.flip(np.polyfit(v, f, 4), 0) |
| // Adjust the scroll acceleration curve |
| DoubleArrayProperty scroll_accel_curve_prop_; |
| |
| // when x is 177, the polynomial curve gives 450, the max pixels to scroll. |
| DoubleProperty scroll_max_allowed_input_speed_; |
| |
| // Force scroll wheel emulation for any devices |
| BoolProperty force_scroll_wheel_emulation_; |
| |
| // Multiplication factor to translate cursor motion into scrolling |
| DoubleProperty scroll_wheel_emulation_speed_; |
| |
| // Movement distance after which to start scroll wheel emulation [in mm] |
| DoubleProperty scroll_wheel_emulation_thresh_; |
| |
| // Whether to output GestureMouseWheel or GestureScroll structs from scrolls. |
| // TODO(chromium:1077644): remove once Chrome is migrated to the new structs. |
| BoolProperty output_mouse_wheel_gestures_; |
| }; |
| |
| } // namespace gestures |
| |
| #endif // GESTURES_MOUSE_INTERPRETER_H_ |