1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ui/events/gestures/gesture_sequence.h"
10 #include "base/command_line.h"
11 #include "base/logging.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/time/time.h"
15 #include "ui/events/event.h"
16 #include "ui/events/event_constants.h"
17 #include "ui/events/event_switches.h"
18 #include "ui/events/gestures/gesture_configuration.h"
19 #include "ui/events/gestures/gesture_util.h"
20 #include "ui/gfx/rect.h"
26 // ui::EventType is mapped to TouchState so it can fit into 3 bits of
37 // ui::EventResult is mapped to TouchStatusInternal to simply indicate whether a
38 // processed touch-event should affect gesture-recognition or not.
39 enum TouchStatusInternal {
40 TSI_NOT_PROCESSED, // The touch-event should take-part into
41 // gesture-recognition only if the touch-event has not
44 TSI_PROCESSED, // The touch-event should affect gesture-recognition only
45 // if the touch-event has been processed. For example,,
46 // this means that a JavaScript touch handler called
47 // |preventDefault| on the associated touch event
48 // or was processed by an aura-window or views-view.
50 TSI_ALWAYS // The touch-event should always affect gesture
54 // Get equivalent TouchState from EventType |type|.
55 TouchState TouchEventTypeToTouchState(ui::EventType type) {
57 case ui::ET_TOUCH_RELEASED:
59 case ui::ET_TOUCH_PRESSED:
61 case ui::ET_TOUCH_MOVED:
63 case ui::ET_TOUCH_STATIONARY:
65 case ui::ET_TOUCH_CANCELLED:
68 DVLOG(1) << "Unknown Touch Event type";
73 // Gesture signature types for different values of combination (GestureState,
74 // touch_id, ui::EventType, touch_handled), see Signature for more info.
76 // Note: New addition of types should be placed as per their Signature value.
77 #define G(gesture_state, id, touch_state, handled) 1 + ( \
78 (((touch_state) & 0x7) << 1) | \
79 ((handled & 0x3) << 4) | \
80 (((id) & 0xfff) << 6) | \
81 ((gesture_state) << 18))
83 enum EdgeStateSignatureType {
86 GST_NO_GESTURE_FIRST_PRESSED =
87 G(GS_NO_GESTURE, 0, TS_PRESSED, TSI_NOT_PROCESSED),
89 GST_PENDING_SYNTHETIC_CLICK_FIRST_RELEASED =
90 G(GS_PENDING_SYNTHETIC_CLICK, 0, TS_RELEASED, TSI_NOT_PROCESSED),
92 GST_PENDING_SYNTHETIC_CLICK_FIRST_RELEASED_HANDLED =
93 G(GS_PENDING_SYNTHETIC_CLICK, 0, TS_RELEASED, TSI_PROCESSED),
95 // Ignore processed touch-move events until gesture-scroll starts.
96 GST_PENDING_SYNTHETIC_CLICK_FIRST_MOVED =
97 G(GS_PENDING_SYNTHETIC_CLICK, 0, TS_MOVED, TSI_NOT_PROCESSED),
99 GST_PENDING_SYNTHETIC_CLICK_FIRST_MOVED_PROCESSED =
100 G(GS_PENDING_SYNTHETIC_CLICK, 0, TS_MOVED, TSI_PROCESSED),
102 GST_PENDING_SYNTHETIC_CLICK_FIRST_STATIONARY =
103 G(GS_PENDING_SYNTHETIC_CLICK, 0, TS_STATIONARY, TSI_NOT_PROCESSED),
105 GST_PENDING_SYNTHETIC_CLICK_FIRST_CANCELLED =
106 G(GS_PENDING_SYNTHETIC_CLICK, 0, TS_CANCELLED, TSI_ALWAYS),
108 GST_PENDING_SYNTHETIC_CLICK_SECOND_PRESSED =
109 G(GS_PENDING_SYNTHETIC_CLICK, 1, TS_PRESSED, TSI_NOT_PROCESSED),
111 GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_RELEASED =
112 G(GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL,
117 GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_RELEASED_HANDLED =
118 G(GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL, 0, TS_RELEASED, TSI_PROCESSED),
120 GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_MOVED =
121 G(GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL, 0, TS_MOVED, TSI_ALWAYS),
123 GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_STATIONARY =
124 G(GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL, 0, TS_STATIONARY, TSI_ALWAYS),
126 GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_CANCELLED =
127 G(GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL, 0, TS_CANCELLED, TSI_ALWAYS),
129 GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_SECOND_PRESSED =
130 G(GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL, 1, TS_PRESSED, TSI_NOT_PROCESSED),
132 GST_SCROLL_FIRST_RELEASED =
133 G(GS_SCROLL, 0, TS_RELEASED, TSI_ALWAYS),
135 // Once scroll has started, process all touch-move events.
136 GST_SCROLL_FIRST_MOVED =
137 G(GS_SCROLL, 0, TS_MOVED, TSI_ALWAYS),
139 GST_SCROLL_FIRST_CANCELLED =
140 G(GS_SCROLL, 0, TS_CANCELLED, TSI_ALWAYS),
142 GST_SCROLL_SECOND_PRESSED =
143 G(GS_SCROLL, 1, TS_PRESSED, TSI_NOT_PROCESSED),
145 GST_PENDING_TWO_FINGER_TAP_FIRST_RELEASED =
146 G(GS_PENDING_TWO_FINGER_TAP, 0, TS_RELEASED, TSI_NOT_PROCESSED),
148 GST_PENDING_TWO_FINGER_TAP_FIRST_RELEASED_HANDLED =
149 G(GS_PENDING_TWO_FINGER_TAP, 0, TS_RELEASED, TSI_PROCESSED),
151 GST_PENDING_TWO_FINGER_TAP_SECOND_RELEASED =
152 G(GS_PENDING_TWO_FINGER_TAP, 1, TS_RELEASED, TSI_NOT_PROCESSED),
154 GST_PENDING_TWO_FINGER_TAP_SECOND_RELEASED_HANDLED =
155 G(GS_PENDING_TWO_FINGER_TAP, 1, TS_RELEASED, TSI_PROCESSED),
157 GST_PENDING_TWO_FINGER_TAP_FIRST_MOVED =
158 G(GS_PENDING_TWO_FINGER_TAP, 0, TS_MOVED, TSI_NOT_PROCESSED),
160 GST_PENDING_TWO_FINGER_TAP_SECOND_MOVED =
161 G(GS_PENDING_TWO_FINGER_TAP, 1, TS_MOVED, TSI_NOT_PROCESSED),
163 GST_PENDING_TWO_FINGER_TAP_FIRST_MOVED_HANDLED =
164 G(GS_PENDING_TWO_FINGER_TAP, 0, TS_MOVED, TSI_PROCESSED),
166 GST_PENDING_TWO_FINGER_TAP_SECOND_MOVED_HANDLED =
167 G(GS_PENDING_TWO_FINGER_TAP, 1, TS_MOVED, TSI_PROCESSED),
169 GST_PENDING_TWO_FINGER_TAP_FIRST_CANCELLED =
170 G(GS_PENDING_TWO_FINGER_TAP, 0, TS_CANCELLED, TSI_ALWAYS),
172 GST_PENDING_TWO_FINGER_TAP_SECOND_CANCELLED =
173 G(GS_PENDING_TWO_FINGER_TAP, 1, TS_CANCELLED, TSI_ALWAYS),
175 GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_RELEASED =
176 G(GS_PENDING_TWO_FINGER_TAP_NO_PINCH, 0, TS_RELEASED, TSI_NOT_PROCESSED),
178 GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_RELEASED_HANDLED =
179 G(GS_PENDING_TWO_FINGER_TAP_NO_PINCH, 0, TS_RELEASED, TSI_PROCESSED),
181 GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_RELEASED =
182 G(GS_PENDING_TWO_FINGER_TAP_NO_PINCH, 1, TS_RELEASED, TSI_NOT_PROCESSED),
184 GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_RELEASED_HANDLED =
185 G(GS_PENDING_TWO_FINGER_TAP_NO_PINCH, 1, TS_RELEASED, TSI_PROCESSED),
187 GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_MOVED =
188 G(GS_PENDING_TWO_FINGER_TAP_NO_PINCH, 0, TS_MOVED, TSI_ALWAYS),
190 GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_MOVED =
191 G(GS_PENDING_TWO_FINGER_TAP_NO_PINCH, 1, TS_MOVED, TSI_ALWAYS),
193 GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_CANCELLED =
194 G(GS_PENDING_TWO_FINGER_TAP_NO_PINCH, 0, TS_CANCELLED, TSI_ALWAYS),
196 GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_CANCELLED =
197 G(GS_PENDING_TWO_FINGER_TAP_NO_PINCH, 1, TS_CANCELLED, TSI_ALWAYS),
199 GST_PENDING_TWO_FINGER_TAP_THIRD_PRESSED =
200 G(GS_PENDING_TWO_FINGER_TAP, 2, TS_PRESSED, TSI_NOT_PROCESSED),
202 GST_PENDING_PINCH_FIRST_MOVED =
203 G(GS_PENDING_PINCH, 0, TS_MOVED, TSI_NOT_PROCESSED),
205 GST_PENDING_PINCH_SECOND_MOVED =
206 G(GS_PENDING_PINCH, 1, TS_MOVED, TSI_NOT_PROCESSED),
208 GST_PENDING_PINCH_FIRST_MOVED_HANDLED =
209 G(GS_PENDING_PINCH, 0, TS_MOVED, TSI_PROCESSED),
211 GST_PENDING_PINCH_SECOND_MOVED_HANDLED =
212 G(GS_PENDING_PINCH, 1, TS_MOVED, TSI_PROCESSED),
214 GST_PENDING_PINCH_FIRST_CANCELLED =
215 G(GS_PENDING_PINCH, 0, TS_CANCELLED, TSI_ALWAYS),
217 GST_PENDING_PINCH_SECOND_CANCELLED =
218 G(GS_PENDING_PINCH, 1, TS_CANCELLED, TSI_ALWAYS),
220 GST_PENDING_PINCH_FIRST_RELEASED =
221 G(GS_PENDING_PINCH, 0, TS_RELEASED, TSI_ALWAYS),
223 GST_PENDING_PINCH_SECOND_RELEASED =
224 G(GS_PENDING_PINCH, 1, TS_RELEASED, TSI_ALWAYS),
226 GST_PENDING_PINCH_NO_PINCH_FIRST_MOVED =
227 G(GS_PENDING_PINCH_NO_PINCH, 0, TS_MOVED, TSI_ALWAYS),
229 GST_PENDING_PINCH_NO_PINCH_SECOND_MOVED =
230 G(GS_PENDING_PINCH_NO_PINCH, 1, TS_MOVED, TSI_ALWAYS),
232 GST_PENDING_PINCH_NO_PINCH_FIRST_CANCELLED =
233 G(GS_PENDING_PINCH_NO_PINCH, 0, TS_CANCELLED, TSI_ALWAYS),
235 GST_PENDING_PINCH_NO_PINCH_SECOND_CANCELLED =
236 G(GS_PENDING_PINCH_NO_PINCH, 1, TS_CANCELLED, TSI_ALWAYS),
238 GST_PENDING_PINCH_NO_PINCH_FIRST_RELEASED =
239 G(GS_PENDING_PINCH_NO_PINCH, 0, TS_RELEASED, TSI_ALWAYS),
241 GST_PENDING_PINCH_NO_PINCH_SECOND_RELEASED =
242 G(GS_PENDING_PINCH_NO_PINCH, 1, TS_RELEASED, TSI_ALWAYS),
244 GST_PINCH_FIRST_MOVED =
245 G(GS_PINCH, 0, TS_MOVED, TSI_NOT_PROCESSED),
247 GST_PINCH_FIRST_MOVED_HANDLED =
248 G(GS_PINCH, 0, TS_MOVED, TSI_PROCESSED),
250 GST_PINCH_SECOND_MOVED =
251 G(GS_PINCH, 1, TS_MOVED, TSI_NOT_PROCESSED),
253 GST_PINCH_SECOND_MOVED_HANDLED =
254 G(GS_PINCH, 1, TS_MOVED, TSI_PROCESSED),
256 GST_PINCH_FIRST_RELEASED =
257 G(GS_PINCH, 0, TS_RELEASED, TSI_ALWAYS),
259 GST_PINCH_SECOND_RELEASED =
260 G(GS_PINCH, 1, TS_RELEASED, TSI_ALWAYS),
262 GST_PINCH_FIRST_CANCELLED =
263 G(GS_PINCH, 0, TS_CANCELLED, TSI_ALWAYS),
265 GST_PINCH_SECOND_CANCELLED =
266 G(GS_PINCH, 1, TS_CANCELLED, TSI_ALWAYS),
268 GST_PINCH_THIRD_PRESSED =
269 G(GS_PINCH, 2, TS_PRESSED, TSI_NOT_PROCESSED),
271 GST_PINCH_THIRD_MOVED =
272 G(GS_PINCH, 2, TS_MOVED, TSI_NOT_PROCESSED),
274 GST_PINCH_THIRD_MOVED_HANDLED =
275 G(GS_PINCH, 2, TS_MOVED, TSI_PROCESSED),
277 GST_PINCH_THIRD_RELEASED =
278 G(GS_PINCH, 2, TS_RELEASED, TSI_ALWAYS),
280 GST_PINCH_THIRD_CANCELLED =
281 G(GS_PINCH, 2, TS_CANCELLED, TSI_ALWAYS),
283 GST_PINCH_FOURTH_PRESSED =
284 G(GS_PINCH, 3, TS_PRESSED, TSI_NOT_PROCESSED),
286 GST_PINCH_FOURTH_MOVED =
287 G(GS_PINCH, 3, TS_MOVED, TSI_NOT_PROCESSED),
289 GST_PINCH_FOURTH_MOVED_HANDLED =
290 G(GS_PINCH, 3, TS_MOVED, TSI_PROCESSED),
292 GST_PINCH_FOURTH_RELEASED =
293 G(GS_PINCH, 3, TS_RELEASED, TSI_ALWAYS),
295 GST_PINCH_FOURTH_CANCELLED =
296 G(GS_PINCH, 3, TS_CANCELLED, TSI_ALWAYS),
298 GST_PINCH_FIFTH_PRESSED =
299 G(GS_PINCH, 4, TS_PRESSED, TSI_NOT_PROCESSED),
301 GST_PINCH_FIFTH_MOVED =
302 G(GS_PINCH, 4, TS_MOVED, TSI_NOT_PROCESSED),
304 GST_PINCH_FIFTH_MOVED_HANDLED =
305 G(GS_PINCH, 4, TS_MOVED, TSI_PROCESSED),
307 GST_PINCH_FIFTH_RELEASED =
308 G(GS_PINCH, 4, TS_RELEASED, TSI_ALWAYS),
310 GST_PINCH_FIFTH_CANCELLED =
311 G(GS_PINCH, 4, TS_CANCELLED, TSI_ALWAYS),
314 // Builds a signature. Signatures are assembled by joining together
316 // 1 LSB bit so that the computed signature is always greater than 0
317 // 3 bits for the |type|.
318 // 2 bit for |touch_status|
319 // 12 bits for |touch_id|
320 // 14 bits for the |gesture_state|.
321 EdgeStateSignatureType Signature(GestureState gesture_state,
322 unsigned int touch_id,
324 TouchStatusInternal touch_status) {
325 CHECK((touch_id & 0xfff) == touch_id);
326 TouchState touch_state = TouchEventTypeToTouchState(type);
327 EdgeStateSignatureType signature = static_cast<EdgeStateSignatureType>
328 (G(gesture_state, touch_id, touch_state, touch_status));
331 case GST_NO_GESTURE_FIRST_PRESSED:
332 case GST_PENDING_SYNTHETIC_CLICK_FIRST_RELEASED:
333 case GST_PENDING_SYNTHETIC_CLICK_FIRST_RELEASED_HANDLED:
334 case GST_PENDING_SYNTHETIC_CLICK_FIRST_MOVED:
335 case GST_PENDING_SYNTHETIC_CLICK_FIRST_MOVED_PROCESSED:
336 case GST_PENDING_SYNTHETIC_CLICK_FIRST_STATIONARY:
337 case GST_PENDING_SYNTHETIC_CLICK_FIRST_CANCELLED:
338 case GST_PENDING_SYNTHETIC_CLICK_SECOND_PRESSED:
339 case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_RELEASED:
340 case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_RELEASED_HANDLED:
341 case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_MOVED:
342 case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_STATIONARY:
343 case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_CANCELLED:
344 case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_SECOND_PRESSED:
345 case GST_SCROLL_FIRST_RELEASED:
346 case GST_SCROLL_FIRST_MOVED:
347 case GST_SCROLL_FIRST_CANCELLED:
348 case GST_SCROLL_SECOND_PRESSED:
349 case GST_PENDING_TWO_FINGER_TAP_FIRST_RELEASED:
350 case GST_PENDING_TWO_FINGER_TAP_FIRST_RELEASED_HANDLED:
351 case GST_PENDING_TWO_FINGER_TAP_SECOND_RELEASED:
352 case GST_PENDING_TWO_FINGER_TAP_SECOND_RELEASED_HANDLED:
353 case GST_PENDING_TWO_FINGER_TAP_FIRST_MOVED:
354 case GST_PENDING_TWO_FINGER_TAP_SECOND_MOVED:
355 case GST_PENDING_TWO_FINGER_TAP_FIRST_MOVED_HANDLED:
356 case GST_PENDING_TWO_FINGER_TAP_SECOND_MOVED_HANDLED:
357 case GST_PENDING_TWO_FINGER_TAP_FIRST_CANCELLED:
358 case GST_PENDING_TWO_FINGER_TAP_SECOND_CANCELLED:
359 case GST_PENDING_TWO_FINGER_TAP_THIRD_PRESSED:
360 case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_MOVED:
361 case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_MOVED:
362 case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_RELEASED:
363 case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_RELEASED:
364 case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_RELEASED_HANDLED:
365 case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_RELEASED_HANDLED:
366 case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_CANCELLED:
367 case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_CANCELLED:
368 case GST_PENDING_PINCH_FIRST_MOVED:
369 case GST_PENDING_PINCH_SECOND_MOVED:
370 case GST_PENDING_PINCH_FIRST_MOVED_HANDLED:
371 case GST_PENDING_PINCH_SECOND_MOVED_HANDLED:
372 case GST_PENDING_PINCH_FIRST_RELEASED:
373 case GST_PENDING_PINCH_SECOND_RELEASED:
374 case GST_PENDING_PINCH_FIRST_CANCELLED:
375 case GST_PENDING_PINCH_SECOND_CANCELLED:
376 case GST_PENDING_PINCH_NO_PINCH_FIRST_MOVED:
377 case GST_PENDING_PINCH_NO_PINCH_SECOND_MOVED:
378 case GST_PENDING_PINCH_NO_PINCH_FIRST_RELEASED:
379 case GST_PENDING_PINCH_NO_PINCH_SECOND_RELEASED:
380 case GST_PENDING_PINCH_NO_PINCH_FIRST_CANCELLED:
381 case GST_PENDING_PINCH_NO_PINCH_SECOND_CANCELLED:
382 case GST_PINCH_FIRST_MOVED:
383 case GST_PINCH_FIRST_MOVED_HANDLED:
384 case GST_PINCH_SECOND_MOVED:
385 case GST_PINCH_SECOND_MOVED_HANDLED:
386 case GST_PINCH_FIRST_RELEASED:
387 case GST_PINCH_SECOND_RELEASED:
388 case GST_PINCH_FIRST_CANCELLED:
389 case GST_PINCH_SECOND_CANCELLED:
390 case GST_PINCH_THIRD_PRESSED:
391 case GST_PINCH_THIRD_MOVED:
392 case GST_PINCH_THIRD_MOVED_HANDLED:
393 case GST_PINCH_THIRD_RELEASED:
394 case GST_PINCH_THIRD_CANCELLED:
395 case GST_PINCH_FOURTH_PRESSED:
396 case GST_PINCH_FOURTH_MOVED:
397 case GST_PINCH_FOURTH_MOVED_HANDLED:
398 case GST_PINCH_FOURTH_RELEASED:
399 case GST_PINCH_FOURTH_CANCELLED:
400 case GST_PINCH_FIFTH_PRESSED:
401 case GST_PINCH_FIFTH_MOVED:
402 case GST_PINCH_FIFTH_MOVED_HANDLED:
403 case GST_PINCH_FIFTH_RELEASED:
404 case GST_PINCH_FIFTH_CANCELLED:
407 signature = GST_INVALID;
415 float BoundingBoxDiagonal(const gfx::Rect& rect) {
416 float width = rect.width() * rect.width();
417 float height = rect.height() * rect.height();
418 return sqrt(width + height);
421 unsigned int ComputeTouchBitmask(const GesturePoint* points) {
422 unsigned int touch_bitmask = 0;
423 for (int i = 0; i < GestureSequence::kMaxGesturePoints; ++i) {
424 if (points[i].in_use())
425 touch_bitmask |= 1 << points[i].touch_id();
427 return touch_bitmask;
430 const float kFlingCurveNormalization = 1.0f / 1875.f;
432 float CalibrateFlingVelocity(float velocity) {
433 const unsigned last_coefficient =
434 GestureConfiguration::NumAccelParams - 1;
435 float normalized_velocity = fabs(velocity * kFlingCurveNormalization);
436 float nu = 0.0f, x = 1.f;
438 for (int i = last_coefficient ; i >= 0; i--) {
439 float a = GestureConfiguration::fling_acceleration_curve_coefficients(i);
441 x *= normalized_velocity;
444 return std::max(nu * velocity, -GestureConfiguration::fling_velocity_cap());
446 return std::min(nu * velocity, GestureConfiguration::fling_velocity_cap());
450 void UpdateGestureEventLatencyInfo(const TouchEvent& event,
451 GestureSequence::Gestures* gestures) {
452 // If the touch event does not cause any rendering scheduled, then
453 // 1) If the touch event does not generate any gesture event, its
454 // LatencyInfo ends here.
455 // 2) If the touch event generates gesture events, its latencyinfo
456 // is copied into the gesture events.
457 if (!event.latency()->FindLatency(
458 ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_COMPONENT, 0, NULL)) {
459 if (gestures->empty()) {
460 ui::LatencyInfo* touch_latency =
461 const_cast<ui::LatencyInfo*>(event.latency());
462 touch_latency->AddLatencyNumber(
463 ui::INPUT_EVENT_LATENCY_TERMINATED_TOUCH_COMPONENT, 0, 0);
465 GestureSequence::Gestures::iterator it = gestures->begin();
466 for (; it != gestures->end(); it++) {
467 (*it)->latency()->MergeWith(*event.latency());
468 // Inheriting the trace_id from the touch event's LatencyInfo
469 (*it)->latency()->trace_id = event.latency()->trace_id;
477 ////////////////////////////////////////////////////////////////////////////////
478 // GestureSequence Public:
480 GestureSequence::GestureSequence(GestureSequenceDelegate* delegate)
481 : state_(GS_NO_GESTURE),
483 pinch_distance_start_(0.f),
484 pinch_distance_current_(0.f),
485 scroll_type_(ST_FREE),
487 delegate_(delegate) {
491 GestureSequence::~GestureSequence() {
494 GestureSequence::Gestures* GestureSequence::ProcessTouchEventForGesture(
495 const TouchEvent& event,
496 EventResult result) {
497 StopTimersIfRequired(event);
498 last_touch_location_ = event.location();
499 if (result & ER_CONSUMED)
502 // Set a limit on the number of simultaneous touches in a gesture.
503 if (event.touch_id() >= kMaxGesturePoints)
506 if (event.type() == ui::ET_TOUCH_PRESSED) {
507 if (point_count_ == kMaxGesturePoints)
509 GesturePoint* new_point = &points_[event.touch_id()];
510 // We shouldn't be able to get two PRESSED events from the same
511 // finger without either a RELEASE or CANCEL in between. But let's not crash
512 // in a release build.
513 if (new_point->in_use()) {
514 LOG(ERROR) << "Received a second press for a point: " << event.touch_id();
515 new_point->ResetVelocity();
516 new_point->UpdateValues(event);
519 new_point->set_point_id(point_count_++);
520 new_point->set_touch_id(event.touch_id());
523 GestureState last_state = state_;
525 // NOTE: when modifying these state transitions, also update gestures.dot
526 scoped_ptr<Gestures> gestures(new Gestures());
527 GesturePoint& point = GesturePointForEvent(event);
528 point.UpdateValues(event);
529 RecreateBoundingBox();
530 flags_ = event.flags();
531 const int point_id = point.point_id();
535 // Send GESTURE_BEGIN for any touch pressed.
536 if (event.type() == ui::ET_TOUCH_PRESSED)
537 AppendBeginGestureEvent(point, gestures.get());
539 TouchStatusInternal status_internal = (result == ER_UNHANDLED) ?
540 TSI_NOT_PROCESSED : TSI_PROCESSED;
542 EdgeStateSignatureType signature = Signature(state_, point_id,
543 event.type(), status_internal);
545 if (signature == GST_INVALID)
546 signature = Signature(state_, point_id, event.type(), TSI_ALWAYS);
552 case GST_NO_GESTURE_FIRST_PRESSED:
553 TouchDown(event, point, gestures.get());
554 set_state(GS_PENDING_SYNTHETIC_CLICK);
556 case GST_PENDING_SYNTHETIC_CLICK_FIRST_RELEASED:
557 if (Click(event, point, gestures.get()))
558 point.UpdateForTap();
560 PrependTapCancelGestureEvent(point, gestures.get());
561 set_state(GS_NO_GESTURE);
563 case GST_PENDING_SYNTHETIC_CLICK_FIRST_MOVED:
564 case GST_PENDING_SYNTHETIC_CLICK_FIRST_STATIONARY:
565 if (ScrollStart(event, point, gestures.get())) {
566 PrependTapCancelGestureEvent(point, gestures.get());
567 set_state(GS_SCROLL);
568 if (ScrollUpdate(event, point, gestures.get()))
569 point.UpdateForScroll();
572 case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_MOVED:
573 case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_STATIONARY:
574 // No scrolling allowed, so nothing happens.
576 case GST_PENDING_SYNTHETIC_CLICK_FIRST_MOVED_PROCESSED:
577 if (point.IsInScrollWindow(event)) {
578 PrependTapCancelGestureEvent(point, gestures.get());
579 set_state(GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL);
582 case GST_PENDING_SYNTHETIC_CLICK_FIRST_RELEASED_HANDLED:
583 case GST_PENDING_SYNTHETIC_CLICK_FIRST_CANCELLED:
584 PrependTapCancelGestureEvent(point, gestures.get());
585 set_state(GS_NO_GESTURE);
587 case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_RELEASED:
588 case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_RELEASED_HANDLED:
589 case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_CANCELLED:
590 set_state(GS_NO_GESTURE);
592 case GST_SCROLL_FIRST_MOVED:
593 if (scroll_type_ == ST_VERTICAL ||
594 scroll_type_ == ST_HORIZONTAL)
595 BreakRailScroll(event, point, gestures.get());
596 if (ScrollUpdate(event, point, gestures.get()))
597 point.UpdateForScroll();
599 case GST_SCROLL_FIRST_RELEASED:
600 case GST_SCROLL_FIRST_CANCELLED:
601 ScrollEnd(event, point, gestures.get());
602 set_state(GS_NO_GESTURE);
604 case GST_PENDING_SYNTHETIC_CLICK_SECOND_PRESSED:
605 PrependTapCancelGestureEvent(point, gestures.get());
606 TwoFingerTapOrPinch(event, point, gestures.get());
608 case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_SECOND_PRESSED:
609 TwoFingerTapOrPinch(event, point, gestures.get());
611 case GST_SCROLL_SECOND_PRESSED:
612 PinchStart(event, point, gestures.get());
615 case GST_PENDING_TWO_FINGER_TAP_FIRST_RELEASED:
616 case GST_PENDING_TWO_FINGER_TAP_SECOND_RELEASED:
617 TwoFingerTouchReleased(event, point, gestures.get());
618 set_state(GS_SCROLL);
620 case GST_PENDING_TWO_FINGER_TAP_FIRST_MOVED:
621 case GST_PENDING_TWO_FINGER_TAP_SECOND_MOVED:
622 if (TwoFingerTouchMove(event, point, gestures.get()))
625 case GST_PENDING_TWO_FINGER_TAP_FIRST_MOVED_HANDLED:
626 case GST_PENDING_TWO_FINGER_TAP_SECOND_MOVED_HANDLED:
627 set_state(GS_PENDING_TWO_FINGER_TAP_NO_PINCH);
629 case GST_PENDING_TWO_FINGER_TAP_FIRST_RELEASED_HANDLED:
630 case GST_PENDING_TWO_FINGER_TAP_SECOND_RELEASED_HANDLED:
631 case GST_PENDING_TWO_FINGER_TAP_FIRST_CANCELLED:
632 case GST_PENDING_TWO_FINGER_TAP_SECOND_CANCELLED:
633 scroll_type_ = ST_FREE;
634 set_state(GS_SCROLL);
636 case GST_PENDING_TWO_FINGER_TAP_THIRD_PRESSED:
637 set_state(GS_PENDING_PINCH);
639 case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_MOVED:
640 case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_MOVED:
641 // No pinch allowed, so nothing happens.
643 case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_RELEASED:
644 case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_RELEASED:
645 TwoFingerTouchReleased(event, point, gestures.get());
646 // We transit into GS_SCROLL even though the touch move can be
647 // consumed and no scroll should happen. crbug.com/240399.
648 set_state(GS_SCROLL);
650 case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_RELEASED_HANDLED:
651 case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_RELEASED_HANDLED:
652 case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_CANCELLED:
653 case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_CANCELLED:
654 // We transit into GS_SCROLL even though the touch move can be
655 // consumed and no scroll should happen. crbug.com/240399.
656 scroll_type_ = ST_FREE;
657 set_state(GS_SCROLL);
659 case GST_PENDING_PINCH_FIRST_MOVED:
660 case GST_PENDING_PINCH_SECOND_MOVED:
661 if (TwoFingerTouchMove(event, point, gestures.get()))
664 case GST_PENDING_PINCH_FIRST_MOVED_HANDLED:
665 case GST_PENDING_PINCH_SECOND_MOVED_HANDLED:
666 set_state(GS_PENDING_PINCH_NO_PINCH);
668 case GST_PENDING_PINCH_FIRST_RELEASED:
669 case GST_PENDING_PINCH_SECOND_RELEASED:
670 case GST_PENDING_PINCH_FIRST_CANCELLED:
671 case GST_PENDING_PINCH_SECOND_CANCELLED:
672 // We transit into GS_SCROLL even though the touch move can be
673 // consumed and no scroll should happen. crbug.com/240399.
674 scroll_type_ = ST_FREE;
675 set_state(GS_SCROLL);
677 case GST_PENDING_PINCH_NO_PINCH_FIRST_MOVED:
678 case GST_PENDING_PINCH_NO_PINCH_SECOND_MOVED:
679 // No pinch allowed, so nothing happens.
681 case GST_PENDING_PINCH_NO_PINCH_FIRST_RELEASED:
682 case GST_PENDING_PINCH_NO_PINCH_SECOND_RELEASED:
683 case GST_PENDING_PINCH_NO_PINCH_FIRST_CANCELLED:
684 case GST_PENDING_PINCH_NO_PINCH_SECOND_CANCELLED:
685 // We transit into GS_SCROLL even though the touch move can be
686 // consumed and no scroll should happen. crbug.com/240399.
687 scroll_type_ = ST_FREE;
688 set_state(GS_SCROLL);
690 case GST_PINCH_FIRST_MOVED_HANDLED:
691 case GST_PINCH_SECOND_MOVED_HANDLED:
692 case GST_PINCH_THIRD_MOVED_HANDLED:
693 case GST_PINCH_FOURTH_MOVED_HANDLED:
694 case GST_PINCH_FIFTH_MOVED_HANDLED:
696 case GST_PINCH_FIRST_MOVED:
697 case GST_PINCH_SECOND_MOVED:
698 case GST_PINCH_THIRD_MOVED:
699 case GST_PINCH_FOURTH_MOVED:
700 case GST_PINCH_FIFTH_MOVED:
701 if (PinchUpdate(event, point, gestures.get())) {
702 for (int i = 0; i < point_count_; ++i)
703 GetPointByPointId(i)->UpdateForScroll();
706 case GST_PINCH_FIRST_RELEASED:
707 case GST_PINCH_SECOND_RELEASED:
708 case GST_PINCH_THIRD_RELEASED:
709 case GST_PINCH_FOURTH_RELEASED:
710 case GST_PINCH_FIFTH_RELEASED:
711 case GST_PINCH_FIRST_CANCELLED:
712 case GST_PINCH_SECOND_CANCELLED:
713 case GST_PINCH_THIRD_CANCELLED:
714 case GST_PINCH_FOURTH_CANCELLED:
715 case GST_PINCH_FIFTH_CANCELLED:
716 // Was it a swipe? i.e. were all the fingers moving in the same
718 MaybeSwipe(event, point, gestures.get());
720 if (point_count_ == 2) {
721 PinchEnd(event, point, gestures.get());
723 // Once pinch ends, it should still be possible to scroll with the
724 // remaining finger on the screen.
725 set_state(GS_SCROLL);
727 // Nothing else to do if we have more than 2 fingers active, since after
728 // the release/cancel, there are still enough fingers to do pinch.
729 // pinch_distance_current_ and pinch_distance_start_ will be updated
730 // when the bounding-box is updated.
734 case GST_PINCH_THIRD_PRESSED:
735 case GST_PINCH_FOURTH_PRESSED:
736 case GST_PINCH_FIFTH_PRESSED:
737 pinch_distance_current_ = BoundingBoxDiagonal(bounding_box_);
738 pinch_distance_start_ = pinch_distance_current_;
742 if (event.type() == ui::ET_TOUCH_RELEASED ||
743 event.type() == ui::ET_TOUCH_CANCELLED)
744 AppendEndGestureEvent(point, gestures.get());
746 if (state_ != last_state)
747 DVLOG(4) << "Gesture Sequence"
748 << " State: " << state_
749 << " touch id: " << event.touch_id();
751 if (last_state == GS_PENDING_SYNTHETIC_CLICK && state_ != last_state) {
752 GetLongPressTimer()->Stop();
753 GetShowPressTimer()->Stop();
756 // The set of point_ids must be contiguous and include 0.
757 // When a touch point is released, all points with ids greater than the
758 // released point must have their ids decremented, or the set of point_ids
759 // could end up with gaps.
760 if (event.type() == ui::ET_TOUCH_RELEASED ||
761 event.type() == ui::ET_TOUCH_CANCELLED) {
762 for (int i = 0; i < kMaxGesturePoints; ++i) {
763 GesturePoint& iter_point = points_[i];
764 if (iter_point.point_id() > point.point_id())
765 iter_point.set_point_id(iter_point.point_id() - 1);
770 CHECK_GE(point_count_, 0);
771 RecreateBoundingBox();
772 if (state_ == GS_PINCH) {
773 pinch_distance_current_ = BoundingBoxDiagonal(bounding_box_);
774 pinch_distance_start_ = pinch_distance_current_;
778 UpdateGestureEventLatencyInfo(event, gestures.get());
779 return gestures.release();
782 void GestureSequence::RecreateBoundingBox() {
783 // TODO(sad): Recreating the bounding box at every touch-event is not very
784 // efficient. This should be made better.
785 if (point_count_ == 0) {
786 bounding_box_.SetRect(0, 0, 0, 0);
787 } else if (point_count_ == 1) {
788 bounding_box_ = GetPointByPointId(0)->enclosing_rectangle();
790 int left = INT_MAX / 20, top = INT_MAX / 20;
791 int right = INT_MIN / 20, bottom = INT_MIN / 20;
792 for (int i = 0; i < kMaxGesturePoints; ++i) {
793 if (!points_[i].in_use())
795 // Using the |enclosing_rectangle()| for the touch-points would be ideal.
796 // However, this becomes brittle especially when a finger is in motion
797 // because the change in radius can overshadow the actual change in
798 // position. So the actual position of the point is used instead.
799 const gfx::Point& point = points_[i].last_touch_position();
800 left = std::min(left, point.x());
801 right = std::max(right, point.x());
802 top = std::min(top, point.y());
803 bottom = std::max(bottom, point.y());
805 bounding_box_.SetRect(left, top, right - left, bottom - top);
809 void GestureSequence::ResetVelocities() {
810 for (int i = 0; i < kMaxGesturePoints; ++i) {
811 if (points_[i].in_use())
812 points_[i].ResetVelocity();
816 ////////////////////////////////////////////////////////////////////////////////
817 // GestureSequence Protected:
819 base::OneShotTimer<GestureSequence>* GestureSequence::CreateTimer() {
820 return new base::OneShotTimer<GestureSequence>();
823 base::OneShotTimer<GestureSequence>* GestureSequence::GetLongPressTimer() {
824 if (!long_press_timer_.get())
825 long_press_timer_.reset(CreateTimer());
826 return long_press_timer_.get();
829 base::OneShotTimer<GestureSequence>* GestureSequence::GetShowPressTimer() {
830 if (!show_press_timer_.get())
831 show_press_timer_.reset(CreateTimer());
832 return show_press_timer_.get();
835 ////////////////////////////////////////////////////////////////////////////////
836 // GestureSequence Private:
838 GesturePoint& GestureSequence::GesturePointForEvent(
839 const TouchEvent& event) {
840 return points_[event.touch_id()];
843 GesturePoint* GestureSequence::GetPointByPointId(int point_id) {
844 DCHECK(0 <= point_id && point_id < kMaxGesturePoints);
845 for (int i = 0; i < kMaxGesturePoints; ++i) {
846 GesturePoint& point = points_[i];
847 if (point.in_use() && point.point_id() == point_id)
854 bool GestureSequence::IsSecondTouchDownCloseEnoughForTwoFingerTap() {
855 gfx::Point p1 = GetPointByPointId(0)->last_touch_position();
856 gfx::Point p2 = GetPointByPointId(1)->last_touch_position();
857 double max_distance =
858 GestureConfiguration::max_distance_for_two_finger_tap_in_pixels();
859 double distance = (p1.x() - p2.x()) * (p1.x() - p2.x()) +
860 (p1.y() - p2.y()) * (p1.y() - p2.y());
861 if (distance < max_distance * max_distance)
866 GestureEvent* GestureSequence::CreateGestureEvent(
867 const GestureEventDetails& details,
868 const gfx::Point& location,
870 base::Time timestamp,
871 unsigned int touch_id_bitmask) {
872 GestureEventDetails gesture_details(details);
873 gesture_details.set_touch_points(point_count_);
874 gesture_details.set_bounding_box(bounding_box_);
875 base::TimeDelta time_stamp =
876 base::TimeDelta::FromMicroseconds(timestamp.ToDoubleT() * 1000000);
877 return new GestureEvent(gesture_details.type(), location.x(), location.y(),
878 flags, time_stamp, gesture_details,
882 void GestureSequence::AppendTapDownGestureEvent(const GesturePoint& point,
883 Gestures* gestures) {
884 gestures->push_back(CreateGestureEvent(
885 GestureEventDetails(ui::ET_GESTURE_TAP_DOWN, 0, 0),
886 point.first_touch_position(),
888 base::Time::FromDoubleT(point.last_touch_time()),
889 1 << point.touch_id()));
892 void GestureSequence::PrependTapCancelGestureEvent(const GesturePoint& point,
893 Gestures* gestures) {
894 gestures->insert(gestures->begin(), CreateGestureEvent(
895 GestureEventDetails(ui::ET_GESTURE_TAP_CANCEL, 0, 0),
896 point.first_touch_position(),
898 base::Time::FromDoubleT(point.last_touch_time()),
899 1 << point.touch_id()));
902 void GestureSequence::AppendBeginGestureEvent(const GesturePoint& point,
903 Gestures* gestures) {
904 gestures->push_back(CreateGestureEvent(
905 GestureEventDetails(ui::ET_GESTURE_BEGIN, 0, 0),
906 point.first_touch_position(),
908 base::Time::FromDoubleT(point.last_touch_time()),
909 1 << point.touch_id()));
912 void GestureSequence::AppendEndGestureEvent(const GesturePoint& point,
913 Gestures* gestures) {
914 gestures->push_back(CreateGestureEvent(
915 GestureEventDetails(ui::ET_GESTURE_END, 0, 0),
916 point.first_touch_position(),
918 base::Time::FromDoubleT(point.last_touch_time()),
919 1 << point.touch_id()));
922 void GestureSequence::AppendClickGestureEvent(const GesturePoint& point,
924 Gestures* gestures) {
925 gfx::Rect er = point.enclosing_rectangle();
926 gfx::Point center = er.CenterPoint();
927 gestures->push_back(CreateGestureEvent(
928 GestureEventDetails(ui::ET_GESTURE_TAP, tap_count, 0),
931 base::Time::FromDoubleT(point.last_touch_time()),
932 1 << point.touch_id()));
935 void GestureSequence::AppendScrollGestureBegin(const GesturePoint& point,
936 const gfx::Point& location,
937 Gestures* gestures) {
938 gestures->push_back(CreateGestureEvent(
939 GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN, 0, 0),
942 base::Time::FromDoubleT(point.last_touch_time()),
943 1 << point.touch_id()));
946 void GestureSequence::AppendScrollGestureEnd(const GesturePoint& point,
947 const gfx::Point& location,
951 float railed_x_velocity = x_velocity;
952 float railed_y_velocity = y_velocity;
953 last_scroll_prediction_offset_.set_x(0);
954 last_scroll_prediction_offset_.set_y(0);
956 if (scroll_type_ == ST_HORIZONTAL)
957 railed_y_velocity = 0;
958 else if (scroll_type_ == ST_VERTICAL)
959 railed_x_velocity = 0;
961 if (railed_x_velocity != 0 || railed_y_velocity != 0) {
963 gestures->push_back(CreateGestureEvent(
964 GestureEventDetails(ui::ET_SCROLL_FLING_START,
965 CalibrateFlingVelocity(railed_x_velocity),
966 CalibrateFlingVelocity(railed_y_velocity),
967 CalibrateFlingVelocity(x_velocity),
968 CalibrateFlingVelocity(y_velocity)),
971 base::Time::FromDoubleT(point.last_touch_time()),
972 1 << point.touch_id()));
974 gestures->push_back(CreateGestureEvent(
975 GestureEventDetails(ui::ET_GESTURE_SCROLL_END, 0, 0),
978 base::Time::FromDoubleT(point.last_touch_time()),
979 1 << point.touch_id()));
983 void GestureSequence::AppendScrollGestureUpdate(GesturePoint& point,
984 Gestures* gestures) {
985 static bool use_scroll_prediction = CommandLine::ForCurrentProcess()->
986 HasSwitch(switches::kEnableScrollPrediction);
989 if (point_count_ == 1) {
990 d = point.ScrollDelta();
991 location = point.last_touch_position();
993 location = bounding_box_.CenterPoint();
994 d = location - latest_multi_scroll_update_location_;
995 latest_multi_scroll_update_location_ = location;
998 if (use_scroll_prediction) {
999 // Remove the extra distance added by the last scroll prediction and add
1000 // the new prediction offset.
1001 d -= last_scroll_prediction_offset_;
1002 last_scroll_prediction_offset_.set_x(
1003 GestureConfiguration::scroll_prediction_seconds() * point.XVelocity());
1004 last_scroll_prediction_offset_.set_y(
1005 GestureConfiguration::scroll_prediction_seconds() * point.YVelocity());
1006 d += last_scroll_prediction_offset_;
1007 location += gfx::Vector2d(last_scroll_prediction_offset_.x(),
1008 last_scroll_prediction_offset_.y());
1011 gfx::Vector2dF o = d;
1013 if (scroll_type_ == ST_HORIZONTAL)
1015 else if (scroll_type_ == ST_VERTICAL)
1020 GestureEventDetails details(ui::ET_GESTURE_SCROLL_UPDATE,
1021 d.x(), d.y(), o.x(), o.y());
1022 details.SetScrollVelocity(
1023 scroll_type_ == ST_VERTICAL ? 0 : point.XVelocity(),
1024 scroll_type_ == ST_HORIZONTAL ? 0 : point.YVelocity(),
1027 gestures->push_back(CreateGestureEvent(
1031 base::Time::FromDoubleT(point.last_touch_time()),
1032 ComputeTouchBitmask(points_)));
1035 void GestureSequence::AppendPinchGestureBegin(const GesturePoint& p1,
1036 const GesturePoint& p2,
1037 Gestures* gestures) {
1038 gfx::Point center = bounding_box_.CenterPoint();
1039 gestures->push_back(CreateGestureEvent(
1040 GestureEventDetails(ui::ET_GESTURE_PINCH_BEGIN, 0, 0),
1043 base::Time::FromDoubleT(p1.last_touch_time()),
1044 1 << p1.touch_id() | 1 << p2.touch_id()));
1047 void GestureSequence::AppendPinchGestureEnd(const GesturePoint& p1,
1048 const GesturePoint& p2,
1050 Gestures* gestures) {
1051 gfx::Point center = bounding_box_.CenterPoint();
1052 gestures->push_back(CreateGestureEvent(
1053 GestureEventDetails(ui::ET_GESTURE_PINCH_END, 0, 0),
1056 base::Time::FromDoubleT(p1.last_touch_time()),
1057 1 << p1.touch_id() | 1 << p2.touch_id()));
1060 void GestureSequence::AppendPinchGestureUpdate(const GesturePoint& point,
1062 Gestures* gestures) {
1063 // TODO(sad): Compute rotation and include it in delta_y.
1064 // http://crbug.com/113145
1065 gestures->push_back(CreateGestureEvent(
1066 GestureEventDetails(ui::ET_GESTURE_PINCH_UPDATE, scale, 0),
1067 bounding_box_.CenterPoint(),
1069 base::Time::FromDoubleT(point.last_touch_time()),
1070 ComputeTouchBitmask(points_)));
1073 void GestureSequence::AppendSwipeGesture(const GesturePoint& point,
1076 Gestures* gestures) {
1077 gestures->push_back(CreateGestureEvent(
1078 GestureEventDetails(ui::ET_GESTURE_MULTIFINGER_SWIPE, swipe_x, swipe_y),
1079 bounding_box_.CenterPoint(),
1081 base::Time::FromDoubleT(point.last_touch_time()),
1082 ComputeTouchBitmask(points_)));
1085 void GestureSequence::AppendTwoFingerTapGestureEvent(Gestures* gestures) {
1086 const GesturePoint* point = GetPointByPointId(0);
1087 const gfx::Rect rect = point->enclosing_rectangle();
1088 gestures->push_back(CreateGestureEvent(
1089 GestureEventDetails(ui::ET_GESTURE_TWO_FINGER_TAP,
1092 point->enclosing_rectangle().CenterPoint(),
1094 base::Time::FromDoubleT(point->last_touch_time()),
1095 1 << point->touch_id()));
1098 bool GestureSequence::Click(const TouchEvent& event,
1099 const GesturePoint& point,
1100 Gestures* gestures) {
1101 DCHECK(state_ == GS_PENDING_SYNTHETIC_CLICK);
1102 if (point.IsInClickWindow(event)) {
1104 if (point.IsInTripleClickWindow(event))
1106 else if (point.IsInDoubleClickWindow(event))
1108 if (tap_count == 1 && GetShowPressTimer()->IsRunning()) {
1109 GetShowPressTimer()->Stop();
1110 AppendShowPressGestureEvent();
1112 AppendClickGestureEvent(point, tap_count, gestures);
1114 } else if (point.IsInsideManhattanSquare(event) &&
1115 !GetLongPressTimer()->IsRunning()) {
1116 AppendLongTapGestureEvent(point, gestures);
1121 bool GestureSequence::ScrollStart(const TouchEvent& event,
1122 GesturePoint& point,
1123 Gestures* gestures) {
1124 DCHECK(state_ == GS_PENDING_SYNTHETIC_CLICK);
1125 if (!point.IsInScrollWindow(event))
1127 AppendScrollGestureBegin(point, point.first_touch_position(), gestures);
1128 if (point.IsInHorizontalRailWindow())
1129 scroll_type_ = ST_HORIZONTAL;
1130 else if (point.IsInVerticalRailWindow())
1131 scroll_type_ = ST_VERTICAL;
1133 scroll_type_ = ST_FREE;
1137 void GestureSequence::BreakRailScroll(const TouchEvent& event,
1138 GesturePoint& point,
1139 Gestures* gestures) {
1140 DCHECK(state_ == GS_SCROLL);
1141 if (scroll_type_ == ST_HORIZONTAL &&
1142 point.BreaksHorizontalRail())
1143 scroll_type_ = ST_FREE;
1144 else if (scroll_type_ == ST_VERTICAL &&
1145 point.BreaksVerticalRail())
1146 scroll_type_ = ST_FREE;
1149 bool GestureSequence::ScrollUpdate(const TouchEvent& event,
1150 GesturePoint& point,
1151 Gestures* gestures) {
1152 DCHECK(state_ == GS_SCROLL);
1153 if (!point.DidScroll(event, 0))
1155 AppendScrollGestureUpdate(point, gestures);
1159 bool GestureSequence::TouchDown(const TouchEvent& event,
1160 const GesturePoint& point,
1161 Gestures* gestures) {
1162 DCHECK(state_ == GS_NO_GESTURE);
1163 AppendTapDownGestureEvent(point, gestures);
1164 GetLongPressTimer()->Start(
1166 base::TimeDelta::FromMilliseconds(
1167 GestureConfiguration::long_press_time_in_seconds() * 1000),
1169 &GestureSequence::AppendLongPressGestureEvent);
1171 GetShowPressTimer()->Start(
1173 base::TimeDelta::FromMilliseconds(
1174 GestureConfiguration::show_press_delay_in_ms()),
1176 &GestureSequence::AppendShowPressGestureEvent);
1181 bool GestureSequence::TwoFingerTouchDown(const TouchEvent& event,
1182 const GesturePoint& point,
1183 Gestures* gestures) {
1184 DCHECK(state_ == GS_PENDING_SYNTHETIC_CLICK ||
1185 state_ == GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL ||
1186 state_ == GS_SCROLL);
1188 if (state_ == GS_SCROLL) {
1189 AppendScrollGestureEnd(point, point.last_touch_position(), gestures,
1192 second_touch_time_ = event.time_stamp();
1196 bool GestureSequence::TwoFingerTouchMove(const TouchEvent& event,
1197 const GesturePoint& point,
1198 Gestures* gestures) {
1199 DCHECK(state_ == GS_PENDING_TWO_FINGER_TAP ||
1200 state_ == GS_PENDING_PINCH);
1202 base::TimeDelta time_delta = event.time_stamp() - second_touch_time_;
1203 base::TimeDelta max_delta = base::TimeDelta::FromMilliseconds(1000 *
1204 ui::GestureConfiguration::max_touch_down_duration_in_seconds_for_click());
1205 if (time_delta > max_delta || !point.IsInsideManhattanSquare(event)) {
1206 PinchStart(event, point, gestures);
1212 bool GestureSequence::TwoFingerTouchReleased(const TouchEvent& event,
1213 const GesturePoint& point,
1214 Gestures* gestures) {
1215 DCHECK(state_ == GS_PENDING_TWO_FINGER_TAP ||
1216 state_ == GS_PENDING_TWO_FINGER_TAP_NO_PINCH);
1217 base::TimeDelta time_delta = event.time_stamp() - second_touch_time_;
1218 base::TimeDelta max_delta = base::TimeDelta::FromMilliseconds(1000 *
1219 ui::GestureConfiguration::max_touch_down_duration_in_seconds_for_click());
1220 if (time_delta < max_delta && point.IsInsideManhattanSquare(event))
1221 AppendTwoFingerTapGestureEvent(gestures);
1225 void GestureSequence::AppendLongPressGestureEvent() {
1226 const GesturePoint* point = GetPointByPointId(0);
1227 scoped_ptr<GestureEvent> gesture(CreateGestureEvent(
1228 GestureEventDetails(ui::ET_GESTURE_LONG_PRESS, 0, 0),
1229 point->first_touch_position(),
1231 base::Time::FromDoubleT(point->last_touch_time()),
1232 1 << point->touch_id()));
1233 delegate_->DispatchPostponedGestureEvent(gesture.get());
1236 void GestureSequence::AppendShowPressGestureEvent() {
1237 const GesturePoint* point = GetPointByPointId(0);
1238 scoped_ptr<GestureEvent> gesture(CreateGestureEvent(
1239 GestureEventDetails(ui::ET_GESTURE_SHOW_PRESS, 0, 0),
1240 point->first_touch_position(),
1242 base::Time::FromDoubleT(point->last_touch_time()),
1243 1 << point->touch_id()));
1244 delegate_->DispatchPostponedGestureEvent(gesture.get());
1247 void GestureSequence::AppendLongTapGestureEvent(const GesturePoint& point,
1248 Gestures* gestures) {
1249 gfx::Rect er = point.enclosing_rectangle();
1250 gfx::Point center = er.CenterPoint();
1251 gestures->push_back(CreateGestureEvent(
1252 GestureEventDetails(ui::ET_GESTURE_LONG_TAP, 0, 0),
1255 base::Time::FromDoubleT(point.last_touch_time()),
1256 1 << point.touch_id()));
1259 bool GestureSequence::ScrollEnd(const TouchEvent& event,
1260 GesturePoint& point,
1261 Gestures* gestures) {
1262 DCHECK(state_ == GS_SCROLL);
1263 if (point.IsInFlickWindow(event)) {
1264 AppendScrollGestureEnd(point, point.last_touch_position(), gestures,
1265 point.XVelocity(), point.YVelocity());
1267 AppendScrollGestureEnd(point, point.last_touch_position(), gestures,
1273 bool GestureSequence::PinchStart(const TouchEvent& event,
1274 const GesturePoint& point,
1275 Gestures* gestures) {
1276 DCHECK(state_ == GS_SCROLL ||
1277 state_ == GS_PENDING_TWO_FINGER_TAP ||
1278 state_ == GS_PENDING_PINCH);
1280 // Once pinch starts, we immediately break rail scroll.
1281 scroll_type_ = ST_FREE;
1283 const GesturePoint* point1 = GetPointByPointId(0);
1284 const GesturePoint* point2 = GetPointByPointId(1);
1286 pinch_distance_current_ = BoundingBoxDiagonal(bounding_box_);
1287 pinch_distance_start_ = pinch_distance_current_;
1288 latest_multi_scroll_update_location_ = bounding_box_.CenterPoint();
1289 AppendPinchGestureBegin(*point1, *point2, gestures);
1291 if (state_ == GS_PENDING_TWO_FINGER_TAP ||
1292 state_ == GS_PENDING_PINCH) {
1293 gfx::Point center = bounding_box_.CenterPoint();
1294 AppendScrollGestureBegin(point, center, gestures);
1300 bool GestureSequence::PinchUpdate(const TouchEvent& event,
1301 GesturePoint& point,
1302 Gestures* gestures) {
1303 DCHECK(state_ == GS_PINCH);
1305 // It is possible that the none of the touch-points changed their position,
1306 // but their radii changed, and that caused the bounding box to also change.
1307 // But in such cases, we do not want to either pinch or scroll.
1308 // To avoid small jiggles, it is also necessary to make sure that at least one
1309 // of the fingers moved enough before a pinch or scroll update is created.
1310 bool did_scroll = false;
1311 for (int i = 0; i < kMaxGesturePoints; ++i) {
1312 if (!points_[i].in_use() || !points_[i].DidScroll(event, 0))
1321 float distance = BoundingBoxDiagonal(bounding_box_);
1323 if (abs(distance - pinch_distance_current_) >=
1324 GestureConfiguration::min_pinch_update_distance_in_pixels()) {
1325 AppendPinchGestureUpdate(point,
1326 distance / pinch_distance_current_, gestures);
1327 pinch_distance_current_ = distance;
1329 AppendScrollGestureUpdate(point, gestures);
1334 bool GestureSequence::PinchEnd(const TouchEvent& event,
1335 const GesturePoint& point,
1336 Gestures* gestures) {
1337 DCHECK(state_ == GS_PINCH);
1339 GesturePoint* point1 = GetPointByPointId(0);
1340 GesturePoint* point2 = GetPointByPointId(1);
1342 float distance = BoundingBoxDiagonal(bounding_box_);
1343 AppendPinchGestureEnd(*point1, *point2,
1344 distance / pinch_distance_start_, gestures);
1346 pinch_distance_start_ = 0;
1347 pinch_distance_current_ = 0;
1351 bool GestureSequence::MaybeSwipe(const TouchEvent& event,
1352 const GesturePoint& point,
1353 Gestures* gestures) {
1354 DCHECK(state_ == GS_PINCH);
1355 float velocity_x = 0.f, velocity_y = 0.f;
1356 bool swipe_x = true, swipe_y = true;
1357 int sign_x = 0, sign_y = 0;
1360 for (i = 0; i < kMaxGesturePoints; ++i) {
1361 if (points_[i].in_use())
1364 DCHECK(i < kMaxGesturePoints);
1366 velocity_x = points_[i].XVelocity();
1367 velocity_y = points_[i].YVelocity();
1368 sign_x = velocity_x < 0.f ? -1 : 1;
1369 sign_y = velocity_y < 0.f ? -1 : 1;
1371 for (++i; i < kMaxGesturePoints; ++i) {
1372 if (!points_[i].in_use())
1375 if (sign_x * points_[i].XVelocity() < 0)
1378 if (sign_y * points_[i].YVelocity() < 0)
1381 velocity_x += points_[i].XVelocity();
1382 velocity_y += points_[i].YVelocity();
1385 float min_velocity = GestureConfiguration::min_swipe_speed();
1386 min_velocity *= min_velocity;
1388 velocity_x = fabs(velocity_x / point_count_);
1389 velocity_y = fabs(velocity_y / point_count_);
1390 if (velocity_x < min_velocity)
1392 if (velocity_y < min_velocity)
1395 if (!swipe_x && !swipe_y)
1399 velocity_x = 0.001f;
1401 velocity_y = 0.001f;
1403 float ratio = velocity_x > velocity_y ? velocity_x / velocity_y :
1404 velocity_y / velocity_x;
1405 if (ratio < GestureConfiguration::max_swipe_deviation_ratio())
1408 if (velocity_x > velocity_y)
1413 AppendSwipeGesture(point, sign_x, sign_y, gestures);
1418 void GestureSequence::TwoFingerTapOrPinch(const TouchEvent& event,
1419 const GesturePoint& point,
1420 Gestures* gestures) {
1421 if (IsSecondTouchDownCloseEnoughForTwoFingerTap()) {
1422 TwoFingerTouchDown(event, point, gestures);
1423 set_state(GS_PENDING_TWO_FINGER_TAP);
1425 set_state(GS_PENDING_PINCH);
1430 void GestureSequence::StopTimersIfRequired(const TouchEvent& event) {
1431 if ((!GetLongPressTimer()->IsRunning() &&
1432 !GetShowPressTimer()->IsRunning()) ||
1433 event.type() != ui::ET_TOUCH_MOVED)
1436 // Since a timer is running, there should be a non-NULL point.
1437 const GesturePoint* point = GetPointByPointId(0);
1438 if (!ui::gestures::IsInsideManhattanSquare(point->first_touch_position(),
1439 event.location())) {
1440 GetLongPressTimer()->Stop();
1441 GetShowPressTimer()->Stop();