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