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