xref: /aosp_15_r20/external/libchrome-gestures/src/mouse_interpreter.cc (revision aed3e5085e770be5b69ce25295ecf6ddf906af95)
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 "include/mouse_interpreter.h"
6 
7 #include <math.h>
8 
9 #include "include/logging.h"
10 #include "include/macros.h"
11 #include "include/tracer.h"
12 
13 namespace gestures {
14 
15 /*
16  * The number of subdivisions that `REL_WHEEL_HI_RES` and `REL_HWHEEL_HI_RES`
17  * have relative to `REL_WHEEL` and `REL_HWHEEL` respectively.
18  */
19 const static int REL_WHEEL_HI_RES_UNITS_PER_NOTCH = 120;
20 
21 // Default value for mouse scroll sensitivity.
22 const static int kMouseScrollSensitivityDefaultValue = 3;
23 
MouseInterpreter(PropRegistry * prop_reg,Tracer * tracer)24 MouseInterpreter::MouseInterpreter(PropRegistry* prop_reg, Tracer* tracer)
25     : Interpreter(nullptr, tracer, false),
26       wheel_emulation_accu_x_(0.0),
27       wheel_emulation_accu_y_(0.0),
28       wheel_emulation_active_(false),
29       reverse_scrolling_(prop_reg, "Mouse Reverse Scrolling", false),
30       scroll_acceleration_(prop_reg, "Mouse Scroll Acceleration", true),
31       scroll_sensitivity_(prop_reg,"Mouse Scroll Sensitivity",
32         kMouseScrollSensitivityDefaultValue),
33       hi_res_scrolling_(prop_reg, "Mouse High Resolution Scrolling", true),
34       scroll_velocity_buffer_size_(prop_reg, "Scroll Wheel Velocity Buffer", 3),
35       scroll_accel_curve_prop_(prop_reg, "Mouse Scroll Accel Curve",
36           scroll_accel_curve_, sizeof(scroll_accel_curve_) / sizeof(double)),
37       scroll_max_allowed_input_speed_(prop_reg,
38                                       "Mouse Scroll Max Input Speed",
39                                       177.0),
40       force_scroll_wheel_emulation_(prop_reg,
41                                      "Force Scroll Wheel Emulation",
42                                      false),
43       scroll_wheel_emulation_speed_(prop_reg,
44                                     "Scroll Wheel Emulation Speed",
45                                     100.0),
46       scroll_wheel_emulation_thresh_(prop_reg,
47                                     "Scroll Wheel Emulation Threshold",
48                                     1.0),
49       output_mouse_wheel_gestures_(prop_reg,
50                                    "Output Mouse Wheel Gestures", false) {
51   InitName();
52   memset(&prev_state_, 0, sizeof(prev_state_));
53   // Scroll acceleration curve coefficients. See the definition for more
54   // details on how to generate them.
55   scroll_accel_curve_[0] = 1.0374e+01;
56   scroll_accel_curve_[1] = 4.1773e-01;
57   scroll_accel_curve_[2] = 2.5737e-02;
58   scroll_accel_curve_[3] = 8.0428e-05;
59   scroll_accel_curve_[4] = -9.1149e-07;
60   scroll_max_allowed_input_speed_.SetDelegate(this);
61 }
62 
SyncInterpretImpl(HardwareState & hwstate,stime_t * timeout)63 void MouseInterpreter::SyncInterpretImpl(HardwareState& hwstate,
64                                          stime_t* timeout) {
65   const char name[] = "MouseInterpreter::SyncInterpretImpl";
66   LogHardwareStatePre(name, hwstate);
67 
68   if(!EmulateScrollWheel(hwstate)) {
69     // Interpret mouse events in the order of pointer moves, scroll wheels and
70     // button clicks.
71     InterpretMouseMotionEvent(prev_state_, hwstate);
72     // Note that unlike touchpad scrolls, we interpret and send separate events
73     // for horizontal/vertical mouse wheel scrolls. This is partly to match what
74     // the xf86-input-evdev driver does and is partly because not all code in
75     // Chrome honors MouseWheelEvent that has both X and Y offsets.
76     InterpretScrollWheelEvent(hwstate, true);
77     InterpretScrollWheelEvent(hwstate, false);
78     InterpretMouseButtonEvent(prev_state_, hwstate);
79   }
80   // Pass max_finger_cnt = 0 to DeepCopy() since we don't care fingers and
81   // did not allocate any space for fingers.
82   prev_state_.DeepCopy(hwstate, 0);
83 
84   LogHardwareStatePost(name, hwstate);
85 }
86 
ComputeScrollAccelFactor(double input_speed)87 double MouseInterpreter::ComputeScrollAccelFactor(double input_speed) {
88   double result = 0.0;
89   double term = 1.0;
90   double allowed_speed = fabs(input_speed);
91   if (allowed_speed > scroll_max_allowed_input_speed_.val_)
92     allowed_speed = scroll_max_allowed_input_speed_.val_;
93 
94   // Compute the scroll acceleration factor.
95   for (size_t i = 0; i < arraysize(scroll_accel_curve_); i++) {
96     result += term * scroll_accel_curve_[i];
97     term *= allowed_speed;
98   }
99   return result;
100 }
101 
EmulateScrollWheel(const HardwareState & hwstate)102 bool MouseInterpreter::EmulateScrollWheel(const HardwareState& hwstate) {
103   const char name[] = "MouseInterpreter::EmulateScrollWheel";
104 
105   if (!force_scroll_wheel_emulation_.val_ && hwprops_->has_wheel)
106     return false;
107   bool down = hwstate.buttons_down & GESTURES_BUTTON_MIDDLE ||
108               (hwstate.buttons_down & GESTURES_BUTTON_LEFT &&
109                hwstate.buttons_down & GESTURES_BUTTON_RIGHT);
110   bool prev_down = prev_state_.buttons_down & GESTURES_BUTTON_MIDDLE ||
111                    (prev_state_.buttons_down & GESTURES_BUTTON_LEFT &&
112                     prev_state_.buttons_down & GESTURES_BUTTON_RIGHT);
113   bool raising = down && !prev_down;
114   bool falling = !down && prev_down;
115 
116   // Reset scroll emulation detection on button down.
117   if (raising) {
118     wheel_emulation_accu_x_ = 0;
119     wheel_emulation_accu_y_ = 0;
120     wheel_emulation_active_ = false;
121   }
122 
123   // Send button event if button has been released without scrolling.
124   if (falling && !wheel_emulation_active_) {
125     auto button_change = Gesture(kGestureButtonsChange,
126                            prev_state_.timestamp,
127                            hwstate.timestamp,
128                            prev_state_.buttons_down,
129                            prev_state_.buttons_down,
130                            false); // is_tap
131     LogGestureProduce(name, button_change);
132     ProduceGesture(button_change);
133   }
134 
135   if (down) {
136     // Detect scroll emulation
137     if (!wheel_emulation_active_) {
138       wheel_emulation_accu_x_ += hwstate.rel_x;
139       wheel_emulation_accu_y_ += hwstate.rel_y;
140       double dist_sq = wheel_emulation_accu_x_ * wheel_emulation_accu_x_ +
141                        wheel_emulation_accu_y_ * wheel_emulation_accu_y_;
142       double thresh_sq = scroll_wheel_emulation_thresh_.val_ *
143                          scroll_wheel_emulation_thresh_.val_;
144       if (dist_sq > thresh_sq) {
145         // Lock into scroll emulation until button is released.
146         wheel_emulation_active_ = true;
147       }
148     }
149 
150     // Transform motion into scrolling.
151     if (wheel_emulation_active_) {
152       double scroll_x = hwstate.rel_x * scroll_wheel_emulation_speed_.val_;
153       double scroll_y = hwstate.rel_y * scroll_wheel_emulation_speed_.val_;
154 
155       auto scroll = Gesture(kGestureScroll, hwstate.timestamp,
156                             hwstate.timestamp, scroll_x, scroll_y);
157       LogGestureProduce(name, scroll);
158       ProduceGesture(scroll);
159     }
160     return true;
161   }
162 
163   return false;
164 }
165 
InterpretScrollWheelEvent(const HardwareState & hwstate,bool is_vertical)166 void MouseInterpreter::InterpretScrollWheelEvent(const HardwareState& hwstate,
167                                                  bool is_vertical) {
168   const char name[] = "MouseInterpreter::InterpretScrollWheelEvent";
169 
170   const size_t max_buffer_size = scroll_velocity_buffer_size_.val_;
171   const float scroll_wheel_event_time_delta_min = 0.008 * max_buffer_size;
172   bool use_high_resolution =
173       is_vertical && hwprops_->wheel_is_hi_res
174       && hi_res_scrolling_.val_;
175   // Vertical wheel or horizontal wheel.
176   WheelRecord current_wheel;
177   current_wheel.timestamp = hwstate.timestamp;
178   int ticks;
179   std::vector<WheelRecord>* last_wheels;
180   if (is_vertical) {
181     // Only vertical high-res scrolling is supported for now.
182     if (use_high_resolution) {
183       current_wheel.change = hwstate.rel_wheel_hi_res
184           / REL_WHEEL_HI_RES_UNITS_PER_NOTCH;
185       ticks = hwstate.rel_wheel_hi_res;
186     } else {
187       current_wheel.change = hwstate.rel_wheel;
188       ticks = hwstate.rel_wheel * REL_WHEEL_HI_RES_UNITS_PER_NOTCH;
189     }
190     last_wheels = &last_vertical_wheels_;
191   } else {
192     last_wheels = &last_horizontal_wheels_;
193     current_wheel.change = hwstate.rel_hwheel;
194     ticks = hwstate.rel_hwheel * REL_WHEEL_HI_RES_UNITS_PER_NOTCH;
195   }
196 
197   // Check if the wheel is scrolled.
198   if (current_wheel.change) {
199     stime_t start_time, end_time = hwstate.timestamp;
200     // Check if this scroll is in same direction as previous scroll event.
201     if (!last_wheels->empty() &&
202         ((current_wheel.change < 0 && last_wheels->back().change < 0) ||
203          (current_wheel.change > 0 && last_wheels->back().change > 0))) {
204       start_time = last_wheels->begin()->timestamp;
205     } else {
206       last_wheels->clear();
207       start_time = end_time;
208     }
209 
210     // We will only accelerate scrolls if we have filled our buffer of scroll
211     // events all in the same direction. If the buffer is full, then calculate
212     // scroll velocity using the average velocity of the entire buffer.
213     float velocity;
214     if (last_wheels->size() < max_buffer_size) {
215       velocity = 0.0;
216     } else {
217       stime_t dt = end_time - last_wheels->back().timestamp;
218       if (dt < scroll_wheel_event_time_delta_min) {
219         // The first packets received after BT wakeup may be delayed, causing
220         // the time delta between that and the subsequent packets to be
221         // artificially very small.
222         // Prevent small time deltas from triggering large amounts of
223         // acceleration by enforcing a minimum time delta.
224         dt = scroll_wheel_event_time_delta_min;
225       }
226 
227       last_wheels->pop_back();
228       float buffer_scroll_distance = current_wheel.change;
229       for (auto wheel : *last_wheels) {
230         buffer_scroll_distance += wheel.change;
231       }
232 
233       velocity = buffer_scroll_distance / dt;
234     }
235     last_wheels->insert(last_wheels->begin(), current_wheel);
236 
237     // When scroll acceleration is off, the scroll factor does not relate to
238     // scroll velocity. It's simply a constant multiplier to the wheel value.
239     const double unaccel_scroll_factors[] = { 20.0, 36.0, 72.0, 112.0, 164.0 };
240 
241     float offset = current_wheel.change * (
242       scroll_acceleration_.val_?
243       ComputeScrollAccelFactor(velocity) :
244       unaccel_scroll_factors[scroll_sensitivity_.val_ - 1]);
245 
246     if (is_vertical) {
247       // For historical reasons the vertical wheel (REL_WHEEL) is inverted
248       if (!reverse_scrolling_.val_) {
249         offset = -offset;
250         ticks = -ticks;
251       }
252       auto scroll_wheel = CreateWheelGesture(start_time, end_time,
253                                              0, offset, 0, ticks);
254       LogGestureProduce(name, scroll_wheel);
255       ProduceGesture(scroll_wheel);
256     } else {
257       auto scroll_wheel = CreateWheelGesture(start_time, end_time,
258                                              offset, 0, ticks, 0);
259       LogGestureProduce(name, scroll_wheel);
260       ProduceGesture(scroll_wheel);
261     }
262   }
263 }
264 
CreateWheelGesture(stime_t start_time,stime_t end_time,float dx,float dy,int tick_120ths_dx,int tick_120ths_dy)265 Gesture MouseInterpreter::CreateWheelGesture(
266     stime_t start_time, stime_t end_time, float dx, float dy,
267     int tick_120ths_dx, int tick_120ths_dy) {
268   if (output_mouse_wheel_gestures_.val_) {
269     return Gesture(kGestureMouseWheel, start_time, end_time, dx, dy,
270                    tick_120ths_dx, tick_120ths_dy);
271   } else {
272     return Gesture(kGestureScroll, start_time, end_time, dx, dy);
273   }
274 }
275 
InterpretMouseButtonEvent(const HardwareState & prev_state,const HardwareState & hwstate)276 void MouseInterpreter::InterpretMouseButtonEvent(
277     const HardwareState& prev_state, const HardwareState& hwstate) {
278   const char name[] = "MouseInterpreter::InterpretMouseButtonEvent";
279 
280   const unsigned buttons[] = {
281     GESTURES_BUTTON_LEFT,
282     GESTURES_BUTTON_MIDDLE,
283     GESTURES_BUTTON_RIGHT,
284     GESTURES_BUTTON_BACK,
285     GESTURES_BUTTON_FORWARD,
286     GESTURES_BUTTON_SIDE,
287     GESTURES_BUTTON_EXTRA,
288   };
289   unsigned down = 0, up = 0;
290 
291   for (unsigned i = 0; i < arraysize(buttons); i++) {
292     if (!(prev_state.buttons_down & buttons[i]) &&
293         (hwstate.buttons_down & buttons[i]))
294       down |= buttons[i];
295     if ((prev_state.buttons_down & buttons[i]) &&
296         !(hwstate.buttons_down & buttons[i]))
297       up |= buttons[i];
298   }
299 
300   if (down || up) {
301     auto button_change = Gesture(kGestureButtonsChange,
302                                  prev_state.timestamp,
303                                  hwstate.timestamp,
304                                  down,
305                                  up,
306                                  false); // is_tap
307     LogGestureProduce(name, button_change);
308     ProduceGesture(button_change);
309   }
310 }
311 
InterpretMouseMotionEvent(const HardwareState & prev_state,const HardwareState & hwstate)312 void MouseInterpreter::InterpretMouseMotionEvent(
313     const HardwareState& prev_state,
314     const HardwareState& hwstate) {
315   const char name[] = "MouseInterpreter::InterpretMouseMotionEvent";
316 
317   if (hwstate.rel_x || hwstate.rel_y) {
318     auto move = Gesture(kGestureMove,
319                         prev_state.timestamp,
320                         hwstate.timestamp,
321                         hwstate.rel_x,
322                         hwstate.rel_y);
323     LogGestureProduce(name, move);
324     ProduceGesture(move);
325   }
326 }
327 
328 }  // namespace gestures
329