summaryrefslogtreecommitdiff
path: root/include/mouse_interpreter.h
blob: d1e12a302b5cc5a335e579382d53c6858e7a8436 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// 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_