Roll DEPS for libelf clang compilation fix.
[chromium-blink-merge.git] / ui / events / gestures / gesture_sequence.cc
blob14824d0f67640c4e6411168302f3ce20631a025d
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"
7 #include <stdlib.h>
8 #include <cmath>
9 #include <limits>
11 #include "base/command_line.h"
12 #include "base/logging.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/time/time.h"
16 #include "ui/events/event.h"
17 #include "ui/events/event_constants.h"
18 #include "ui/events/event_switches.h"
19 #include "ui/events/gestures/gesture_configuration.h"
20 #include "ui/gfx/rect.h"
22 namespace ui {
24 namespace {
26 // ui::EventType is mapped to TouchState so it can fit into 3 bits of
27 // Signature.
28 enum TouchState {
29 TS_RELEASED,
30 TS_PRESSED,
31 TS_MOVED,
32 TS_CANCELLED,
33 TS_UNKNOWN,
36 // ui::EventResult is mapped to TouchStatusInternal to simply indicate whether a
37 // processed touch-event should affect gesture-recognition or not.
38 enum TouchStatusInternal {
39 TSI_NOT_PROCESSED, // The touch-event should take-part into
40 // gesture-recognition only if the touch-event has not
41 // been processed.
43 TSI_PROCESSED, // The touch-event should affect gesture-recognition only
44 // if the touch-event has been processed. For example,,
45 // this means that a JavaScript touch handler called
46 // |preventDefault| on the associated touch event
47 // or was processed by an aura-window or views-view.
49 TSI_ALWAYS // The touch-event should always affect gesture
50 // recognition.
53 // Get equivalent TouchState from EventType |type|.
54 TouchState TouchEventTypeToTouchState(ui::EventType type) {
55 switch (type) {
56 case ui::ET_TOUCH_RELEASED:
57 return TS_RELEASED;
58 case ui::ET_TOUCH_PRESSED:
59 return TS_PRESSED;
60 case ui::ET_TOUCH_MOVED:
61 return TS_MOVED;
62 case ui::ET_TOUCH_CANCELLED:
63 return TS_CANCELLED;
64 default:
65 DVLOG(1) << "Unknown Touch Event type";
67 return TS_UNKNOWN;
70 // Gesture signature types for different values of combination (GestureState,
71 // touch_id, ui::EventType, touch_handled), see Signature for more info.
73 // Note: New addition of types should be placed as per their Signature value.
74 #define G(gesture_state, id, touch_state, handled) 1 + ( \
75 (((touch_state) & 0x7) << 1) | \
76 ((handled & 0x3) << 4) | \
77 (((id) & 0xfff) << 6) | \
78 ((gesture_state) << 18))
80 enum EdgeStateSignatureType {
81 GST_INVALID = -1,
83 GST_NO_GESTURE_FIRST_PRESSED =
84 G(GS_NO_GESTURE, 0, TS_PRESSED, TSI_NOT_PROCESSED),
86 GST_PENDING_SYNTHETIC_CLICK_FIRST_RELEASED =
87 G(GS_PENDING_SYNTHETIC_CLICK, 0, TS_RELEASED, TSI_NOT_PROCESSED),
89 GST_PENDING_SYNTHETIC_CLICK_FIRST_RELEASED_HANDLED =
90 G(GS_PENDING_SYNTHETIC_CLICK, 0, TS_RELEASED, TSI_PROCESSED),
92 // Ignore processed touch-move events until gesture-scroll starts.
93 GST_PENDING_SYNTHETIC_CLICK_FIRST_MOVED =
94 G(GS_PENDING_SYNTHETIC_CLICK, 0, TS_MOVED, TSI_NOT_PROCESSED),
96 GST_PENDING_SYNTHETIC_CLICK_FIRST_MOVED_PROCESSED =
97 G(GS_PENDING_SYNTHETIC_CLICK, 0, TS_MOVED, TSI_PROCESSED),
99 GST_PENDING_SYNTHETIC_CLICK_FIRST_CANCELLED =
100 G(GS_PENDING_SYNTHETIC_CLICK, 0, TS_CANCELLED, TSI_ALWAYS),
102 GST_PENDING_SYNTHETIC_CLICK_SECOND_PRESSED =
103 G(GS_PENDING_SYNTHETIC_CLICK, 1, TS_PRESSED, TSI_NOT_PROCESSED),
105 GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_RELEASED =
106 G(GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL,
108 TS_RELEASED,
109 TSI_NOT_PROCESSED),
111 GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_RELEASED_HANDLED =
112 G(GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL, 0, TS_RELEASED, TSI_PROCESSED),
114 GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_MOVED =
115 G(GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL, 0, TS_MOVED, TSI_ALWAYS),
117 GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_CANCELLED =
118 G(GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL, 0, TS_CANCELLED, TSI_ALWAYS),
120 GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_SECOND_PRESSED =
121 G(GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL, 1, TS_PRESSED, TSI_NOT_PROCESSED),
123 GST_SYNTHETIC_CLICK_ABORTED_FIRST_RELEASED =
124 G(GS_SYNTHETIC_CLICK_ABORTED, 0, TS_RELEASED, TSI_ALWAYS),
126 GST_SYNTHETIC_CLICK_ABORTED_SECOND_PRESSED =
127 G(GS_SYNTHETIC_CLICK_ABORTED, 1, TS_PRESSED, TSI_NOT_PROCESSED),
129 GST_SCROLL_FIRST_RELEASED =
130 G(GS_SCROLL, 0, TS_RELEASED, TSI_ALWAYS),
132 GST_SCROLL_FIRST_MOVED =
133 G(GS_SCROLL, 0, TS_MOVED, TSI_NOT_PROCESSED),
135 GST_SCROLL_FIRST_MOVED_HANDLED =
136 G(GS_SCROLL, 0, TS_MOVED, TSI_PROCESSED),
138 GST_SCROLL_FIRST_CANCELLED =
139 G(GS_SCROLL, 0, TS_CANCELLED, TSI_ALWAYS),
141 GST_SCROLL_SECOND_PRESSED =
142 G(GS_SCROLL, 1, TS_PRESSED, TSI_NOT_PROCESSED),
144 GST_PENDING_TWO_FINGER_TAP_FIRST_RELEASED =
145 G(GS_PENDING_TWO_FINGER_TAP, 0, TS_RELEASED, TSI_NOT_PROCESSED),
147 GST_PENDING_TWO_FINGER_TAP_FIRST_RELEASED_HANDLED =
148 G(GS_PENDING_TWO_FINGER_TAP, 0, TS_RELEASED, TSI_PROCESSED),
150 GST_PENDING_TWO_FINGER_TAP_SECOND_RELEASED =
151 G(GS_PENDING_TWO_FINGER_TAP, 1, TS_RELEASED, TSI_NOT_PROCESSED),
153 GST_PENDING_TWO_FINGER_TAP_SECOND_RELEASED_HANDLED =
154 G(GS_PENDING_TWO_FINGER_TAP, 1, TS_RELEASED, TSI_PROCESSED),
156 GST_PENDING_TWO_FINGER_TAP_FIRST_MOVED =
157 G(GS_PENDING_TWO_FINGER_TAP, 0, TS_MOVED, TSI_NOT_PROCESSED),
159 GST_PENDING_TWO_FINGER_TAP_SECOND_MOVED =
160 G(GS_PENDING_TWO_FINGER_TAP, 1, TS_MOVED, TSI_NOT_PROCESSED),
162 GST_PENDING_TWO_FINGER_TAP_FIRST_MOVED_HANDLED =
163 G(GS_PENDING_TWO_FINGER_TAP, 0, TS_MOVED, TSI_PROCESSED),
165 GST_PENDING_TWO_FINGER_TAP_SECOND_MOVED_HANDLED =
166 G(GS_PENDING_TWO_FINGER_TAP, 1, TS_MOVED, TSI_PROCESSED),
168 GST_PENDING_TWO_FINGER_TAP_FIRST_CANCELLED =
169 G(GS_PENDING_TWO_FINGER_TAP, 0, TS_CANCELLED, TSI_ALWAYS),
171 GST_PENDING_TWO_FINGER_TAP_SECOND_CANCELLED =
172 G(GS_PENDING_TWO_FINGER_TAP, 1, TS_CANCELLED, TSI_ALWAYS),
174 GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_RELEASED =
175 G(GS_PENDING_TWO_FINGER_TAP_NO_PINCH, 0, TS_RELEASED, TSI_NOT_PROCESSED),
177 GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_RELEASED_HANDLED =
178 G(GS_PENDING_TWO_FINGER_TAP_NO_PINCH, 0, TS_RELEASED, TSI_PROCESSED),
180 GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_RELEASED =
181 G(GS_PENDING_TWO_FINGER_TAP_NO_PINCH, 1, TS_RELEASED, TSI_NOT_PROCESSED),
183 GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_RELEASED_HANDLED =
184 G(GS_PENDING_TWO_FINGER_TAP_NO_PINCH, 1, TS_RELEASED, TSI_PROCESSED),
186 GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_MOVED =
187 G(GS_PENDING_TWO_FINGER_TAP_NO_PINCH, 0, TS_MOVED, TSI_ALWAYS),
189 GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_MOVED =
190 G(GS_PENDING_TWO_FINGER_TAP_NO_PINCH, 1, TS_MOVED, TSI_ALWAYS),
192 GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_CANCELLED =
193 G(GS_PENDING_TWO_FINGER_TAP_NO_PINCH, 0, TS_CANCELLED, TSI_ALWAYS),
195 GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_CANCELLED =
196 G(GS_PENDING_TWO_FINGER_TAP_NO_PINCH, 1, TS_CANCELLED, TSI_ALWAYS),
198 GST_PENDING_TWO_FINGER_TAP_THIRD_PRESSED =
199 G(GS_PENDING_TWO_FINGER_TAP, 2, TS_PRESSED, TSI_NOT_PROCESSED),
201 GST_PENDING_PINCH_FIRST_MOVED =
202 G(GS_PENDING_PINCH, 0, TS_MOVED, TSI_NOT_PROCESSED),
204 GST_PENDING_PINCH_SECOND_MOVED =
205 G(GS_PENDING_PINCH, 1, TS_MOVED, TSI_NOT_PROCESSED),
207 GST_PENDING_PINCH_FIRST_MOVED_HANDLED =
208 G(GS_PENDING_PINCH, 0, TS_MOVED, TSI_PROCESSED),
210 GST_PENDING_PINCH_SECOND_MOVED_HANDLED =
211 G(GS_PENDING_PINCH, 1, TS_MOVED, TSI_PROCESSED),
213 GST_PENDING_PINCH_FIRST_CANCELLED =
214 G(GS_PENDING_PINCH, 0, TS_CANCELLED, TSI_ALWAYS),
216 GST_PENDING_PINCH_SECOND_CANCELLED =
217 G(GS_PENDING_PINCH, 1, TS_CANCELLED, TSI_ALWAYS),
219 GST_PENDING_PINCH_FIRST_RELEASED =
220 G(GS_PENDING_PINCH, 0, TS_RELEASED, TSI_ALWAYS),
222 GST_PENDING_PINCH_SECOND_RELEASED =
223 G(GS_PENDING_PINCH, 1, TS_RELEASED, TSI_ALWAYS),
225 GST_PENDING_PINCH_NO_PINCH_FIRST_MOVED =
226 G(GS_PENDING_PINCH_NO_PINCH, 0, TS_MOVED, TSI_ALWAYS),
228 GST_PENDING_PINCH_NO_PINCH_SECOND_MOVED =
229 G(GS_PENDING_PINCH_NO_PINCH, 1, TS_MOVED, TSI_ALWAYS),
231 GST_PENDING_PINCH_NO_PINCH_FIRST_CANCELLED =
232 G(GS_PENDING_PINCH_NO_PINCH, 0, TS_CANCELLED, TSI_ALWAYS),
234 GST_PENDING_PINCH_NO_PINCH_SECOND_CANCELLED =
235 G(GS_PENDING_PINCH_NO_PINCH, 1, TS_CANCELLED, TSI_ALWAYS),
237 GST_PENDING_PINCH_NO_PINCH_FIRST_RELEASED =
238 G(GS_PENDING_PINCH_NO_PINCH, 0, TS_RELEASED, TSI_ALWAYS),
240 GST_PENDING_PINCH_NO_PINCH_SECOND_RELEASED =
241 G(GS_PENDING_PINCH_NO_PINCH, 1, TS_RELEASED, TSI_ALWAYS),
243 GST_PINCH_FIRST_MOVED =
244 G(GS_PINCH, 0, TS_MOVED, TSI_NOT_PROCESSED),
246 GST_PINCH_FIRST_MOVED_HANDLED =
247 G(GS_PINCH, 0, TS_MOVED, TSI_PROCESSED),
249 GST_PINCH_SECOND_MOVED =
250 G(GS_PINCH, 1, TS_MOVED, TSI_NOT_PROCESSED),
252 GST_PINCH_SECOND_MOVED_HANDLED =
253 G(GS_PINCH, 1, TS_MOVED, TSI_PROCESSED),
255 GST_PINCH_FIRST_RELEASED =
256 G(GS_PINCH, 0, TS_RELEASED, TSI_ALWAYS),
258 GST_PINCH_SECOND_RELEASED =
259 G(GS_PINCH, 1, TS_RELEASED, TSI_ALWAYS),
261 GST_PINCH_FIRST_CANCELLED =
262 G(GS_PINCH, 0, TS_CANCELLED, TSI_ALWAYS),
264 GST_PINCH_SECOND_CANCELLED =
265 G(GS_PINCH, 1, TS_CANCELLED, TSI_ALWAYS),
267 GST_PINCH_THIRD_PRESSED =
268 G(GS_PINCH, 2, TS_PRESSED, TSI_NOT_PROCESSED),
270 GST_PINCH_THIRD_MOVED =
271 G(GS_PINCH, 2, TS_MOVED, TSI_NOT_PROCESSED),
273 GST_PINCH_THIRD_MOVED_HANDLED =
274 G(GS_PINCH, 2, TS_MOVED, TSI_PROCESSED),
276 GST_PINCH_THIRD_RELEASED =
277 G(GS_PINCH, 2, TS_RELEASED, TSI_ALWAYS),
279 GST_PINCH_THIRD_CANCELLED =
280 G(GS_PINCH, 2, TS_CANCELLED, TSI_ALWAYS),
282 GST_PINCH_FOURTH_PRESSED =
283 G(GS_PINCH, 3, TS_PRESSED, TSI_NOT_PROCESSED),
285 GST_PINCH_FOURTH_MOVED =
286 G(GS_PINCH, 3, TS_MOVED, TSI_NOT_PROCESSED),
288 GST_PINCH_FOURTH_MOVED_HANDLED =
289 G(GS_PINCH, 3, TS_MOVED, TSI_PROCESSED),
291 GST_PINCH_FOURTH_RELEASED =
292 G(GS_PINCH, 3, TS_RELEASED, TSI_ALWAYS),
294 GST_PINCH_FOURTH_CANCELLED =
295 G(GS_PINCH, 3, TS_CANCELLED, TSI_ALWAYS),
297 GST_PINCH_FIFTH_PRESSED =
298 G(GS_PINCH, 4, TS_PRESSED, TSI_NOT_PROCESSED),
300 GST_PINCH_FIFTH_MOVED =
301 G(GS_PINCH, 4, TS_MOVED, TSI_NOT_PROCESSED),
303 GST_PINCH_FIFTH_MOVED_HANDLED =
304 G(GS_PINCH, 4, TS_MOVED, TSI_PROCESSED),
306 GST_PINCH_FIFTH_RELEASED =
307 G(GS_PINCH, 4, TS_RELEASED, TSI_ALWAYS),
309 GST_PINCH_FIFTH_CANCELLED =
310 G(GS_PINCH, 4, TS_CANCELLED, TSI_ALWAYS),
313 // Builds a signature. Signatures are assembled by joining together
314 // multiple bits.
315 // 1 LSB bit so that the computed signature is always greater than 0
316 // 3 bits for the |type|.
317 // 2 bit for |touch_status|
318 // 12 bits for |touch_id|
319 // 14 bits for the |gesture_state|.
320 EdgeStateSignatureType Signature(GestureState gesture_state,
321 unsigned int touch_id,
322 ui::EventType type,
323 TouchStatusInternal touch_status) {
324 CHECK((touch_id & 0xfff) == touch_id);
325 TouchState touch_state = TouchEventTypeToTouchState(type);
326 EdgeStateSignatureType signature = static_cast<EdgeStateSignatureType>
327 (G(gesture_state, touch_id, touch_state, touch_status));
329 switch (signature) {
330 case GST_NO_GESTURE_FIRST_PRESSED:
331 case GST_PENDING_SYNTHETIC_CLICK_FIRST_RELEASED:
332 case GST_PENDING_SYNTHETIC_CLICK_FIRST_RELEASED_HANDLED:
333 case GST_PENDING_SYNTHETIC_CLICK_FIRST_MOVED:
334 case GST_PENDING_SYNTHETIC_CLICK_FIRST_MOVED_PROCESSED:
335 case GST_PENDING_SYNTHETIC_CLICK_FIRST_CANCELLED:
336 case GST_PENDING_SYNTHETIC_CLICK_SECOND_PRESSED:
337 case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_RELEASED:
338 case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_RELEASED_HANDLED:
339 case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_MOVED:
340 case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_CANCELLED:
341 case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_SECOND_PRESSED:
342 case GST_SYNTHETIC_CLICK_ABORTED_FIRST_RELEASED:
343 case GST_SYNTHETIC_CLICK_ABORTED_SECOND_PRESSED:
344 case GST_SCROLL_FIRST_RELEASED:
345 case GST_SCROLL_FIRST_MOVED:
346 case GST_SCROLL_FIRST_MOVED_HANDLED:
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:
405 break;
406 default:
407 signature = GST_INVALID;
408 break;
411 return signature;
413 #undef G
415 float BoundingBoxDiagonal(const gfx::RectF& 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);
440 nu += x * a;
441 x *= normalized_velocity;
443 if (velocity < 0.f)
444 return std::max(nu * velocity, -GestureConfiguration::fling_velocity_cap());
445 else
446 return std::min(nu * velocity, GestureConfiguration::fling_velocity_cap());
450 void UpdateGestureEventLatencyInfo(const TouchEvent& event,
451 GestureSequence::Gestures* gestures) {
452 // Copy some of the touch event's LatencyInfo into the generated gesture's
453 // LatencyInfo so we can compute touch to scroll latency from gesture
454 // event's LatencyInfo.
455 GestureSequence::Gestures::iterator it = gestures->begin();
456 for (; it != gestures->end(); it++) {
457 ui::LatencyInfo* gesture_latency = (*it)->latency();
458 gesture_latency->CopyLatencyFrom(
459 *event.latency(), ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT);
460 gesture_latency->CopyLatencyFrom(
461 *event.latency(), ui::INPUT_EVENT_LATENCY_UI_COMPONENT);
462 gesture_latency->CopyLatencyFrom(
463 *event.latency(), ui::INPUT_EVENT_LATENCY_ACKED_TOUCH_COMPONENT);
467 bool GestureStateSupportsActiveTimer(GestureState state) {
468 switch(state) {
469 case GS_PENDING_SYNTHETIC_CLICK:
470 case GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL:
471 return true;
472 default:
473 return false;
477 } // namespace
479 ////////////////////////////////////////////////////////////////////////////////
480 // GestureSequence Public:
482 GestureSequence::GestureSequence(GestureSequenceDelegate* delegate)
483 : state_(GS_NO_GESTURE),
484 flags_(0),
485 pinch_distance_start_(0.f),
486 pinch_distance_current_(0.f),
487 scroll_type_(ST_FREE),
488 point_count_(0),
489 delegate_(delegate) {
490 CHECK(delegate_);
493 GestureSequence::~GestureSequence() {
496 GestureSequence::Gestures* GestureSequence::ProcessTouchEventForGesture(
497 const TouchEvent& event,
498 EventResult result) {
499 StopTimersIfRequired(event);
500 last_touch_location_ = event.location();
501 if (result & ER_CONSUMED)
502 return NULL;
504 // Set a limit on the number of simultaneous touches in a gesture.
505 if (event.touch_id() >= kMaxGesturePoints)
506 return NULL;
508 if (event.type() == ui::ET_TOUCH_PRESSED) {
509 if (point_count_ == kMaxGesturePoints)
510 return NULL;
511 GesturePoint* new_point = &points_[event.touch_id()];
512 // We shouldn't be able to get two PRESSED events from the same
513 // finger without either a RELEASE or CANCEL in between. But let's not crash
514 // in a release build.
515 if (new_point->in_use()) {
516 LOG(ERROR) << "Received a second press for a point: " << event.touch_id();
517 new_point->ResetVelocity();
518 new_point->UpdateValues(event);
519 return NULL;
521 new_point->set_point_id(point_count_++);
522 new_point->set_touch_id(event.touch_id());
523 new_point->set_source_device_id(event.source_device_id());
526 GestureState last_state = state_;
528 // NOTE: when modifying these state transitions, also update gestures.dot
529 scoped_ptr<Gestures> gestures(new Gestures());
530 GesturePoint& point = GesturePointForEvent(event);
531 point.UpdateValues(event);
532 RecreateBoundingBox();
533 flags_ = event.flags();
534 const int point_id = point.point_id();
535 if (point_id < 0)
536 return NULL;
538 // Send GESTURE_BEGIN for any touch pressed.
539 if (event.type() == ui::ET_TOUCH_PRESSED)
540 AppendBeginGestureEvent(point, gestures.get());
542 TouchStatusInternal status_internal = (result == ER_UNHANDLED) ?
543 TSI_NOT_PROCESSED : TSI_PROCESSED;
545 EdgeStateSignatureType signature = Signature(state_, point_id,
546 event.type(), status_internal);
548 if (signature == GST_INVALID)
549 signature = Signature(state_, point_id, event.type(), TSI_ALWAYS);
551 switch (signature) {
552 case GST_INVALID:
553 break;
555 case GST_NO_GESTURE_FIRST_PRESSED:
556 TouchDown(event, point, gestures.get());
557 set_state(GS_PENDING_SYNTHETIC_CLICK);
558 break;
559 case GST_PENDING_SYNTHETIC_CLICK_FIRST_RELEASED:
560 case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_RELEASED:
561 if (Click(event, point, gestures.get()))
562 point.UpdateForTap();
563 else
564 PrependTapCancelGestureEvent(point, gestures.get());
565 set_state(GS_NO_GESTURE);
566 break;
567 case GST_PENDING_SYNTHETIC_CLICK_FIRST_MOVED:
568 if (ScrollStart(event, point, gestures.get())) {
569 PrependTapCancelGestureEvent(point, gestures.get());
570 set_state(GS_SCROLL);
571 if (ScrollUpdate(event, point, gestures.get(), FS_FIRST_SCROLL))
572 point.UpdateForScroll();
574 break;
575 case GST_PENDING_SYNTHETIC_CLICK_FIRST_MOVED_PROCESSED:
576 case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_MOVED:
577 if (point.IsInScrollWindow(event)) {
578 PrependTapCancelGestureEvent(point, gestures.get());
579 set_state(GS_SYNTHETIC_CLICK_ABORTED);
580 } else {
581 set_state(GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL);
583 break;
584 case GST_PENDING_SYNTHETIC_CLICK_FIRST_RELEASED_HANDLED:
585 case GST_PENDING_SYNTHETIC_CLICK_FIRST_CANCELLED:
586 case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_RELEASED_HANDLED:
587 case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_CANCELLED:
588 PrependTapCancelGestureEvent(point, gestures.get());
589 set_state(GS_NO_GESTURE);
590 break;
591 case GST_SYNTHETIC_CLICK_ABORTED_FIRST_RELEASED:
592 set_state(GS_NO_GESTURE);
593 break;
594 case GST_SCROLL_FIRST_MOVED:
595 if (scroll_type_ == ST_VERTICAL ||
596 scroll_type_ == ST_HORIZONTAL)
597 BreakRailScroll(event, point, gestures.get());
598 if (ScrollUpdate(event, point, gestures.get(), FS_NOT_FIRST_SCROLL))
599 point.UpdateForScroll();
600 break;
601 case GST_SCROLL_FIRST_MOVED_HANDLED:
602 if (point.DidScroll(event, 0))
603 point.UpdateForScroll();
604 break;
605 case GST_SCROLL_FIRST_RELEASED:
606 case GST_SCROLL_FIRST_CANCELLED:
607 ScrollEnd(event, point, gestures.get());
608 set_state(GS_NO_GESTURE);
609 break;
610 case GST_PENDING_SYNTHETIC_CLICK_SECOND_PRESSED:
611 case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_SECOND_PRESSED:
612 PrependTapCancelGestureEvent(point, gestures.get());
613 TwoFingerTapOrPinch(event, point, gestures.get());
614 break;
615 case GST_SYNTHETIC_CLICK_ABORTED_SECOND_PRESSED:
616 TwoFingerTapOrPinch(event, point, gestures.get());
617 break;
618 case GST_SCROLL_SECOND_PRESSED:
619 PinchStart(event, point, gestures.get());
620 set_state(GS_PINCH);
621 break;
622 case GST_PENDING_TWO_FINGER_TAP_FIRST_RELEASED:
623 case GST_PENDING_TWO_FINGER_TAP_SECOND_RELEASED:
624 TwoFingerTouchReleased(event, point, gestures.get());
625 StartRailFreeScroll(point, gestures.get());
626 break;
627 case GST_PENDING_TWO_FINGER_TAP_FIRST_MOVED:
628 case GST_PENDING_TWO_FINGER_TAP_SECOND_MOVED:
629 if (TwoFingerTouchMove(event, point, gestures.get()))
630 set_state(GS_PINCH);
631 break;
632 case GST_PENDING_TWO_FINGER_TAP_FIRST_MOVED_HANDLED:
633 case GST_PENDING_TWO_FINGER_TAP_SECOND_MOVED_HANDLED:
634 set_state(GS_PENDING_TWO_FINGER_TAP_NO_PINCH);
635 break;
636 case GST_PENDING_TWO_FINGER_TAP_FIRST_RELEASED_HANDLED:
637 case GST_PENDING_TWO_FINGER_TAP_SECOND_RELEASED_HANDLED:
638 case GST_PENDING_TWO_FINGER_TAP_FIRST_CANCELLED:
639 case GST_PENDING_TWO_FINGER_TAP_SECOND_CANCELLED:
640 StartRailFreeScroll(point, gestures.get());
641 break;
642 case GST_PENDING_TWO_FINGER_TAP_THIRD_PRESSED:
643 set_state(GS_PENDING_PINCH);
644 break;
645 case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_MOVED:
646 case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_MOVED:
647 // No pinch allowed, so nothing happens.
648 break;
649 case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_RELEASED:
650 case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_RELEASED:
651 TwoFingerTouchReleased(event, point, gestures.get());
652 // We transition into GS_SCROLL even though the touch move can be consumed
653 // and no scroll should happen. crbug.com/240399.
654 StartRailFreeScroll(point, gestures.get());
655 break;
656 case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_RELEASED_HANDLED:
657 case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_RELEASED_HANDLED:
658 case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_CANCELLED:
659 case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_CANCELLED:
660 // We transition into GS_SCROLL even though the touch move can be consumed
661 // and no scroll should happen. crbug.com/240399.
662 StartRailFreeScroll(point, gestures.get());
663 break;
664 case GST_PENDING_PINCH_FIRST_MOVED:
665 case GST_PENDING_PINCH_SECOND_MOVED:
666 if (TwoFingerTouchMove(event, point, gestures.get()))
667 set_state(GS_PINCH);
668 break;
669 case GST_PENDING_PINCH_FIRST_MOVED_HANDLED:
670 case GST_PENDING_PINCH_SECOND_MOVED_HANDLED:
671 set_state(GS_PENDING_PINCH_NO_PINCH);
672 break;
673 case GST_PENDING_PINCH_FIRST_RELEASED:
674 case GST_PENDING_PINCH_SECOND_RELEASED:
675 case GST_PENDING_PINCH_FIRST_CANCELLED:
676 case GST_PENDING_PINCH_SECOND_CANCELLED:
677 // We transition into GS_SCROLL even though the touch move can be consumed
678 // and no scroll should happen. crbug.com/240399.
679 StartRailFreeScroll(point, gestures.get());
680 break;
681 case GST_PENDING_PINCH_NO_PINCH_FIRST_MOVED:
682 case GST_PENDING_PINCH_NO_PINCH_SECOND_MOVED:
683 // No pinch allowed, so nothing happens.
684 break;
685 case GST_PENDING_PINCH_NO_PINCH_FIRST_RELEASED:
686 case GST_PENDING_PINCH_NO_PINCH_SECOND_RELEASED:
687 case GST_PENDING_PINCH_NO_PINCH_FIRST_CANCELLED:
688 case GST_PENDING_PINCH_NO_PINCH_SECOND_CANCELLED:
689 // We transition into GS_SCROLL even though the touch move can be consumed
690 // and no scroll should happen. crbug.com/240399.
691 StartRailFreeScroll(point, gestures.get());
692 break;
693 case GST_PINCH_FIRST_MOVED_HANDLED:
694 case GST_PINCH_SECOND_MOVED_HANDLED:
695 case GST_PINCH_THIRD_MOVED_HANDLED:
696 case GST_PINCH_FOURTH_MOVED_HANDLED:
697 case GST_PINCH_FIFTH_MOVED_HANDLED:
698 // If touches are consumed for a while, and then left unconsumed, we don't
699 // want a PinchUpdate or ScrollUpdate with a massive delta.
700 latest_multi_scroll_update_location_ = bounding_box_.CenterPoint();
701 pinch_distance_current_ = BoundingBoxDiagonal(bounding_box_);
702 break;
703 case GST_PINCH_FIRST_MOVED:
704 case GST_PINCH_SECOND_MOVED:
705 case GST_PINCH_THIRD_MOVED:
706 case GST_PINCH_FOURTH_MOVED:
707 case GST_PINCH_FIFTH_MOVED:
708 if (PinchUpdate(event, point, gestures.get())) {
709 for (int i = 0; i < point_count_; ++i)
710 GetPointByPointId(i)->UpdateForScroll();
712 break;
713 case GST_PINCH_FIRST_RELEASED:
714 case GST_PINCH_SECOND_RELEASED:
715 case GST_PINCH_THIRD_RELEASED:
716 case GST_PINCH_FOURTH_RELEASED:
717 case GST_PINCH_FIFTH_RELEASED:
718 case GST_PINCH_FIRST_CANCELLED:
719 case GST_PINCH_SECOND_CANCELLED:
720 case GST_PINCH_THIRD_CANCELLED:
721 case GST_PINCH_FOURTH_CANCELLED:
722 case GST_PINCH_FIFTH_CANCELLED:
723 // Was it a swipe? i.e. were all the fingers moving in the same
724 // direction?
725 MaybeSwipe(event, point, gestures.get());
727 if (point_count_ == 2) {
728 PinchEnd(event, point, gestures.get());
730 // Once pinch ends, it should still be possible to scroll with the
731 // remaining finger on the screen.
732 set_state(GS_SCROLL);
733 } else {
734 // Nothing else to do if we have more than 2 fingers active, since after
735 // the release/cancel, there are still enough fingers to do pinch.
736 // pinch_distance_current_ and pinch_distance_start_ will be updated
737 // when the bounding-box is updated.
739 ResetVelocities();
740 break;
741 case GST_PINCH_THIRD_PRESSED:
742 case GST_PINCH_FOURTH_PRESSED:
743 case GST_PINCH_FIFTH_PRESSED:
744 pinch_distance_current_ = BoundingBoxDiagonal(bounding_box_);
745 pinch_distance_start_ = pinch_distance_current_;
746 break;
749 if (event.type() == ui::ET_TOUCH_RELEASED ||
750 event.type() == ui::ET_TOUCH_CANCELLED)
751 AppendEndGestureEvent(point, gestures.get());
753 if (state_ != last_state)
754 DVLOG(4) << "Gesture Sequence"
755 << " State: " << state_
756 << " touch id: " << event.touch_id();
758 // If the state has changed from one in which a long/show press is possible to
759 // one in which they are not possible, cancel the timers.
760 if (GestureStateSupportsActiveTimer(last_state) &&
761 !GestureStateSupportsActiveTimer(state_)) {
762 GetLongPressTimer()->Stop();
763 GetShowPressTimer()->Stop();
766 // The set of point_ids must be contiguous and include 0.
767 // When a touch point is released, all points with ids greater than the
768 // released point must have their ids decremented, or the set of point_ids
769 // could end up with gaps.
770 if (event.type() == ui::ET_TOUCH_RELEASED ||
771 event.type() == ui::ET_TOUCH_CANCELLED) {
772 for (int i = 0; i < kMaxGesturePoints; ++i) {
773 GesturePoint& iter_point = points_[i];
774 if (iter_point.point_id() > point.point_id())
775 iter_point.set_point_id(iter_point.point_id() - 1);
778 point.Reset();
779 --point_count_;
780 CHECK_GE(point_count_, 0);
781 RecreateBoundingBox();
782 if (state_ == GS_PINCH) {
783 pinch_distance_current_ = BoundingBoxDiagonal(bounding_box_);
784 pinch_distance_start_ = pinch_distance_current_;
788 UpdateGestureEventLatencyInfo(event, gestures.get());
789 return gestures.release();
792 void GestureSequence::RecreateBoundingBox() {
793 // TODO(sad): Recreating the bounding box at every touch-event is not very
794 // efficient. This should be made better.
795 if (point_count_ == 0) {
796 bounding_box_.SetRect(0, 0, 0, 0);
797 } else if (point_count_ == 1) {
798 bounding_box_ = GetPointByPointId(0)->enclosing_rectangle();
799 } else {
800 float left = std::numeric_limits<float>::max();
801 float top = std::numeric_limits<float>::max();
802 float right = -std::numeric_limits<float>::max();
803 float bottom = -std::numeric_limits<float>::max();
804 for (int i = 0; i < kMaxGesturePoints; ++i) {
805 if (!points_[i].in_use())
806 continue;
807 // Using the |enclosing_rectangle()| for the touch-points would be ideal.
808 // However, this becomes brittle especially when a finger is in motion
809 // because the change in radius can overshadow the actual change in
810 // position. So the actual position of the point is used instead.
811 const gfx::PointF& point = points_[i].last_touch_position();
812 left = std::min(left, point.x());
813 right = std::max(right, point.x());
814 top = std::min(top, point.y());
815 bottom = std::max(bottom, point.y());
817 bounding_box_.SetRect(left, top, right - left, bottom - top);
821 void GestureSequence::ResetVelocities() {
822 for (int i = 0; i < kMaxGesturePoints; ++i) {
823 if (points_[i].in_use())
824 points_[i].ResetVelocity();
828 ////////////////////////////////////////////////////////////////////////////////
829 // GestureSequence Protected:
831 base::OneShotTimer<GestureSequence>* GestureSequence::CreateTimer() {
832 return new base::OneShotTimer<GestureSequence>();
835 base::OneShotTimer<GestureSequence>* GestureSequence::GetLongPressTimer() {
836 if (!long_press_timer_.get())
837 long_press_timer_.reset(CreateTimer());
838 return long_press_timer_.get();
841 base::OneShotTimer<GestureSequence>* GestureSequence::GetShowPressTimer() {
842 if (!show_press_timer_.get())
843 show_press_timer_.reset(CreateTimer());
844 return show_press_timer_.get();
847 ////////////////////////////////////////////////////////////////////////////////
848 // GestureSequence Private:
850 GesturePoint& GestureSequence::GesturePointForEvent(
851 const TouchEvent& event) {
852 return points_[event.touch_id()];
855 GesturePoint* GestureSequence::GetPointByPointId(int point_id) {
856 DCHECK(0 <= point_id && point_id < kMaxGesturePoints);
857 for (int i = 0; i < kMaxGesturePoints; ++i) {
858 GesturePoint& point = points_[i];
859 if (point.in_use() && point.point_id() == point_id)
860 return &point;
862 NOTREACHED();
863 return NULL;
866 bool GestureSequence::IsSecondTouchDownCloseEnoughForTwoFingerTap() {
867 gfx::PointF p1 = GetPointByPointId(0)->last_touch_position();
868 gfx::PointF p2 = GetPointByPointId(1)->last_touch_position();
869 double max_distance =
870 GestureConfiguration::max_distance_for_two_finger_tap_in_pixels();
871 double distance = (p1.x() - p2.x()) * (p1.x() - p2.x()) +
872 (p1.y() - p2.y()) * (p1.y() - p2.y());
873 if (distance < max_distance * max_distance)
874 return true;
875 return false;
878 GestureEvent* GestureSequence::CreateGestureEvent(
879 const GestureEventDetails& details,
880 const gfx::PointF& location,
881 int flags,
882 base::Time timestamp,
883 unsigned int touch_id_bitmask) {
884 GestureEventDetails gesture_details(details);
885 gesture_details.set_touch_points(point_count_);
886 gesture_details.set_bounding_box(bounding_box_);
887 base::TimeDelta time_stamp =
888 base::TimeDelta::FromMicroseconds(timestamp.ToDoubleT() * 1000000);
889 return new GestureEvent(gesture_details.type(), location.x(), location.y(),
890 flags, time_stamp, gesture_details,
891 touch_id_bitmask);
894 void GestureSequence::AppendTapDownGestureEvent(const GesturePoint& point,
895 Gestures* gestures) {
896 gestures->push_back(CreateGestureEvent(
897 GestureEventDetails(ui::ET_GESTURE_TAP_DOWN, 0, 0),
898 point.first_touch_position(),
899 flags_,
900 base::Time::FromDoubleT(point.last_touch_time()),
901 1 << point.touch_id()));
904 void GestureSequence::PrependTapCancelGestureEvent(const GesturePoint& point,
905 Gestures* gestures) {
906 gestures->insert(gestures->begin(), CreateGestureEvent(
907 GestureEventDetails(ui::ET_GESTURE_TAP_CANCEL, 0, 0),
908 point.first_touch_position(),
909 flags_,
910 base::Time::FromDoubleT(point.last_touch_time()),
911 1 << point.touch_id()));
914 void GestureSequence::AppendBeginGestureEvent(const GesturePoint& point,
915 Gestures* gestures) {
916 gestures->push_back(CreateGestureEvent(
917 GestureEventDetails(ui::ET_GESTURE_BEGIN, 0, 0),
918 point.first_touch_position(),
919 flags_,
920 base::Time::FromDoubleT(point.last_touch_time()),
921 1 << point.touch_id()));
924 void GestureSequence::AppendEndGestureEvent(const GesturePoint& point,
925 Gestures* gestures) {
926 gestures->push_back(CreateGestureEvent(
927 GestureEventDetails(ui::ET_GESTURE_END, 0, 0),
928 point.last_touch_position(),
929 flags_,
930 base::Time::FromDoubleT(point.last_touch_time()),
931 1 << point.touch_id()));
934 void GestureSequence::AppendClickGestureEvent(const GesturePoint& point,
935 int tap_count,
936 Gestures* gestures) {
937 gfx::RectF er = point.enclosing_rectangle();
938 gfx::PointF center = er.CenterPoint();
939 gestures->push_back(CreateGestureEvent(
940 GestureEventDetails(ui::ET_GESTURE_TAP, tap_count, 0),
941 center,
942 flags_,
943 base::Time::FromDoubleT(point.last_touch_time()),
944 1 << point.touch_id()));
947 void GestureSequence::AppendScrollGestureBegin(const GesturePoint& point,
948 const gfx::PointF& location,
949 Gestures* gestures) {
950 gfx::Vector2dF d = point.ScrollDelta();
951 gestures->push_back(CreateGestureEvent(
952 GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN, d.x(), d.y()),
953 location,
954 flags_,
955 base::Time::FromDoubleT(point.last_touch_time()),
956 1 << point.touch_id()));
959 void GestureSequence::AppendScrollGestureEnd(const GesturePoint& point,
960 const gfx::PointF& location,
961 Gestures* gestures,
962 float x_velocity,
963 float y_velocity) {
964 float railed_x_velocity = x_velocity;
965 float railed_y_velocity = y_velocity;
966 last_scroll_prediction_offset_.set_x(0);
967 last_scroll_prediction_offset_.set_y(0);
969 if (scroll_type_ == ST_HORIZONTAL)
970 railed_y_velocity = 0;
971 else if (scroll_type_ == ST_VERTICAL)
972 railed_x_velocity = 0;
974 if (railed_x_velocity != 0 || railed_y_velocity != 0) {
976 gestures->push_back(CreateGestureEvent(
977 GestureEventDetails(ui::ET_SCROLL_FLING_START,
978 CalibrateFlingVelocity(railed_x_velocity),
979 CalibrateFlingVelocity(railed_y_velocity)),
980 location,
981 flags_,
982 base::Time::FromDoubleT(point.last_touch_time()),
983 1 << point.touch_id()));
984 } else {
985 gestures->push_back(CreateGestureEvent(
986 GestureEventDetails(ui::ET_GESTURE_SCROLL_END, 0, 0),
987 location,
988 flags_,
989 base::Time::FromDoubleT(point.last_touch_time()),
990 1 << point.touch_id()));
994 void GestureSequence::AppendScrollGestureUpdate(GesturePoint& point,
995 Gestures* gestures,
996 IsFirstScroll is_first_scroll) {
997 static bool use_scroll_prediction = CommandLine::ForCurrentProcess()->
998 HasSwitch(switches::kEnableScrollPrediction);
999 gfx::Vector2dF d;
1000 gfx::PointF location;
1001 if (point_count_ == 1) {
1002 d = point.ScrollDelta();
1003 location = point.last_touch_position();
1004 } else {
1005 location = bounding_box_.CenterPoint();
1006 d = location - latest_multi_scroll_update_location_;
1007 latest_multi_scroll_update_location_ = location;
1010 if (use_scroll_prediction) {
1011 // Remove the extra distance added by the last scroll prediction and add
1012 // the new prediction offset.
1013 d -= last_scroll_prediction_offset_;
1014 last_scroll_prediction_offset_.set_x(
1015 GestureConfiguration::scroll_prediction_seconds() * point.XVelocity());
1016 last_scroll_prediction_offset_.set_y(
1017 GestureConfiguration::scroll_prediction_seconds() * point.YVelocity());
1018 d += last_scroll_prediction_offset_;
1019 location += gfx::Vector2dF(last_scroll_prediction_offset_.x(),
1020 last_scroll_prediction_offset_.y());
1023 if (is_first_scroll == FS_FIRST_SCROLL) {
1024 float slop = GestureConfiguration::max_touch_move_in_pixels_for_click();
1025 float length = d.Length();
1026 float ratio = std::max((length - slop) / length, 0.0f);
1028 d.set_x(d.x() * ratio);
1029 d.set_y(d.y() * ratio);
1032 if (scroll_type_ == ST_HORIZONTAL)
1033 d.set_y(0);
1034 else if (scroll_type_ == ST_VERTICAL)
1035 d.set_x(0);
1036 if (d.IsZero())
1037 return;
1039 GestureEventDetails details(ui::ET_GESTURE_SCROLL_UPDATE, d.x(), d.y());
1040 gestures->push_back(CreateGestureEvent(
1041 details,
1042 location,
1043 flags_,
1044 base::Time::FromDoubleT(point.last_touch_time()),
1045 ComputeTouchBitmask(points_)));
1048 void GestureSequence::AppendPinchGestureBegin(const GesturePoint& p1,
1049 const GesturePoint& p2,
1050 Gestures* gestures) {
1051 gfx::PointF center = bounding_box_.CenterPoint();
1052 gestures->push_back(CreateGestureEvent(
1053 GestureEventDetails(ui::ET_GESTURE_PINCH_BEGIN, 0, 0),
1054 center,
1055 flags_,
1056 base::Time::FromDoubleT(p1.last_touch_time()),
1057 1 << p1.touch_id() | 1 << p2.touch_id()));
1060 void GestureSequence::AppendPinchGestureEnd(const GesturePoint& p1,
1061 const GesturePoint& p2,
1062 float scale,
1063 Gestures* gestures) {
1064 gfx::PointF center = bounding_box_.CenterPoint();
1065 gestures->push_back(CreateGestureEvent(
1066 GestureEventDetails(ui::ET_GESTURE_PINCH_END, 0, 0),
1067 center,
1068 flags_,
1069 base::Time::FromDoubleT(p1.last_touch_time()),
1070 1 << p1.touch_id() | 1 << p2.touch_id()));
1073 void GestureSequence::AppendPinchGestureUpdate(const GesturePoint& point,
1074 float scale,
1075 Gestures* gestures) {
1076 // TODO(sad): Compute rotation and include it in delta_y.
1077 // http://crbug.com/113145
1078 gestures->push_back(CreateGestureEvent(
1079 GestureEventDetails(ui::ET_GESTURE_PINCH_UPDATE, scale, 0),
1080 bounding_box_.CenterPoint(),
1081 flags_,
1082 base::Time::FromDoubleT(point.last_touch_time()),
1083 ComputeTouchBitmask(points_)));
1086 void GestureSequence::AppendSwipeGesture(const GesturePoint& point,
1087 int swipe_x,
1088 int swipe_y,
1089 Gestures* gestures) {
1090 gestures->push_back(CreateGestureEvent(
1091 GestureEventDetails(ui::ET_GESTURE_SWIPE, swipe_x, swipe_y),
1092 bounding_box_.CenterPoint(),
1093 flags_,
1094 base::Time::FromDoubleT(point.last_touch_time()),
1095 ComputeTouchBitmask(points_)));
1098 void GestureSequence::AppendTwoFingerTapGestureEvent(Gestures* gestures) {
1099 const GesturePoint* point = GetPointByPointId(0);
1100 const gfx::RectF& rect = point->enclosing_rectangle();
1101 gestures->push_back(CreateGestureEvent(
1102 GestureEventDetails(ui::ET_GESTURE_TWO_FINGER_TAP,
1103 rect.width(),
1104 rect.height()),
1105 point->enclosing_rectangle().CenterPoint(),
1106 flags_,
1107 base::Time::FromDoubleT(point->last_touch_time()),
1108 1 << point->touch_id()));
1111 bool GestureSequence::Click(const TouchEvent& event,
1112 const GesturePoint& point,
1113 Gestures* gestures) {
1114 DCHECK(state_ == GS_PENDING_SYNTHETIC_CLICK ||
1115 state_ == GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL);
1116 if (point.IsInClickWindow(event)) {
1117 int tap_count = 1;
1118 if (point.IsInTripleClickWindow(event))
1119 tap_count = 3;
1120 else if (point.IsInDoubleClickWindow(event))
1121 tap_count = 2;
1122 if (tap_count == 1 && GetShowPressTimer()->IsRunning()) {
1123 GetShowPressTimer()->Stop();
1124 AppendShowPressGestureEvent();
1126 AppendClickGestureEvent(point, tap_count, gestures);
1127 return true;
1128 } else if (point.IsInsideTouchSlopRegion(event) &&
1129 !GetLongPressTimer()->IsRunning()) {
1130 AppendLongTapGestureEvent(point, gestures);
1132 return false;
1135 bool GestureSequence::ScrollStart(const TouchEvent& event,
1136 GesturePoint& point,
1137 Gestures* gestures) {
1138 DCHECK(state_ == GS_PENDING_SYNTHETIC_CLICK);
1139 if (!point.IsInScrollWindow(event))
1140 return false;
1141 AppendScrollGestureBegin(point, point.first_touch_position(), gestures);
1142 if (point.IsInHorizontalRailWindow())
1143 scroll_type_ = ST_HORIZONTAL;
1144 else if (point.IsInVerticalRailWindow())
1145 scroll_type_ = ST_VERTICAL;
1146 else
1147 scroll_type_ = ST_FREE;
1148 return true;
1151 void GestureSequence::BreakRailScroll(const TouchEvent& event,
1152 GesturePoint& point,
1153 Gestures* gestures) {
1154 DCHECK(state_ == GS_SCROLL);
1155 if (scroll_type_ == ST_HORIZONTAL &&
1156 point.BreaksHorizontalRail())
1157 scroll_type_ = ST_FREE;
1158 else if (scroll_type_ == ST_VERTICAL &&
1159 point.BreaksVerticalRail())
1160 scroll_type_ = ST_FREE;
1163 bool GestureSequence::ScrollUpdate(const TouchEvent& event,
1164 GesturePoint& point,
1165 Gestures* gestures,
1166 IsFirstScroll is_first_scroll) {
1167 DCHECK(state_ == GS_SCROLL);
1168 if (!point.DidScroll(event, 0))
1169 return false;
1170 AppendScrollGestureUpdate(point, gestures, is_first_scroll);
1171 return true;
1174 bool GestureSequence::TouchDown(const TouchEvent& event,
1175 const GesturePoint& point,
1176 Gestures* gestures) {
1177 DCHECK(state_ == GS_NO_GESTURE);
1178 AppendTapDownGestureEvent(point, gestures);
1179 GetLongPressTimer()->Start(
1180 FROM_HERE,
1181 base::TimeDelta::FromMilliseconds(
1182 GestureConfiguration::long_press_time_in_seconds() * 1000),
1183 this,
1184 &GestureSequence::AppendLongPressGestureEvent);
1186 GetShowPressTimer()->Start(
1187 FROM_HERE,
1188 base::TimeDelta::FromMilliseconds(
1189 GestureConfiguration::show_press_delay_in_ms()),
1190 this,
1191 &GestureSequence::AppendShowPressGestureEvent);
1193 return true;
1196 bool GestureSequence::TwoFingerTouchDown(const TouchEvent& event,
1197 const GesturePoint& point,
1198 Gestures* gestures) {
1199 DCHECK(state_ == GS_PENDING_SYNTHETIC_CLICK ||
1200 state_ == GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL ||
1201 state_ == GS_SYNTHETIC_CLICK_ABORTED ||
1202 state_ == GS_SCROLL);
1204 if (state_ == GS_SCROLL) {
1205 AppendScrollGestureEnd(point,
1206 point.last_touch_position(),
1207 gestures, 0.f, 0.f);
1209 second_touch_time_ = event.time_stamp();
1210 return true;
1213 bool GestureSequence::TwoFingerTouchMove(const TouchEvent& event,
1214 const GesturePoint& point,
1215 Gestures* gestures) {
1216 DCHECK(state_ == GS_PENDING_TWO_FINGER_TAP ||
1217 state_ == GS_PENDING_PINCH);
1219 base::TimeDelta time_delta = event.time_stamp() - second_touch_time_;
1220 base::TimeDelta max_delta = base::TimeDelta::FromMilliseconds(1000 *
1221 ui::GestureConfiguration::max_touch_down_duration_in_seconds_for_click());
1222 if (time_delta > max_delta || !point.IsInsideTouchSlopRegion(event)) {
1223 PinchStart(event, point, gestures);
1224 return true;
1226 return false;
1229 bool GestureSequence::TwoFingerTouchReleased(const TouchEvent& event,
1230 const GesturePoint& point,
1231 Gestures* gestures) {
1232 DCHECK(state_ == GS_PENDING_TWO_FINGER_TAP ||
1233 state_ == GS_PENDING_TWO_FINGER_TAP_NO_PINCH);
1234 base::TimeDelta time_delta = event.time_stamp() - second_touch_time_;
1235 base::TimeDelta max_delta = base::TimeDelta::FromMilliseconds(1000 *
1236 ui::GestureConfiguration::max_touch_down_duration_in_seconds_for_click());
1237 if (time_delta < max_delta && point.IsInsideTouchSlopRegion(event))
1238 AppendTwoFingerTapGestureEvent(gestures);
1239 return true;
1242 void GestureSequence::AppendLongPressGestureEvent() {
1243 const GesturePoint* point = GetPointByPointId(0);
1244 scoped_ptr<GestureEvent> gesture(CreateGestureEvent(
1245 GestureEventDetails(ui::ET_GESTURE_LONG_PRESS, 0, 0),
1246 point->first_touch_position(),
1247 flags_,
1248 base::Time::FromDoubleT(point->last_touch_time()),
1249 1 << point->touch_id()));
1250 delegate_->DispatchPostponedGestureEvent(gesture.get());
1253 void GestureSequence::AppendShowPressGestureEvent() {
1254 const GesturePoint* point = GetPointByPointId(0);
1255 scoped_ptr<GestureEvent> gesture(CreateGestureEvent(
1256 GestureEventDetails(ui::ET_GESTURE_SHOW_PRESS, 0, 0),
1257 point->first_touch_position(),
1258 flags_,
1259 base::Time::FromDoubleT(point->last_touch_time()),
1260 1 << point->touch_id()));
1261 delegate_->DispatchPostponedGestureEvent(gesture.get());
1264 void GestureSequence::AppendLongTapGestureEvent(const GesturePoint& point,
1265 Gestures* gestures) {
1266 gestures->push_back(CreateGestureEvent(
1267 GestureEventDetails(ui::ET_GESTURE_LONG_TAP, 0, 0),
1268 point.enclosing_rectangle().CenterPoint(),
1269 flags_,
1270 base::Time::FromDoubleT(point.last_touch_time()),
1271 1 << point.touch_id()));
1274 bool GestureSequence::ScrollEnd(const TouchEvent& event,
1275 GesturePoint& point,
1276 Gestures* gestures) {
1277 DCHECK(state_ == GS_SCROLL);
1278 if (point.IsInFlickWindow(event)) {
1279 AppendScrollGestureEnd(point,
1280 point.last_touch_position(),
1281 gestures,
1282 point.XVelocity(), point.YVelocity());
1283 } else {
1284 AppendScrollGestureEnd(point,
1285 point.last_touch_position(),
1286 gestures, 0.f, 0.f);
1288 return true;
1291 bool GestureSequence::PinchStart(const TouchEvent& event,
1292 const GesturePoint& point,
1293 Gestures* gestures) {
1294 DCHECK(state_ == GS_SCROLL ||
1295 state_ == GS_PENDING_TWO_FINGER_TAP ||
1296 state_ == GS_PENDING_PINCH);
1298 // Once pinch starts, we immediately break rail scroll.
1299 scroll_type_ = ST_FREE;
1301 const GesturePoint* point1 = GetPointByPointId(0);
1302 const GesturePoint* point2 = GetPointByPointId(1);
1304 if (state_ == GS_PENDING_TWO_FINGER_TAP ||
1305 state_ == GS_PENDING_PINCH) {
1306 AppendScrollGestureBegin(point, bounding_box_.CenterPoint(), gestures);
1309 pinch_distance_current_ = BoundingBoxDiagonal(bounding_box_);
1310 pinch_distance_start_ = pinch_distance_current_;
1311 latest_multi_scroll_update_location_ = bounding_box_.CenterPoint();
1312 AppendPinchGestureBegin(*point1, *point2, gestures);
1314 return true;
1317 bool GestureSequence::PinchUpdate(const TouchEvent& event,
1318 GesturePoint& point,
1319 Gestures* gestures) {
1320 DCHECK(state_ == GS_PINCH);
1322 // It is possible that the none of the touch-points changed their position,
1323 // but their radii changed, and that caused the bounding box to also change.
1324 // But in such cases, we do not want to either pinch or scroll.
1325 // To avoid small jiggles, it is also necessary to make sure that at least one
1326 // of the fingers moved enough before a pinch or scroll update is created.
1327 bool did_scroll = false;
1328 for (int i = 0; i < kMaxGesturePoints; ++i) {
1329 if (!points_[i].in_use() || !points_[i].DidScroll(event, 0))
1330 continue;
1331 did_scroll = true;
1332 break;
1335 if (!did_scroll)
1336 return false;
1338 float distance = BoundingBoxDiagonal(bounding_box_);
1340 if (std::abs(distance - pinch_distance_current_) >=
1341 GestureConfiguration::min_pinch_update_distance_in_pixels()) {
1342 AppendPinchGestureUpdate(point,
1343 distance / pinch_distance_current_, gestures);
1344 pinch_distance_current_ = distance;
1346 AppendScrollGestureUpdate(point, gestures, FS_NOT_FIRST_SCROLL);
1348 return true;
1351 bool GestureSequence::PinchEnd(const TouchEvent& event,
1352 const GesturePoint& point,
1353 Gestures* gestures) {
1354 DCHECK(state_ == GS_PINCH);
1356 GesturePoint* point1 = GetPointByPointId(0);
1357 GesturePoint* point2 = GetPointByPointId(1);
1359 float distance = BoundingBoxDiagonal(bounding_box_);
1360 AppendPinchGestureEnd(*point1, *point2,
1361 distance / pinch_distance_start_, gestures);
1363 pinch_distance_start_ = 0;
1364 pinch_distance_current_ = 0;
1365 return true;
1368 bool GestureSequence::MaybeSwipe(const TouchEvent& event,
1369 const GesturePoint& point,
1370 Gestures* gestures) {
1371 DCHECK(state_ == GS_PINCH);
1372 float velocity_x = 0.f, velocity_y = 0.f;
1373 bool swipe_x = true, swipe_y = true;
1374 int sign_x = 0, sign_y = 0;
1375 int i = 0;
1377 for (i = 0; i < kMaxGesturePoints; ++i) {
1378 if (points_[i].in_use())
1379 break;
1381 DCHECK(i < kMaxGesturePoints);
1383 velocity_x = points_[i].XVelocity();
1384 velocity_y = points_[i].YVelocity();
1385 sign_x = velocity_x < 0.f ? -1 : 1;
1386 sign_y = velocity_y < 0.f ? -1 : 1;
1388 for (++i; i < kMaxGesturePoints; ++i) {
1389 if (!points_[i].in_use())
1390 continue;
1392 if (sign_x * points_[i].XVelocity() < 0)
1393 swipe_x = false;
1395 if (sign_y * points_[i].YVelocity() < 0)
1396 swipe_y = false;
1398 velocity_x += points_[i].XVelocity();
1399 velocity_y += points_[i].YVelocity();
1402 float min_velocity = GestureConfiguration::min_swipe_speed();
1404 velocity_x = fabs(velocity_x / point_count_);
1405 velocity_y = fabs(velocity_y / point_count_);
1406 if (velocity_x < min_velocity)
1407 swipe_x = false;
1408 if (velocity_y < min_velocity)
1409 swipe_y = false;
1411 if (!swipe_x && !swipe_y)
1412 return false;
1414 if (!swipe_x)
1415 velocity_x = 0.001f;
1416 if (!swipe_y)
1417 velocity_y = 0.001f;
1419 float ratio = velocity_x > velocity_y ? velocity_x / velocity_y :
1420 velocity_y / velocity_x;
1421 if (ratio < GestureConfiguration::max_swipe_deviation_ratio())
1422 return false;
1424 if (velocity_x > velocity_y)
1425 sign_y = 0;
1426 else
1427 sign_x = 0;
1429 AppendSwipeGesture(point, sign_x, sign_y, gestures);
1431 return true;
1434 void GestureSequence::TwoFingerTapOrPinch(const TouchEvent& event,
1435 const GesturePoint& point,
1436 Gestures* gestures) {
1437 if (IsSecondTouchDownCloseEnoughForTwoFingerTap()) {
1438 TwoFingerTouchDown(event, point, gestures);
1439 set_state(GS_PENDING_TWO_FINGER_TAP);
1440 } else {
1441 set_state(GS_PENDING_PINCH);
1446 void GestureSequence::StopTimersIfRequired(const TouchEvent& event) {
1447 if ((!GetLongPressTimer()->IsRunning() &&
1448 !GetShowPressTimer()->IsRunning()) ||
1449 event.type() != ui::ET_TOUCH_MOVED)
1450 return;
1452 // Since a timer is running, there should be a non-NULL point.
1453 const GesturePoint* point = GetPointByPointId(0);
1454 if (!point->IsInsideTouchSlopRegion(event)) {
1455 GetLongPressTimer()->Stop();
1456 GetShowPressTimer()->Stop();
1460 void GestureSequence::StartRailFreeScroll(const GesturePoint& point,
1461 Gestures* gestures) {
1462 AppendScrollGestureBegin(point, point.first_touch_position(), gestures);
1463 scroll_type_ = ST_FREE;
1464 set_state(GS_SCROLL);
1467 } // namespace ui