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"
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"
26 // ui::EventType is mapped to TouchState so it can fit into 3 bits of
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
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
53 // Get equivalent TouchState from EventType |type|.
54 TouchState
TouchEventTypeToTouchState(ui::EventType type
) {
56 case ui::ET_TOUCH_RELEASED
:
58 case ui::ET_TOUCH_PRESSED
:
60 case ui::ET_TOUCH_MOVED
:
62 case ui::ET_TOUCH_CANCELLED
:
65 DVLOG(1) << "Unknown Touch Event type";
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
{
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
,
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
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
,
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
));
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
:
407 signature
= GST_INVALID
;
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
);
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 // 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
) {
469 case GS_PENDING_SYNTHETIC_CLICK
:
470 case GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL
:
479 ////////////////////////////////////////////////////////////////////////////////
480 // GestureSequence Public:
482 GestureSequence::GestureSequence(GestureSequenceDelegate
* delegate
)
483 : state_(GS_NO_GESTURE
),
485 pinch_distance_start_(0.f
),
486 pinch_distance_current_(0.f
),
487 scroll_type_(ST_FREE
),
489 delegate_(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
)
504 // Set a limit on the number of simultaneous touches in a gesture.
505 if (event
.touch_id() >= kMaxGesturePoints
)
508 if (event
.type() == ui::ET_TOUCH_PRESSED
) {
509 if (point_count_
== kMaxGesturePoints
)
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
);
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();
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
);
555 case GST_NO_GESTURE_FIRST_PRESSED
:
556 TouchDown(event
, point
, gestures
.get());
557 set_state(GS_PENDING_SYNTHETIC_CLICK
);
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();
564 PrependTapCancelGestureEvent(point
, gestures
.get());
565 set_state(GS_NO_GESTURE
);
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();
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
);
581 set_state(GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL
);
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
);
591 case GST_SYNTHETIC_CLICK_ABORTED_FIRST_RELEASED
:
592 set_state(GS_NO_GESTURE
);
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();
601 case GST_SCROLL_FIRST_MOVED_HANDLED
:
602 if (point
.DidScroll(event
, 0))
603 point
.UpdateForScroll();
605 case GST_SCROLL_FIRST_RELEASED
:
606 case GST_SCROLL_FIRST_CANCELLED
:
607 ScrollEnd(event
, point
, gestures
.get());
608 set_state(GS_NO_GESTURE
);
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());
615 case GST_SYNTHETIC_CLICK_ABORTED_SECOND_PRESSED
:
616 TwoFingerTapOrPinch(event
, point
, gestures
.get());
618 case GST_SCROLL_SECOND_PRESSED
:
619 PinchStart(event
, point
, gestures
.get());
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());
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()))
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
);
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());
642 case GST_PENDING_TWO_FINGER_TAP_THIRD_PRESSED
:
643 set_state(GS_PENDING_PINCH
);
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.
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());
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());
664 case GST_PENDING_PINCH_FIRST_MOVED
:
665 case GST_PENDING_PINCH_SECOND_MOVED
:
666 if (TwoFingerTouchMove(event
, point
, gestures
.get()))
669 case GST_PENDING_PINCH_FIRST_MOVED_HANDLED
:
670 case GST_PENDING_PINCH_SECOND_MOVED_HANDLED
:
671 set_state(GS_PENDING_PINCH_NO_PINCH
);
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());
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.
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());
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_
);
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();
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
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
);
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.
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_
;
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);
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();
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())
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
)
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
)
878 GestureEvent
* GestureSequence::CreateGestureEvent(
879 const GestureEventDetails
& details
,
880 const gfx::PointF
& location
,
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
,
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(),
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(),
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(),
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(),
930 base::Time::FromDoubleT(point
.last_touch_time()),
931 1 << point
.touch_id()));
934 void GestureSequence::AppendClickGestureEvent(const GesturePoint
& point
,
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),
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()),
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
,
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
)),
982 base::Time::FromDoubleT(point
.last_touch_time()),
983 1 << point
.touch_id()));
985 gestures
->push_back(CreateGestureEvent(
986 GestureEventDetails(ui::ET_GESTURE_SCROLL_END
, 0, 0),
989 base::Time::FromDoubleT(point
.last_touch_time()),
990 1 << point
.touch_id()));
994 void GestureSequence::AppendScrollGestureUpdate(GesturePoint
& point
,
996 IsFirstScroll is_first_scroll
) {
997 static bool use_scroll_prediction
= CommandLine::ForCurrentProcess()->
998 HasSwitch(switches::kEnableScrollPrediction
);
1000 gfx::PointF location
;
1001 if (point_count_
== 1) {
1002 d
= point
.ScrollDelta();
1003 location
= point
.last_touch_position();
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
)
1034 else if (scroll_type_
== ST_VERTICAL
)
1039 GestureEventDetails
details(ui::ET_GESTURE_SCROLL_UPDATE
, d
.x(), d
.y());
1040 gestures
->push_back(CreateGestureEvent(
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),
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
,
1063 Gestures
* gestures
) {
1064 gfx::PointF center
= bounding_box_
.CenterPoint();
1065 gestures
->push_back(CreateGestureEvent(
1066 GestureEventDetails(ui::ET_GESTURE_PINCH_END
, 0, 0),
1069 base::Time::FromDoubleT(p1
.last_touch_time()),
1070 1 << p1
.touch_id() | 1 << p2
.touch_id()));
1073 void GestureSequence::AppendPinchGestureUpdate(const GesturePoint
& point
,
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(),
1082 base::Time::FromDoubleT(point
.last_touch_time()),
1083 ComputeTouchBitmask(points_
)));
1086 void GestureSequence::AppendSwipeGesture(const GesturePoint
& point
,
1089 Gestures
* gestures
) {
1090 gestures
->push_back(CreateGestureEvent(
1091 GestureEventDetails(ui::ET_GESTURE_SWIPE
, swipe_x
, swipe_y
),
1092 bounding_box_
.CenterPoint(),
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
,
1105 point
->enclosing_rectangle().CenterPoint(),
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
)) {
1118 if (point
.IsInTripleClickWindow(event
))
1120 else if (point
.IsInDoubleClickWindow(event
))
1122 if (tap_count
== 1 && GetShowPressTimer()->IsRunning()) {
1123 GetShowPressTimer()->Stop();
1124 AppendShowPressGestureEvent();
1126 AppendClickGestureEvent(point
, tap_count
, gestures
);
1128 } else if (point
.IsInsideTouchSlopRegion(event
) &&
1129 !GetLongPressTimer()->IsRunning()) {
1130 AppendLongTapGestureEvent(point
, gestures
);
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
))
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
;
1147 scroll_type_
= ST_FREE
;
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
,
1166 IsFirstScroll is_first_scroll
) {
1167 DCHECK(state_
== GS_SCROLL
);
1168 if (!point
.DidScroll(event
, 0))
1170 AppendScrollGestureUpdate(point
, gestures
, is_first_scroll
);
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(
1181 base::TimeDelta::FromMilliseconds(
1182 GestureConfiguration::long_press_time_in_seconds() * 1000),
1184 &GestureSequence::AppendLongPressGestureEvent
);
1186 GetShowPressTimer()->Start(
1188 base::TimeDelta::FromMilliseconds(
1189 GestureConfiguration::show_press_delay_in_ms()),
1191 &GestureSequence::AppendShowPressGestureEvent
);
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();
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
);
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
);
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(),
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(),
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(),
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(),
1282 point
.XVelocity(), point
.YVelocity());
1284 AppendScrollGestureEnd(point
,
1285 point
.last_touch_position(),
1286 gestures
, 0.f
, 0.f
);
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
);
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))
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
);
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;
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;
1377 for (i
= 0; i
< kMaxGesturePoints
; ++i
) {
1378 if (points_
[i
].in_use())
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())
1392 if (sign_x
* points_
[i
].XVelocity() < 0)
1395 if (sign_y
* points_
[i
].YVelocity() < 0)
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
)
1408 if (velocity_y
< min_velocity
)
1411 if (!swipe_x
&& !swipe_y
)
1415 velocity_x
= 0.001f
;
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())
1424 if (velocity_x
> velocity_y
)
1429 AppendSwipeGesture(point
, sign_x
, sign_y
, gestures
);
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
);
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
)
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
);