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