xref: /aosp_15_r20/external/libchrome-gestures/src/immediate_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/immediate_interpreter.h"
6 
7 #include <algorithm>
8 #include <cmath>
9 #include <cstdlib>
10 #include <cstring>
11 #include <functional>
12 #include <limits>
13 #include <tuple>
14 #include <vector>
15 
16 #include "include/gestures.h"
17 #include "include/logging.h"
18 #include "include/util.h"
19 
20 using std::bind;
21 using std::for_each;
22 using std::make_pair;
23 using std::make_tuple;
24 using std::max;
25 using std::min;
26 using std::tuple;
27 
28 namespace gestures {
29 
30 namespace {
31 
MaxMag(float a,float b)32 float MaxMag(float a, float b) {
33   if (fabsf(a) > fabsf(b))
34     return a;
35   return b;
36 }
MinMag(float a,float b)37 float MinMag(float a, float b) {
38   if (fabsf(a) < fabsf(b))
39     return a;
40   return b;
41 }
42 
43 // A comparator class for use with STL algorithms that sorts FingerStates
44 // by their origin timestamp.
45 class FingerOriginCompare {
46  public:
FingerOriginCompare(const ImmediateInterpreter * interpreter)47   explicit FingerOriginCompare(const ImmediateInterpreter* interpreter)
48       : interpreter_(interpreter) {
49   }
operator ()(const FingerState * a,const FingerState * b) const50   bool operator() (const FingerState* a, const FingerState* b) const {
51     return interpreter_->finger_origin_timestamp(a->tracking_id) <
52            interpreter_->finger_origin_timestamp(b->tracking_id);
53   }
54 
55  private:
56   const ImmediateInterpreter* interpreter_;
57 };
58 
59 }  // namespace {}
60 
NoteTouch(short the_id,const FingerState & fs)61 void TapRecord::NoteTouch(short the_id, const FingerState& fs) {
62   // New finger must be close enough to an existing finger
63   if (!touched_.empty()) {
64     bool reject_new_finger = true;
65     for (const auto& [tracking_id, existing_fs] : touched_) {
66       if (immediate_interpreter_->metrics_->CloseEnoughToGesture(
67               Vector2(existing_fs),
68               Vector2(fs))) {
69         reject_new_finger = false;
70         break;
71       }
72     }
73     if (reject_new_finger)
74       return;
75   }
76   touched_[the_id] = fs;
77 }
78 
NoteRelease(short the_id)79 void TapRecord::NoteRelease(short the_id) {
80   if (touched_.find(the_id) != touched_.end())
81     released_.insert(the_id);
82 }
83 
Remove(short the_id)84 void TapRecord::Remove(short the_id) {
85   min_tap_pressure_met_.erase(the_id);
86   min_cotap_pressure_met_.erase(the_id);
87   touched_.erase(the_id);
88   released_.erase(the_id);
89 }
90 
CotapMinPressure() const91 float TapRecord::CotapMinPressure() const {
92   return immediate_interpreter_->tap_min_pressure() * 0.5;
93 }
94 
Update(const HardwareState & hwstate,const HardwareState & prev_hwstate,const std::set<short> & added,const std::set<short> & removed,const std::set<short> & dead)95 void TapRecord::Update(const HardwareState& hwstate,
96                        const HardwareState& prev_hwstate,
97                        const std::set<short>& added,
98                        const std::set<short>& removed,
99                        const std::set<short>& dead) {
100   if (!t5r2_ && (hwstate.finger_cnt != hwstate.touch_cnt ||
101                  prev_hwstate.finger_cnt != prev_hwstate.touch_cnt)) {
102     // switch to T5R2 mode
103     t5r2_ = true;
104     t5r2_touched_size_ = touched_.size();
105     t5r2_released_size_ = released_.size();
106   }
107   if (t5r2_) {
108     short diff = static_cast<short>(hwstate.touch_cnt) -
109         static_cast<short>(prev_hwstate.touch_cnt);
110     if (diff > 0)
111       t5r2_touched_size_ += diff;
112     else if (diff < 0)
113       t5r2_released_size_ += -diff;
114   }
115   for (short tracking_id : added) {
116     Log("TapRecord::Update: Added: %d", tracking_id);
117   }
118   for (short tracking_id: removed) {
119     Log("TapRecord::Update: Removed: %d", tracking_id);
120   }
121   for (short tracking_id : dead) {
122     Log("TapRecord::Update: Dead: %d", tracking_id);
123   }
124   for_each(dead.begin(), dead.end(),
125            bind(&TapRecord::Remove, this, std::placeholders::_1));
126   for (short tracking_id : added) {
127     NoteTouch(tracking_id, *hwstate.GetFingerState(tracking_id));
128   }
129   for_each(removed.begin(), removed.end(),
130            bind(&TapRecord::NoteRelease, this, std::placeholders::_1));
131   // Check if min tap/cotap pressure met yet
132   const float cotap_min_pressure = CotapMinPressure();
133   for (auto& [tracking_id, existing_fs] : touched_) {
134     const FingerState* fs = hwstate.GetFingerState(tracking_id);
135     if (fs) {
136       if (fs->pressure >= immediate_interpreter_->tap_min_pressure() ||
137           !immediate_interpreter_->device_reports_pressure())
138         min_tap_pressure_met_.insert(fs->tracking_id);
139       if (fs->pressure >= cotap_min_pressure ||
140           !immediate_interpreter_->device_reports_pressure()) {
141         min_cotap_pressure_met_.insert(fs->tracking_id);
142         if (existing_fs.pressure < cotap_min_pressure &&
143             immediate_interpreter_->device_reports_pressure()) {
144           // Update existing record, since the old one hadn't met the cotap
145           // pressure
146           existing_fs = *fs;
147         }
148       }
149       stime_t finger_age = hwstate.timestamp -
150           immediate_interpreter_->finger_origin_timestamp(fs->tracking_id);
151       if (finger_age > immediate_interpreter_->tap_max_finger_age())
152         fingers_below_max_age_ = false;
153     }
154   }
155 }
156 
Clear()157 void TapRecord::Clear() {
158   min_tap_pressure_met_.clear();
159   min_cotap_pressure_met_.clear();
160   t5r2_ = false;
161   t5r2_touched_size_ = 0;
162   t5r2_released_size_ = 0;
163   fingers_below_max_age_ = true;
164   touched_.clear();
165   released_.clear();
166 }
167 
Moving(const HardwareState & hwstate,const float dist_max) const168 bool TapRecord::Moving(const HardwareState& hwstate,
169                        const float dist_max) const {
170   const float cotap_min_pressure = CotapMinPressure();
171   for (const auto& [tracking_id, existing_fs] : touched_) {
172     const FingerState* fs = hwstate.GetFingerState(tracking_id);
173     if (!fs)
174       continue;
175     // Only look for moving when current frame meets cotap pressure and
176     // our history contains a contact that's met cotap pressure.
177     if ((fs->pressure < cotap_min_pressure ||
178         existing_fs.pressure < cotap_min_pressure) &&
179         immediate_interpreter_->device_reports_pressure())
180       continue;
181     // Compute distance moved
182     float dist_x = fs->position_x - existing_fs.position_x;
183     float dist_y = fs->position_y - existing_fs.position_y;
184     // Respect WARP flags
185     if (fs->flags & GESTURES_FINGER_WARP_X_TAP_MOVE)
186       dist_x = 0.0;
187     if (fs->flags & GESTURES_FINGER_WARP_Y_TAP_MOVE)
188       dist_y = 0.0;
189 
190     bool moving =
191         dist_x * dist_x + dist_y * dist_y > dist_max * dist_max;
192     if (moving)
193       return true;
194   }
195   return false;
196 }
197 
Motionless(const HardwareState & hwstate,const HardwareState & prev_hwstate,const float max_speed) const198 bool TapRecord::Motionless(const HardwareState& hwstate, const HardwareState&
199                            prev_hwstate, const float max_speed) const {
200   const float cotap_min_pressure = CotapMinPressure();
201   for (const auto& [tracking_id, _] : touched_) {
202     const FingerState* fs = hwstate.GetFingerState(tracking_id);
203     const FingerState* prev_fs = prev_hwstate.GetFingerState(tracking_id);
204     if (!fs || !prev_fs)
205       continue;
206     // Only look for moving when current frame meets cotap pressure and
207     // our history contains a contact that's met cotap pressure.
208     if ((fs->pressure < cotap_min_pressure ||
209         prev_fs->pressure < cotap_min_pressure) &&
210         immediate_interpreter_->device_reports_pressure())
211       continue;
212     // Compute distance moved
213     if (DistSq(*fs, *prev_fs) > max_speed * max_speed)
214       return false;
215   }
216   return true;
217 }
218 
TapBegan() const219 bool TapRecord::TapBegan() const {
220   if (t5r2_)
221     return t5r2_touched_size_ > 0;
222   return !touched_.empty();
223 }
224 
TapComplete() const225 bool TapRecord::TapComplete() const {
226   bool ret = false;
227   if (t5r2_)
228     ret = t5r2_touched_size_ && t5r2_touched_size_ == t5r2_released_size_;
229   else
230     ret = !touched_.empty() && (touched_.size() == released_.size());
231   for (const auto& [tracking_id, finger_state] : touched_) {
232     Log("TapRecord::TapComplete: touched_: %d", tracking_id);
233   }
234   for (short tracking_id : released_) {
235     Log("TapRecord::TapComplete: released_: %d", tracking_id);
236   }
237   return ret;
238 }
239 
MinTapPressureMet() const240 bool TapRecord::MinTapPressureMet() const {
241   // True if any touching finger met minimum pressure
242   return t5r2_ || !min_tap_pressure_met_.empty();
243 }
244 
FingersBelowMaxAge() const245 bool TapRecord::FingersBelowMaxAge() const {
246   return fingers_below_max_age_;
247 }
248 
TapType() const249 int TapRecord::TapType() const {
250   size_t touched_size =
251       t5r2_ ? t5r2_touched_size_ : min_cotap_pressure_met_.size();
252   int ret = GESTURES_BUTTON_LEFT;
253   if (touched_size > 1)
254     ret = GESTURES_BUTTON_RIGHT;
255   if (touched_size == 3 &&
256       immediate_interpreter_->three_finger_click_enable_.val_ &&
257       (!t5r2_ || immediate_interpreter_->t5r2_three_finger_click_enable_.val_))
258     ret = GESTURES_BUTTON_MIDDLE;
259   return ret;
260 }
261 
262 // static
Add(const ScrollEvent & evt_a,const ScrollEvent & evt_b)263 ScrollEvent ScrollEvent::Add(const ScrollEvent& evt_a,
264                              const ScrollEvent& evt_b) {
265   ScrollEvent ret = { evt_a.dx + evt_b.dx,
266                       evt_a.dy + evt_b.dy,
267                       evt_a.dt + evt_b.dt };
268   return ret;
269 }
270 
Insert(float dx,float dy,stime_t timestamp,stime_t prev_timestamp)271 void ScrollEventBuffer::Insert(float dx, float dy, stime_t timestamp,
272                                stime_t prev_timestamp) {
273   float dt;
274   if (size_ > 0) {
275     dt = timestamp - last_scroll_timestamp_;
276   } else {
277     dt = timestamp - prev_timestamp;
278   }
279   last_scroll_timestamp_ = timestamp;
280   head_ = (head_ + max_size_ - 1) % max_size_;
281   buf_[head_].dx = dx;
282   buf_[head_].dy = dy;
283   buf_[head_].dt = dt;
284   size_ = std::min(size_ + 1, max_size_);
285 }
286 
Clear()287 void ScrollEventBuffer::Clear() {
288   size_ = 0;
289 }
290 
Get(size_t offset) const291 const ScrollEvent& ScrollEventBuffer::Get(size_t offset) const {
292   if (offset >= size_) {
293     Err("Out of bounds access!");
294     // avoid returning null pointer
295     static ScrollEvent dummy_event = { 0.0, 0.0, 0.0 };
296     return dummy_event;
297   }
298   return buf_[(head_ + offset) % max_size_];
299 }
300 
GetSpeedSq(size_t num_events,float * dist_sq,float * dt) const301 void ScrollEventBuffer::GetSpeedSq(size_t num_events, float* dist_sq,
302                                    float* dt) const {
303   float dx = 0.0;
304   float dy = 0.0;
305   *dt = 0.0;
306   for (size_t i = 0; i < Size() && i < num_events; i++) {
307     const ScrollEvent& evt = Get(i);
308     dx += evt.dx;
309     dy += evt.dy;
310     *dt += evt.dt;
311   }
312   *dist_sq = dx * dx + dy * dy;
313 }
314 
HardwareStateBuffer(size_t size)315 HardwareStateBuffer::HardwareStateBuffer(size_t size)
316     : states_(new HardwareState[size]),
317       newest_index_(0), size_(size), max_finger_cnt_(0) {
318   for (size_t i = 0; i < size_; i++) {
319     memset(&states_[i], 0, sizeof(HardwareState));
320   }
321 }
322 
~HardwareStateBuffer()323 HardwareStateBuffer::~HardwareStateBuffer() {
324   for (size_t i = 0; i < size_; i++) {
325     delete[] states_[i].fingers;
326   }
327 }
328 
Reset(size_t max_finger_cnt)329 void HardwareStateBuffer::Reset(size_t max_finger_cnt) {
330   max_finger_cnt_ = max_finger_cnt;
331   for (size_t i = 0; i < size_; i++) {
332     delete[] states_[i].fingers;
333   }
334   if (max_finger_cnt_) {
335     for (size_t i = 0; i < size_; i++) {
336       states_[i].fingers = new FingerState[max_finger_cnt_];
337       memset(states_[i].fingers, 0, sizeof(FingerState) * max_finger_cnt_);
338     }
339   } else {
340     for (size_t i = 0; i < size_; i++) {
341       states_[i].fingers = nullptr;
342     }
343   }
344 }
345 
PushState(const HardwareState & state)346 void HardwareStateBuffer::PushState(const HardwareState& state) {
347   newest_index_ = (newest_index_ + size_ - 1) % size_;
348   Get(0).DeepCopy(state, max_finger_cnt_);
349 }
350 
PopState()351 void HardwareStateBuffer::PopState() {
352   newest_index_ = (newest_index_ + 1) % size_;
353 }
354 
ScrollManager(PropRegistry * prop_reg)355 ScrollManager::ScrollManager(PropRegistry* prop_reg)
356     : prev_result_suppress_finger_movement_(false),
357       did_generate_scroll_(false),
358       max_stationary_move_speed_(prop_reg, "Max Stationary Move Speed", 0.0),
359       max_stationary_move_speed_hysteresis_(
360           prop_reg, "Max Stationary Move Speed Hysteresis", 0.0),
361       max_stationary_move_suppress_distance_(
362           prop_reg, "Max Stationary Move Suppress Distance", 1.0),
363       max_pressure_change_(prop_reg, "Max Allowed Pressure Change Per Sec",
364                            800.0),
365       max_pressure_change_hysteresis_(prop_reg,
366                                       "Max Hysteresis Pressure Per Sec",
367                                       600.0),
368       max_pressure_change_duration_(prop_reg,
369                                     "Max Pressure Change Duration",
370                                     0.016),
371       max_stationary_speed_(prop_reg, "Max Finger Stationary Speed", 0.0),
372       vertical_scroll_snap_slope_(prop_reg, "Vertical Scroll Snap Slope",
373                                   tanf(DegToRad(50.0))),  // 50 deg. from horiz.
374       horizontal_scroll_snap_slope_(prop_reg, "Horizontal Scroll Snap Slope",
375                                     tanf(DegToRad(30.0))),
376 
377       fling_buffer_depth_(prop_reg, "Fling Buffer Depth", 10),
378       fling_buffer_suppress_zero_length_scrolls_(
379           prop_reg, "Fling Buffer Suppress Zero Length Scrolls", true),
380       fling_buffer_min_avg_speed_(prop_reg,
381                                   "Fling Buffer Min Avg Speed",
382                                   10.0) {
383 }
384 
StationaryFingerPressureChangingSignificantly(const HardwareStateBuffer & state_buffer,const FingerState & current) const385 bool ScrollManager::StationaryFingerPressureChangingSignificantly(
386     const HardwareStateBuffer& state_buffer,
387     const FingerState& current) const {
388   bool pressure_is_increasing = false;
389   bool pressure_direction_established = false;
390   const FingerState* prev = &current;
391   stime_t now = state_buffer.Get(0).timestamp;
392   stime_t duration = 0.0;
393 
394   if (max_pressure_change_duration_.val_ > 0.0) {
395     for (size_t i = 1; i < state_buffer.Size(); i++) {
396       const HardwareState& state = state_buffer.Get(i);
397       stime_t local_duration = now - state.timestamp;
398       if (local_duration > max_pressure_change_duration_.val_)
399         break;
400 
401       duration = local_duration;
402       const FingerState* fs = state.GetFingerState(current.tracking_id);
403       // If the finger just appeared, skip to check pressure change then
404       if (!fs)
405         break;
406 
407       float pressure_difference = prev->pressure - fs->pressure;
408       if (pressure_difference) {
409         bool is_currently_increasing = pressure_difference > 0.0;
410         if (!pressure_direction_established) {
411           pressure_is_increasing = is_currently_increasing;
412           pressure_direction_established = true;
413         }
414 
415         // If pressure changes are unstable, it's likely just noise.
416         if (is_currently_increasing != pressure_is_increasing)
417           return false;
418       }
419       prev = fs;
420     }
421   } else {
422     // To disable this feature, max_pressure_change_duration_ can be set to a
423     // negative number. When this occurs it reverts to just checking the last
424     // event, not looking through the backlog as well.
425     prev = state_buffer.Get(1).GetFingerState(current.tracking_id);
426     duration = now - state_buffer.Get(1).timestamp;
427   }
428 
429   if (max_stationary_speed_.val_ != 0.0) {
430     // If finger moves too fast, we don't consider it stationary.
431     float dist_sq = (current.position_x - prev->position_x) *
432                     (current.position_x - prev->position_x) +
433                     (current.position_y - prev->position_y) *
434                     (current.position_y - prev->position_y);
435     float dist_sq_thresh = duration * duration *
436         max_stationary_speed_.val_ * max_stationary_speed_.val_;
437     if (dist_sq > dist_sq_thresh)
438       return false;
439   }
440 
441   float dp_thresh = duration *
442       (prev_result_suppress_finger_movement_ ?
443        max_pressure_change_hysteresis_.val_ :
444        max_pressure_change_.val_);
445   float dp = fabsf(current.pressure - prev->pressure);
446   return dp > dp_thresh;
447 }
448 
FillResultScroll(const HardwareStateBuffer & state_buffer,const FingerMap & prev_gs_fingers,const FingerMap & gs_fingers,GestureType prev_gesture_type,const Gesture & prev_result,Gesture * result,ScrollEventBuffer * scroll_buffer)449 bool ScrollManager::FillResultScroll(
450     const HardwareStateBuffer& state_buffer,
451     const FingerMap& prev_gs_fingers,
452     const FingerMap& gs_fingers,
453     GestureType prev_gesture_type,
454     const Gesture& prev_result,
455     Gesture* result,
456     ScrollEventBuffer* scroll_buffer) {
457   // For now, we take the movement of the biggest moving finger.
458   float max_mag_sq = 0.0;  // square of max mag
459   float dx = 0.0;
460   float dy = 0.0;
461   bool stationary = true;
462   bool pressure_changing = false;
463   for (short tracking_id : gs_fingers) {
464     const FingerState* fs = state_buffer.Get(0).GetFingerState(tracking_id);
465     const FingerState* prev = state_buffer.Get(1).GetFingerState(tracking_id);
466     if (!prev)
467       return false;
468     const stime_t dt =
469         state_buffer.Get(0).timestamp - state_buffer.Get(1).timestamp;
470     pressure_changing =
471         pressure_changing ||
472         StationaryFingerPressureChangingSignificantly(state_buffer, *fs);
473     // Call SuppressStationaryFingerMovement even if stationary is already true,
474     // because it records updates.
475     stationary =
476         SuppressStationaryFingerMovement(*fs, *prev, dt) &&
477         stationary;
478     float local_dx = fs->position_x - prev->position_x;
479     if (fs->flags & GESTURES_FINGER_WARP_X_NON_MOVE)
480       local_dx = 0.0;
481     float local_dy = fs->position_y - prev->position_y;
482     if (fs->flags & GESTURES_FINGER_WARP_Y_NON_MOVE)
483       local_dy = 0.0;
484     float local_max_mag_sq = local_dx * local_dx + local_dy * local_dy;
485     if (local_max_mag_sq > max_mag_sq) {
486       max_mag_sq = local_max_mag_sq;
487       dx = local_dx;
488       dy = local_dy;
489     }
490   }
491 
492   // See if we should snap to vertical/horizontal
493   if (fabsf(dy) < horizontal_scroll_snap_slope_.val_ * fabsf(dx))
494     dy = 0.0;  // snap to horizontal
495   else if (fabsf(dy) > vertical_scroll_snap_slope_.val_ * fabsf(dx))
496     dx = 0.0;  // snap to vertical
497 
498   prev_result_suppress_finger_movement_ = pressure_changing || stationary;
499   if (pressure_changing) {
500     // If we get here, it means that the pressure of the finger causing
501     // the scroll is changing a lot, so we don't trust it. It's likely
502     // leaving the touchpad. Normally we might just do nothing, but having
503     // a frame or two of 0 length scroll before a fling looks janky. We
504     // could also just start the fling now, but we don't want to do that
505     // because the fingers may not actually be leaving. What seems to work
506     // well is sort of dead-reckoning approach where we just repeat the
507     // scroll event from the previous input frame.
508     // Since this isn't a "real" scroll event, we don't put it into
509     // scroll_buffer_.
510     // Also, only use previous gesture if it's in the same direction.
511     if (prev_result.type == kGestureTypeScroll &&
512         prev_result.details.scroll.dy * dy >= 0 &&
513         prev_result.details.scroll.dx * dx >= 0) {
514       did_generate_scroll_ = true;
515       *result = prev_result;
516     }
517     return false;
518   }
519 
520   if (stationary) {
521     scroll_buffer->Clear();
522     return false;
523   }
524 
525   if (max_mag_sq > 0) {
526     did_generate_scroll_ = true;
527     *result = Gesture(kGestureScroll,
528                       state_buffer.Get(1).timestamp,
529                       state_buffer.Get(0).timestamp,
530                       dx, dy);
531   }
532   if (prev_gesture_type != kGestureTypeScroll || prev_gs_fingers != gs_fingers)
533     scroll_buffer->Clear();
534   if (!fling_buffer_suppress_zero_length_scrolls_.val_ ||
535       !FloatEq(dx, 0.0) || !FloatEq(dy, 0.0))
536     scroll_buffer->Insert(
537         dx, dy,
538         state_buffer.Get(0).timestamp, state_buffer.Get(1).timestamp);
539   return true;
540 }
541 
UpdateScrollEventBuffer(GestureType gesture_type,ScrollEventBuffer * scroll_buffer) const542 void ScrollManager::UpdateScrollEventBuffer(
543     GestureType gesture_type, ScrollEventBuffer* scroll_buffer) const {
544   if (gesture_type != kGestureTypeScroll)
545     scroll_buffer->Clear();
546 }
547 
ScrollEventsForFlingCount(const ScrollEventBuffer & scroll_buffer) const548 size_t ScrollManager::ScrollEventsForFlingCount(
549     const ScrollEventBuffer& scroll_buffer) const {
550   if (scroll_buffer.Size() <= 1)
551     return scroll_buffer.Size();
552   enum Direction { kNone, kUp, kDown, kLeft, kRight };
553   size_t i = 0;
554   Direction prev_direction = kNone;
555   size_t fling_buffer_depth = static_cast<size_t>(fling_buffer_depth_.val_);
556   for (; i < scroll_buffer.Size() && i < fling_buffer_depth; i++) {
557     const ScrollEvent& event = scroll_buffer.Get(i);
558     if (FloatEq(event.dx, 0.0) && FloatEq(event.dy, 0.0))
559       break;
560     Direction direction;
561     if (fabsf(event.dx) > fabsf(event.dy))
562       direction = event.dx > 0 ? kRight : kLeft;
563     else
564       direction = event.dy > 0 ? kDown : kUp;
565     if (i > 0 && direction != prev_direction)
566       break;
567     prev_direction = direction;
568   }
569   return i;
570 }
571 
RegressScrollVelocity(const ScrollEventBuffer & scroll_buffer,int count,ScrollEvent * out) const572 void ScrollManager::RegressScrollVelocity(
573     const ScrollEventBuffer& scroll_buffer, int count, ScrollEvent* out) const {
574   struct RegressionSums {
575     float tt_;  // Cumulative sum of t^2.
576     float t_;   // Cumulative sum of t.
577     float tx_;  // Cumulative sum of t * x.
578     float ty_;  // Cumulative sum of t * y.
579     float x_;   // Cumulative sum of x.
580     float y_;   // Cumulative sum of y.
581   };
582 
583   out->dt = 1;
584   if (count <= 1) {
585     out->dx = 0;
586     out->dy = 0;
587     return;
588   }
589 
590   RegressionSums sums = {0, 0, 0, 0, 0, 0};
591 
592   float time = 0;
593   float x_coord = 0;
594   float y_coord = 0;
595 
596   for (int i = count - 1; i >= 0; --i) {
597     const ScrollEvent& event = scroll_buffer.Get(i);
598 
599     time += event.dt;
600     x_coord += event.dx;
601     y_coord += event.dy;
602 
603     sums.tt_ += time * time;
604     sums.t_ += time;
605     sums.tx_ += time * x_coord;
606     sums.ty_ += time * y_coord;
607     sums.x_ += x_coord;
608     sums.y_ += y_coord;
609   }
610 
611   // Note the regression determinant only depends on the values of t, and should
612   // never be zero so long as (1) count > 1, and (2) dt values are all non-zero.
613   float det = count * sums.tt_ - sums.t_ * sums.t_;
614 
615   if (det) {
616     float det_inv = 1.0 / det;
617 
618     out->dx = (count * sums.tx_ - sums.t_ * sums.x_) * det_inv;
619     out->dy = (count * sums.ty_ - sums.t_ * sums.y_) * det_inv;
620   } else {
621     out->dx = 0;
622     out->dy = 0;
623   }
624 }
625 
SuppressStationaryFingerMovement(const FingerState & fs,const FingerState & prev,stime_t dt)626 bool ScrollManager::SuppressStationaryFingerMovement(const FingerState& fs,
627                                                      const FingerState& prev,
628                                                      stime_t dt) {
629   if (max_stationary_move_speed_.val_ <= 0.0 ||
630       max_stationary_move_suppress_distance_.val_ <= 0.0)
631     return false;
632   float dist_sq = DistSq(fs, prev);
633   // If speed exceeded, allow free movement and discard history
634   if (dist_sq > dt * dt *
635       max_stationary_move_speed_.val_ * max_stationary_move_speed_.val_) {
636     stationary_start_positions_.erase(fs.tracking_id);
637     return false;
638   }
639 
640   if (dist_sq <= dt * dt *
641       max_stationary_move_speed_hysteresis_.val_ *
642       max_stationary_move_speed_hysteresis_.val_ &&
643       !MapContainsKey(stationary_start_positions_, fs.tracking_id)) {
644     // We assume that the first nearly-stationay event won't exceed the
645     // distance threshold and return from here.
646     Point point(fs.position_x, fs.position_y);
647     stationary_start_positions_[fs.tracking_id] = point;
648     return true;
649   }
650 
651   if (!MapContainsKey(stationary_start_positions_, fs.tracking_id)) {
652     return false;
653   }
654 
655   // Check if distance exceeded. If so, erase history and allow motion
656   float dx = fs.position_x - stationary_start_positions_[fs.tracking_id].x_;
657   float dy = fs.position_y - stationary_start_positions_[fs.tracking_id].y_;
658   if (dx * dx + dy * dy > max_stationary_move_suppress_distance_.val_ *
659       max_stationary_move_suppress_distance_.val_) {
660     stationary_start_positions_.erase(fs.tracking_id);
661     return false;
662   }
663 
664   return true;
665 }
666 
FillResultFling(const HardwareStateBuffer & state_buffer,const ScrollEventBuffer & scroll_buffer,Gesture * result)667 void ScrollManager::FillResultFling(const HardwareStateBuffer& state_buffer,
668                                  const ScrollEventBuffer& scroll_buffer,
669                                  Gesture* result) {
670   if (!did_generate_scroll_)
671     return;
672   ScrollEvent out = { 0.0, 0.0, 0.0 };
673   ScrollEvent zero = { 0.0, 0.0, 0.0 };
674   size_t count = 0;
675 
676   // Make sure fling buffer met the minimum average speed for a fling.
677   float buf_dist_sq = 0.0;
678   float buf_dt = 0.0;
679   scroll_buffer.GetSpeedSq(fling_buffer_depth_.val_, &buf_dist_sq, &buf_dt);
680   if (fling_buffer_min_avg_speed_.val_ * fling_buffer_min_avg_speed_.val_ *
681       buf_dt * buf_dt > buf_dist_sq) {
682     out = zero;
683     goto done;
684   }
685 
686   count = ScrollEventsForFlingCount(scroll_buffer);
687   if (count > scroll_buffer.Size()) {
688     Err("Too few events in scroll buffer");
689     out = zero;
690     goto done;
691   }
692 
693   if (count < 2) {
694     if (count == 0)
695       out = zero;
696     else if (count == 1)
697       out = scroll_buffer.Get(0);
698     goto done;
699   }
700 
701   // If we get here, count == 3 && scroll_buffer.Size() >= 3
702   RegressScrollVelocity(scroll_buffer, count, &out);
703 
704 done:
705   float vx = out.dt ? (out.dx / out.dt) : 0.0;
706   float vy = out.dt ? (out.dy / out.dt) : 0.0;
707   *result = Gesture(kGestureFling,
708                     state_buffer.Get(1).timestamp,
709                     state_buffer.Get(0).timestamp,
710                     vx,
711                     vy,
712                     GESTURES_FLING_START);
713   did_generate_scroll_ = false;
714 }
715 
FingerButtonClick(const ImmediateInterpreter * interpreter)716 FingerButtonClick::FingerButtonClick(const ImmediateInterpreter* interpreter)
717     : interpreter_(interpreter),
718       fingers_(),
719       fingers_status_(),
720       num_fingers_(0),
721       num_recent_(0),
722       num_cold_(0),
723       num_hot_(0) {
724 }
725 
Update(const HardwareState & hwstate,stime_t button_down_time)726 bool FingerButtonClick::Update(const HardwareState& hwstate,
727                                stime_t button_down_time) {
728   const float kMoveDistSq = interpreter_->button_move_dist_.val_ *
729                             interpreter_->button_move_dist_.val_;
730 
731   // Copy all fingers to an array, but leave out palms
732   num_fingers_ = 0;
733   for (int i = 0; i < hwstate.finger_cnt; ++i) {
734     const FingerState& fs = hwstate.fingers[i];
735     if (fs.flags & (GESTURES_FINGER_PALM | GESTURES_FINGER_POSSIBLE_PALM))
736       continue;
737     // we don't support more than 4 fingers
738     if (num_fingers_ >= 4)
739       return false;
740     fingers_[num_fingers_++] = &fs;
741   }
742 
743   // Single finger is trivial
744   if (num_fingers_ <= 1)
745     return false;
746 
747   // Sort fingers array by origin timestamp
748   FingerOriginCompare comparator(interpreter_);
749   std::sort(fingers_, fingers_ + num_fingers_, comparator);
750 
751   // The status describes if a finger is recent (touched down recently),
752   // cold (touched down a while ago, but did not move) or hot (has moved).
753   // However thumbs are always forced to be "cold".
754   for (int i = 0; i < num_fingers_; ++i) {
755     stime_t finger_age =
756         button_down_time -
757         interpreter_->finger_origin_timestamp(fingers_[i]->tracking_id);
758     bool moving_finger =
759         SetContainsValue(interpreter_->moving_, fingers_[i]->tracking_id) ||
760         (interpreter_->DistanceTravelledSq(*fingers_[i], true) > kMoveDistSq);
761     if (!SetContainsValue(interpreter_->pointing_, fingers_[i]->tracking_id))
762       fingers_status_[i] = STATUS_COLD;
763     else if (moving_finger)
764       fingers_status_[i] = STATUS_HOT;
765     else if (finger_age < interpreter_->right_click_second_finger_age_.val_)
766       fingers_status_[i] = STATUS_RECENT;
767     else
768       fingers_status_[i] = STATUS_COLD;
769   }
770 
771   num_recent_ = 0;
772   for (int i = 0; i < num_fingers_; ++i)
773     num_recent_ += (fingers_status_[i] == STATUS_RECENT);
774 
775   num_cold_ = 0;
776   for (int i = 0; i < num_fingers_; ++i)
777     num_cold_ += (fingers_status_[i] == STATUS_COLD);
778 
779   num_hot_ = num_fingers_ - num_recent_ - num_cold_;
780   return true;
781 }
782 
GetButtonTypeForTouchCount(int touch_count) const783 int FingerButtonClick::GetButtonTypeForTouchCount(int touch_count) const {
784   if (touch_count == 2)
785     return GESTURES_BUTTON_RIGHT;
786   if (touch_count == 3 && interpreter_->three_finger_click_enable_.val_)
787     return GESTURES_BUTTON_MIDDLE;
788   return GESTURES_BUTTON_LEFT;
789 }
790 
EvaluateTwoFingerButtonType()791 int FingerButtonClick::EvaluateTwoFingerButtonType() {
792   // Only one finger hot -> moving -> left click
793   if (num_hot_ == 1)
794     return GESTURES_BUTTON_LEFT;
795 
796   float start_delta =
797       fabs(interpreter_->finger_origin_timestamp(fingers_[0]->tracking_id) -
798            interpreter_->finger_origin_timestamp(fingers_[1]->tracking_id));
799 
800   // check if fingers are too close for a right click
801   const float kMin2fDistThreshSq =
802       interpreter_->tapping_finger_min_separation_.val_ *
803       interpreter_->tapping_finger_min_separation_.val_;
804   float dist_sq = DistSq(*fingers_[0], *fingers_[1]);
805   if (dist_sq < kMin2fDistThreshSq)
806     return GESTURES_BUTTON_LEFT;
807 
808   // fingers touched down at approx the same time
809   if (start_delta < interpreter_->right_click_start_time_diff_.val_) {
810     // If two fingers are both very recent, it could either be a right-click
811     // or the left-click of one click-and-drag gesture. Our heuristic is that
812     // for real right-clicks, two finger's pressure should be roughly the same
813     // and they tend not be vertically aligned.
814     const FingerState* min_fs = nullptr;
815     const FingerState* fs = nullptr;
816     if (fingers_[0]->pressure < fingers_[1]->pressure)
817       min_fs = fingers_[0], fs = fingers_[1];
818     else
819       min_fs = fingers_[1], fs = fingers_[0];
820     float min_pressure = min_fs->pressure;
821     // It takes higher pressure for the bottom finger to trigger the physical
822     // click and people tend to place fingers more vertically so that they have
823     // enough space to drag the content with ease.
824     bool likely_click_drag =
825         (fs->pressure >
826              min_pressure +
827                  interpreter_->click_drag_pressure_diff_thresh_.val_ &&
828          fs->pressure >
829              min_pressure *
830                  interpreter_->click_drag_pressure_diff_factor_.val_ &&
831          fs->position_y > min_fs->position_y);
832     float xdist = fabsf(fs->position_x - min_fs->position_x);
833     float ydist = fabsf(fs->position_y - min_fs->position_y);
834     if (likely_click_drag &&
835         ydist >= xdist * interpreter_->click_drag_min_slope_.val_)
836       return GESTURES_BUTTON_LEFT;
837     return GESTURES_BUTTON_RIGHT;
838   }
839 
840   // 1 finger is cold and in the dampened zone? Probably a thumb!
841   if (num_cold_ == 1 && interpreter_->FingerInDampenedZone(*fingers_[0]))
842     return GESTURES_BUTTON_LEFT;
843 
844   // Close fingers -> same hand -> right click
845   // Fingers apart -> second hand finger or thumb -> left click
846   if (interpreter_->TwoFingersGesturing(*fingers_[0], *fingers_[1], true))
847     return GESTURES_BUTTON_RIGHT;
848   else
849     return GESTURES_BUTTON_LEFT;
850 }
851 
EvaluateThreeOrMoreFingerButtonType()852 int FingerButtonClick::EvaluateThreeOrMoreFingerButtonType() {
853   // Treat recent, ambiguous fingers as thumbs if they are in the dampened
854   // zone.
855   int num_dampened_recent = 0;
856   for (int i = num_fingers_ - num_recent_; i < num_fingers_; ++i)
857     num_dampened_recent += interpreter_->FingerInDampenedZone(*fingers_[i]);
858 
859   // Re-use the 2f button type logic in case that all recent fingers are
860   // presumed thumbs because the recent fingers could be from thumb splits
861   // due to the increased pressure when doing a physical click and should be
862   // ignored.
863   if ((num_fingers_ - num_recent_ == 2) &&
864       (num_recent_ == num_dampened_recent))
865     return EvaluateTwoFingerButtonType();
866 
867   // Only one finger hot with all others cold -> moving -> left click
868   if (num_hot_ == 1 && num_cold_ == num_fingers_ - 1)
869     return GESTURES_BUTTON_LEFT;
870 
871   // A single recent touch, or a single cold touch (with all others being hot)
872   // could be a thumb or a second hand finger.
873   if (num_recent_ == 1 || (num_cold_ == 1 && num_hot_ == num_fingers_ - 1)) {
874     // The ambiguous finger is either the most recent one, or the only cold one.
875     const FingerState* ambiguous_finger = fingers_[num_fingers_ - 1];
876     if (num_recent_ != 1) {
877       for (int i = 0; i < num_fingers_; ++i) {
878         if (fingers_status_[i] == STATUS_COLD) {
879           ambiguous_finger = fingers_[i];
880           break;
881         }
882       }
883     }
884     // If it's in the dampened zone we will expect it to be a thumb.
885     // Otherwise it's a second hand finger
886     if (interpreter_->FingerInDampenedZone(*ambiguous_finger))
887       return GetButtonTypeForTouchCount(num_fingers_ - 1);
888     else
889       return GESTURES_BUTTON_LEFT;
890   }
891 
892   // If all fingers are recent we can be sure they are from the same hand.
893   if (num_recent_ == num_fingers_) {
894     // Only if all fingers are in the same zone, we can be sure that none
895     // of them is a thumb.
896     Log("EvaluateThreeOrMoreFingerButtonType: Dampened: %d",
897         num_dampened_recent);
898     if (num_dampened_recent == 0 || num_dampened_recent == num_recent_)
899       return GetButtonTypeForTouchCount(num_recent_);
900   }
901 
902   // To make a decision after this point we need to figure out if and how
903   // many of the fingers are grouped together. We do so by finding the pair
904   // of closest fingers, and then calculate where we expect the remaining
905   // fingers to be found.
906   // If they are not in the expected place, they will be called separate.
907   Log("EvaluateThreeOrMoreFingerButtonType: Falling back to location based "
908       "detection");
909   return EvaluateButtonTypeUsingFigureLocation();
910 }
911 
EvaluateButtonTypeUsingFigureLocation()912 int FingerButtonClick::EvaluateButtonTypeUsingFigureLocation() {
913   const float kMaxDistSq = interpreter_->button_max_dist_from_expected_.val_ *
914                            interpreter_->button_max_dist_from_expected_.val_;
915 
916   // Find pair with the closest distance
917   const FingerState* pair_a = nullptr;
918   const FingerState* pair_b = nullptr;
919   float pair_dist_sq = std::numeric_limits<float>::infinity();
920   for (int i = 0; i < num_fingers_; ++i) {
921     for (int j = 0; j < i; ++j) {
922       float dist_sq = DistSq(*fingers_[i], *fingers_[j]);
923       if (dist_sq < pair_dist_sq) {
924         pair_a = fingers_[i];
925         pair_b = fingers_[j];
926         pair_dist_sq = dist_sq;
927       }
928     }
929   }
930 
931   int num_separate = 0;
932   const FingerState* last_separate = nullptr;
933 
934   if (interpreter_->metrics_->CloseEnoughToGesture(Vector2(*pair_a),
935                                                    Vector2(*pair_b))) {
936     // Expect the remaining fingers to be next to the pair, all with the same
937     // distance from each other.
938     float dx = pair_b->position_x - pair_a->position_x;
939     float dy = pair_b->position_y - pair_a->position_y;
940     float expected1_x = pair_a->position_x + 2 * dx;
941     float expected1_y = pair_a->position_y + 2 * dy;
942     float expected2_x = pair_b->position_x - 2 * dx;
943     float expected2_y = pair_b->position_y - 2 * dy;
944 
945     // Check if remaining fingers are close to the expected positions
946     for (int i = 0; i < num_fingers_; ++i) {
947       if (fingers_[i] == pair_a || fingers_[i] == pair_b)
948         continue;
949       float dist1_sq = DistSqXY(*fingers_[i], expected1_x, expected1_y);
950       float dist2_sq = DistSqXY(*fingers_[i], expected2_x, expected2_y);
951       if (dist1_sq > kMaxDistSq && dist2_sq > kMaxDistSq) {
952         num_separate++;
953         last_separate = fingers_[i];
954       }
955     }
956   } else {
957     // In case the pair is not close we have to fall back to using the
958     // dampened zone
959     Log("EvaluateButtonTypeUsingFigureLocation: Falling back to dampened zone "
960         "separation");
961     for (int i = 0; i < num_fingers_; ++i) {
962       if (interpreter_->FingerInDampenedZone(*fingers_[i])) {
963         num_separate++;
964         last_separate = fingers_[i];
965       }
966     }
967   }
968 
969   // All fingers next to each other
970   if (num_separate == 0)
971     return GetButtonTypeForTouchCount(num_fingers_);
972 
973   // The group with the last finger counts!
974   // Exception: If the separates have only one finger and it's a thumb
975   //            count the other group
976   int num_pressing;
977   if (fingers_[num_fingers_ - 1] == last_separate &&
978       !(num_separate == 1 &&
979         interpreter_->FingerInDampenedZone(*last_separate))) {
980     num_pressing = num_separate;
981   } else {
982     num_pressing = num_fingers_ - num_separate;
983   }
984   Log("EvaluateButtonTypeUsingFigureLocation: Pressing: %d", num_pressing);
985   return GetButtonTypeForTouchCount(num_pressing);
986 }
987 
ImmediateInterpreter(PropRegistry * prop_reg,Tracer * tracer)988 ImmediateInterpreter::ImmediateInterpreter(PropRegistry* prop_reg,
989                                            Tracer* tracer)
990     : Interpreter(nullptr, tracer, false),
991       button_type_(0),
992       finger_button_click_(this),
993       sent_button_down_(false),
994       button_down_deadline_(0.0),
995       started_moving_time_(-1.0),
996       gs_changed_time_(-1.0),
997       finger_leave_time_(-1.0),
998       moving_finger_id_(-1),
999       tap_to_click_state_(kTtcIdle),
1000       tap_to_click_state_entered_(-1.0),
1001       tap_record_(this),
1002       last_movement_timestamp_(-1.0),
1003       swipe_is_vertical_(false),
1004       current_gesture_type_(kGestureTypeNull),
1005       prev_gesture_type_(kGestureTypeNull),
1006       state_buffer_(8),
1007       scroll_buffer_(20),
1008       pinch_guess_start_(-1.0),
1009       pinch_locked_(false),
1010       pinch_status_(GESTURES_ZOOM_START),
1011       pinch_prev_direction_(0),
1012       pinch_prev_time_(-1.0),
1013       finger_seen_shortly_after_button_down_(false),
1014       keyboard_touched_(0.0),
1015       scroll_manager_(prop_reg),
1016       tap_enable_(prop_reg, "Tap Enable", true),
1017       tap_paused_(prop_reg, "Tap Paused", false),
1018       tap_timeout_(prop_reg, "Tap Timeout", 0.2),
1019       inter_tap_timeout_(prop_reg, "Inter-Tap Timeout", 0.15),
1020       tap_drag_delay_(prop_reg, "Tap Drag Delay", 0),
1021       tap_drag_timeout_(prop_reg, "Tap Drag Timeout", 0.3),
1022       tap_drag_enable_(prop_reg, "Tap Drag Enable", false),
1023       drag_lock_enable_(prop_reg, "Tap Drag Lock Enable", false),
1024       tap_drag_stationary_time_(prop_reg, "Tap Drag Stationary Time", 0),
1025       tap_move_dist_(prop_reg, "Tap Move Distance", 2.0),
1026       tap_min_pressure_(prop_reg, "Tap Minimum Pressure", 25.0),
1027       tap_max_movement_(prop_reg, "Tap Maximum Movement", 0.0001),
1028       tap_max_finger_age_(prop_reg, "Tap Maximum Finger Age", 1.2),
1029       three_finger_click_enable_(prop_reg, "Three Finger Click Enable", true),
1030       zero_finger_click_enable_(prop_reg, "Zero Finger Click Enable", false),
1031       t5r2_three_finger_click_enable_(prop_reg,
1032                                       "T5R2 Three Finger Click Enable",
1033                                       false),
1034       change_move_distance_(prop_reg, "Change Min Move Distance", 3.0),
1035       move_lock_speed_(prop_reg, "Move Lock Speed", 10.0),
1036       move_change_lock_speed_(prop_reg, "Move Change Lock Speed", 20.0),
1037       move_change_lock_ratio_(prop_reg, "Move Change Lock Ratio", 2.0),
1038       move_report_distance_(prop_reg, "Move Report Distance", 0.35),
1039       change_timeout_(prop_reg, "Change Timeout", 0.2),
1040       evaluation_timeout_(prop_reg, "Evaluation Timeout", 0.15),
1041       pinch_evaluation_timeout_(prop_reg, "Pinch Evaluation Timeout", 0.1),
1042       thumb_pinch_evaluation_timeout_(prop_reg,
1043                                       "Thumb Pinch Evaluation Timeout", 0.25),
1044       thumb_pinch_min_movement_(prop_reg,
1045                                 "Thumb Pinch Minimum Movement", 0.8),
1046       thumb_pinch_movement_ratio_(prop_reg, "Thumb Pinch Movement Ratio", 20),
1047       thumb_slow_pinch_similarity_ratio_(prop_reg,
1048                                          "Thumb Slow Pinch Similarity Ratio",
1049                                          5),
1050       thumb_pinch_delay_factor_(prop_reg, "Thumb Pinch Delay Factor", 9.0),
1051       minimum_movement_direction_detection_(prop_reg,
1052           "Minimum Movement Direction Detection", 0.003),
1053       damp_scroll_min_movement_factor_(prop_reg,
1054                                        "Damp Scroll Min Move Factor",
1055                                        0.2),
1056       two_finger_pressure_diff_thresh_(prop_reg,
1057                                        "Two Finger Pressure Diff Thresh",
1058                                        32.0),
1059       two_finger_pressure_diff_factor_(prop_reg,
1060                                        "Two Finger Pressure Diff Factor",
1061                                        1.65),
1062       click_drag_pressure_diff_thresh_(prop_reg,
1063                                        "Click Drag Pressure Diff Thresh",
1064                                        10.0),
1065       click_drag_pressure_diff_factor_(prop_reg,
1066                                        "Click Drag Pressure Diff Factor",
1067                                        1.20),
1068       click_drag_min_slope_(prop_reg, "Click Drag Min Slope", 2.22),
1069       thumb_movement_factor_(prop_reg, "Thumb Movement Factor", 0.5),
1070       thumb_speed_factor_(prop_reg, "Thumb Speed Factor", 0.5),
1071       thumb_eval_timeout_(prop_reg, "Thumb Evaluation Timeout", 0.06),
1072       thumb_pinch_threshold_ratio_(prop_reg,
1073                                    "Thumb Pinch Threshold Ratio", 0.25),
1074       thumb_click_prevention_timeout_(prop_reg,
1075                                       "Thumb Click Prevention Timeout", 0.15),
1076       two_finger_scroll_distance_thresh_(prop_reg,
1077                                          "Two Finger Scroll Distance Thresh",
1078                                          1.5),
1079       two_finger_move_distance_thresh_(prop_reg,
1080                                        "Two Finger Move Distance Thresh",
1081                                        7.0),
1082       three_finger_swipe_distance_thresh_(prop_reg,
1083                                           "Three Finger Swipe Distance Thresh",
1084                                           1.5),
1085       four_finger_swipe_distance_thresh_(prop_reg,
1086                                          "Four Finger Swipe Distance Thresh",
1087                                          1.5),
1088       three_finger_swipe_distance_ratio_(prop_reg,
1089                                           "Three Finger Swipe Distance Ratio",
1090                                           0.2),
1091       four_finger_swipe_distance_ratio_(prop_reg,
1092                                          "Four Finger Swipe Distance Ratio",
1093                                          0.2),
1094       three_finger_swipe_enable_(prop_reg, "Three Finger Swipe Enable", true),
1095       bottom_zone_size_(prop_reg, "Bottom Zone Size", 10.0),
1096       button_evaluation_timeout_(prop_reg, "Button Evaluation Timeout", 0.05),
1097       button_finger_timeout_(prop_reg, "Button Finger Timeout", 0.03),
1098       button_move_dist_(prop_reg, "Button Move Distance", 10.0),
1099       button_max_dist_from_expected_(prop_reg,
1100                                      "Button Max Distance From Expected", 20.0),
1101       button_right_click_zone_enable_(prop_reg,
1102                                       "Button Right Click Zone Enable", false),
1103       button_right_click_zone_size_(prop_reg,
1104                                     "Button Right Click Zone Size", 20.0),
1105       keyboard_touched_timeval_high_(prop_reg, "Keyboard Touched Timeval High",
1106                                      0),
1107       keyboard_touched_timeval_low_(prop_reg, "Keyboard Touched Timeval Low",
1108                                     0),
1109       keyboard_palm_prevent_timeout_(prop_reg, "Keyboard Palm Prevent Timeout",
1110                                      0.5),
1111       motion_tap_prevent_timeout_(prop_reg, "Motion Tap Prevent Timeout",
1112                                   0.05),
1113       tapping_finger_min_separation_(prop_reg, "Tap Min Separation", 10.0),
1114       pinch_noise_level_sq_(prop_reg, "Pinch Noise Level Squared", 2.0),
1115       pinch_guess_min_movement_(prop_reg, "Pinch Guess Minimum Movement", 2.0),
1116       pinch_thumb_min_movement_(prop_reg,
1117                                 "Pinch Thumb Minimum Movement", 1.41),
1118       pinch_certain_min_movement_(prop_reg,
1119                                   "Pinch Certain Minimum Movement", 8.0),
1120       inward_pinch_min_angle_(prop_reg, "Inward Pinch Minimum Angle", 0.3),
1121       pinch_zoom_max_angle_(prop_reg, "Pinch Zoom Maximum Angle", -0.4),
1122       scroll_min_angle_(prop_reg, "Scroll Minimum Angle", -0.2),
1123       pinch_guess_consistent_mov_ratio_(prop_reg,
1124           "Pinch Guess Consistent Movement Ratio", 0.4),
1125       pinch_zoom_min_events_(prop_reg, "Pinch Zoom Minimum Events", 3),
1126       pinch_initial_scale_time_inv_(prop_reg,
1127                                     "Pinch Initial Scale Time Inverse",
1128                                     3.33),
1129       pinch_res_(prop_reg, "Minimum Pinch Scale Resolution Squared", 1.005),
1130       pinch_stationary_res_(prop_reg,
1131                             "Stationary Pinch Scale Resolution Squared",
1132                             1.05),
1133       pinch_stationary_time_(prop_reg,
1134                              "Stationary Pinch Time",
1135                              0.10),
1136       pinch_hysteresis_res_(prop_reg,
1137                             "Hysteresis Pinch Scale Resolution Squared",
1138                             1.05),
1139       pinch_enable_(prop_reg, "Pinch Enable", true),
1140       right_click_start_time_diff_(prop_reg,
1141                                    "Right Click Start Time Diff Thresh",
1142                                    0.1),
1143       right_click_second_finger_age_(prop_reg,
1144                                      "Right Click Second Finger Age Thresh",
1145                                      0.5),
1146       quick_acceleration_factor_(prop_reg, "Quick Acceleration Factor", 0.0) {
1147   InitName();
1148   requires_metrics_ = true;
1149   keyboard_touched_timeval_low_.SetDelegate(this);
1150 }
1151 
SyncInterpretImpl(HardwareState & hwstate,stime_t * timeout)1152 void ImmediateInterpreter::SyncInterpretImpl(HardwareState& hwstate,
1153                                              stime_t* timeout) {
1154   const char name[] = "ImmediateInterpreter::SyncInterpretImpl";
1155   LogHardwareStatePre(name, hwstate);
1156 
1157   if (!state_buffer_.Get(0).fingers) {
1158     Err("Must call SetHardwareProperties() before Push().");
1159     return;
1160   }
1161 
1162   state_buffer_.PushState(hwstate);
1163 
1164   FillOriginInfo(hwstate);
1165   result_.type = kGestureTypeNull;
1166   const bool same_fingers = state_buffer_.Get(1).SameFingersAs(hwstate) &&
1167       (hwstate.buttons_down == state_buffer_.Get(1).buttons_down);
1168   if (!same_fingers) {
1169     // Fingers changed, do nothing this time
1170     FingerMap new_gs_fingers;
1171     FingerMap gs_fingers = GetGesturingFingers(hwstate);
1172     std::set_difference(gs_fingers.begin(), gs_fingers.end(),
1173                         non_gs_fingers_.begin(), non_gs_fingers_.end(),
1174                         std::inserter(new_gs_fingers, new_gs_fingers.begin()));
1175     ResetSameFingersState(hwstate);
1176     FillStartPositions(hwstate);
1177     if (pinch_enable_.val_ &&
1178         (hwstate.finger_cnt <= 2 || new_gs_fingers.size() != 2)) {
1179       // Release the zoom lock
1180       UpdatePinchState(hwstate, true, new_gs_fingers);
1181     }
1182     moving_finger_id_ = -1;
1183   }
1184 
1185   if (hwstate.finger_cnt < state_buffer_.Get(1).finger_cnt &&
1186       AnyGesturingFingerLeft(hwstate, prev_active_gs_fingers_)) {
1187     finger_leave_time_ = hwstate.timestamp;
1188   }
1189 
1190   // Check if clock changed backwards
1191   if (hwstate.timestamp < state_buffer_.Get(1).timestamp)
1192     ResetTime();
1193 
1194   UpdatePointingFingers(hwstate);
1195   UpdateThumbState(hwstate);
1196   FingerMap newly_moving_fingers = UpdateMovingFingers(hwstate);
1197   UpdateNonGsFingers(hwstate);
1198   FingerMap gs_fingers;
1199   FingerMap old_gs_fingers = GetGesturingFingers(hwstate);
1200   std::set_difference(old_gs_fingers.begin(), old_gs_fingers.end(),
1201                       non_gs_fingers_.begin(), non_gs_fingers_.end(),
1202                       std::inserter(gs_fingers, gs_fingers.begin()));
1203   if (gs_fingers != prev_gs_fingers_)
1204     gs_changed_time_ = hwstate.timestamp;
1205   UpdateStartedMovingTime(hwstate.timestamp, gs_fingers, newly_moving_fingers);
1206 
1207   UpdateButtons(hwstate, timeout);
1208   UpdateTapGesture(&hwstate,
1209                    gs_fingers,
1210                    same_fingers,
1211                    hwstate.timestamp,
1212                    timeout);
1213 
1214   FingerMap active_gs_fingers;
1215   UpdateCurrentGestureType(hwstate, gs_fingers, &active_gs_fingers);
1216   GenerateFingerLiftGesture();
1217   if (result_.type == kGestureTypeNull)
1218     FillResultGesture(hwstate, active_gs_fingers);
1219 
1220   // Prevent moves while in a tap
1221   if ((tap_to_click_state_ == kTtcFirstTapBegan ||
1222        tap_to_click_state_ == kTtcSubsequentTapBegan) &&
1223       result_.type == kGestureTypeMove)
1224     result_.type = kGestureTypeNull;
1225 
1226   prev_active_gs_fingers_ = active_gs_fingers;
1227   prev_gs_fingers_ = gs_fingers;
1228   prev_result_ = result_;
1229   prev_gesture_type_ = current_gesture_type_;
1230   if (result_.type != kGestureTypeNull) {
1231     non_gs_fingers_.clear();
1232     std::set_difference(gs_fingers.begin(), gs_fingers.end(),
1233                         active_gs_fingers.begin(), active_gs_fingers.end(),
1234                         std::inserter(non_gs_fingers_,
1235                         non_gs_fingers_.begin()));
1236     LogGestureProduce(name, result_);
1237     ProduceGesture(result_);
1238   }
1239   LogHardwareStatePost(name, hwstate);
1240 }
1241 
HandleTimerImpl(stime_t now,stime_t * timeout)1242 void ImmediateInterpreter::HandleTimerImpl(stime_t now, stime_t* timeout) {
1243   const char name[] = "ImmediateInterpreter::HandleTimerImpl";
1244   LogHandleTimerPre(name, now, timeout);
1245 
1246   result_.type = kGestureTypeNull;
1247   // Tap-to-click always aborts when real button(s) are being used, so we
1248   // don't need to worry about conflicts with these two types of callback.
1249   UpdateButtonsTimeout(now);
1250   UpdateTapGesture(nullptr,
1251                    FingerMap(),
1252                    false,
1253                    now,
1254                    timeout);
1255   if (result_.type != kGestureTypeNull) {
1256     LogGestureProduce(name, result_);
1257     ProduceGesture(result_);
1258   }
1259   LogHandleTimerPost(name, now, timeout);
1260 }
1261 
FillOriginInfo(const HardwareState & hwstate)1262 void ImmediateInterpreter::FillOriginInfo(
1263     const HardwareState& hwstate) {
1264   RemoveMissingIdsFromMap(&distance_walked_, hwstate);
1265   for (size_t i = 0; i < hwstate.finger_cnt; i++) {
1266     const FingerState& fs = hwstate.fingers[i];
1267     if (distance_walked_.find(fs.tracking_id) != distance_walked_.end() &&
1268         state_buffer_.Size() > 1 &&
1269         state_buffer_.Get(1).GetFingerState(fs.tracking_id)) {
1270       float delta_x = hwstate.GetFingerState(fs.tracking_id)->position_x -
1271           state_buffer_.Get(1).GetFingerState(fs.tracking_id)->position_x;
1272       float delta_y = hwstate.GetFingerState(fs.tracking_id)->position_y -
1273           state_buffer_.Get(1).GetFingerState(fs.tracking_id)->position_y;
1274       distance_walked_[fs.tracking_id] += sqrtf(delta_x * delta_x +
1275                                                 delta_y * delta_y);
1276       continue;
1277     }
1278     distance_walked_[fs.tracking_id] = 0.0;
1279   }
1280 }
1281 
ResetSameFingersState(const HardwareState & hwstate)1282 void ImmediateInterpreter::ResetSameFingersState(const HardwareState& hwstate) {
1283   pointing_.clear();
1284   fingers_.clear();
1285   start_positions_.clear();
1286   three_finger_swipe_start_positions_.clear();
1287   four_finger_swipe_start_positions_.clear();
1288   scroll_manager_.ResetSameFingerState();
1289   RemoveMissingIdsFromSet(&moving_, hwstate);
1290   changed_time_ = hwstate.timestamp;
1291 }
1292 
ResetTime()1293 void ImmediateInterpreter::ResetTime() {
1294   started_moving_time_ = -1.0;
1295   gs_changed_time_ = -1.0;
1296   finger_leave_time_ = -1.0;
1297   tap_to_click_state_entered_ = -1.0;
1298   last_movement_timestamp_ = -1.0;
1299   pinch_guess_start_ = -1.0;
1300   pinch_prev_time_ = -1.0;
1301 }
1302 
UpdatePointingFingers(const HardwareState & hwstate)1303 void ImmediateInterpreter::UpdatePointingFingers(const HardwareState& hwstate) {
1304   for (size_t i = 0; i < hwstate.finger_cnt; i++) {
1305     if (hwstate.fingers[i].flags & GESTURES_FINGER_PALM)
1306       pointing_.erase(hwstate.fingers[i].tracking_id);
1307     else
1308       pointing_.insert(hwstate.fingers[i].tracking_id);
1309   }
1310   fingers_ = pointing_;
1311 }
1312 
DistanceTravelledSq(const FingerState & fs,bool origin,bool permit_warp) const1313 float ImmediateInterpreter::DistanceTravelledSq(const FingerState& fs,
1314                                                 bool origin,
1315                                                 bool permit_warp) const {
1316   Point delta = FingerTraveledVector(fs, origin, permit_warp);
1317   return delta.x_ * delta.x_ + delta.y_ * delta.y_;
1318 }
1319 
FingerTraveledVector(const FingerState & fs,bool origin,bool permit_warp) const1320 Point ImmediateInterpreter::FingerTraveledVector(
1321     const FingerState& fs, bool origin, bool permit_warp) const {
1322   const std::map<short, Point>* positions;
1323   if (origin)
1324     positions = &origin_positions_;
1325   else
1326     positions = &start_positions_;
1327 
1328   if (!MapContainsKey(*positions, fs.tracking_id))
1329     return Point(0.0f, 0.0f);
1330 
1331   const Point& start = positions->at(fs.tracking_id);
1332   float dx = fs.position_x - start.x_;
1333   float dy = fs.position_y - start.y_;
1334   bool suppress_move =
1335       (!permit_warp || (fs.flags & GESTURES_FINGER_WARP_TELEPORTATION));
1336   if ((fs.flags & GESTURES_FINGER_WARP_X) && suppress_move)
1337     dx = 0;
1338   if ((fs.flags & GESTURES_FINGER_WARP_Y) && suppress_move)
1339     dy = 0;
1340   return Point(dx, dy);
1341 }
1342 
EarlyZoomPotential(const HardwareState & hwstate) const1343 bool ImmediateInterpreter::EarlyZoomPotential(const HardwareState& hwstate)
1344     const {
1345   if (fingers_.size() != 2)
1346     return false;
1347   int id1 = *(fingers_.begin());
1348   int id2 = *(++fingers_.begin());
1349   const FingerState* finger1 = hwstate.GetFingerState(id1);
1350   const FingerState* finger2 = hwstate.GetFingerState(id2);
1351   float pinch_eval_timeout = pinch_evaluation_timeout_.val_;
1352   if (finger1 == nullptr || finger2 == nullptr)
1353     return false;
1354   // Wait for a longer time if fingers arrived together
1355   stime_t t1 = metrics_->GetFinger(id1)->origin_time();
1356   stime_t t2 = metrics_->GetFinger(id2)->origin_time();
1357   if (fabs(t1 - t2) < evaluation_timeout_.val_ &&
1358       hwstate.timestamp - max(t1, t2) <
1359           thumb_pinch_evaluation_timeout_.val_ * thumb_pinch_delay_factor_.val_)
1360     pinch_eval_timeout *= thumb_pinch_delay_factor_.val_;
1361   bool early_decision = hwstate.timestamp - min(t1, t2) < pinch_eval_timeout;
1362   // Avoid extra computation if it's too late for a pinch zoom
1363   if (!early_decision &&
1364       hwstate.timestamp - t1 > thumb_pinch_evaluation_timeout_.val_)
1365     return false;
1366 
1367   float walked_distance1 = distance_walked_.at(finger1->tracking_id);
1368   float walked_distance2 = distance_walked_.at(finger2->tracking_id);
1369   if (walked_distance1 > walked_distance2)
1370     std::swap(walked_distance1,walked_distance2);
1371   if ((walked_distance1 > thumb_pinch_min_movement_.val_ ||
1372            hwstate.timestamp - t1 > thumb_pinch_evaluation_timeout_.val_) &&
1373       walked_distance1 > 0 &&
1374       walked_distance2 / walked_distance1 > thumb_pinch_movement_ratio_.val_)
1375     return false;
1376 
1377   bool motionless_cycles = false;
1378   for (int i = 1;
1379        i < min<int>(state_buffer_.Size(), pinch_zoom_min_events_.val_); i++) {
1380     const FingerState* curr1 = state_buffer_.Get(i - 1).GetFingerState(id1);
1381     const FingerState* curr2 = state_buffer_.Get(i - 1).GetFingerState(id2);
1382     const FingerState* prev1 = state_buffer_.Get(i).GetFingerState(id1);
1383     const FingerState* prev2 = state_buffer_.Get(i).GetFingerState(id2);
1384     if (!curr1 || !curr2 || !prev1 || !prev2) {
1385        motionless_cycles = true;
1386        break;
1387     }
1388     bool finger1_moved = (curr1->position_x - prev1->position_x) != 0 ||
1389                          (curr1->position_y - prev1->position_y) != 0;
1390     bool finger2_moved = (curr2->position_x - prev2->position_x) != 0 ||
1391                          (curr2->position_y - prev2->position_y) != 0;
1392     if (!finger1_moved && !finger2_moved) {
1393        motionless_cycles = true;
1394        break;
1395     }
1396   }
1397   if (motionless_cycles > 0 && early_decision)
1398     return true;
1399 
1400   Point delta1 = FingerTraveledVector(*finger1, true, true);
1401   Point delta2 = FingerTraveledVector(*finger2, true, true);
1402   float dot = delta1.x_ * delta2.x_ + delta1.y_ * delta2.y_;
1403   if ((pinch_guess_start_ > 0 || dot < 0) && early_decision)
1404     return true;
1405 
1406   if (t1 - t2 < evaluation_timeout_.val_ &&
1407       t2 - t1 < evaluation_timeout_.val_ &&
1408       hwstate.timestamp - t1 < thumb_pinch_evaluation_timeout_.val_)
1409     return true;
1410 
1411   return false;
1412 }
1413 
ZoomFingersAreConsistent(const HardwareStateBuffer & state_buffer) const1414 bool ImmediateInterpreter::ZoomFingersAreConsistent(
1415     const HardwareStateBuffer& state_buffer) const {
1416   if (fingers_.size() != 2)
1417     return false;
1418 
1419   int id1 = *(fingers_.begin());
1420   int id2 = *(++fingers_.begin());
1421 
1422   const FingerState* curr1 = state_buffer.Get(min<int>(state_buffer.Size() - 1,
1423       pinch_zoom_min_events_.val_)).GetFingerState(id1);
1424   const FingerState* curr2 = state_buffer.Get(min<int>(state_buffer.Size() - 1,
1425       pinch_zoom_min_events_.val_)).GetFingerState(id2);
1426   if (!curr1 || !curr2)
1427     return false;
1428   for (int i = 0;
1429        i < min<int>(state_buffer.Size(), pinch_zoom_min_events_.val_); i++) {
1430     const FingerState* prev1 = state_buffer.Get(i).GetFingerState(id1);
1431     const FingerState* prev2 = state_buffer.Get(i).GetFingerState(id2);
1432     if (!prev1 || !prev2)
1433       return false;
1434     float dot = FingersAngle(prev1, prev2, curr1, curr2);
1435     if (dot >= 0)
1436       return false;
1437   }
1438   const FingerState* last1 = state_buffer.Get(0).GetFingerState(id1);
1439   const FingerState* last2 = state_buffer.Get(0).GetFingerState(id2);
1440   float angle = FingersAngle(last1, last2, curr1, curr2);
1441   if (angle > pinch_zoom_max_angle_.val_)
1442     return false;
1443   return true;
1444 }
1445 
InwardPinch(const HardwareStateBuffer & state_buffer,const FingerState & fs) const1446 bool ImmediateInterpreter::InwardPinch(
1447     const HardwareStateBuffer& state_buffer, const FingerState& fs) const {
1448   if (fingers_.size() != 2)
1449     return false;
1450 
1451   int id = fs.tracking_id;
1452 
1453   const FingerState* curr =
1454       state_buffer.Get(min<int>(state_buffer.Size(),
1455           pinch_zoom_min_events_.val_)).GetFingerState(id);
1456   if (!curr)
1457     return false;
1458   for (int i = 0;
1459        i < min<int>(state_buffer.Size(), pinch_zoom_min_events_.val_); i++) {
1460     const FingerState* prev = state_buffer.Get(i).GetFingerState(id);
1461     if (!prev)
1462       return false;
1463     float dot = (curr->position_y - prev->position_y);
1464     if (dot <= 0)
1465       return false;
1466   }
1467   const FingerState* last = state_buffer.Get(0).GetFingerState(id);
1468   float dot_last = (curr->position_y - last->position_y);
1469   float size_last = sqrt((curr->position_x - last->position_x) *
1470                          (curr->position_x - last->position_x) +
1471                          (curr->position_y - last->position_y) *
1472                          (curr->position_y - last->position_y));
1473 
1474   float angle = dot_last / size_last;
1475   if (angle < inward_pinch_min_angle_.val_)
1476     return false;
1477   return true;
1478 }
1479 
FingersAngle(const FingerState * prev1,const FingerState * prev2,const FingerState * curr1,const FingerState * curr2) const1480 float ImmediateInterpreter::FingersAngle(const FingerState* prev1,
1481                                          const FingerState* prev2,
1482                                          const FingerState* curr1,
1483                                          const FingerState* curr2) const {
1484   float dot_last = (curr1->position_x - prev1->position_x) *
1485                    (curr2->position_x - prev2->position_x) +
1486                    (curr1->position_y - prev1->position_y) *
1487                    (curr2->position_y - prev2->position_y);
1488   float size_last1_sq = (curr1->position_x - prev1->position_x) *
1489                         (curr1->position_x - prev1->position_x) +
1490                         (curr1->position_y - prev1->position_y) *
1491                         (curr1->position_y - prev1->position_y);
1492   float size_last2_sq = (curr2->position_x - prev2->position_x) *
1493                         (curr2->position_x - prev2->position_x) +
1494                         (curr2->position_y - prev2->position_y) *
1495                         (curr2->position_y - prev2->position_y);
1496   float overall_size = sqrt(size_last1_sq * size_last2_sq);
1497   // If one of the two vectors is too small, return 0.
1498   if (overall_size < minimum_movement_direction_detection_.val_ *
1499                      minimum_movement_direction_detection_.val_)
1500     return 0.0;
1501   return dot_last / overall_size;
1502 }
1503 
ScrollAngle(const FingerState & finger1,const FingerState & finger2)1504 bool ImmediateInterpreter::ScrollAngle(const FingerState& finger1,
1505                                        const FingerState& finger2) {
1506     const FingerState* curr1 = state_buffer_.Get(
1507         min<int>(state_buffer_.Size() - 1, 3))
1508             .GetFingerState(finger1.tracking_id);
1509     const FingerState* curr2 = state_buffer_.Get(
1510         min<int>(state_buffer_.Size() - 1, 3))
1511             .GetFingerState(finger2.tracking_id);
1512     const FingerState* last1 =
1513         state_buffer_.Get(0).GetFingerState(finger1.tracking_id);
1514     const FingerState* last2 =
1515         state_buffer_.Get(0).GetFingerState(finger2.tracking_id);
1516     if (last1 && last2 && curr1 && curr2) {
1517       if (FingersAngle(last1, last2, curr1, curr2) < scroll_min_angle_.val_)
1518         return false;
1519     }
1520     return true;
1521 }
1522 
TwoFingerDistanceSq(const HardwareState & hwstate) const1523 float ImmediateInterpreter::TwoFingerDistanceSq(
1524     const HardwareState& hwstate) const {
1525   if (fingers_.size() == 2) {
1526     return TwoSpecificFingerDistanceSq(hwstate, fingers_);
1527   } else {
1528     return -1;
1529   }
1530 }
1531 
TwoSpecificFingerDistanceSq(const HardwareState & hwstate,const FingerMap & fingers) const1532 float ImmediateInterpreter::TwoSpecificFingerDistanceSq(
1533     const HardwareState& hwstate, const FingerMap& fingers) const {
1534   if (fingers.size() == 2) {
1535     const FingerState* finger_a = hwstate.GetFingerState(*fingers.begin());
1536     const FingerState* finger_b = hwstate.GetFingerState(*(++fingers.begin()));
1537     if (finger_a == nullptr || finger_b == nullptr) {
1538       Err("Finger unexpectedly null");
1539       return -1;
1540     }
1541     return DistSq(*finger_a, *finger_b);
1542   } else if (hwstate.finger_cnt == 2) {
1543     return DistSq(hwstate.fingers[0], hwstate.fingers[1]);
1544   } else {
1545     return -1;
1546   }
1547 }
1548 
1549 // Updates thumb_ below.
UpdateThumbState(const HardwareState & hwstate)1550 void ImmediateInterpreter::UpdateThumbState(const HardwareState& hwstate) {
1551   // Remove old ids from thumb_
1552   RemoveMissingIdsFromMap(&thumb_, hwstate);
1553   RemoveMissingIdsFromMap(&thumb_eval_timer_, hwstate);
1554   float min_pressure = INFINITY;
1555   const FingerState* min_fs = nullptr;
1556   for (size_t i = 0; i < hwstate.finger_cnt; i++) {
1557     const FingerState& fs = hwstate.fingers[i];
1558     if (fs.flags & GESTURES_FINGER_PALM)
1559       continue;
1560     if (fs.pressure < min_pressure) {
1561       min_pressure = fs.pressure;
1562       min_fs = &fs;
1563     }
1564   }
1565   if (!min_fs) {
1566     // Only palms on the touchpad
1567     return;
1568   }
1569   // We respect warp flags only if we really have little information of the
1570   // finger positions and not just because we want to suppress unintentional
1571   // cursor moves. See the definition of GESTURES_FINGER_WARP_TELEPORTATION
1572   // for more detail.
1573   bool min_warp_move = (min_fs->flags & GESTURES_FINGER_WARP_TELEPORTATION) &&
1574                        ((min_fs->flags & GESTURES_FINGER_WARP_X_MOVE) ||
1575                         (min_fs->flags & GESTURES_FINGER_WARP_Y_MOVE));
1576   float min_dist_sq = DistanceTravelledSq(*min_fs, false, true);
1577   float min_dt = hwstate.timestamp -
1578       metrics_->GetFinger(min_fs->tracking_id)->origin_time();
1579   float thumb_dist_sq_thresh = min_dist_sq *
1580       thumb_movement_factor_.val_ * thumb_movement_factor_.val_;
1581   float thumb_speed_sq_thresh = min_dist_sq *
1582       thumb_speed_factor_.val_ * thumb_speed_factor_.val_;
1583   // Make all large-pressure, less moving contacts located below the
1584   // min-pressure contact as thumbs.
1585   bool similar_movement = false;
1586 
1587   if (pinch_enable_.val_ && hwstate.finger_cnt == 2) {
1588     float dt1 = hwstate.timestamp -
1589                 metrics_->GetFinger(hwstate.fingers[0].tracking_id)
1590                         ->origin_time();
1591     float dist_sq1 = DistanceTravelledSq(hwstate.fingers[0], true, true);
1592     float dt2 = hwstate.timestamp -
1593                 metrics_->GetFinger(hwstate.fingers[1].tracking_id)
1594                         ->origin_time();
1595     float dist_sq2 = DistanceTravelledSq(hwstate.fingers[1], true, true);
1596     if (dist_sq1 * dt1 && dist_sq2 * dt2)
1597       similar_movement = max((dist_sq1 * dt1 * dt1) / (dist_sq2 * dt2 * dt2),
1598                              (dist_sq2 * dt2 * dt2) / (dist_sq1 * dt1 * dt1)) <
1599                          thumb_slow_pinch_similarity_ratio_.val_;
1600     else
1601       similar_movement = false;
1602   }
1603   for (size_t i = 0; i < hwstate.finger_cnt; i++) {
1604     const FingerState& fs = hwstate.fingers[i];
1605     if (fs.flags & GESTURES_FINGER_PALM)
1606       continue;
1607     if (pinch_enable_.val_ && InwardPinch(state_buffer_, fs)) {
1608       thumb_speed_sq_thresh *= thumb_pinch_threshold_ratio_.val_;
1609       thumb_dist_sq_thresh *= thumb_pinch_threshold_ratio_.val_;
1610     }
1611     float dist_sq = DistanceTravelledSq(fs, false, true);
1612     float dt = hwstate.timestamp -
1613                metrics_->GetFinger(fs.tracking_id)->origin_time();
1614     bool closer_to_origin = dist_sq <= thumb_dist_sq_thresh;
1615     bool slower_moved = (dist_sq * min_dt &&
1616                          dist_sq * min_dt * min_dt <
1617                          thumb_speed_sq_thresh * dt * dt);
1618     bool relatively_motionless = closer_to_origin || slower_moved;
1619     bool likely_thumb =
1620         (fs.pressure > min_pressure + two_finger_pressure_diff_thresh_.val_ &&
1621          fs.pressure > min_pressure * two_finger_pressure_diff_factor_.val_ &&
1622          fs.position_y > min_fs->position_y);
1623     bool non_gs = (hwstate.timestamp > changed_time_ &&
1624                    (prev_active_gs_fingers_.find(fs.tracking_id) ==
1625                     prev_active_gs_fingers_.end()) &&
1626                    prev_result_.type != kGestureTypeNull);
1627     non_gs |= moving_finger_id_ >= 0 && moving_finger_id_ != fs.tracking_id;
1628     likely_thumb |= non_gs;
1629     // We sometimes can't decide the thumb state if some fingers are undergoing
1630     // warp moves as the decision could be off (DistanceTravelledSq may
1631     // under-estimate the real distance). The cases that we need to re-evaluate
1632     // the thumb in the next frame are:
1633     // 1. Both fingers warp.
1634     // 2. Min-pressure finger warps and relatively_motionless is false.
1635     // 3. Thumb warps and relatively_motionless is true.
1636     bool warp_move = (fs.flags & GESTURES_FINGER_WARP_TELEPORTATION) &&
1637                      ((fs.flags & GESTURES_FINGER_WARP_X_MOVE) ||
1638                       (fs.flags & GESTURES_FINGER_WARP_Y_MOVE));
1639     if (likely_thumb &&
1640         ((warp_move && min_warp_move) ||
1641          (!warp_move && min_warp_move && !relatively_motionless) ||
1642          (warp_move && !min_warp_move && relatively_motionless))) {
1643       continue;
1644     }
1645     likely_thumb &= relatively_motionless;
1646     if (MapContainsKey(thumb_, fs.tracking_id)) {
1647       // Beyond the evaluation period. Stick to being thumbs.
1648       if (thumb_eval_timer_[fs.tracking_id] <= 0.0) {
1649         if (!pinch_enable_.val_ || hwstate.finger_cnt == 1)
1650           continue;
1651         bool slow_pinch_guess =
1652             dist_sq * min_dt * min_dt / (thumb_speed_sq_thresh * dt * dt) >
1653                 thumb_pinch_min_movement_.val_ &&
1654             similar_movement;
1655         stime_t origin_time =
1656             metrics_->GetFinger(fs.tracking_id) ->origin_time();
1657         bool might_be_pinch =
1658             slow_pinch_guess &&
1659             hwstate.timestamp - origin_time < 2 *
1660                 thumb_pinch_evaluation_timeout_.val_ &&
1661             ZoomFingersAreConsistent(state_buffer_);
1662         if (relatively_motionless ||
1663             hwstate.timestamp - origin_time >
1664                 thumb_pinch_evaluation_timeout_.val_) {
1665           if (!might_be_pinch)
1666             continue;
1667           else
1668             likely_thumb = false;
1669         }
1670       }
1671 
1672       // Finger is still under evaluation.
1673       if (likely_thumb) {
1674         // Decrease the timer as the finger is thumb-like in the previous
1675         // frame.
1676         const FingerState* prev =
1677             state_buffer_.Get(1).GetFingerState(fs.tracking_id);
1678         if (!prev)
1679           continue;
1680         thumb_eval_timer_[fs.tracking_id] -=
1681             hwstate.timestamp - state_buffer_.Get(1).timestamp;
1682       } else {
1683         // The finger wasn't thumb-like in the frame. Remove it from the thumb
1684         // list.
1685         thumb_.erase(fs.tracking_id);
1686         thumb_eval_timer_.erase(fs.tracking_id);
1687       }
1688     } else if (likely_thumb) {
1689       // Finger is thumb-like, so we add it to the list.
1690       thumb_[fs.tracking_id] = hwstate.timestamp;
1691       thumb_eval_timer_[fs.tracking_id] = thumb_eval_timeout_.val_;
1692     }
1693   }
1694   for (const auto& [tracking_id, _] : thumb_) {
1695     pointing_.erase(tracking_id);
1696   }
1697 }
1698 
UpdateNonGsFingers(const HardwareState & hwstate)1699 void ImmediateInterpreter::UpdateNonGsFingers(const HardwareState& hwstate) {
1700   RemoveMissingIdsFromSet(&non_gs_fingers_, hwstate);
1701   // moving fingers may be gesturing, so take them out from the set.
1702   FingerMap temp;
1703   std::set_difference(non_gs_fingers_.begin(), non_gs_fingers_.end(),
1704                       moving_.begin(), moving_.end(),
1705                       std::inserter(temp, temp.begin()));
1706   non_gs_fingers_ = temp;
1707 }
1708 
KeyboardRecentlyUsed(stime_t now) const1709 bool ImmediateInterpreter::KeyboardRecentlyUsed(stime_t now) const {
1710   // For tests, values of 0 mean keyboard not used recently.
1711   if (keyboard_touched_ == 0.0)
1712     return false;
1713   // Sanity check. If keyboard_touched_ is more than 10 seconds away from now,
1714   // ignore it.
1715   if (fabs(now - keyboard_touched_) > 10)
1716     return false;
1717 
1718   return keyboard_touched_ + keyboard_palm_prevent_timeout_.val_ > now;
1719 }
1720 
1721 namespace {
1722 struct GetGesturingFingersCompare {
1723   // Returns true if finger_a is strictly closer to keyboard than finger_b
operator ()gestures::__anon7399c5d70211::GetGesturingFingersCompare1724   bool operator()(const FingerState* finger_a, const FingerState* finger_b) {
1725     return finger_a->position_y < finger_b->position_y;
1726   }
1727 };
1728 }  // namespace {}
1729 
GetGesturingFingers(const HardwareState & hwstate) const1730 FingerMap ImmediateInterpreter::GetGesturingFingers(
1731     const HardwareState& hwstate) const {
1732   // We support up to kMaxGesturingFingers finger gestures
1733   if (pointing_.size() <= kMaxGesturingFingers)
1734     return pointing_;
1735 
1736   if (hwstate.finger_cnt <= 0) {
1737     return {};
1738   }
1739 
1740   std::vector<FingerState*> fs(hwstate.finger_cnt);
1741   for (size_t i = 0; i < hwstate.finger_cnt; ++i)
1742     fs[i] = &hwstate.fingers[i];
1743 
1744   // Pull the kMaxSize FingerStates w/ the lowest position_y to the
1745   // front of fs[].
1746   GetGesturingFingersCompare compare;
1747   FingerMap ret;
1748   size_t sorted_cnt;
1749   if (hwstate.finger_cnt > kMaxGesturingFingers) {
1750     std::partial_sort(fs.begin(), fs.begin() + kMaxGesturingFingers,
1751                       fs.end(),
1752                       compare);
1753     sorted_cnt = kMaxGesturingFingers;
1754   } else {
1755     std::sort(fs.begin(), fs.end(), compare);
1756     sorted_cnt = hwstate.finger_cnt;
1757   }
1758   for (size_t i = 0; i < sorted_cnt; i++)
1759     ret.insert(fs[i]->tracking_id);
1760   return ret;
1761 }
1762 
UpdateCurrentGestureType(const HardwareState & hwstate,const FingerMap & gs_fingers,FingerMap * active_gs_fingers)1763 void ImmediateInterpreter::UpdateCurrentGestureType(
1764     const HardwareState& hwstate,
1765     const FingerMap& gs_fingers,
1766     FingerMap* active_gs_fingers) {
1767   *active_gs_fingers = gs_fingers;
1768 
1769   size_t num_gesturing = gs_fingers.size();
1770 
1771   // Physical button or tap overrides current gesture state
1772   if (sent_button_down_ || tap_to_click_state_ == kTtcDrag) {
1773     current_gesture_type_ = kGestureTypeMove;
1774     return;
1775   }
1776 
1777   // current gesture state machine
1778   switch (current_gesture_type_) {
1779     case kGestureTypeContactInitiated:
1780     case kGestureTypeButtonsChange:
1781     case kGestureTypeMouseWheel:
1782       break;
1783 
1784     case kGestureTypeScroll:
1785     case kGestureTypeSwipe:
1786     case kGestureTypeFourFingerSwipe:
1787     case kGestureTypeSwipeLift:
1788     case kGestureTypeFourFingerSwipeLift:
1789     case kGestureTypeFling:
1790     case kGestureTypeMove:
1791     case kGestureTypeNull:
1792       // When a finger leaves, we hold the gesture processing for
1793       // change_timeout_ time.
1794       if (hwstate.timestamp < finger_leave_time_ + change_timeout_.val_) {
1795         current_gesture_type_ = kGestureTypeNull;
1796         return;
1797       }
1798 
1799       // Scrolling detection for T5R2 devices
1800       if ((hwprops_->supports_t5r2 || hwprops_->support_semi_mt) &&
1801           (hwstate.touch_cnt > 2)) {
1802         current_gesture_type_ = kGestureTypeScroll;
1803         return;
1804       }
1805 
1806       // Finger gesture decision process
1807       if (num_gesturing == 0) {
1808         current_gesture_type_ = kGestureTypeNull;
1809       } else if (num_gesturing == 1) {
1810         const FingerState* finger =
1811             hwstate.GetFingerState(*gs_fingers.begin());
1812         if (PalmIsArrivingOrDeparting(*finger))
1813           current_gesture_type_ = kGestureTypeNull;
1814         else
1815           current_gesture_type_ = kGestureTypeMove;
1816       } else {
1817         if (changed_time_ > started_moving_time_ ||
1818             hwstate.timestamp - max(started_moving_time_, gs_changed_time_) <
1819             evaluation_timeout_.val_ ||
1820             current_gesture_type_ == kGestureTypeNull) {
1821           // Try to recognize gestures, starting from many-finger gestures
1822           // first. We choose this order b/c 3-finger gestures are very strict
1823           // in their interpretation.
1824           vector<short, kMaxGesturingFingers> sorted_ids;
1825           SortFingersByProximity(gs_fingers, hwstate, &sorted_ids);
1826           for (; sorted_ids.size() >= 2;
1827                sorted_ids.erase(sorted_ids.end() - 1)) {
1828             if (sorted_ids.size() == 2) {
1829               GestureType new_gs_type = kGestureTypeNull;
1830               const FingerState* fingers[] = {
1831                 hwstate.GetFingerState(*sorted_ids.begin()),
1832                 hwstate.GetFingerState(*(sorted_ids.begin() + 1))
1833               };
1834               if (!fingers[0] || !fingers[1]) {
1835                 Err("Unable to find gesturing fingers!");
1836                 return;
1837               }
1838               // See if two pointers are close together
1839               bool potential_two_finger_gesture =
1840                   TwoFingersGesturing(*fingers[0], *fingers[1], false);
1841               if (!potential_two_finger_gesture) {
1842                 new_gs_type = kGestureTypeMove;
1843               } else {
1844                 new_gs_type =
1845                     GetTwoFingerGestureType(*fingers[0], *fingers[1]);
1846                 // Two fingers that don't end up causing scroll may be
1847                 // ambiguous. Only move if they've been down long enough.
1848                 if (new_gs_type == kGestureTypeMove &&
1849                     hwstate.timestamp -
1850                         min(metrics_->GetFinger(fingers[0]->tracking_id)
1851                                     ->origin_time(),
1852                             metrics_->GetFinger(fingers[1]->tracking_id)
1853                                     ->origin_time()) <
1854                     evaluation_timeout_.val_)
1855                   new_gs_type = kGestureTypeNull;
1856               }
1857               if (new_gs_type != kGestureTypeMove ||
1858                   gs_fingers.size() == 2) {
1859                 // We only allow this path to set a move gesture if there
1860                 // are two fingers gesturing
1861                 current_gesture_type_ = new_gs_type;
1862               }
1863             } else if (sorted_ids.size() == 3) {
1864               const FingerState* fingers[] = {
1865                 hwstate.GetFingerState(*sorted_ids.begin()),
1866                 hwstate.GetFingerState(*(sorted_ids.begin() + 1)),
1867                 hwstate.GetFingerState(*(sorted_ids.begin() + 2))
1868               };
1869               if (!fingers[0] || !fingers[1] || !fingers[2]) {
1870                 Err("Unable to find gesturing fingers!");
1871                 return;
1872               }
1873               current_gesture_type_ = GetMultiFingerGestureType(fingers, 3);
1874             } else if (sorted_ids.size() == 4) {
1875               const FingerState* fingers[] = {
1876                 hwstate.GetFingerState(*sorted_ids.begin()),
1877                 hwstate.GetFingerState(*(sorted_ids.begin() + 1)),
1878                 hwstate.GetFingerState(*(sorted_ids.begin() + 2)),
1879                 hwstate.GetFingerState(*(sorted_ids.begin() + 3))
1880               };
1881               if (!fingers[0] || !fingers[1] || !fingers[2] || !fingers[3]) {
1882                 Err("Unable to find gesturing fingers!");
1883                 return;
1884               }
1885               current_gesture_type_ = GetMultiFingerGestureType(fingers, 4);
1886               if (current_gesture_type_ == kGestureTypeFourFingerSwipe)
1887                 current_gesture_type_ = kGestureTypeFourFingerSwipe;
1888             }
1889             if (current_gesture_type_ != kGestureTypeNull) {
1890               active_gs_fingers->clear();
1891               active_gs_fingers->insert(sorted_ids.begin(), sorted_ids.end());
1892               break;
1893             }
1894           }
1895         }
1896       }
1897 
1898       if ((current_gesture_type_ == kGestureTypeMove ||
1899            current_gesture_type_ == kGestureTypeNull) &&
1900           (pinch_enable_.val_ && !hwprops_->support_semi_mt) &&
1901           !IsScrollOrSwipe(prev_gesture_type_)) {
1902         bool do_pinch = UpdatePinchState(hwstate, false, gs_fingers);
1903         if (do_pinch) {
1904           current_gesture_type_ = kGestureTypePinch;
1905         } else if (EarlyZoomPotential(hwstate)) {
1906           current_gesture_type_ = kGestureTypeNull;
1907         }
1908       }
1909       break;
1910 
1911     case kGestureTypePinch:
1912       if (fingers_.size() == 2 ||
1913           (pinch_status_ == GESTURES_ZOOM_END &&
1914            prev_gesture_type_ == kGestureTypePinch) ||
1915           (prev_gesture_type_ == kGestureTypePinch &&
1916            pinch_locked_ == true)) {
1917         return;
1918       } else {
1919         current_gesture_type_ = kGestureTypeNull;
1920       }
1921       break;
1922 
1923     case kGestureTypeMetrics:
1924       // One shouldn't reach here
1925       Err("Metrics gestures reached ImmediateInterpreter");
1926       break;
1927   }
1928   return;
1929 }
1930 
IsScrollOrSwipe(GestureType gesture_type)1931 bool ImmediateInterpreter::IsScrollOrSwipe(GestureType gesture_type) {
1932   switch(gesture_type) {
1933     case kGestureTypeScroll:
1934     case kGestureTypeSwipe:
1935     case kGestureTypeFourFingerSwipe:
1936       return true;
1937     default:
1938       return false;
1939   }
1940 }
1941 
GenerateFingerLiftGesture()1942 void ImmediateInterpreter::GenerateFingerLiftGesture() {
1943   // If we have just finished scrolling, we set current_gesture_type_ to the
1944   // appropriate lift gesture.
1945   if (IsScrollOrSwipe(prev_gesture_type_) &&
1946       current_gesture_type_ != prev_gesture_type_) {
1947     current_gesture_type_ = GetFingerLiftGesture(prev_gesture_type_);
1948   }
1949 }
1950 
1951 namespace {
1952 // Can't use tuple<float, short, short> b/c we want to make a variable
1953 // sized array of them on the stack
1954 struct DistSqElt {
1955   float dist_sq;
1956   short tracking_id[2];
1957 };
1958 
1959 struct DistSqCompare {
1960   // Returns true if finger_a is strictly closer to keyboard than finger_b
operator ()gestures::__anon7399c5d70311::DistSqCompare1961   bool operator()(const DistSqElt& finger_a, const DistSqElt& finger_b) {
1962     return finger_a.dist_sq < finger_b.dist_sq;
1963   }
1964 };
1965 
1966 }  // namespace {}
1967 
SortFingersByProximity(const FingerMap & finger_ids,const HardwareState & hwstate,vector<short,kMaxGesturingFingers> * out_sorted_ids)1968 void ImmediateInterpreter::SortFingersByProximity(
1969     const FingerMap& finger_ids,
1970     const HardwareState& hwstate,
1971     vector<short, kMaxGesturingFingers>* out_sorted_ids) {
1972   if (finger_ids.size() <= 2) {
1973     for (short finger_id : finger_ids)
1974       out_sorted_ids->push_back(finger_id);
1975     return;
1976   }
1977   // To do the sort, we sort all inter-point distances^2, then scan through
1978   // that until we have enough points
1979   size_t dist_sq_capacity =
1980       (finger_ids.size() * (finger_ids.size() - 1)) / 2;
1981 
1982   std::vector<DistSqElt> dist_sq;
1983   dist_sq.reserve(dist_sq_capacity);
1984 
1985   for (size_t i = 0; i < hwstate.finger_cnt; i++) {
1986     const FingerState& fs1 = hwstate.fingers[i];
1987     if (!SetContainsValue(finger_ids, fs1.tracking_id))
1988       continue;
1989     for (size_t j = i + 1; j < hwstate.finger_cnt; j++) {
1990       const FingerState& fs2 = hwstate.fingers[j];
1991       if (!SetContainsValue(finger_ids, fs2.tracking_id))
1992         continue;
1993       DistSqElt elt = {
1994         DistSq(fs1, fs2),
1995         { fs1.tracking_id, fs2.tracking_id }
1996       };
1997       dist_sq.push_back(elt);
1998     }
1999   }
2000 
2001   DistSqCompare distSqCompare;
2002   std::sort(dist_sq.begin(), dist_sq.end(), distSqCompare);
2003 
2004   if (out_sorted_ids == nullptr) {
2005     Err("out_sorted_ids became null");
2006     return;
2007   }
2008   for (auto const & d: dist_sq) {
2009     short id1 = d.tracking_id[0];
2010     short id2 = d.tracking_id[1];
2011     bool contains1 = out_sorted_ids->find(id1) != out_sorted_ids->end();
2012     bool contains2 = out_sorted_ids->find(id2) != out_sorted_ids->end();
2013     if (contains1 == contains2 && !out_sorted_ids->empty()) {
2014       // Assuming we have some ids in the out vector, then either we have both
2015       // of these new ids, we have neither. Either way, we can't use this edge.
2016       continue;
2017     }
2018     if (!contains1)
2019       out_sorted_ids->push_back(id1);
2020     if (!contains2)
2021       out_sorted_ids->push_back(id2);
2022     if (out_sorted_ids->size() == finger_ids.size())
2023       break;  // We've got all the IDs
2024   }
2025 }
2026 
2027 
UpdatePinchState(const HardwareState & hwstate,bool reset,const FingerMap & gs_fingers)2028 bool ImmediateInterpreter::UpdatePinchState(
2029     const HardwareState& hwstate, bool reset, const FingerMap& gs_fingers) {
2030 
2031   if (reset) {
2032     if (pinch_locked_ && prev_gesture_type_ == kGestureTypePinch) {
2033       current_gesture_type_ = kGestureTypePinch;
2034       pinch_status_ = GESTURES_ZOOM_END;
2035     }
2036     // perform reset to "don't know" state
2037     pinch_guess_start_ = -1.0f;
2038     pinch_locked_ = false;
2039     pinch_prev_distance_sq_ = -1.0f;
2040     return false;
2041   }
2042 
2043   // once locked stay locked until reset.
2044   if (pinch_locked_) {
2045     pinch_status_ = GESTURES_ZOOM_UPDATE;
2046     return false;
2047   }
2048 
2049   // check if we have two valid fingers
2050   if (gs_fingers.size() != 2) {
2051     return false;
2052   }
2053   const FingerState* finger1 = hwstate.GetFingerState(*(gs_fingers.begin()));
2054   const FingerState* finger2 =
2055       hwstate.GetFingerState(*(++gs_fingers.begin()));
2056   if (finger1 == nullptr || finger2 == nullptr) {
2057     Err("Finger unexpectedly null");
2058     return false;
2059   }
2060 
2061   // don't allow pinching with possible palms
2062   if ((finger1->flags & GESTURES_FINGER_POSSIBLE_PALM) ||
2063       (finger2->flags & GESTURES_FINGER_POSSIBLE_PALM)) {
2064     return false;
2065   }
2066 
2067   // assign the bottom finger to finger2
2068   if (finger1->position_y > finger2->position_y) {
2069     std::swap(finger1, finger2);
2070   }
2071 
2072   // Check if the two fingers have start positions
2073   if (!MapContainsKey(start_positions_, finger1->tracking_id) ||
2074       !MapContainsKey(start_positions_, finger2->tracking_id)) {
2075     return false;
2076   }
2077 
2078   if (pinch_prev_distance_sq_ < 0)
2079     pinch_prev_distance_sq_ = TwoFingerDistanceSq(hwstate);
2080 
2081   // Pinch gesture detection
2082   //
2083   // The pinch gesture detection will try to make a guess about whether a pinch
2084   // or not-a-pinch is performed. If the guess stays valid for a specific time
2085   // (slow but consistent movement) or we get a certain decision (fast
2086   // gesturing) the decision is locked until the state is reset.
2087   // * A high ratio of the traveled distances between fingers indicates
2088   //   that a pinch is NOT performed.
2089   // * Strong movement of both fingers in opposite directions indicates
2090   //   that a pinch IS performed.
2091 
2092   Point delta1 = FingerTraveledVector(*finger1, false, true);
2093   Point delta2 = FingerTraveledVector(*finger2, false, true);
2094 
2095   // dot product. dot < 0 if fingers move away from each other.
2096   float dot = delta1.x_ * delta2.x_ + delta1.y_ * delta2.y_;
2097   // squared distances both finger have been traveled.
2098   float d1sq = delta1.x_ * delta1.x_ + delta1.y_ * delta1.y_;
2099   float d2sq = delta2.x_ * delta2.x_ + delta2.y_ * delta2.y_;
2100 
2101   // True if movement is not strong enough to be distinguished from noise.
2102   // This is not equivalent to a comparison of unsquared values, but seems to
2103   // work well in practice.
2104   bool movement_below_noise = (d1sq + d2sq < pinch_noise_level_sq_.val_);
2105 
2106   // guesses if a pinch is being performed or not.
2107   double guess_min_mov_sq = pinch_guess_min_movement_.val_;
2108   guess_min_mov_sq *= guess_min_mov_sq;
2109   bool guess_no = (d1sq > guess_min_mov_sq) ^ (d2sq > guess_min_mov_sq) ||
2110                   dot > 0;
2111   bool guess_yes = ((d1sq > guess_min_mov_sq || d2sq > guess_min_mov_sq) &&
2112                     dot < 0);
2113   bool pinch_certain = false;
2114 
2115   // true if the lower finger is in the dampened zone
2116   bool in_dampened_zone = origin_positions_[finger2->tracking_id].y_ >
2117                           hwprops_->bottom - bottom_zone_size_.val_;
2118 
2119   float lo_dsq;
2120   float hi_dsq;
2121   if (d1sq < d2sq) {
2122     lo_dsq = d1sq;
2123     hi_dsq = d2sq;
2124   } else {
2125     lo_dsq = d2sq;
2126     hi_dsq = d1sq;
2127   }
2128   bool bad_mov_ratio = lo_dsq <= hi_dsq *
2129                                  pinch_guess_consistent_mov_ratio_.val_ *
2130                                  pinch_guess_consistent_mov_ratio_.val_;
2131 
2132   if (!bad_mov_ratio &&
2133       !in_dampened_zone &&
2134       guess_yes &&
2135       !guess_no &&
2136       ZoomFingersAreConsistent(state_buffer_)) {
2137     pinch_certain = true;
2138   }
2139 
2140   // Thumb is in dampened zone: Only allow inward pinch
2141   if (in_dampened_zone &&
2142       (d2sq < pinch_thumb_min_movement_.val_ * pinch_thumb_min_movement_.val_ ||
2143        !InwardPinch(state_buffer_, *finger2))) {
2144     guess_yes = false;
2145     guess_no = true;
2146     pinch_certain = false;
2147   }
2148 
2149   // do state transitions and final decision
2150   if (pinch_guess_start_ < 0) {
2151     // "Don't Know"-state
2152 
2153     // Determine guess.
2154     if (!movement_below_noise) {
2155       if (guess_no && !guess_yes) {
2156         pinch_guess_ = false;
2157         pinch_guess_start_ = hwstate.timestamp;
2158       }
2159       if (guess_yes && !guess_no) {
2160         pinch_guess_ = true;
2161         pinch_guess_start_ = hwstate.timestamp;
2162       }
2163     }
2164   }
2165   if (pinch_guess_start_ >= 0) {
2166     // "Guessed"-state
2167 
2168     // suppress cursor movement when we guess a pinch gesture
2169     if (pinch_guess_) {
2170       for (size_t i = 0; i < hwstate.finger_cnt; ++i) {
2171         FingerState* finger_state = &hwstate.fingers[i];
2172         finger_state->flags |= GESTURES_FINGER_WARP_X;
2173         finger_state->flags |= GESTURES_FINGER_WARP_Y;
2174       }
2175     }
2176 
2177     // Go back to "Don't Know"-state if guess is no longer valid
2178     if (pinch_guess_ != guess_yes ||
2179         pinch_guess_ == guess_no ||
2180         movement_below_noise) {
2181       pinch_guess_start_ = -1.0f;
2182       return false;
2183     }
2184 
2185     // certain decisions if pinch is being performed or not
2186     double cert_min_mov_sq = pinch_certain_min_movement_.val_;
2187     cert_min_mov_sq *= cert_min_mov_sq;
2188     pinch_certain |= (d1sq > cert_min_mov_sq &&
2189                       d2sq > cert_min_mov_sq) &&
2190                      dot < 0;
2191     bool no_pinch_certain = (d1sq > cert_min_mov_sq ||
2192                              d2sq > cert_min_mov_sq) &&
2193                             dot > 0;
2194     pinch_guess_ |= pinch_certain;
2195     pinch_guess_ &= !no_pinch_certain;
2196 
2197     // guessed for long enough or certain decision was made: lock
2198     if ((hwstate.timestamp - pinch_guess_start_ >
2199          pinch_evaluation_timeout_.val_) ||
2200         pinch_certain ||
2201         no_pinch_certain) {
2202       pinch_status_ = GESTURES_ZOOM_START;
2203       pinch_locked_ = true;
2204       return pinch_guess_;
2205     }
2206   }
2207 
2208   return false;
2209 }
2210 
PalmIsArrivingOrDeparting(const FingerState & finger) const2211 bool ImmediateInterpreter::PalmIsArrivingOrDeparting(
2212     const FingerState& finger) const {
2213   if (((finger.flags & GESTURES_FINGER_POSSIBLE_PALM) ||
2214        (finger.flags & GESTURES_FINGER_PALM)) &&
2215       ((finger.flags & GESTURES_FINGER_TREND_INC_TOUCH_MAJOR) ||
2216        (finger.flags & GESTURES_FINGER_TREND_DEC_TOUCH_MAJOR)) &&
2217       ((finger.flags & GESTURES_FINGER_TREND_INC_PRESSURE) ||
2218        (finger.flags & GESTURES_FINGER_TREND_DEC_PRESSURE)))
2219     return true;
2220   return false;
2221 }
2222 
IsTooCloseToThumb(const FingerState & finger) const2223 bool ImmediateInterpreter::IsTooCloseToThumb(const FingerState& finger) const {
2224   const float kMin2fDistThreshSq = tapping_finger_min_separation_.val_ *
2225       tapping_finger_min_separation_.val_;
2226   for (const auto& [tracking_id, _] : thumb_) {
2227     const FingerState* thumb = state_buffer_.Get(0).GetFingerState(tracking_id);
2228     float xdist = fabsf(finger.position_x - thumb->position_x);
2229     float ydist = fabsf(finger.position_y - thumb->position_y);
2230     if (xdist * xdist + ydist * ydist < kMin2fDistThreshSq)
2231       return true;
2232   }
2233   return false;
2234 }
2235 
TwoFingersGesturing(const FingerState & finger1,const FingerState & finger2,bool check_button_type) const2236 bool ImmediateInterpreter::TwoFingersGesturing(
2237     const FingerState& finger1,
2238     const FingerState& finger2,
2239     bool check_button_type) const {
2240   // Make sure distance between fingers isn't too great
2241   if (!metrics_->CloseEnoughToGesture(Vector2(finger1), Vector2(finger2)))
2242     return false;
2243 
2244   // Next, if two fingers are moving a lot, they are gesturing together.
2245   if (started_moving_time_ > changed_time_) {
2246     // Fingers are moving
2247     float dist1_sq = DistanceTravelledSq(finger1, false);
2248     float dist2_sq = DistanceTravelledSq(finger2, false);
2249     if (thumb_movement_factor_.val_ * thumb_movement_factor_.val_ *
2250         max(dist1_sq, dist2_sq) < min(dist1_sq, dist2_sq)) {
2251       return true;
2252     }
2253   }
2254 
2255   // Make sure the pressure difference isn't too great for vertically
2256   // aligned contacts
2257   float pdiff = fabsf(finger1.pressure - finger2.pressure);
2258   float xdist = fabsf(finger1.position_x - finger2.position_x);
2259   float ydist = fabsf(finger1.position_y - finger2.position_y);
2260   if (pdiff > two_finger_pressure_diff_thresh_.val_ && ydist > xdist &&
2261       ((finger1.pressure > finger2.pressure) ==
2262        (finger1.position_y > finger2.position_y)))
2263     return false;
2264 
2265   const float kMin2fDistThreshSq = tapping_finger_min_separation_.val_ *
2266       tapping_finger_min_separation_.val_;
2267   float dist_sq = xdist * xdist + ydist * ydist;
2268   // Make sure distance between fingers isn't too small
2269   if ((dist_sq < kMin2fDistThreshSq) &&
2270       !(finger1.flags & GESTURES_FINGER_MERGE))
2271     return false;
2272 
2273   // If both fingers have a tendency of moving at the same direction, they
2274   // are gesturing together. This check is disabled if we are using the
2275   // function to distinguish left/right clicks.
2276   if (!check_button_type) {
2277     unsigned and_flags = finger1.flags & finger2.flags;
2278     if ((and_flags & GESTURES_FINGER_TREND_INC_X) ||
2279         (and_flags & GESTURES_FINGER_TREND_DEC_X) ||
2280         (and_flags & GESTURES_FINGER_TREND_INC_Y) ||
2281         (and_flags & GESTURES_FINGER_TREND_DEC_Y))
2282       return true;
2283   }
2284 
2285   // Next, if fingers are vertically aligned and one is in the bottom zone,
2286   // consider that one a resting thumb (thus, do not scroll/right click)
2287   // if it has greater pressure. For clicking, we relax the pressure requirement
2288   // because we may not have enough time to determine.
2289   if (xdist < ydist && (FingerInDampenedZone(finger1) ||
2290                         FingerInDampenedZone(finger2)) &&
2291       (FingerInDampenedZone(finger1) == (finger1.pressure > finger2.pressure) ||
2292        check_button_type))
2293     return false;
2294   return true;
2295 }
2296 
GetTwoFingerGestureType(const FingerState & finger1,const FingerState & finger2)2297 GestureType ImmediateInterpreter::GetTwoFingerGestureType(
2298     const FingerState& finger1,
2299     const FingerState& finger2) {
2300   if (!MapContainsKey(start_positions_, finger1.tracking_id) ||
2301       !MapContainsKey(start_positions_, finger2.tracking_id))
2302     return kGestureTypeNull;
2303 
2304   // If a finger is close to any thumb, we believe it to be due to thumb-splits
2305   // and ignore it.
2306   int num_close_to_thumb = 0;
2307   num_close_to_thumb += static_cast<int>(IsTooCloseToThumb(finger1));
2308   num_close_to_thumb += static_cast<int>(IsTooCloseToThumb(finger2));
2309   if (num_close_to_thumb == 1)
2310     return kGestureTypeMove;
2311   else if (num_close_to_thumb == 2)
2312     return kGestureTypeNull;
2313 
2314   // Compute distance traveled since fingers changed for each finger
2315   float dx1 = finger1.position_x - start_positions_[finger1.tracking_id].x_;
2316   float dy1 = finger1.position_y - start_positions_[finger1.tracking_id].y_;
2317   float dx2 = finger2.position_x - start_positions_[finger2.tracking_id].x_;
2318   float dy2 = finger2.position_y - start_positions_[finger2.tracking_id].y_;
2319 
2320   float large_dx = MaxMag(dx1, dx2);
2321   float large_dy = MaxMag(dy1, dy2);
2322   // These compares are okay if d{x,y}1 == d{x,y}2:
2323   short large_dx_id =
2324       (large_dx == dx1) ? finger1.tracking_id : finger2.tracking_id;
2325   short large_dy_id =
2326       (large_dy == dy1) ? finger1.tracking_id : finger2.tracking_id;
2327   float small_dx = MinMag(dx1, dx2);
2328   float small_dy = MinMag(dy1, dy2);
2329 
2330   short small_dx_id =
2331       (small_dx == dx1) ? finger1.tracking_id : finger2.tracking_id;
2332   short small_dy_id =
2333       (small_dy == dy1) ? finger1.tracking_id : finger2.tracking_id;
2334 
2335   bool dampened_zone_occupied = false;
2336   // movements of the finger in the dampened zone. If there are multiple
2337   // fingers in the dampened zone, dx is min(dx_1, dx_2), dy is min(dy_1, dy_2).
2338   float damp_dx = INFINITY;
2339   float damp_dy = INFINITY;
2340   float non_damp_dx = 0.0;
2341   float non_damp_dy = 0.0;
2342   if (FingerInDampenedZone(finger1) ||
2343       (finger1.flags & GESTURES_FINGER_POSSIBLE_PALM)) {
2344     dampened_zone_occupied = true;
2345     damp_dx = dx1;
2346     damp_dy = dy1;
2347     non_damp_dx = dx2;
2348     non_damp_dy = dy2;
2349   }
2350   if (FingerInDampenedZone(finger2) ||
2351       (finger2.flags & GESTURES_FINGER_POSSIBLE_PALM)) {
2352     dampened_zone_occupied = true;
2353     damp_dx = MinMag(damp_dx, dx2);
2354     damp_dy = MinMag(damp_dy, dy2);
2355     non_damp_dx = MaxMag(non_damp_dx, dx1);
2356     non_damp_dy = MaxMag(non_damp_dy, dy1);
2357   }
2358 
2359   // Trending in the same direction?
2360   const unsigned kTrendX =
2361       GESTURES_FINGER_TREND_INC_X | GESTURES_FINGER_TREND_DEC_X;
2362   const unsigned kTrendY =
2363       GESTURES_FINGER_TREND_INC_Y | GESTURES_FINGER_TREND_DEC_Y;
2364   unsigned common_trend_flags = finger1.flags & finger2.flags &
2365       (kTrendX | kTrendY);
2366 
2367   bool large_dx_moving =
2368       fabsf(large_dx) >= two_finger_scroll_distance_thresh_.val_ ||
2369       SetContainsValue(moving_, large_dx_id);
2370   bool large_dy_moving =
2371       fabsf(large_dy) >= two_finger_scroll_distance_thresh_.val_ ||
2372       SetContainsValue(moving_, large_dy_id);
2373   bool small_dx_moving =
2374       fabsf(small_dx) >= two_finger_scroll_distance_thresh_.val_ ||
2375       SetContainsValue(moving_, small_dx_id);
2376   bool small_dy_moving =
2377       fabsf(small_dy) >= two_finger_scroll_distance_thresh_.val_ ||
2378       SetContainsValue(moving_, small_dy_id);
2379   bool trend_scrolling_x = (common_trend_flags & kTrendX) &&
2380        large_dx_moving && small_dx_moving;
2381   bool trend_scrolling_y = (common_trend_flags & kTrendY) &&
2382        large_dy_moving && small_dy_moving;
2383 
2384   if (pointing_.size() == 2 && (trend_scrolling_x || trend_scrolling_y)) {
2385     if (pinch_enable_.val_ && !ScrollAngle(finger1, finger2))
2386          return kGestureTypeNull;
2387     return kGestureTypeScroll;
2388   }
2389 
2390   if (fabsf(large_dx) > fabsf(large_dy)) {
2391     // consider horizontal scroll
2392     if (fabsf(small_dx) < two_finger_scroll_distance_thresh_.val_)
2393       small_dx = 0.0;
2394     if (large_dx * small_dx <= 0.0) {
2395       // not same direction
2396       if (fabsf(large_dx) < two_finger_move_distance_thresh_.val_)
2397         return kGestureTypeNull;
2398       else
2399         return kGestureTypeMove;
2400     }
2401     if (fabsf(large_dx) < two_finger_scroll_distance_thresh_.val_)
2402       return kGestureTypeNull;
2403     if (dampened_zone_occupied) {
2404       // Require damp to move at least some amount with the other finger
2405       if (fabsf(damp_dx) <
2406           damp_scroll_min_movement_factor_.val_ * fabsf(non_damp_dx)) {
2407         return kGestureTypeNull;
2408       }
2409     }
2410     if (pinch_enable_.val_ && !ScrollAngle(finger1, finger2))
2411          return kGestureTypeNull;
2412     return kGestureTypeScroll;
2413   } else {
2414     // consider vertical scroll
2415     if (fabsf(small_dy) < two_finger_scroll_distance_thresh_.val_)
2416       small_dy = 0.0;
2417     if (large_dy * small_dy <= 0.0) {
2418       if (fabsf(large_dy) < two_finger_move_distance_thresh_.val_)
2419         return kGestureTypeNull;
2420       else
2421         return kGestureTypeMove;
2422     }
2423     if (dampened_zone_occupied) {
2424       // Require damp to move at least some amount with the other finger
2425       if (fabsf(damp_dy) <
2426           damp_scroll_min_movement_factor_.val_ * fabsf(non_damp_dy)) {
2427         return kGestureTypeNull;
2428       }
2429     }
2430     if (pinch_enable_.val_ && !ScrollAngle(finger1, finger2))
2431          return kGestureTypeNull;
2432     return kGestureTypeScroll;
2433   }
2434 }
2435 
GetFingerLiftGesture(GestureType current_gesture_type)2436 GestureType ImmediateInterpreter::GetFingerLiftGesture(
2437     GestureType current_gesture_type) {
2438   switch(current_gesture_type) {
2439     case kGestureTypeScroll: return kGestureTypeFling;
2440     case kGestureTypeSwipe: return kGestureTypeSwipeLift;
2441     case kGestureTypeFourFingerSwipe: return kGestureTypeFourFingerSwipeLift;
2442     default: return kGestureTypeNull;
2443   }
2444 }
2445 
GetMultiFingerGestureType(const FingerState * const fingers[],const int num_fingers)2446 GestureType ImmediateInterpreter::GetMultiFingerGestureType(
2447     const FingerState* const fingers[], const int num_fingers) {
2448   float swipe_distance_thresh;
2449   float swipe_distance_ratio;
2450   std::map<short, Point> *swipe_start_positions;
2451   GestureType gesture_type;
2452   if (num_fingers == 4) {
2453     swipe_distance_thresh = four_finger_swipe_distance_thresh_.val_;
2454     swipe_distance_ratio = four_finger_swipe_distance_ratio_.val_;
2455     swipe_start_positions = &four_finger_swipe_start_positions_;
2456     gesture_type = kGestureTypeFourFingerSwipe;
2457   } else if (num_fingers == 3) {
2458     swipe_distance_thresh = three_finger_swipe_distance_thresh_.val_;
2459     swipe_distance_ratio = three_finger_swipe_distance_ratio_.val_;
2460     swipe_start_positions = &three_finger_swipe_start_positions_;
2461     gesture_type = kGestureTypeSwipe;
2462   } else {
2463     return kGestureTypeNull;
2464   }
2465 
2466   assert(num_fingers <= (int) kMaxGesturingFingers);
2467 
2468   const FingerState* x_fingers[kMaxGesturingFingers];
2469   const FingerState* y_fingers[kMaxGesturingFingers];
2470   for (int i = 0; i < num_fingers; i++) {
2471     x_fingers[i] = fingers[i];
2472     y_fingers[i] = fingers[i];
2473   }
2474   std::sort(x_fingers, x_fingers + num_fingers,
2475             [] (const FingerState* a, const FingerState* b) ->
2476                 bool { return a->position_x < b->position_x; });
2477   std::sort(y_fingers, y_fingers + num_fingers,
2478             [] (const FingerState* a, const FingerState* b) ->
2479                 bool { return a->position_y < b->position_y; });
2480   bool horizontal =
2481       (x_fingers[num_fingers - 1]->position_x - x_fingers[0]->position_x) >=
2482       (y_fingers[num_fingers -1]->position_y - y_fingers[0]->position_y);
2483   const FingerState* sorted_fingers[4];
2484   for (int i = 0; i < num_fingers; i++) {
2485     sorted_fingers[i] = horizontal ? x_fingers[i] : y_fingers[i];
2486   }
2487 
2488   float dx[kMaxGesturingFingers];
2489   float dy[kMaxGesturingFingers];
2490   float dy_sum = 0;
2491   float dx_sum = 0;
2492   for (int i = 0; i < num_fingers; i++) {
2493     dx[i] = sorted_fingers[i]->position_x -
2494             (*swipe_start_positions)[sorted_fingers[i]->tracking_id].x_;
2495     dy[i] = sorted_fingers[i]->position_y -
2496             (*swipe_start_positions)[sorted_fingers[i]->tracking_id].y_;
2497     dx_sum += dx[i];
2498     dy_sum += dy[i];
2499   }
2500   // pick horizontal or vertical
2501   float *deltas = fabsf(dx_sum) > fabsf(dy_sum) ? dx : dy;
2502   swipe_is_vertical_ = deltas == dy;
2503 
2504   // All fingers must move in the same direction.
2505   for (int i = 1; i < num_fingers; i++) {
2506     if (deltas[i] * deltas[0] <= 0.0) {
2507       for (int i = 0; i < num_fingers; i++) {
2508         Point point(sorted_fingers[i]->position_x,
2509                     sorted_fingers[i]->position_y);
2510         (*swipe_start_positions)[sorted_fingers[i]->tracking_id] =
2511             point;
2512       }
2513       return kGestureTypeNull;
2514     }
2515   }
2516 
2517   // All fingers must have traveled far enough.
2518   float max_delta = fabsf(deltas[0]);
2519   float min_delta = fabsf(deltas[0]);
2520   for (int i = 1; i < num_fingers; i++) {
2521     max_delta = max(max_delta, fabsf(deltas[i]));
2522     min_delta = min(min_delta, fabsf(deltas[i]));
2523   }
2524   if (max_delta >= swipe_distance_thresh &&
2525       min_delta >= swipe_distance_ratio * max_delta)
2526     return gesture_type;
2527   return kGestureTypeNull;
2528 }
2529 
TapToClickStateName(TapToClickState state)2530 const char* ImmediateInterpreter::TapToClickStateName(TapToClickState state) {
2531   switch (state) {
2532     case kTtcIdle: return "Idle";
2533     case kTtcFirstTapBegan: return "FirstTapBegan";
2534     case kTtcTapComplete: return "TapComplete";
2535     case kTtcSubsequentTapBegan: return "SubsequentTapBegan";
2536     case kTtcDrag: return "Drag";
2537     case kTtcDragRelease: return "DragRelease";
2538     case kTtcDragRetouch: return "DragRetouch";
2539     default: return "<unknown>";
2540   }
2541 }
2542 
TimeoutForTtcState(TapToClickState state)2543 stime_t ImmediateInterpreter::TimeoutForTtcState(TapToClickState state) {
2544   switch (state) {
2545     case kTtcIdle: return tap_timeout_.val_;
2546     case kTtcFirstTapBegan: return tap_timeout_.val_;
2547     case kTtcTapComplete: return inter_tap_timeout_.val_;
2548     case kTtcSubsequentTapBegan: return tap_timeout_.val_;
2549     case kTtcDrag: return tap_timeout_.val_;
2550     case kTtcDragRelease: return tap_drag_timeout_.val_;
2551     case kTtcDragRetouch: return tap_timeout_.val_;
2552     default:
2553       Err("Unknown TapToClickState %u!", state);
2554       return 0.0;
2555   }
2556 }
2557 
SetTapToClickState(TapToClickState state,stime_t now)2558 void ImmediateInterpreter::SetTapToClickState(TapToClickState state,
2559                                               stime_t now) {
2560   if (tap_to_click_state_ != state) {
2561     tap_to_click_state_ = state;
2562     tap_to_click_state_entered_ = now;
2563   }
2564 }
2565 
UpdateTapGesture(const HardwareState * hwstate,const FingerMap & gs_fingers,const bool same_fingers,stime_t now,stime_t * timeout)2566 void ImmediateInterpreter::UpdateTapGesture(
2567     const HardwareState* hwstate,
2568     const FingerMap& gs_fingers,
2569     const bool same_fingers,
2570     stime_t now,
2571     stime_t* timeout) {
2572   unsigned down = 0;
2573   unsigned up = 0;
2574   UpdateTapState(hwstate, gs_fingers, same_fingers, now, &down, &up, timeout);
2575   if (down == 0 && up == 0) {
2576     return;
2577   }
2578   Log("UpdateTapGesture: Tap Generated");
2579   result_ = Gesture(kGestureButtonsChange,
2580                     state_buffer_.Get(1).timestamp,
2581                     now,
2582                     down,
2583                     up,
2584                     true); // is_tap
2585 }
2586 
UpdateTapState(const HardwareState * hwstate,const FingerMap & gs_fingers,const bool same_fingers,stime_t now,unsigned * buttons_down,unsigned * buttons_up,stime_t * timeout)2587 void ImmediateInterpreter::UpdateTapState(
2588     const HardwareState* hwstate,
2589     const FingerMap& gs_fingers,
2590     const bool same_fingers,
2591     stime_t now,
2592     unsigned* buttons_down,
2593     unsigned* buttons_up,
2594     stime_t* timeout) {
2595   if (tap_to_click_state_ == kTtcIdle && (!tap_enable_.val_ ||
2596                                           tap_paused_.val_))
2597     return;
2598 
2599   FingerMap tap_gs_fingers;
2600 
2601   if (hwstate)
2602     RemoveMissingIdsFromSet(&tap_dead_fingers_, *hwstate);
2603 
2604   bool cancel_tapping = false;
2605   if (hwstate) {
2606     for (int i = 0; i < hwstate->finger_cnt; ++i) {
2607       if (hwstate->fingers[i].flags &
2608           (GESTURES_FINGER_NO_TAP | GESTURES_FINGER_MERGE))
2609         cancel_tapping = true;
2610     }
2611     for (short tracking_id : gs_fingers) {
2612       const FingerState* fs = hwstate->GetFingerState(tracking_id);
2613       if (!fs) {
2614         Err("Missing finger state?!");
2615         continue;
2616       }
2617       tap_gs_fingers.insert(tracking_id);
2618     }
2619   }
2620   std::set<short> added_fingers;
2621 
2622   // Fingers removed from the pad entirely
2623   std::set<short> removed_fingers;
2624 
2625   // Fingers that were gesturing, but now aren't
2626   std::set<short> dead_fingers;
2627 
2628   const bool phys_click_in_progress = hwstate && hwstate->buttons_down != 0 &&
2629     (zero_finger_click_enable_.val_ || finger_seen_shortly_after_button_down_);
2630 
2631   bool is_timeout = (now - tap_to_click_state_entered_ >
2632                      TimeoutForTtcState(tap_to_click_state_));
2633 
2634   if (phys_click_in_progress) {
2635     // Don't allow any current fingers to tap ever
2636     for (size_t i = 0; i < hwstate->finger_cnt; i++)
2637       tap_dead_fingers_.insert(hwstate->fingers[i].tracking_id);
2638   }
2639 
2640   if (hwstate && (!same_fingers || prev_tap_gs_fingers_ != tap_gs_fingers)) {
2641     // See if fingers were added
2642     for (short tracking_id : tap_gs_fingers) {
2643       // If the finger was marked as a thumb before, it is not new.
2644       if (hwstate->timestamp - finger_origin_timestamp(tracking_id) >
2645                thumb_click_prevention_timeout_.val_)
2646         continue;
2647 
2648       if (!SetContainsValue(prev_tap_gs_fingers_, tracking_id)) {
2649         // Gesturing finger wasn't in prev state. It's new.
2650         const FingerState* fs = hwstate->GetFingerState(tracking_id);
2651         if (FingerTooCloseToTap(*hwstate, *fs) ||
2652             FingerTooCloseToTap(state_buffer_.Get(1), *fs) ||
2653             SetContainsValue(tap_dead_fingers_, fs->tracking_id))
2654           continue;
2655         added_fingers.insert(tracking_id);
2656         Log("TTC: Added %d", tracking_id);
2657       }
2658     }
2659 
2660     // See if fingers were removed or are now non-gesturing (dead)
2661     for (short tracking_id : prev_tap_gs_fingers_) {
2662       if (tap_gs_fingers.find(tracking_id) != tap_gs_fingers.end())
2663         // still gesturing; neither removed nor dead
2664         continue;
2665       if (!hwstate->GetFingerState(tracking_id)) {
2666         // Previously gesturing finger isn't in current state. It's gone.
2667         removed_fingers.insert(tracking_id);
2668         Log("TTC: Removed %d", tracking_id);
2669       } else {
2670         // Previously gesturing finger is in current state. It's dead.
2671         dead_fingers.insert(tracking_id);
2672         Log("TTC: Dead %d", tracking_id);
2673       }
2674     }
2675   }
2676 
2677   prev_tap_gs_fingers_ = tap_gs_fingers;
2678 
2679   // The state machine:
2680 
2681   // If you are updating the code, keep this diagram correct.
2682   // We have a TapRecord which stores current tap state.
2683   // Also, if the physical button is down or previous gesture type is scroll,
2684   // we go to (or stay in) Idle state.
2685 
2686   //     Start
2687   //       ↓
2688   //    [Idle**] <----------------------------------------------------------,
2689   //       ↓ added finger(s)                                                ^
2690   //  ,>[FirstTapBegan] -<right click: send right click, timeout/movement>->|
2691   //  |    ↓ released all fingers                                           |
2692   // ,->[TapComplete*] --<timeout: send click>----------------------------->|
2693   // ||    | | two finger touching: send left click.                        |
2694   // |'<---+-'                                                              ^
2695   // |     ↓ add finger(s)                                                  |
2696   // ^  [SubsequentTapBegan] --<timeout/move w/o delay: send click>-------->|
2697   // |     | | | release all fingers: send left click                       |
2698   // |<----+-+-'                                                            ^
2699   // |     | `-> start non-left click: send left click; goto FirstTapBegan  |
2700   // |     ↓ timeout/movement with delay: send button down                  |
2701   // | ,->[Drag] --<detect 2 finger gesture: send button up>--------------->|
2702   // | |   ↓ release all fingers                                            ^
2703   // | |  [DragRelease*]  --<timeout: send button up>---------------------->|
2704   // ^ ^   ↓ add finger(s)                                                  ^
2705   // | |  [DragRetouch]  --<remove fingers (left tap): send button up>----->'
2706   // | |   | | timeout/movement
2707   // | '-<-+-'
2708   // |     |  remove all fingers (non-left tap): send button up
2709   // '<----'
2710   //
2711   // * When entering TapComplete or DragRelease, we set a timer, since
2712   //   we will have no fingers on the pad and want to run possibly before
2713   //   fingers are put on the pad. Note that we use different timeouts
2714   //   based on which state we're in (tap_timeout_ or tap_drag_timeout_).
2715   // ** When entering idle, we reset the TapRecord.
2716 
2717   if (tap_to_click_state_ != kTtcIdle)
2718     Log("TTC State: %s", TapToClickStateName(tap_to_click_state_));
2719   if (!hwstate)
2720     Log("TTC: This is a timer callback");
2721   if (phys_click_in_progress || KeyboardRecentlyUsed(now) ||
2722       prev_result_.type == kGestureTypeScroll ||
2723       cancel_tapping) {
2724     Log("TTC: Forced to idle");
2725     SetTapToClickState(kTtcIdle, now);
2726     return;
2727   }
2728 
2729   switch (tap_to_click_state_) {
2730     case kTtcIdle:
2731       tap_record_.Clear();
2732       if (hwstate &&
2733           hwstate->timestamp - last_movement_timestamp_ >=
2734           motion_tap_prevent_timeout_.val_) {
2735         tap_record_.Update(
2736             *hwstate, state_buffer_.Get(1), added_fingers, removed_fingers,
2737             dead_fingers);
2738         if (tap_record_.TapBegan())
2739           SetTapToClickState(kTtcFirstTapBegan, now);
2740       }
2741       break;
2742     case kTtcFirstTapBegan:
2743       if (is_timeout) {
2744         SetTapToClickState(kTtcIdle, now);
2745         break;
2746       }
2747       if (!hwstate) {
2748         Err("hwstate is null but not a timeout?!");
2749         break;
2750       }
2751       tap_record_.Update(
2752           *hwstate, state_buffer_.Get(1), added_fingers,
2753           removed_fingers, dead_fingers);
2754       Log("TTC: Is tap? %d Is moving? %d",
2755           tap_record_.TapComplete(),
2756           tap_record_.Moving(*hwstate, tap_move_dist_.val_));
2757       if (tap_record_.TapComplete()) {
2758         if (!tap_record_.MinTapPressureMet() ||
2759             !tap_record_.FingersBelowMaxAge()) {
2760           SetTapToClickState(kTtcIdle, now);
2761         } else if (tap_record_.TapType() == GESTURES_BUTTON_LEFT &&
2762                    tap_drag_enable_.val_) {
2763           SetTapToClickState(kTtcTapComplete, now);
2764         } else {
2765           *buttons_down = *buttons_up = tap_record_.TapType();
2766           SetTapToClickState(kTtcIdle, now);
2767         }
2768       } else if (tap_record_.Moving(*hwstate, tap_move_dist_.val_)) {
2769         SetTapToClickState(kTtcIdle, now);
2770       }
2771       break;
2772     case kTtcTapComplete:
2773       if (!added_fingers.empty()) {
2774 
2775         tap_record_.Clear();
2776         tap_record_.Update(
2777             *hwstate, state_buffer_.Get(1), added_fingers, removed_fingers,
2778             dead_fingers);
2779 
2780         // If more than one finger is touching: Send click
2781         // and return to FirstTapBegan state.
2782         if (tap_record_.TapType() != GESTURES_BUTTON_LEFT) {
2783           *buttons_down = *buttons_up = GESTURES_BUTTON_LEFT;
2784           SetTapToClickState(kTtcFirstTapBegan, now);
2785         } else {
2786           tap_drag_last_motion_time_ = now;
2787           tap_drag_finger_was_stationary_ = false;
2788           SetTapToClickState(kTtcSubsequentTapBegan, now);
2789         }
2790       } else if (is_timeout) {
2791         *buttons_down = *buttons_up =
2792             tap_record_.MinTapPressureMet() ? tap_record_.TapType() : 0;
2793         SetTapToClickState(kTtcIdle, now);
2794       }
2795       break;
2796     case kTtcSubsequentTapBegan:
2797       if (!is_timeout && !hwstate) {
2798         Err("hwstate is null but not a timeout?!");
2799         break;
2800       }
2801       if (hwstate)
2802         tap_record_.Update(*hwstate, state_buffer_.Get(1), added_fingers,
2803                            removed_fingers, dead_fingers);
2804 
2805       if (!tap_record_.Motionless(*hwstate, state_buffer_.Get(1),
2806                                   tap_max_movement_.val_)) {
2807         tap_drag_last_motion_time_ = now;
2808       }
2809       if (tap_record_.TapType() == GESTURES_BUTTON_LEFT &&
2810           now - tap_drag_last_motion_time_ >= tap_drag_stationary_time_.val_) {
2811         tap_drag_finger_was_stationary_ = true;
2812       }
2813 
2814       if (is_timeout || tap_record_.Moving(*hwstate, tap_move_dist_.val_)) {
2815         if (tap_record_.TapType() == GESTURES_BUTTON_LEFT) {
2816           if (is_timeout) {
2817             // moving with just one finger. Start dragging.
2818             *buttons_down = GESTURES_BUTTON_LEFT;
2819             SetTapToClickState(kTtcDrag, now);
2820           } else {
2821             bool drag_delay_met = (now - tap_to_click_state_entered_
2822                                    >= tap_drag_delay_.val_);
2823             if (drag_delay_met && tap_drag_finger_was_stationary_) {
2824               *buttons_down = GESTURES_BUTTON_LEFT;
2825               SetTapToClickState(kTtcDrag, now);
2826             } else {
2827               *buttons_down = GESTURES_BUTTON_LEFT;
2828               *buttons_up = GESTURES_BUTTON_LEFT;
2829               SetTapToClickState(kTtcIdle, now);
2830             }
2831           }
2832         } else if (!tap_record_.TapComplete()) {
2833           // not just one finger. Send button click and go to idle.
2834           *buttons_down = *buttons_up = GESTURES_BUTTON_LEFT;
2835           SetTapToClickState(kTtcIdle, now);
2836         }
2837         break;
2838       }
2839       if (tap_record_.TapType() != GESTURES_BUTTON_LEFT) {
2840         // We aren't going to drag, so send left click now and handle current
2841         // tap afterwards.
2842         *buttons_down = *buttons_up = GESTURES_BUTTON_LEFT;
2843         SetTapToClickState(kTtcFirstTapBegan, now);
2844       }
2845       if (tap_record_.TapComplete()) {
2846         *buttons_down = *buttons_up = GESTURES_BUTTON_LEFT;
2847         SetTapToClickState(kTtcTapComplete, now);
2848         Log("TTC: Subsequent left tap complete");
2849       }
2850       break;
2851     case kTtcDrag:
2852       if (hwstate)
2853         tap_record_.Update(
2854             *hwstate, state_buffer_.Get(1), added_fingers, removed_fingers,
2855             dead_fingers);
2856       if (tap_record_.TapComplete()) {
2857         tap_record_.Clear();
2858         if (drag_lock_enable_.val_) {
2859           SetTapToClickState(kTtcDragRelease, now);
2860         } else {
2861           *buttons_up = GESTURES_BUTTON_LEFT;
2862           SetTapToClickState(kTtcIdle, now);
2863         }
2864       }
2865       if (tap_record_.TapType() != GESTURES_BUTTON_LEFT &&
2866           now - tap_to_click_state_entered_ <= evaluation_timeout_.val_) {
2867         // We thought we were dragging, but actually we're doing a
2868         // non-tap-to-click multitouch gesture.
2869         *buttons_up = GESTURES_BUTTON_LEFT;
2870         SetTapToClickState(kTtcIdle, now);
2871       }
2872       break;
2873     case kTtcDragRelease:
2874       if (!added_fingers.empty()) {
2875         tap_record_.Update(
2876             *hwstate, state_buffer_.Get(1), added_fingers, removed_fingers,
2877             dead_fingers);
2878         SetTapToClickState(kTtcDragRetouch, now);
2879       } else if (is_timeout) {
2880         *buttons_up = GESTURES_BUTTON_LEFT;
2881         SetTapToClickState(kTtcIdle, now);
2882       }
2883       break;
2884     case kTtcDragRetouch:
2885       if (hwstate)
2886         tap_record_.Update(
2887             *hwstate, state_buffer_.Get(1), added_fingers, removed_fingers,
2888             dead_fingers);
2889       if (tap_record_.TapComplete()) {
2890         *buttons_up = GESTURES_BUTTON_LEFT;
2891         if (tap_record_.TapType() == GESTURES_BUTTON_LEFT)
2892           SetTapToClickState(kTtcIdle, now);
2893         else
2894           SetTapToClickState(kTtcTapComplete, now);
2895         break;
2896       }
2897       if (is_timeout) {
2898         SetTapToClickState(kTtcDrag, now);
2899         break;
2900       }
2901       if (!hwstate) {
2902         Err("hwstate is null but not a timeout?!");
2903         break;
2904       }
2905       if (tap_record_.Moving(*hwstate, tap_move_dist_.val_))
2906         SetTapToClickState(kTtcDrag, now);
2907       break;
2908   }
2909   if (tap_to_click_state_ != kTtcIdle)
2910     Log("TTC: New state: %s", TapToClickStateName(tap_to_click_state_));
2911   // Take action based on new state:
2912   switch (tap_to_click_state_) {
2913     case kTtcTapComplete:
2914       *timeout = TimeoutForTtcState(tap_to_click_state_);
2915       break;
2916     case kTtcDragRelease:
2917       *timeout = TimeoutForTtcState(tap_to_click_state_);
2918       break;
2919     default:  // so gcc doesn't complain about missing enums
2920       break;
2921   }
2922 }
2923 
FingerTooCloseToTap(const HardwareState & hwstate,const FingerState & fs)2924 bool ImmediateInterpreter::FingerTooCloseToTap(const HardwareState& hwstate,
2925                                                const FingerState& fs) {
2926   const float kMinAllowableSq =
2927       tapping_finger_min_separation_.val_ * tapping_finger_min_separation_.val_;
2928   for (size_t i = 0; i < hwstate.finger_cnt; i++) {
2929     const FingerState* iter_fs = &hwstate.fingers[i];
2930     if (iter_fs->tracking_id == fs.tracking_id)
2931       continue;
2932     float dist_sq = DistSq(fs, *iter_fs);
2933     if (dist_sq < kMinAllowableSq)
2934       return true;
2935   }
2936   return false;
2937 }
2938 
FingerInDampenedZone(const FingerState & finger) const2939 bool ImmediateInterpreter::FingerInDampenedZone(
2940     const FingerState& finger) const {
2941   // TODO(adlr): cache thresh
2942   float thresh = hwprops_->bottom - bottom_zone_size_.val_;
2943   return finger.position_y > thresh;
2944 }
2945 
FillStartPositions(const HardwareState & hwstate)2946 void ImmediateInterpreter::FillStartPositions(const HardwareState& hwstate) {
2947   RemoveMissingIdsFromMap(&origin_positions_, hwstate);
2948 
2949   for (short i = 0; i < hwstate.finger_cnt; i++) {
2950     Point point(hwstate.fingers[i].position_x,
2951                 hwstate.fingers[i].position_y);
2952     start_positions_[hwstate.fingers[i].tracking_id] = point;
2953     three_finger_swipe_start_positions_[hwstate.fingers[i].tracking_id] = point;
2954     four_finger_swipe_start_positions_[hwstate.fingers[i].tracking_id] = point;
2955     if (!MapContainsKey(origin_positions_, hwstate.fingers[i].tracking_id))
2956       origin_positions_[hwstate.fingers[i].tracking_id] = point;
2957   }
2958 }
2959 
GetButtonTypeFromPosition(const HardwareState & hwstate)2960 int ImmediateInterpreter::GetButtonTypeFromPosition(
2961     const HardwareState& hwstate) {
2962   if (hwstate.finger_cnt <= 0 || hwstate.finger_cnt > 1 ||
2963       !button_right_click_zone_enable_.val_) {
2964     return GESTURES_BUTTON_LEFT;
2965   }
2966 
2967   const FingerState& fs = hwstate.fingers[0];
2968   if (fs.position_x > hwprops_->right - button_right_click_zone_size_.val_) {
2969     return GESTURES_BUTTON_RIGHT;
2970   }
2971 
2972   return GESTURES_BUTTON_LEFT;
2973 }
2974 
EvaluateButtonType(const HardwareState & hwstate,stime_t button_down_time)2975 int ImmediateInterpreter::EvaluateButtonType(
2976     const HardwareState& hwstate, stime_t button_down_time) {
2977   // Handle T5R2/SemiMT touchpads
2978   if ((hwprops_->supports_t5r2 || hwprops_->support_semi_mt) &&
2979       hwstate.touch_cnt > 2) {
2980     if (hwstate.touch_cnt - thumb_.size() == 3 &&
2981         three_finger_click_enable_.val_ && t5r2_three_finger_click_enable_.val_)
2982       return GESTURES_BUTTON_MIDDLE;
2983     return GESTURES_BUTTON_RIGHT;
2984   }
2985 
2986   // Just return the hardware state button, based on finger position,
2987   // if no further analysis is needed.
2988   bool finger_update = finger_button_click_.Update(hwstate, button_down_time);
2989   if (!finger_update && hwprops_->is_button_pad &&
2990       hwstate.buttons_down == GESTURES_BUTTON_LEFT) {
2991     return GetButtonTypeFromPosition(hwstate);
2992   } else if (!finger_update) {
2993     return hwstate.buttons_down;
2994   }
2995   Log("EvaluateButtonType: R/C/H: %d/%d/%d",
2996       finger_button_click_.num_recent(),
2997       finger_button_click_.num_cold(),
2998       finger_button_click_.num_hot());
2999 
3000   // Handle 2 finger cases:
3001   if (finger_button_click_.num_fingers() == 2)
3002     return finger_button_click_.EvaluateTwoFingerButtonType();
3003 
3004   // Handle cases with 3 or more fingers:
3005   return finger_button_click_.EvaluateThreeOrMoreFingerButtonType();
3006 }
3007 
UpdateMovingFingers(const HardwareState & hwstate)3008 FingerMap ImmediateInterpreter::UpdateMovingFingers(
3009     const HardwareState& hwstate) {
3010   FingerMap newly_moving_fingers;
3011   if (moving_.size() == hwstate.finger_cnt)
3012     return newly_moving_fingers;  // All fingers already started moving
3013   const float kMinDistSq =
3014       change_move_distance_.val_ * change_move_distance_.val_;
3015   for (size_t i = 0; i < hwstate.finger_cnt; i++) {
3016     const FingerState& fs = hwstate.fingers[i];
3017     if (!MapContainsKey(start_positions_, fs.tracking_id)) {
3018       Err("Missing start position!");
3019       continue;
3020     }
3021     if (SetContainsValue(moving_, fs.tracking_id)) {
3022       // This finger already moving
3023       continue;
3024     }
3025     float dist_sq = DistanceTravelledSq(fs, false);
3026     if (dist_sq > kMinDistSq) {
3027       moving_.insert(fs.tracking_id);
3028       newly_moving_fingers.insert(fs.tracking_id);
3029     }
3030   }
3031   return newly_moving_fingers;
3032 }
3033 
UpdateStartedMovingTime(stime_t now,const FingerMap & gs_fingers,const FingerMap & newly_moving_fingers)3034 void ImmediateInterpreter::UpdateStartedMovingTime(
3035     stime_t now,
3036     const FingerMap& gs_fingers,
3037     const FingerMap& newly_moving_fingers) {
3038   // Update started moving time if any gesturing finger is newly moving.
3039   for (short gs_tracking_id : gs_fingers) {
3040     if (SetContainsValue(newly_moving_fingers, gs_tracking_id)) {
3041       started_moving_time_ = now;
3042       // Extend the thumb evaluation period for any finger that is still under
3043       // evaluation as there is a new moving finger.
3044       for (auto& [_, time] : thumb_) {
3045         if (time < thumb_eval_timeout_.val_ && time > 0.0)
3046           time = thumb_eval_timeout_.val_;
3047       }
3048       return;
3049     }
3050   }
3051 }
3052 
UpdateButtons(const HardwareState & hwstate,stime_t * timeout)3053 void ImmediateInterpreter::UpdateButtons(const HardwareState& hwstate,
3054                                          stime_t* timeout) {
3055   // TODO(miletus): To distinguish between left/right buttons down
3056   bool prev_button_down = state_buffer_.Get(1).buttons_down;
3057   bool button_down = hwstate.buttons_down;
3058   if (!prev_button_down && !button_down)
3059     return;
3060   // For haptic touchpads, we need to minimize latency for physical button
3061   // events because they are used to signal the touchpad to perform haptic
3062   // feedback.
3063   double button_evaluation_timeout = is_haptic_pad_ ? 0.0 :
3064       button_evaluation_timeout_.val_;
3065   double button_finger_timeout = is_haptic_pad_ ? 0.0 :
3066       button_finger_timeout_.val_;
3067   bool phys_down_edge = button_down && !prev_button_down;
3068   bool phys_up_edge = !button_down && prev_button_down;
3069   if (phys_down_edge) {
3070     finger_seen_shortly_after_button_down_ = false;
3071     sent_button_down_ = false;
3072     button_down_deadline_ = hwstate.timestamp + button_evaluation_timeout;
3073   }
3074 
3075   // If we haven't seen a finger on the pad shortly after the click, do nothing
3076   if (!finger_seen_shortly_after_button_down_ &&
3077       hwstate.timestamp <= button_down_deadline_)
3078     finger_seen_shortly_after_button_down_ = (hwstate.finger_cnt > 0);
3079   if (!finger_seen_shortly_after_button_down_ &&
3080       !zero_finger_click_enable_.val_)
3081     return;
3082 
3083   if (!sent_button_down_) {
3084     stime_t button_down_time = button_down_deadline_ -
3085                                button_evaluation_timeout;
3086     button_type_ = EvaluateButtonType(hwstate, button_down_time);
3087 
3088     if (!hwstate.SameFingersAs(state_buffer_.Get(0))) {
3089       // Fingers have changed since last state, reset timeout
3090       button_down_deadline_ = hwstate.timestamp + button_finger_timeout;
3091     }
3092 
3093     // button_up before button_evaluation_timeout expired.
3094     // Send up & down for button that was previously down, but not yet sent.
3095     if (button_type_ == GESTURES_BUTTON_NONE)
3096       button_type_ = prev_button_down;
3097     // Send button down if timeout has been reached or button up happened
3098     if (button_down_deadline_ <= hwstate.timestamp ||
3099         phys_up_edge) {
3100       // Send button down
3101       if (result_.type == kGestureTypeButtonsChange)
3102         Err("Gesture type already button?!");
3103       result_ = Gesture(kGestureButtonsChange,
3104                         state_buffer_.Get(1).timestamp,
3105                         hwstate.timestamp,
3106                         button_type_,
3107                         0,
3108                         false); // is_tap
3109       sent_button_down_ = true;
3110     } else if (timeout) {
3111       *timeout = button_down_deadline_ - hwstate.timestamp;
3112     }
3113   }
3114   if (phys_up_edge) {
3115     // Send button up
3116     if (result_.type != kGestureTypeButtonsChange)
3117       result_ = Gesture(kGestureButtonsChange,
3118                         state_buffer_.Get(1).timestamp,
3119                         hwstate.timestamp,
3120                         0,
3121                         button_type_,
3122                         false); // is_tap
3123     else
3124       result_.details.buttons.up = button_type_;
3125     // Reset button state
3126     button_type_ = GESTURES_BUTTON_NONE;
3127     button_down_deadline_ = 0;
3128     sent_button_down_ = false;
3129     // When a buttons_up event is generated, we need to reset the
3130     // finger_leave_time_ in order to defer any gesture generation
3131     // right after it.
3132     finger_leave_time_ = hwstate.timestamp;
3133   }
3134 }
3135 
UpdateButtonsTimeout(stime_t now)3136 void ImmediateInterpreter::UpdateButtonsTimeout(stime_t now) {
3137   if (sent_button_down_) {
3138     Err("How is sent_button_down_ set?");
3139     return;
3140   }
3141   if (button_type_ == GESTURES_BUTTON_NONE)
3142     return;
3143   sent_button_down_ = true;
3144   result_ = Gesture(kGestureButtonsChange,
3145                     state_buffer_.Get(1).timestamp,
3146                     now,
3147                     button_type_,
3148                     0,
3149                     false); // is_tap
3150 }
3151 
FillResultGesture(const HardwareState & hwstate,const FingerMap & fingers)3152 void ImmediateInterpreter::FillResultGesture(
3153     const HardwareState& hwstate,
3154     const FingerMap& fingers) {
3155   bool zero_move = false;
3156   switch (current_gesture_type_) {
3157     case kGestureTypeMove: {
3158       if (fingers.empty())
3159         return;
3160       // Use the finger which has moved the most to compute motion.
3161       // First, check if we have locked onto a fast finger in the past.
3162       const FingerState* current = nullptr;
3163       if (moving_finger_id_ >= 0)
3164         current = hwstate.GetFingerState(moving_finger_id_);
3165 
3166       // Determine which finger is moving fastest.
3167       const FingerState* fastest = nullptr;
3168       const HardwareState& prev_hs = state_buffer_.Get(1);
3169       float curr_dist_sq = -1;
3170       for (short tracking_id : fingers) {
3171         const FingerState* fs = hwstate.GetFingerState(tracking_id);
3172         const FingerState* prev_fs = prev_hs.GetFingerState(fs->tracking_id);
3173         if (!prev_fs)
3174           break;
3175         float dist_sq = DistSq(*fs, *prev_fs);
3176         if (dist_sq > curr_dist_sq) {
3177           fastest = fs;
3178           curr_dist_sq = dist_sq;
3179         }
3180       }
3181 
3182       if (!current)
3183         current = fastest;
3184       if (!current)
3185         return;
3186 
3187       const FingerState* prev =
3188           state_buffer_.Get(1).GetFingerState(current->tracking_id);
3189       if (!prev)
3190         return;
3191 
3192       float dx = current->position_x - prev->position_x;
3193       if (current->flags & GESTURES_FINGER_WARP_X_MOVE)
3194         dx = 0.0;
3195       float dy = current->position_y - prev->position_y;
3196       if (current->flags & GESTURES_FINGER_WARP_Y_MOVE)
3197         dy = 0.0;
3198       float dsq = dx * dx + dy * dy;
3199       stime_t dt = hwstate.timestamp - state_buffer_.Get(1).timestamp;
3200 
3201       // If we are locked on to a finger that is not the fastest moving,
3202       // determine if we want to switch the lock to the fastest finger.
3203       const FingerState* prev_fastest = nullptr;
3204       if (fastest) {
3205           prev_fastest =
3206               state_buffer_.Get(1).GetFingerState(fastest->tracking_id);
3207       }
3208       if (prev_fastest && fastest != current) {
3209         float fastest_dx = fastest->position_x - prev_fastest->position_x;
3210         if (fastest->flags & GESTURES_FINGER_WARP_X_MOVE)
3211           fastest_dx = 0.0;
3212         float fastest_dy = fastest->position_y - prev_fastest->position_y;
3213         if (fastest->flags & GESTURES_FINGER_WARP_Y_MOVE)
3214           fastest_dy = 0.0;
3215         float fastest_dsq = fastest_dx * fastest_dx + fastest_dy * fastest_dy;
3216 
3217         float change_lock_dsq_thresh =
3218             (move_change_lock_speed_.val_ * move_change_lock_speed_.val_) *
3219             (dt * dt);
3220         if (fastest_dsq > dsq * move_change_lock_ratio_.val_ &&
3221             fastest_dsq > change_lock_dsq_thresh) {
3222           moving_finger_id_ = fastest->tracking_id;
3223           current = fastest;
3224           dx = fastest_dx;
3225           dy = fastest_dy;
3226           dsq = fastest_dsq;
3227           prev = prev_fastest;
3228         }
3229       }
3230 
3231       const FingerState* prev2 =
3232           state_buffer_.Get(2).GetFingerState(current->tracking_id);
3233       if (!prev || !current)
3234         return;
3235       if (current->flags & GESTURES_FINGER_MERGE)
3236         return;
3237       bool suppress_finger_movement =
3238           scroll_manager_.SuppressStationaryFingerMovement(
3239               *current, *prev, dt) ||
3240           scroll_manager_.StationaryFingerPressureChangingSignificantly(
3241               state_buffer_, *current);
3242       if (quick_acceleration_factor_.val_ && prev2) {
3243         stime_t dt2 =
3244             state_buffer_.Get(1).timestamp - state_buffer_.Get(2).timestamp;
3245         float dist_sq = DistSq(*current, *prev);
3246         float dist_sq2 = DistSq(*prev, *prev2);
3247         if (dist_sq2 * dt &&  // have prev dist and current time
3248             dist_sq2 * dt * dt *
3249             quick_acceleration_factor_.val_ * quick_acceleration_factor_.val_ <
3250             dist_sq * dt2 * dt2) {
3251           return;
3252         }
3253       }
3254       if (suppress_finger_movement) {
3255         scroll_manager_.prev_result_suppress_finger_movement_ = true;
3256         result_ = Gesture(kGestureMove,
3257                           state_buffer_.Get(1).timestamp,
3258                           hwstate.timestamp,
3259                           0,
3260                           0);
3261         return;
3262       }
3263       scroll_manager_.prev_result_suppress_finger_movement_ = false;
3264       float dx_total = current->position_x -
3265                        start_positions_[current->tracking_id].x_;
3266       float dy_total = current->position_y -
3267                        start_positions_[current->tracking_id].y_;
3268       float dsq_total = dx_total * dx_total + dy_total * dy_total;
3269 
3270       float dsq_thresh = (move_lock_speed_.val_ * move_lock_speed_.val_) *
3271                          (dt * dt);
3272       if (dsq > dsq_thresh) {
3273         // lock onto this finger
3274         moving_finger_id_ = current->tracking_id;
3275       }
3276 
3277       float dsq_total_thresh =
3278           move_report_distance_.val_ * move_report_distance_.val_;
3279       if (dsq_total >= dsq_total_thresh) {
3280         zero_move = dsq == 0.0;
3281         result_ = Gesture(kGestureMove,
3282                           state_buffer_.Get(1).timestamp,
3283                           hwstate.timestamp,
3284                           dx,
3285                           dy);
3286       }
3287       break;
3288     }
3289     case kGestureTypeScroll: {
3290       if (!scroll_manager_.FillResultScroll(state_buffer_,
3291                                          prev_active_gs_fingers_,
3292                                          fingers,
3293                                          prev_gesture_type_,
3294                                          prev_result_,
3295                                          &result_,
3296                                          &scroll_buffer_))
3297         return;
3298       break;
3299     }
3300     case kGestureTypeFling: {
3301       scroll_manager_.FillResultFling(state_buffer_, scroll_buffer_, &result_);
3302       break;
3303     }
3304     case kGestureTypeSwipe:
3305     case kGestureTypeFourFingerSwipe: {
3306       if (!three_finger_swipe_enable_.val_)
3307         break;
3308       float sum_delta[] = { 0.0, 0.0 };
3309       bool valid[] = { true, true };
3310       float finger_cnt[] = { 0.0, 0.0 };
3311       float FingerState::*fields[] = { &FingerState::position_x,
3312                                        &FingerState::position_y };
3313       for (short tracking_id : fingers) {
3314         if (!state_buffer_.Get(1).GetFingerState(tracking_id)) {
3315           Err("missing prev state?");
3316           continue;
3317         }
3318         // We have this loop in case we want to compute diagonal swipes at
3319         // some point, even if currently we go with just one axis.
3320         for (size_t i = 0; i < arraysize(fields); i++) {
3321           bool correct_axis = (i == 1) == swipe_is_vertical_;
3322           if (!valid[i] || !correct_axis)
3323             continue;
3324           float FingerState::*field = fields[i];
3325           float delta = hwstate.GetFingerState(tracking_id)->*field -
3326               state_buffer_.Get(1).GetFingerState(tracking_id)->*field;
3327           // The multiply is to see if they have the same sign:
3328           if (sum_delta[i] == 0.0 || sum_delta[i] * delta > 0) {
3329             sum_delta[i] += delta;
3330             finger_cnt[i] += 1.0;
3331           } else {
3332             sum_delta[i] = 0.0;
3333             valid[i] = false;
3334           }
3335         }
3336       }
3337       if (current_gesture_type_ == kGestureTypeSwipe) {
3338         result_ = Gesture(
3339             kGestureSwipe, state_buffer_.Get(1).timestamp,
3340             hwstate.timestamp,
3341             (!swipe_is_vertical_ && finger_cnt[0]) ?
3342             sum_delta[0] / finger_cnt[0] : 0.0,
3343             (swipe_is_vertical_ && finger_cnt[1]) ?
3344             sum_delta[1] / finger_cnt[1] : 0.0);
3345       } else if (current_gesture_type_ == kGestureTypeFourFingerSwipe) {
3346         result_ = Gesture(
3347             kGestureFourFingerSwipe, state_buffer_.Get(1).timestamp,
3348             hwstate.timestamp,
3349             (!swipe_is_vertical_ && finger_cnt[0]) ?
3350             sum_delta[0] / finger_cnt[0] : 0.0,
3351             (swipe_is_vertical_ && finger_cnt[1]) ?
3352             sum_delta[1] / finger_cnt[1] : 0.0);
3353       }
3354       break;
3355     }
3356     case kGestureTypeSwipeLift: {
3357       result_ = Gesture(kGestureSwipeLift,
3358                         state_buffer_.Get(1).timestamp,
3359                         hwstate.timestamp);
3360       break;
3361     }
3362 
3363     case kGestureTypeFourFingerSwipeLift: {
3364       result_ = Gesture(kGestureFourFingerSwipeLift,
3365                         state_buffer_.Get(1).timestamp,
3366                         hwstate.timestamp);
3367       break;
3368     }
3369     case kGestureTypePinch: {
3370       if (pinch_status_ == GESTURES_ZOOM_START ||
3371           (pinch_status_ == GESTURES_ZOOM_END &&
3372            prev_gesture_type_ == kGestureTypePinch)) {
3373         result_ = Gesture(kGesturePinch, changed_time_, hwstate.timestamp,
3374                           1.0, pinch_status_);
3375         pinch_prev_time_ = hwstate.timestamp;
3376         if (pinch_status_ == GESTURES_ZOOM_END) {
3377           current_gesture_type_ = kGestureTypeNull;
3378           pinch_prev_direction_ = 0;
3379         }
3380       } else if (pinch_status_ == GESTURES_ZOOM_UPDATE) {
3381         float current_dist_sq = TwoSpecificFingerDistanceSq(hwstate, fingers);
3382         if (current_dist_sq < 0) {
3383           current_dist_sq = pinch_prev_distance_sq_;
3384         }
3385 
3386         // Check if pinch scale has changed enough since last update to send a
3387         // new update.  To prevent stationary jitter, we always require the
3388         // scale to change by at least a small amount.  We require more change
3389         // if the pinch has been stationary or changed direction recently.
3390         float jitter_threshold = pinch_res_.val_;
3391         if (hwstate.timestamp - pinch_prev_time_ > pinch_stationary_time_.val_)
3392           jitter_threshold = pinch_stationary_res_.val_;
3393         if ((current_dist_sq - pinch_prev_distance_sq_) *
3394             pinch_prev_direction_ < 0)
3395           jitter_threshold = jitter_threshold > pinch_hysteresis_res_.val_ ?
3396                              jitter_threshold :
3397                              pinch_hysteresis_res_.val_;
3398         bool above_jitter_threshold =
3399             (pinch_prev_distance_sq_ > jitter_threshold * current_dist_sq ||
3400              current_dist_sq > jitter_threshold * pinch_prev_distance_sq_);
3401 
3402         if (above_jitter_threshold) {
3403           result_ = Gesture(kGesturePinch, changed_time_, hwstate.timestamp,
3404                             sqrt(current_dist_sq / pinch_prev_distance_sq_),
3405                             GESTURES_ZOOM_UPDATE);
3406           pinch_prev_direction_ =
3407               current_dist_sq > pinch_prev_distance_sq_ ? 1 : -1;
3408           pinch_prev_distance_sq_ = current_dist_sq;
3409           pinch_prev_time_ = hwstate.timestamp;
3410         }
3411       }
3412       if (pinch_status_ == GESTURES_ZOOM_START) {
3413         pinch_status_ = GESTURES_ZOOM_UPDATE;
3414         // If there is a slow pinch, it may take a little while to detect it,
3415         // allowing the fingers to travel a significant distance, and causing an
3416         // inappropriately large scale in a single frame, followed by slow
3417         // scaling. Here we reduce the initial scale factor depending on how
3418         // quickly we detected the pinch.
3419         float current_dist_sq = TwoSpecificFingerDistanceSq(hwstate, fingers);
3420         float pinch_slowness_ratio = (hwstate.timestamp - changed_time_) *
3421                                      pinch_initial_scale_time_inv_.val_;
3422         pinch_slowness_ratio = fmin(1.0, pinch_slowness_ratio);
3423         pinch_prev_distance_sq_ =
3424             (pinch_slowness_ratio * current_dist_sq) +
3425             ((1 - pinch_slowness_ratio) * pinch_prev_distance_sq_);
3426       }
3427       break;
3428     }
3429     default:
3430       result_.type = kGestureTypeNull;
3431   }
3432   scroll_manager_.UpdateScrollEventBuffer(current_gesture_type_,
3433                                           &scroll_buffer_);
3434   if ((result_.type == kGestureTypeMove && !zero_move) ||
3435       result_.type == kGestureTypeScroll)
3436     last_movement_timestamp_ = hwstate.timestamp;
3437 }
3438 
IntWasWritten(IntProperty * prop)3439 void ImmediateInterpreter::IntWasWritten(IntProperty* prop) {
3440   if (prop == &keyboard_touched_timeval_low_) {
3441     struct timeval tv = {
3442       keyboard_touched_timeval_high_.val_,
3443       keyboard_touched_timeval_low_.val_
3444     };
3445     keyboard_touched_ = StimeFromTimeval(&tv);
3446   }
3447 }
3448 
Initialize(const HardwareProperties * hwprops,Metrics * metrics,MetricsProperties * mprops,GestureConsumer * consumer)3449 void ImmediateInterpreter::Initialize(const HardwareProperties* hwprops,
3450                                       Metrics* metrics,
3451                                       MetricsProperties* mprops,
3452                                       GestureConsumer* consumer) {
3453   Interpreter::Initialize(hwprops, metrics, mprops, consumer);
3454   state_buffer_.Reset(hwprops_->max_finger_cnt);
3455   // Zero finger click needs to be disabled for touchpads that
3456   // integrate their buttons into the pad itself but enabled
3457   // for any other touchpad in case they have separate buttons.
3458   zero_finger_click_enable_.val_ = !hwprops_->is_button_pad;
3459 
3460   is_haptic_pad_ = hwprops_->is_haptic_pad;
3461 }
3462 
AnyGesturingFingerLeft(const HardwareState & state,const FingerMap & prev_gs_fingers)3463 bool AnyGesturingFingerLeft(const HardwareState& state,
3464                             const FingerMap& prev_gs_fingers) {
3465   for (short tracking_id : prev_gs_fingers) {
3466     if (!state.GetFingerState(tracking_id)) {
3467       return true;
3468     }
3469   }
3470   return false;
3471 }
3472 
3473 }  // namespace gestures
3474