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. 4 5 #include "ui/events/gestures/gesture_sequence.h" 6 7 #include <stdlib.h> 8 #include <cmath> 9 #include <limits> 10 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" 21 22 namespace ui { 23 24 namespace { 25 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, 34 }; 35 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. 42 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. 48 49 TSI_ALWAYS // The touch-event should always affect gesture 50 // recognition. 51 }; 52 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"; 66 } 67 return TS_UNKNOWN; 68 } 69 70 // Gesture signature types for different values of combination (GestureState, 71 // touch_id, ui::EventType, touch_handled), see Signature for more info. 72 // 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)) 79 80 enum EdgeStateSignatureType { 81 GST_INVALID = -1, 82 83 GST_NO_GESTURE_FIRST_PRESSED = 84 G(GS_NO_GESTURE, 0, TS_PRESSED, TSI_NOT_PROCESSED), 85 86 GST_PENDING_SYNTHETIC_CLICK_FIRST_RELEASED = 87 G(GS_PENDING_SYNTHETIC_CLICK, 0, TS_RELEASED, TSI_NOT_PROCESSED), 88 89 GST_PENDING_SYNTHETIC_CLICK_FIRST_RELEASED_HANDLED = 90 G(GS_PENDING_SYNTHETIC_CLICK, 0, TS_RELEASED, TSI_PROCESSED), 91 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), 95 96 GST_PENDING_SYNTHETIC_CLICK_FIRST_MOVED_PROCESSED = 97 G(GS_PENDING_SYNTHETIC_CLICK, 0, TS_MOVED, TSI_PROCESSED), 98 99 GST_PENDING_SYNTHETIC_CLICK_FIRST_CANCELLED = 100 G(GS_PENDING_SYNTHETIC_CLICK, 0, TS_CANCELLED, TSI_ALWAYS), 101 102 GST_PENDING_SYNTHETIC_CLICK_SECOND_PRESSED = 103 G(GS_PENDING_SYNTHETIC_CLICK, 1, TS_PRESSED, TSI_NOT_PROCESSED), 104 105 GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_RELEASED = 106 G(GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL, 107 0, 108 TS_RELEASED, 109 TSI_NOT_PROCESSED), 110 111 GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_RELEASED_HANDLED = 112 G(GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL, 0, TS_RELEASED, TSI_PROCESSED), 113 114 GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_MOVED = 115 G(GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL, 0, TS_MOVED, TSI_ALWAYS), 116 117 GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_CANCELLED = 118 G(GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL, 0, TS_CANCELLED, TSI_ALWAYS), 119 120 GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_SECOND_PRESSED = 121 G(GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL, 1, TS_PRESSED, TSI_NOT_PROCESSED), 122 123 GST_SYNTHETIC_CLICK_ABORTED_FIRST_RELEASED = 124 G(GS_SYNTHETIC_CLICK_ABORTED, 0, TS_RELEASED, TSI_ALWAYS), 125 126 GST_SYNTHETIC_CLICK_ABORTED_SECOND_PRESSED = 127 G(GS_SYNTHETIC_CLICK_ABORTED, 1, TS_PRESSED, TSI_NOT_PROCESSED), 128 129 GST_SCROLL_FIRST_RELEASED = 130 G(GS_SCROLL, 0, TS_RELEASED, TSI_ALWAYS), 131 132 GST_SCROLL_FIRST_MOVED = 133 G(GS_SCROLL, 0, TS_MOVED, TSI_NOT_PROCESSED), 134 135 GST_SCROLL_FIRST_MOVED_HANDLED = 136 G(GS_SCROLL, 0, TS_MOVED, TSI_PROCESSED), 137 138 GST_SCROLL_FIRST_CANCELLED = 139 G(GS_SCROLL, 0, TS_CANCELLED, TSI_ALWAYS), 140 141 GST_SCROLL_SECOND_PRESSED = 142 G(GS_SCROLL, 1, TS_PRESSED, TSI_NOT_PROCESSED), 143 144 GST_PENDING_TWO_FINGER_TAP_FIRST_RELEASED = 145 G(GS_PENDING_TWO_FINGER_TAP, 0, TS_RELEASED, TSI_NOT_PROCESSED), 146 147 GST_PENDING_TWO_FINGER_TAP_FIRST_RELEASED_HANDLED = 148 G(GS_PENDING_TWO_FINGER_TAP, 0, TS_RELEASED, TSI_PROCESSED), 149 150 GST_PENDING_TWO_FINGER_TAP_SECOND_RELEASED = 151 G(GS_PENDING_TWO_FINGER_TAP, 1, TS_RELEASED, TSI_NOT_PROCESSED), 152 153 GST_PENDING_TWO_FINGER_TAP_SECOND_RELEASED_HANDLED = 154 G(GS_PENDING_TWO_FINGER_TAP, 1, TS_RELEASED, TSI_PROCESSED), 155 156 GST_PENDING_TWO_FINGER_TAP_FIRST_MOVED = 157 G(GS_PENDING_TWO_FINGER_TAP, 0, TS_MOVED, TSI_NOT_PROCESSED), 158 159 GST_PENDING_TWO_FINGER_TAP_SECOND_MOVED = 160 G(GS_PENDING_TWO_FINGER_TAP, 1, TS_MOVED, TSI_NOT_PROCESSED), 161 162 GST_PENDING_TWO_FINGER_TAP_FIRST_MOVED_HANDLED = 163 G(GS_PENDING_TWO_FINGER_TAP, 0, TS_MOVED, TSI_PROCESSED), 164 165 GST_PENDING_TWO_FINGER_TAP_SECOND_MOVED_HANDLED = 166 G(GS_PENDING_TWO_FINGER_TAP, 1, TS_MOVED, TSI_PROCESSED), 167 168 GST_PENDING_TWO_FINGER_TAP_FIRST_CANCELLED = 169 G(GS_PENDING_TWO_FINGER_TAP, 0, TS_CANCELLED, TSI_ALWAYS), 170 171 GST_PENDING_TWO_FINGER_TAP_SECOND_CANCELLED = 172 G(GS_PENDING_TWO_FINGER_TAP, 1, TS_CANCELLED, TSI_ALWAYS), 173 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), 176 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), 179 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), 182 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), 185 186 GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_MOVED = 187 G(GS_PENDING_TWO_FINGER_TAP_NO_PINCH, 0, TS_MOVED, TSI_ALWAYS), 188 189 GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_MOVED = 190 G(GS_PENDING_TWO_FINGER_TAP_NO_PINCH, 1, TS_MOVED, TSI_ALWAYS), 191 192 GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_CANCELLED = 193 G(GS_PENDING_TWO_FINGER_TAP_NO_PINCH, 0, TS_CANCELLED, TSI_ALWAYS), 194 195 GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_CANCELLED = 196 G(GS_PENDING_TWO_FINGER_TAP_NO_PINCH, 1, TS_CANCELLED, TSI_ALWAYS), 197 198 GST_PENDING_TWO_FINGER_TAP_THIRD_PRESSED = 199 G(GS_PENDING_TWO_FINGER_TAP, 2, TS_PRESSED, TSI_NOT_PROCESSED), 200 201 GST_PENDING_PINCH_FIRST_MOVED = 202 G(GS_PENDING_PINCH, 0, TS_MOVED, TSI_NOT_PROCESSED), 203 204 GST_PENDING_PINCH_SECOND_MOVED = 205 G(GS_PENDING_PINCH, 1, TS_MOVED, TSI_NOT_PROCESSED), 206 207 GST_PENDING_PINCH_FIRST_MOVED_HANDLED = 208 G(GS_PENDING_PINCH, 0, TS_MOVED, TSI_PROCESSED), 209 210 GST_PENDING_PINCH_SECOND_MOVED_HANDLED = 211 G(GS_PENDING_PINCH, 1, TS_MOVED, TSI_PROCESSED), 212 213 GST_PENDING_PINCH_FIRST_CANCELLED = 214 G(GS_PENDING_PINCH, 0, TS_CANCELLED, TSI_ALWAYS), 215 216 GST_PENDING_PINCH_SECOND_CANCELLED = 217 G(GS_PENDING_PINCH, 1, TS_CANCELLED, TSI_ALWAYS), 218 219 GST_PENDING_PINCH_FIRST_RELEASED = 220 G(GS_PENDING_PINCH, 0, TS_RELEASED, TSI_ALWAYS), 221 222 GST_PENDING_PINCH_SECOND_RELEASED = 223 G(GS_PENDING_PINCH, 1, TS_RELEASED, TSI_ALWAYS), 224 225 GST_PENDING_PINCH_NO_PINCH_FIRST_MOVED = 226 G(GS_PENDING_PINCH_NO_PINCH, 0, TS_MOVED, TSI_ALWAYS), 227 228 GST_PENDING_PINCH_NO_PINCH_SECOND_MOVED = 229 G(GS_PENDING_PINCH_NO_PINCH, 1, TS_MOVED, TSI_ALWAYS), 230 231 GST_PENDING_PINCH_NO_PINCH_FIRST_CANCELLED = 232 G(GS_PENDING_PINCH_NO_PINCH, 0, TS_CANCELLED, TSI_ALWAYS), 233 234 GST_PENDING_PINCH_NO_PINCH_SECOND_CANCELLED = 235 G(GS_PENDING_PINCH_NO_PINCH, 1, TS_CANCELLED, TSI_ALWAYS), 236 237 GST_PENDING_PINCH_NO_PINCH_FIRST_RELEASED = 238 G(GS_PENDING_PINCH_NO_PINCH, 0, TS_RELEASED, TSI_ALWAYS), 239 240 GST_PENDING_PINCH_NO_PINCH_SECOND_RELEASED = 241 G(GS_PENDING_PINCH_NO_PINCH, 1, TS_RELEASED, TSI_ALWAYS), 242 243 GST_PINCH_FIRST_MOVED = 244 G(GS_PINCH, 0, TS_MOVED, TSI_NOT_PROCESSED), 245 246 GST_PINCH_FIRST_MOVED_HANDLED = 247 G(GS_PINCH, 0, TS_MOVED, TSI_PROCESSED), 248 249 GST_PINCH_SECOND_MOVED = 250 G(GS_PINCH, 1, TS_MOVED, TSI_NOT_PROCESSED), 251 252 GST_PINCH_SECOND_MOVED_HANDLED = 253 G(GS_PINCH, 1, TS_MOVED, TSI_PROCESSED), 254 255 GST_PINCH_FIRST_RELEASED = 256 G(GS_PINCH, 0, TS_RELEASED, TSI_ALWAYS), 257 258 GST_PINCH_SECOND_RELEASED = 259 G(GS_PINCH, 1, TS_RELEASED, TSI_ALWAYS), 260 261 GST_PINCH_FIRST_CANCELLED = 262 G(GS_PINCH, 0, TS_CANCELLED, TSI_ALWAYS), 263 264 GST_PINCH_SECOND_CANCELLED = 265 G(GS_PINCH, 1, TS_CANCELLED, TSI_ALWAYS), 266 267 GST_PINCH_THIRD_PRESSED = 268 G(GS_PINCH, 2, TS_PRESSED, TSI_NOT_PROCESSED), 269 270 GST_PINCH_THIRD_MOVED = 271 G(GS_PINCH, 2, TS_MOVED, TSI_NOT_PROCESSED), 272 273 GST_PINCH_THIRD_MOVED_HANDLED = 274 G(GS_PINCH, 2, TS_MOVED, TSI_PROCESSED), 275 276 GST_PINCH_THIRD_RELEASED = 277 G(GS_PINCH, 2, TS_RELEASED, TSI_ALWAYS), 278 279 GST_PINCH_THIRD_CANCELLED = 280 G(GS_PINCH, 2, TS_CANCELLED, TSI_ALWAYS), 281 282 GST_PINCH_FOURTH_PRESSED = 283 G(GS_PINCH, 3, TS_PRESSED, TSI_NOT_PROCESSED), 284 285 GST_PINCH_FOURTH_MOVED = 286 G(GS_PINCH, 3, TS_MOVED, TSI_NOT_PROCESSED), 287 288 GST_PINCH_FOURTH_MOVED_HANDLED = 289 G(GS_PINCH, 3, TS_MOVED, TSI_PROCESSED), 290 291 GST_PINCH_FOURTH_RELEASED = 292 G(GS_PINCH, 3, TS_RELEASED, TSI_ALWAYS), 293 294 GST_PINCH_FOURTH_CANCELLED = 295 G(GS_PINCH, 3, TS_CANCELLED, TSI_ALWAYS), 296 297 GST_PINCH_FIFTH_PRESSED = 298 G(GS_PINCH, 4, TS_PRESSED, TSI_NOT_PROCESSED), 299 300 GST_PINCH_FIFTH_MOVED = 301 G(GS_PINCH, 4, TS_MOVED, TSI_NOT_PROCESSED), 302 303 GST_PINCH_FIFTH_MOVED_HANDLED = 304 G(GS_PINCH, 4, TS_MOVED, TSI_PROCESSED), 305 306 GST_PINCH_FIFTH_RELEASED = 307 G(GS_PINCH, 4, TS_RELEASED, TSI_ALWAYS), 308 309 GST_PINCH_FIFTH_CANCELLED = 310 G(GS_PINCH, 4, TS_CANCELLED, TSI_ALWAYS), 311 }; 312 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)); 328 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; 409 } 410 411 return signature; 412 } 413 #undef G 414 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); 419 } 420 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(); 426 } 427 return touch_bitmask; 428 } 429 430 const float kFlingCurveNormalization = 1.0f / 1875.f; 431 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; 437 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; 442 } 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()); 447 } 448 449 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); 464 } 465 } 466 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; 474 } 475 } 476 477 } // namespace 478 479 //////////////////////////////////////////////////////////////////////////////// 480 // GestureSequence Public: 481 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_); 491 } 492 493 GestureSequence::~GestureSequence() { 494 } 495 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; 503 504 // Set a limit on the number of simultaneous touches in a gesture. 505 if (event.touch_id() >= kMaxGesturePoints) 506 return NULL; 507 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; 520 } 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()); 524 } 525 526 GestureState last_state = state_; 527 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; 537 538 // Send GESTURE_BEGIN for any touch pressed. 539 if (event.type() == ui::ET_TOUCH_PRESSED) 540 AppendBeginGestureEvent(point, gestures.get()); 541 542 TouchStatusInternal status_internal = (result == ER_UNHANDLED) ? 543 TSI_NOT_PROCESSED : TSI_PROCESSED; 544 545 EdgeStateSignatureType signature = Signature(state_, point_id, 546 event.type(), status_internal); 547 548 if (signature == GST_INVALID) 549 signature = Signature(state_, point_id, event.type(), TSI_ALWAYS); 550 551 switch (signature) { 552 case GST_INVALID: 553 break; 554 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(); 573 } 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); 582 } 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(); 711 } 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()); 726 727 if (point_count_ == 2) { 728 PinchEnd(event, point, gestures.get()); 729 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. 738 } 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; 747 } 748 749 if (event.type() == ui::ET_TOUCH_RELEASED || 750 event.type() == ui::ET_TOUCH_CANCELLED) 751 AppendEndGestureEvent(point, gestures.get()); 752 753 if (state_ != last_state) 754 DVLOG(4) << "Gesture Sequence" 755 << " State: " << state_ 756 << " touch id: " << event.touch_id(); 757 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(); 764 } 765 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); 776 } 777 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_; 785 } 786 } 787 788 UpdateGestureEventLatencyInfo(event, gestures.get()); 789 return gestures.release(); 790 } 791 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()); 816 } 817 bounding_box_.SetRect(left, top, right - left, bottom - top); 818 } 819 } 820 821 void GestureSequence::ResetVelocities() { 822 for (int i = 0; i < kMaxGesturePoints; ++i) { 823 if (points_[i].in_use()) 824 points_[i].ResetVelocity(); 825 } 826 } 827 828 //////////////////////////////////////////////////////////////////////////////// 829 // GestureSequence Protected: 830 831 base::OneShotTimer<GestureSequence>* GestureSequence::CreateTimer() { 832 return new base::OneShotTimer<GestureSequence>(); 833 } 834 835 base::OneShotTimer<GestureSequence>* GestureSequence::GetLongPressTimer() { 836 if (!long_press_timer_.get()) 837 long_press_timer_.reset(CreateTimer()); 838 return long_press_timer_.get(); 839 } 840 841 base::OneShotTimer<GestureSequence>* GestureSequence::GetShowPressTimer() { 842 if (!show_press_timer_.get()) 843 show_press_timer_.reset(CreateTimer()); 844 return show_press_timer_.get(); 845 } 846 847 //////////////////////////////////////////////////////////////////////////////// 848 // GestureSequence Private: 849 850 GesturePoint& GestureSequence::GesturePointForEvent( 851 const TouchEvent& event) { 852 return points_[event.touch_id()]; 853 } 854 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; 861 } 862 NOTREACHED(); 863 return NULL; 864 } 865 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; 876 } 877 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); 892 } 893 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())); 902 } 903 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())); 912 } 913 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())); 922 } 923 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())); 932 } 933 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())); 945 } 946 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())); 957 } 958 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); 968 969 if (scroll_type_ == ST_HORIZONTAL) 970 railed_y_velocity = 0; 971 else if (scroll_type_ == ST_VERTICAL) 972 railed_x_velocity = 0; 973 974 if (railed_x_velocity != 0 || railed_y_velocity != 0) { 975 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())); 991 } 992 } 993 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; 1008 } 1009 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()); 1021 } 1022 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); 1027 1028 d.set_x(d.x() * ratio); 1029 d.set_y(d.y() * ratio); 1030 } 1031 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; 1038 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_))); 1046 } 1047 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())); 1058 } 1059 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())); 1071 } 1072 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_))); 1084 } 1085 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_))); 1096 } 1097 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())); 1109 } 1110 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(); 1125 } 1126 AppendClickGestureEvent(point, tap_count, gestures); 1127 return true; 1128 } else if (point.IsInsideTouchSlopRegion(event) && 1129 !GetLongPressTimer()->IsRunning()) { 1130 AppendLongTapGestureEvent(point, gestures); 1131 } 1132 return false; 1133 } 1134 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; 1149 } 1150 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; 1161 } 1162 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; 1172 } 1173 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); 1185 1186 GetShowPressTimer()->Start( 1187 FROM_HERE, 1188 base::TimeDelta::FromMilliseconds( 1189 GestureConfiguration::show_press_delay_in_ms()), 1190 this, 1191 &GestureSequence::AppendShowPressGestureEvent); 1192 1193 return true; 1194 } 1195 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); 1203 1204 if (state_ == GS_SCROLL) { 1205 AppendScrollGestureEnd(point, 1206 point.last_touch_position(), 1207 gestures, 0.f, 0.f); 1208 } 1209 second_touch_time_ = event.time_stamp(); 1210 return true; 1211 } 1212 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); 1218 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; 1225 } 1226 return false; 1227 } 1228 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; 1240 } 1241 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()); 1251 } 1252 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()); 1262 } 1263 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())); 1272 } 1273 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); 1287 } 1288 return true; 1289 } 1290 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); 1297 1298 // Once pinch starts, we immediately break rail scroll. 1299 scroll_type_ = ST_FREE; 1300 1301 const GesturePoint* point1 = GetPointByPointId(0); 1302 const GesturePoint* point2 = GetPointByPointId(1); 1303 1304 if (state_ == GS_PENDING_TWO_FINGER_TAP || 1305 state_ == GS_PENDING_PINCH) { 1306 AppendScrollGestureBegin(point, bounding_box_.CenterPoint(), gestures); 1307 } 1308 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); 1313 1314 return true; 1315 } 1316 1317 bool GestureSequence::PinchUpdate(const TouchEvent& event, 1318 GesturePoint& point, 1319 Gestures* gestures) { 1320 DCHECK(state_ == GS_PINCH); 1321 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; 1333 } 1334 1335 if (!did_scroll) 1336 return false; 1337 1338 float distance = BoundingBoxDiagonal(bounding_box_); 1339 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; 1345 } 1346 AppendScrollGestureUpdate(point, gestures, FS_NOT_FIRST_SCROLL); 1347 1348 return true; 1349 } 1350 1351 bool GestureSequence::PinchEnd(const TouchEvent& event, 1352 const GesturePoint& point, 1353 Gestures* gestures) { 1354 DCHECK(state_ == GS_PINCH); 1355 1356 GesturePoint* point1 = GetPointByPointId(0); 1357 GesturePoint* point2 = GetPointByPointId(1); 1358 1359 float distance = BoundingBoxDiagonal(bounding_box_); 1360 AppendPinchGestureEnd(*point1, *point2, 1361 distance / pinch_distance_start_, gestures); 1362 1363 pinch_distance_start_ = 0; 1364 pinch_distance_current_ = 0; 1365 return true; 1366 } 1367 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; 1376 1377 for (i = 0; i < kMaxGesturePoints; ++i) { 1378 if (points_[i].in_use()) 1379 break; 1380 } 1381 DCHECK(i < kMaxGesturePoints); 1382 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; 1387 1388 for (++i; i < kMaxGesturePoints; ++i) { 1389 if (!points_[i].in_use()) 1390 continue; 1391 1392 if (sign_x * points_[i].XVelocity() < 0) 1393 swipe_x = false; 1394 1395 if (sign_y * points_[i].YVelocity() < 0) 1396 swipe_y = false; 1397 1398 velocity_x += points_[i].XVelocity(); 1399 velocity_y += points_[i].YVelocity(); 1400 } 1401 1402 float min_velocity = GestureConfiguration::min_swipe_speed(); 1403 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; 1410 1411 if (!swipe_x && !swipe_y) 1412 return false; 1413 1414 if (!swipe_x) 1415 velocity_x = 0.001f; 1416 if (!swipe_y) 1417 velocity_y = 0.001f; 1418 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; 1423 1424 if (velocity_x > velocity_y) 1425 sign_y = 0; 1426 else 1427 sign_x = 0; 1428 1429 AppendSwipeGesture(point, sign_x, sign_y, gestures); 1430 1431 return true; 1432 } 1433 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); 1442 } 1443 } 1444 1445 1446 void GestureSequence::StopTimersIfRequired(const TouchEvent& event) { 1447 if ((!GetLongPressTimer()->IsRunning() && 1448 !GetShowPressTimer()->IsRunning()) || 1449 event.type() != ui::ET_TOUCH_MOVED) 1450 return; 1451 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(); 1457 } 1458 } 1459 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); 1465 } 1466 1467 } // namespace ui 1468