1 // Copyright 2014 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/chromeos/touch_exploration_controller.h" 6 7 #include "base/logging.h" 8 #include "base/strings/string_number_conversions.h" 9 #include "base/time/default_tick_clock.h" 10 #include "ui/aura/client/cursor_client.h" 11 #include "ui/aura/window.h" 12 #include "ui/aura/window_event_dispatcher.h" 13 #include "ui/aura/window_tree_host.h" 14 #include "ui/events/event.h" 15 #include "ui/events/event_processor.h" 16 #include "ui/events/event_utils.h" 17 #include "ui/gfx/geometry/rect.h" 18 19 #define SET_STATE(state) SetState(state, __func__) 20 #define VLOG_EVENT(event) if (VLOG_IS_ON(0)) VlogEvent(event, __func__) 21 22 namespace ui { 23 24 namespace { 25 26 // Delay between adjustment sounds. 27 const base::TimeDelta kSoundDelay = base::TimeDelta::FromMilliseconds(150); 28 29 // Delay before corner passthrough activates. 30 const base::TimeDelta kCornerPassthroughDelay = 31 base::TimeDelta::FromMilliseconds(700); 32 33 // In ChromeOS, VKEY_LWIN is synonymous for the search key. 34 const ui::KeyboardCode kChromeOSSearchKey = ui::VKEY_LWIN; 35 } // namespace 36 37 TouchExplorationController::TouchExplorationController( 38 aura::Window* root_window, 39 TouchExplorationControllerDelegate* delegate) 40 : root_window_(root_window), 41 delegate_(delegate), 42 state_(NO_FINGERS_DOWN), 43 gesture_provider_(new GestureProviderAura(this)), 44 prev_state_(NO_FINGERS_DOWN), 45 VLOG_on_(true), 46 tick_clock_(NULL) { 47 CHECK(root_window); 48 root_window->GetHost()->GetEventSource()->AddEventRewriter(this); 49 InitializeSwipeGestureMaps(); 50 } 51 52 TouchExplorationController::~TouchExplorationController() { 53 root_window_->GetHost()->GetEventSource()->RemoveEventRewriter(this); 54 } 55 56 ui::EventRewriteStatus TouchExplorationController::RewriteEvent( 57 const ui::Event& event, 58 scoped_ptr<ui::Event>* rewritten_event) { 59 if (!event.IsTouchEvent()) { 60 if (event.IsKeyEvent()) { 61 const ui::KeyEvent& key_event = static_cast<const ui::KeyEvent&>(event); 62 VLOG(0) << "\nKeyboard event: " << key_event.name() 63 << "\n Key code: " << key_event.key_code() 64 << ", Flags: " << key_event.flags() 65 << ", Is char: " << key_event.is_char(); 66 } 67 return ui::EVENT_REWRITE_CONTINUE; 68 } 69 const ui::TouchEvent& touch_event = static_cast<const ui::TouchEvent&>(event); 70 71 // If the tap timer should have fired by now but hasn't, run it now and 72 // stop the timer. This is important so that behavior is consistent with 73 // the timestamps of the events, and not dependent on the granularity of 74 // the timer. 75 if (tap_timer_.IsRunning() && 76 touch_event.time_stamp() - initial_press_->time_stamp() > 77 gesture_detector_config_.double_tap_timeout) { 78 tap_timer_.Stop(); 79 OnTapTimerFired(); 80 // Note: this may change the state. We should now continue and process 81 // this event under this new state. 82 } 83 84 if (passthrough_timer_.IsRunning() && 85 event.time_stamp() - initial_press_->time_stamp() > 86 gesture_detector_config_.longpress_timeout) { 87 passthrough_timer_.Stop(); 88 OnPassthroughTimerFired(); 89 } 90 91 const ui::EventType type = touch_event.type(); 92 const gfx::PointF& location = touch_event.location_f(); 93 const int touch_id = touch_event.touch_id(); 94 95 // Always update touch ids and touch locations, so we can use those 96 // no matter what state we're in. 97 if (type == ui::ET_TOUCH_PRESSED) { 98 current_touch_ids_.push_back(touch_id); 99 touch_locations_.insert(std::pair<int, gfx::PointF>(touch_id, location)); 100 } else if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) { 101 std::vector<int>::iterator it = std::find( 102 current_touch_ids_.begin(), current_touch_ids_.end(), touch_id); 103 104 // Can happen if touch exploration is enabled while fingers were down. 105 if (it == current_touch_ids_.end()) 106 return ui::EVENT_REWRITE_CONTINUE; 107 108 current_touch_ids_.erase(it); 109 touch_locations_.erase(touch_id); 110 } else if (type == ui::ET_TOUCH_MOVED) { 111 std::vector<int>::iterator it = std::find( 112 current_touch_ids_.begin(), current_touch_ids_.end(), touch_id); 113 114 // Can happen if touch exploration is enabled while fingers were down. 115 if (it == current_touch_ids_.end()) 116 return ui::EVENT_REWRITE_CONTINUE; 117 118 touch_locations_[*it] = location; 119 } else { 120 NOTREACHED() << "Unexpected event type received: " << event.name(); 121 return ui::EVENT_REWRITE_CONTINUE; 122 } 123 VLOG_EVENT(touch_event); 124 125 // In order to avoid accidentally double tapping when moving off the edge 126 // of the screen, the state will be rewritten to NoFingersDown. 127 if ((type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) && 128 FindEdgesWithinBounds(touch_event.location(), kLeavingScreenEdge) != 129 NO_EDGE) { 130 if (VLOG_on_) 131 VLOG(0) << "Leaving screen"; 132 133 // Indicates to the user that they are leaving the screen. 134 delegate_->PlayExitScreenEarcon(); 135 136 if (current_touch_ids_.size() == 0) { 137 SET_STATE(NO_FINGERS_DOWN); 138 if (VLOG_on_) { 139 VLOG(0) << "Reset to no fingers in Rewrite event because the touch " 140 "release or cancel was on the edge of the screen."; 141 } 142 return ui::EVENT_REWRITE_DISCARD; 143 } 144 } 145 146 // If the user is in a gesture state, or if there is a possiblity that the 147 // user will enter it in the future, we send the event to the gesture 148 // provider so it can keep track of the state of the fingers. When the user 149 // leaves one of these states, SET_STATE will set the gesture provider to 150 // NULL. 151 if (gesture_provider_.get()) { 152 gesture_provider_->OnTouchEvent(touch_event); 153 gesture_provider_->OnTouchEventAck(false); 154 ProcessGestureEvents(); 155 } 156 157 // The rest of the processing depends on what state we're in. 158 switch (state_) { 159 case NO_FINGERS_DOWN: 160 return InNoFingersDown(touch_event, rewritten_event); 161 case SINGLE_TAP_PRESSED: 162 return InSingleTapPressed(touch_event, rewritten_event); 163 case SINGLE_TAP_RELEASED: 164 case TOUCH_EXPLORE_RELEASED: 165 return InSingleTapOrTouchExploreReleased(touch_event, rewritten_event); 166 case DOUBLE_TAP_PENDING: 167 return InDoubleTapPending(touch_event, rewritten_event); 168 case TOUCH_RELEASE_PENDING: 169 return InTouchReleasePending(touch_event, rewritten_event); 170 case TOUCH_EXPLORATION: 171 return InTouchExploration(touch_event, rewritten_event); 172 case GESTURE_IN_PROGRESS: 173 return InGestureInProgress(touch_event, rewritten_event); 174 case TOUCH_EXPLORE_SECOND_PRESS: 175 return InTouchExploreSecondPress(touch_event, rewritten_event); 176 case SLIDE_GESTURE: 177 return InSlideGesture(touch_event, rewritten_event); 178 case ONE_FINGER_PASSTHROUGH: 179 return InOneFingerPassthrough(touch_event, rewritten_event); 180 case CORNER_PASSTHROUGH: 181 return InCornerPassthrough(touch_event, rewritten_event); 182 case WAIT_FOR_NO_FINGERS: 183 return InWaitForNoFingers(touch_event, rewritten_event); 184 case TWO_FINGER_TAP: 185 return InTwoFingerTap(touch_event, rewritten_event); 186 } 187 NOTREACHED(); 188 return ui::EVENT_REWRITE_CONTINUE; 189 } 190 191 ui::EventRewriteStatus TouchExplorationController::NextDispatchEvent( 192 const ui::Event& last_event, scoped_ptr<ui::Event>* new_event) { 193 NOTREACHED(); 194 return ui::EVENT_REWRITE_CONTINUE; 195 } 196 197 ui::EventRewriteStatus TouchExplorationController::InNoFingersDown( 198 const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event) { 199 const ui::EventType type = event.type(); 200 if (type != ui::ET_TOUCH_PRESSED) { 201 NOTREACHED() << "Unexpected event type received: " << event.name(); 202 return ui::EVENT_REWRITE_CONTINUE; 203 } 204 205 // If the user enters the screen from the edge then send an earcon. 206 int edge = FindEdgesWithinBounds(event.location(), kLeavingScreenEdge); 207 if (edge != NO_EDGE) 208 delegate_->PlayEnterScreenEarcon(); 209 210 int location = FindEdgesWithinBounds(event.location(), kSlopDistanceFromEdge); 211 // If the press was at a corner, the user might go into corner passthrough 212 // instead. 213 bool in_a_bottom_corner = 214 (BOTTOM_LEFT_CORNER == location) || (BOTTOM_RIGHT_CORNER == location); 215 if (in_a_bottom_corner) { 216 passthrough_timer_.Start( 217 FROM_HERE, 218 gesture_detector_config_.longpress_timeout, 219 this, 220 &TouchExplorationController::OnPassthroughTimerFired); 221 } 222 initial_press_.reset(new TouchEvent(event)); 223 initial_presses_[event.touch_id()] = event.location(); 224 last_unused_finger_event_.reset(new TouchEvent(event)); 225 StartTapTimer(); 226 SET_STATE(SINGLE_TAP_PRESSED); 227 return ui::EVENT_REWRITE_DISCARD; 228 } 229 230 ui::EventRewriteStatus TouchExplorationController::InSingleTapPressed( 231 const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event) { 232 const ui::EventType type = event.type(); 233 234 int location = FindEdgesWithinBounds(event.location(), kMaxDistanceFromEdge); 235 bool in_a_bottom_corner = 236 (location == BOTTOM_LEFT_CORNER) || (location == BOTTOM_RIGHT_CORNER); 237 // If the event is from the initial press and the location is no longer in the 238 // corner, then we are not waiting for a corner passthrough anymore. 239 if (event.touch_id() == initial_press_->touch_id() && !in_a_bottom_corner) { 240 if (passthrough_timer_.IsRunning()) { 241 passthrough_timer_.Stop(); 242 // Since the long press timer has been running, it is possible that the 243 // tap timer has timed out before the long press timer has. If the tap 244 // timer timeout has elapsed, then fire the tap timer. 245 if (event.time_stamp() - initial_press_->time_stamp() > 246 gesture_detector_config_.double_tap_timeout) { 247 OnTapTimerFired(); 248 } 249 } 250 } 251 252 if (type == ui::ET_TOUCH_PRESSED) { 253 initial_presses_[event.touch_id()] = event.location(); 254 SET_STATE(TWO_FINGER_TAP); 255 return EVENT_REWRITE_DISCARD; 256 } else if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) { 257 if (passthrough_timer_.IsRunning()) 258 passthrough_timer_.Stop(); 259 if (current_touch_ids_.size() == 0 && 260 event.touch_id() == initial_press_->touch_id()) { 261 SET_STATE(SINGLE_TAP_RELEASED); 262 } else if (current_touch_ids_.size() == 0) { 263 SET_STATE(NO_FINGERS_DOWN); 264 } 265 return EVENT_REWRITE_DISCARD; 266 } else if (type == ui::ET_TOUCH_MOVED) { 267 float distance = (event.location() - initial_press_->location()).Length(); 268 // If the user does not move far enough from the original position, then the 269 // resulting movement should not be considered to be a deliberate gesture or 270 // touch exploration. 271 if (distance <= gesture_detector_config_.touch_slop) 272 return EVENT_REWRITE_DISCARD; 273 274 float delta_time = 275 (event.time_stamp() - initial_press_->time_stamp()).InSecondsF(); 276 float velocity = distance / delta_time; 277 if (VLOG_on_) { 278 VLOG(0) << "\n Delta time: " << delta_time << "\n Distance: " << distance 279 << "\n Velocity of click: " << velocity 280 << "\n Minimum swipe velocity: " 281 << gesture_detector_config_.minimum_swipe_velocity; 282 } 283 // Change to slide gesture if the slide occurred at the right edge. 284 int edge = FindEdgesWithinBounds(event.location(), kMaxDistanceFromEdge); 285 if (edge & RIGHT_EDGE && edge != BOTTOM_RIGHT_CORNER) { 286 SET_STATE(SLIDE_GESTURE); 287 return InSlideGesture(event, rewritten_event); 288 } 289 290 // If the user moves fast enough from the initial touch location, start 291 // gesture detection. Otherwise, jump to the touch exploration mode early. 292 if (velocity > gesture_detector_config_.minimum_swipe_velocity) { 293 SET_STATE(GESTURE_IN_PROGRESS); 294 return InGestureInProgress(event, rewritten_event); 295 } 296 EnterTouchToMouseMode(); 297 SET_STATE(TOUCH_EXPLORATION); 298 return InTouchExploration(event, rewritten_event); 299 } 300 NOTREACHED(); 301 return ui::EVENT_REWRITE_CONTINUE; 302 } 303 304 ui::EventRewriteStatus 305 TouchExplorationController::InSingleTapOrTouchExploreReleased( 306 const ui::TouchEvent& event, 307 scoped_ptr<ui::Event>* rewritten_event) { 308 const ui::EventType type = event.type(); 309 // If there is more than one finger down, then discard to wait until no 310 // fingers are down. 311 if (current_touch_ids_.size() > 1) { 312 SET_STATE(WAIT_FOR_NO_FINGERS); 313 return ui::EVENT_REWRITE_DISCARD; 314 } 315 if (type == ui::ET_TOUCH_PRESSED) { 316 // If there is no touch exploration yet, we can't send a click, so discard. 317 if (!last_touch_exploration_) { 318 tap_timer_.Stop(); 319 return ui::EVENT_REWRITE_DISCARD; 320 } 321 // This is the second tap in a double-tap (or double tap-hold). 322 // We set the tap timer. If it fires before the user lifts their finger, 323 // one-finger passthrough begins. Otherwise, there is a touch press and 324 // release at the location of the last touch exploration. 325 SET_STATE(DOUBLE_TAP_PENDING); 326 // The old tap timer (from the initial click) is stopped if it is still 327 // going, and the new one is set. 328 tap_timer_.Stop(); 329 StartTapTimer(); 330 // This will update as the finger moves before a possible passthrough, and 331 // will determine the offset. 332 last_unused_finger_event_.reset(new ui::TouchEvent(event)); 333 return ui::EVENT_REWRITE_DISCARD; 334 } else if (type == ui::ET_TOUCH_RELEASED && !last_touch_exploration_) { 335 // If the previous press was discarded, we need to also handle its 336 // release. 337 if (current_touch_ids_.size() == 0) { 338 SET_STATE(NO_FINGERS_DOWN); 339 } 340 return ui::EVENT_REWRITE_DISCARD; 341 } else if (type == ui::ET_TOUCH_MOVED) { 342 return ui::EVENT_REWRITE_DISCARD; 343 } 344 NOTREACHED(); 345 return ui::EVENT_REWRITE_CONTINUE; 346 } 347 348 ui::EventRewriteStatus TouchExplorationController::InDoubleTapPending( 349 const ui::TouchEvent& event, 350 scoped_ptr<ui::Event>* rewritten_event) { 351 const ui::EventType type = event.type(); 352 if (type == ui::ET_TOUCH_PRESSED) { 353 return ui::EVENT_REWRITE_DISCARD; 354 } else if (type == ui::ET_TOUCH_MOVED) { 355 // If the user moves far enough from the initial touch location (outside 356 // the "slop" region, jump to passthrough mode early. 357 float delta = (event.location() - initial_press_->location()).Length(); 358 if (delta > gesture_detector_config_.touch_slop) { 359 tap_timer_.Stop(); 360 OnTapTimerFired(); 361 } 362 return EVENT_REWRITE_DISCARD; 363 } else if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) { 364 if (current_touch_ids_.size() != 0) 365 return EVENT_REWRITE_DISCARD; 366 367 scoped_ptr<ui::TouchEvent> touch_press; 368 touch_press.reset(new ui::TouchEvent(ui::ET_TOUCH_PRESSED, 369 last_touch_exploration_->location(), 370 initial_press_->touch_id(), 371 event.time_stamp())); 372 DispatchEvent(touch_press.get()); 373 374 rewritten_event->reset( 375 new ui::TouchEvent(ui::ET_TOUCH_RELEASED, 376 last_touch_exploration_->location(), 377 initial_press_->touch_id(), 378 event.time_stamp())); 379 (*rewritten_event)->set_flags(event.flags()); 380 SET_STATE(NO_FINGERS_DOWN); 381 return ui::EVENT_REWRITE_REWRITTEN; 382 } 383 NOTREACHED(); 384 return ui::EVENT_REWRITE_CONTINUE; 385 } 386 387 ui::EventRewriteStatus TouchExplorationController::InTouchReleasePending( 388 const ui::TouchEvent& event, 389 scoped_ptr<ui::Event>* rewritten_event) { 390 const ui::EventType type = event.type(); 391 if (type == ui::ET_TOUCH_PRESSED || type == ui::ET_TOUCH_MOVED) { 392 return ui::EVENT_REWRITE_DISCARD; 393 } else if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) { 394 if (current_touch_ids_.size() != 0) 395 return EVENT_REWRITE_DISCARD; 396 397 rewritten_event->reset( 398 new ui::TouchEvent(ui::ET_TOUCH_RELEASED, 399 last_touch_exploration_->location(), 400 initial_press_->touch_id(), 401 event.time_stamp())); 402 (*rewritten_event)->set_flags(event.flags()); 403 SET_STATE(NO_FINGERS_DOWN); 404 return ui::EVENT_REWRITE_REWRITTEN; 405 } 406 NOTREACHED(); 407 return ui::EVENT_REWRITE_CONTINUE; 408 } 409 410 ui::EventRewriteStatus TouchExplorationController::InTouchExploration( 411 const ui::TouchEvent& event, 412 scoped_ptr<ui::Event>* rewritten_event) { 413 const ui::EventType type = event.type(); 414 if (type == ui::ET_TOUCH_PRESSED) { 415 // Handle split-tap. 416 initial_press_.reset(new TouchEvent(event)); 417 tap_timer_.Stop(); 418 rewritten_event->reset( 419 new ui::TouchEvent(ui::ET_TOUCH_PRESSED, 420 last_touch_exploration_->location(), 421 event.touch_id(), 422 event.time_stamp())); 423 (*rewritten_event)->set_flags(event.flags()); 424 SET_STATE(TOUCH_EXPLORE_SECOND_PRESS); 425 return ui::EVENT_REWRITE_REWRITTEN; 426 } else if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) { 427 initial_press_.reset(new TouchEvent(event)); 428 StartTapTimer(); 429 SET_STATE(TOUCH_EXPLORE_RELEASED); 430 } else if (type != ui::ET_TOUCH_MOVED) { 431 NOTREACHED(); 432 return ui::EVENT_REWRITE_CONTINUE; 433 } 434 435 // Rewrite as a mouse-move event. 436 *rewritten_event = CreateMouseMoveEvent(event.location(), event.flags()); 437 last_touch_exploration_.reset(new TouchEvent(event)); 438 return ui::EVENT_REWRITE_REWRITTEN; 439 } 440 441 ui::EventRewriteStatus TouchExplorationController::InGestureInProgress( 442 const ui::TouchEvent& event, 443 scoped_ptr<ui::Event>* rewritten_event) { 444 // The events were sent to the gesture provider in RewriteEvent already. 445 // If no gesture is registered before the tap timer times out, the state 446 // will change to "wait for no fingers down" or "touch exploration" depending 447 // on the number of fingers down, and this function will stop being called. 448 if (current_touch_ids_.size() == 0) { 449 SET_STATE(NO_FINGERS_DOWN); 450 } 451 return ui::EVENT_REWRITE_DISCARD; 452 } 453 454 ui::EventRewriteStatus TouchExplorationController::InCornerPassthrough( 455 const ui::TouchEvent& event, 456 scoped_ptr<ui::Event>* rewritten_event) { 457 ui::EventType type = event.type(); 458 459 // If the first finger has left the corner, then exit passthrough. 460 if (event.touch_id() == initial_press_->touch_id()) { 461 int edges = FindEdgesWithinBounds(event.location(), kSlopDistanceFromEdge); 462 bool in_a_bottom_corner = (edges == BOTTOM_LEFT_CORNER) || 463 (edges == BOTTOM_RIGHT_CORNER); 464 if (type == ui::ET_TOUCH_MOVED && in_a_bottom_corner) 465 return ui::EVENT_REWRITE_DISCARD; 466 467 if (current_touch_ids_.size() == 0) { 468 SET_STATE(NO_FINGERS_DOWN); 469 return ui::EVENT_REWRITE_DISCARD; 470 } 471 SET_STATE(WAIT_FOR_NO_FINGERS); 472 return ui::EVENT_REWRITE_DISCARD; 473 } 474 475 rewritten_event->reset(new ui::TouchEvent( 476 type, event.location(), event.touch_id(), event.time_stamp())); 477 (*rewritten_event)->set_flags(event.flags()); 478 479 if (current_touch_ids_.size() == 0) 480 SET_STATE(NO_FINGERS_DOWN); 481 482 return ui::EVENT_REWRITE_REWRITTEN; 483 } 484 485 ui::EventRewriteStatus TouchExplorationController::InOneFingerPassthrough( 486 const ui::TouchEvent& event, 487 scoped_ptr<ui::Event>* rewritten_event) { 488 if (event.touch_id() != initial_press_->touch_id()) { 489 if (current_touch_ids_.size() == 0) { 490 SET_STATE(NO_FINGERS_DOWN); 491 } 492 return ui::EVENT_REWRITE_DISCARD; 493 } 494 rewritten_event->reset( 495 new ui::TouchEvent(event.type(), 496 event.location() - passthrough_offset_, 497 event.touch_id(), 498 event.time_stamp())); 499 500 (*rewritten_event)->set_flags(event.flags()); 501 if (current_touch_ids_.size() == 0) { 502 SET_STATE(NO_FINGERS_DOWN); 503 } 504 return ui::EVENT_REWRITE_REWRITTEN; 505 } 506 507 ui::EventRewriteStatus TouchExplorationController::InTouchExploreSecondPress( 508 const ui::TouchEvent& event, 509 scoped_ptr<ui::Event>* rewritten_event) { 510 ui::EventType type = event.type(); 511 gfx::PointF location = event.location_f(); 512 if (type == ui::ET_TOUCH_PRESSED) { 513 // A third finger being pressed means that a split tap can no longer go 514 // through. The user enters the wait state, Since there has already been 515 // a press dispatched when split tap began, the touch needs to be 516 // cancelled. 517 rewritten_event->reset( 518 new ui::TouchEvent(ui::ET_TOUCH_CANCELLED, 519 last_touch_exploration_->location(), 520 initial_press_->touch_id(), 521 event.time_stamp())); 522 (*rewritten_event)->set_flags(event.flags()); 523 SET_STATE(WAIT_FOR_NO_FINGERS); 524 return ui::EVENT_REWRITE_REWRITTEN; 525 } else if (type == ui::ET_TOUCH_MOVED) { 526 // If the fingers have moved too far from their original locations, 527 // the user can no longer split tap. 528 ui::TouchEvent* original_touch; 529 if (event.touch_id() == last_touch_exploration_->touch_id()) 530 original_touch = last_touch_exploration_.get(); 531 else if (event.touch_id() == initial_press_->touch_id()) 532 original_touch = initial_press_.get(); 533 else { 534 NOTREACHED(); 535 SET_STATE(WAIT_FOR_NO_FINGERS); 536 return ui::EVENT_REWRITE_DISCARD; 537 } 538 // Check the distance between the current finger location and the original 539 // location. The slop for this is a bit more generous since keeping two 540 // fingers in place is a bit harder. If the user has left the slop, the 541 // split tap press (which was previous dispatched) is lifted with a touch 542 // cancelled, and the user enters the wait state. 543 if ((event.location() - original_touch->location()).Length() > 544 GetSplitTapTouchSlop()) { 545 rewritten_event->reset( 546 new ui::TouchEvent(ui::ET_TOUCH_CANCELLED, 547 last_touch_exploration_->location(), 548 initial_press_->touch_id(), 549 event.time_stamp())); 550 (*rewritten_event)->set_flags(event.flags()); 551 SET_STATE(WAIT_FOR_NO_FINGERS); 552 return ui::EVENT_REWRITE_REWRITTEN; 553 } 554 return ui::EVENT_REWRITE_DISCARD; 555 } else if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) { 556 // If the touch exploration finger is lifted, there is no option to return 557 // to touch explore anymore. The remaining finger acts as a pending 558 // tap or long tap for the last touch explore location. 559 if (event.touch_id() == last_touch_exploration_->touch_id()){ 560 SET_STATE(TOUCH_RELEASE_PENDING); 561 return EVENT_REWRITE_DISCARD; 562 } 563 564 // Continue to release the touch only if the touch explore finger is the 565 // only finger remaining. 566 if (current_touch_ids_.size() != 1) 567 return EVENT_REWRITE_DISCARD; 568 569 // Rewrite at location of last touch exploration. 570 rewritten_event->reset( 571 new ui::TouchEvent(ui::ET_TOUCH_RELEASED, 572 last_touch_exploration_->location(), 573 initial_press_->touch_id(), 574 event.time_stamp())); 575 (*rewritten_event)->set_flags(event.flags()); 576 SET_STATE(TOUCH_EXPLORATION); 577 EnterTouchToMouseMode(); 578 return ui::EVENT_REWRITE_REWRITTEN; 579 } 580 NOTREACHED(); 581 return ui::EVENT_REWRITE_CONTINUE; 582 } 583 584 ui::EventRewriteStatus TouchExplorationController::InWaitForNoFingers( 585 const ui::TouchEvent& event, 586 scoped_ptr<ui::Event>* rewritten_event) { 587 if (current_touch_ids_.size() == 0) 588 SET_STATE(NO_FINGERS_DOWN); 589 return EVENT_REWRITE_DISCARD; 590 } 591 592 void TouchExplorationController::PlaySoundForTimer() { 593 delegate_->PlayVolumeAdjustEarcon(); 594 } 595 596 ui::EventRewriteStatus TouchExplorationController::InSlideGesture( 597 const ui::TouchEvent& event, 598 scoped_ptr<ui::Event>* rewritten_event) { 599 // The timer should not fire when sliding. 600 tap_timer_.Stop(); 601 602 ui::EventType type = event.type(); 603 // If additional fingers are added before a swipe gesture has been registered, 604 // then wait until all fingers have been lifted. 605 if (type == ui::ET_TOUCH_PRESSED || 606 event.touch_id() != initial_press_->touch_id()) { 607 if (sound_timer_.IsRunning()) 608 sound_timer_.Stop(); 609 SET_STATE(WAIT_FOR_NO_FINGERS); 610 return EVENT_REWRITE_DISCARD; 611 } 612 613 // There should not be more than one finger down. 614 DCHECK(current_touch_ids_.size() <= 1); 615 616 // Allows user to return to the edge to adjust the sound if they have left the 617 // boundaries. 618 int edge = FindEdgesWithinBounds(event.location(), kSlopDistanceFromEdge); 619 if (!(edge & RIGHT_EDGE) && (type != ui::ET_TOUCH_RELEASED)) { 620 if (sound_timer_.IsRunning()) { 621 sound_timer_.Stop(); 622 } 623 return EVENT_REWRITE_DISCARD; 624 } 625 626 // This can occur if the user leaves the screen edge and then returns to it to 627 // continue adjusting the sound. 628 if (!sound_timer_.IsRunning()) { 629 sound_timer_.Start(FROM_HERE, 630 kSoundDelay, 631 this, 632 &ui::TouchExplorationController::PlaySoundForTimer); 633 delegate_->PlayVolumeAdjustEarcon(); 634 } 635 636 if (current_touch_ids_.size() == 0) { 637 SET_STATE(NO_FINGERS_DOWN); 638 } 639 return ui::EVENT_REWRITE_DISCARD; 640 } 641 642 ui::EventRewriteStatus TouchExplorationController::InTwoFingerTap( 643 const ui::TouchEvent& event, 644 scoped_ptr<ui::Event>* rewritten_event) { 645 ui::EventType type = event.type(); 646 if (type == ui::ET_TOUCH_PRESSED) { 647 // This is now a three finger gesture. 648 SET_STATE(GESTURE_IN_PROGRESS); 649 return ui::EVENT_REWRITE_DISCARD; 650 } 651 652 if (type == ui::ET_TOUCH_MOVED) { 653 // Determine if it was a swipe. 654 gfx::Point original_location = initial_presses_[event.touch_id()]; 655 float distance = (event.location() - original_location).Length(); 656 // If the user moves too far from the original position, consider the 657 // movement a swipe. 658 if (distance > gesture_detector_config_.touch_slop) { 659 SET_STATE(GESTURE_IN_PROGRESS); 660 } 661 return ui::EVENT_REWRITE_DISCARD; 662 } 663 664 if (current_touch_ids_.size() != 0) 665 return ui::EVENT_REWRITE_DISCARD; 666 667 if (type == ui::ET_TOUCH_RELEASED) { 668 // In ChromeVox, pressing control will stop ChromeVox from speaking. 669 ui::KeyEvent control_down( 670 ui::ET_KEY_PRESSED, ui::VKEY_CONTROL, ui::EF_CONTROL_DOWN); 671 ui::KeyEvent control_up(ui::ET_KEY_RELEASED, ui::VKEY_CONTROL, ui::EF_NONE); 672 673 DispatchEvent(&control_down); 674 DispatchEvent(&control_up); 675 SET_STATE(NO_FINGERS_DOWN); 676 return ui::EVENT_REWRITE_DISCARD; 677 } 678 return ui::EVENT_REWRITE_DISCARD; 679 } 680 681 base::TimeDelta TouchExplorationController::Now() { 682 if (tick_clock_) { 683 // This is the same as what EventTimeForNow() does, but here we do it 684 // with a clock that can be replaced with a simulated clock for tests. 685 return base::TimeDelta::FromInternalValue( 686 tick_clock_->NowTicks().ToInternalValue()); 687 } 688 return ui::EventTimeForNow(); 689 } 690 691 void TouchExplorationController::StartTapTimer() { 692 tap_timer_.Start(FROM_HERE, 693 gesture_detector_config_.double_tap_timeout, 694 this, 695 &TouchExplorationController::OnTapTimerFired); 696 } 697 698 void TouchExplorationController::OnTapTimerFired() { 699 switch (state_) { 700 case SINGLE_TAP_RELEASED: 701 SET_STATE(NO_FINGERS_DOWN); 702 break; 703 case TOUCH_EXPLORE_RELEASED: 704 SET_STATE(NO_FINGERS_DOWN); 705 last_touch_exploration_.reset(new TouchEvent(*initial_press_)); 706 return; 707 case DOUBLE_TAP_PENDING: { 708 SET_STATE(ONE_FINGER_PASSTHROUGH); 709 passthrough_offset_ = last_unused_finger_event_->location() - 710 last_touch_exploration_->location(); 711 scoped_ptr<ui::TouchEvent> passthrough_press( 712 new ui::TouchEvent(ui::ET_TOUCH_PRESSED, 713 last_touch_exploration_->location(), 714 last_unused_finger_event_->touch_id(), 715 Now())); 716 DispatchEvent(passthrough_press.get()); 717 return; 718 } 719 case SINGLE_TAP_PRESSED: 720 if (passthrough_timer_.IsRunning()) 721 return; 722 case GESTURE_IN_PROGRESS: 723 // If only one finger is down, go into touch exploration. 724 if (current_touch_ids_.size() == 1) { 725 EnterTouchToMouseMode(); 726 SET_STATE(TOUCH_EXPLORATION); 727 break; 728 } 729 // Otherwise wait for all fingers to be lifted. 730 SET_STATE(WAIT_FOR_NO_FINGERS); 731 return; 732 case TWO_FINGER_TAP: 733 SET_STATE(WAIT_FOR_NO_FINGERS); 734 break; 735 default: 736 return; 737 } 738 EnterTouchToMouseMode(); 739 scoped_ptr<ui::Event> mouse_move = 740 CreateMouseMoveEvent(initial_press_->location(), initial_press_->flags()); 741 DispatchEvent(mouse_move.get()); 742 last_touch_exploration_.reset(new TouchEvent(*initial_press_)); 743 } 744 745 void TouchExplorationController::OnPassthroughTimerFired() { 746 // The passthrough timer will only fire if if the user has held a finger in 747 // one of the passthrough corners for the duration of the passthrough timeout. 748 749 // Check that initial press isn't null. Also a check that if the initial 750 // corner press was released, then it should not be in corner passthrough. 751 if (!initial_press_ || 752 touch_locations_.find(initial_press_->touch_id()) != 753 touch_locations_.end()) { 754 LOG(ERROR) << "No initial press or the initial press has been released."; 755 } 756 757 gfx::Point location = 758 ToRoundedPoint(touch_locations_[initial_press_->touch_id()]); 759 int corner = FindEdgesWithinBounds(location, kSlopDistanceFromEdge); 760 if (corner != BOTTOM_LEFT_CORNER && corner != BOTTOM_RIGHT_CORNER) 761 return; 762 763 if (sound_timer_.IsRunning()) 764 sound_timer_.Stop(); 765 delegate_->PlayPassthroughEarcon(); 766 SET_STATE(CORNER_PASSTHROUGH); 767 return; 768 } 769 770 void TouchExplorationController::DispatchEvent(ui::Event* event) { 771 ui::EventDispatchDetails result ALLOW_UNUSED = 772 root_window_->GetHost()->dispatcher()->OnEventFromSource(event); 773 } 774 775 // This is an override for a function that is only called for timer-based events 776 // like long press. Events that are created synchronously as a result of 777 // certain touch events are added to the vector accessible via 778 // GetAndResetPendingGestures(). We only care about swipes (which are created 779 // synchronously), so we ignore this callback. 780 void TouchExplorationController::OnGestureEvent(ui::GestureEvent* gesture) { 781 } 782 783 void TouchExplorationController::ProcessGestureEvents() { 784 scoped_ptr<ScopedVector<ui::GestureEvent> > gestures( 785 gesture_provider_->GetAndResetPendingGestures()); 786 if (gestures) { 787 for (ScopedVector<GestureEvent>::iterator i = gestures->begin(); 788 i != gestures->end(); 789 ++i) { 790 if ((*i)->type() == ui::ET_GESTURE_SWIPE && 791 state_ == GESTURE_IN_PROGRESS) { 792 OnSwipeEvent(*i); 793 // The tap timer to leave gesture state is ended, and we now wait for 794 // all fingers to be released. 795 tap_timer_.Stop(); 796 SET_STATE(WAIT_FOR_NO_FINGERS); 797 return; 798 } 799 if (state_ == SLIDE_GESTURE && (*i)->IsScrollGestureEvent()) { 800 SideSlideControl(*i); 801 } 802 } 803 } 804 } 805 806 void TouchExplorationController::SideSlideControl(ui::GestureEvent* gesture) { 807 ui::EventType type = gesture->type(); 808 809 if (type == ET_GESTURE_SCROLL_BEGIN) { 810 delegate_->PlayVolumeAdjustEarcon(); 811 } 812 813 if (type == ET_GESTURE_SCROLL_END) { 814 if (sound_timer_.IsRunning()) 815 sound_timer_.Stop(); 816 delegate_->PlayVolumeAdjustEarcon(); 817 } 818 819 // If the user is in the corner of the right side of the screen, the volume 820 // will be automatically set to 100% or muted depending on which corner they 821 // are in. Otherwise, the user will be able to adjust the volume by sliding 822 // their finger along the right side of the screen. Volume is relative to 823 // where they are on the right side of the screen. 824 gfx::Point location = gesture->location(); 825 int edge = FindEdgesWithinBounds(location, kSlopDistanceFromEdge); 826 if (!(edge & RIGHT_EDGE)) 827 return; 828 829 if (edge & TOP_EDGE) { 830 delegate_->SetOutputLevel(100); 831 return; 832 } 833 if (edge & BOTTOM_EDGE) { 834 delegate_->SetOutputLevel(0); 835 return; 836 } 837 838 location = gesture->location(); 839 root_window_->GetHost()->ConvertPointFromNativeScreen(&location); 840 float volume_adjust_height = 841 root_window_->bounds().height() - 2 * kMaxDistanceFromEdge; 842 float ratio = (location.y() - kMaxDistanceFromEdge) / volume_adjust_height; 843 float volume = 100 - 100 * ratio; 844 if (VLOG_on_) { 845 VLOG(0) << "\n Volume = " << volume 846 << "\n Location = " << location.ToString() 847 << "\n Bounds = " << root_window_->bounds().right(); 848 } 849 delegate_->SetOutputLevel(int(volume)); 850 } 851 852 void TouchExplorationController::OnSwipeEvent(ui::GestureEvent* swipe_gesture) { 853 // A swipe gesture contains details for the direction in which the swipe 854 // occurred. TODO(evy) : Research which swipe results users most want and 855 // remap these swipes to the best events. Hopefully in the near future 856 // there will also be a menu for users to pick custom mappings. 857 GestureEventDetails event_details = swipe_gesture->details(); 858 int num_fingers = event_details.touch_points(); 859 if(VLOG_on_) 860 VLOG(0) << "\nSwipe with " << num_fingers << " fingers."; 861 862 if (num_fingers > 4) 863 return; 864 865 if (event_details.swipe_left() && 866 !left_swipe_gestures_[num_fingers].is_null()) { 867 left_swipe_gestures_[num_fingers].Run(); 868 } else if (event_details.swipe_right() && 869 !right_swipe_gestures_[num_fingers].is_null()) { 870 right_swipe_gestures_[num_fingers].Run(); 871 } else if (event_details.swipe_up() && 872 !up_swipe_gestures_[num_fingers].is_null()) { 873 up_swipe_gestures_[num_fingers].Run(); 874 } else if (event_details.swipe_down() && 875 !down_swipe_gestures_[num_fingers].is_null()) { 876 down_swipe_gestures_[num_fingers].Run(); 877 } 878 } 879 880 int TouchExplorationController::FindEdgesWithinBounds(gfx::Point point, 881 float bounds) { 882 // Since GetBoundsInScreen is in DIPs but point is not, then point needs to be 883 // converted. 884 root_window_->GetHost()->ConvertPointFromNativeScreen(&point); 885 gfx::Rect window = root_window_->GetBoundsInScreen(); 886 887 float left_edge_limit = window.x() + bounds; 888 float right_edge_limit = window.right() - bounds; 889 float top_edge_limit = window.y() + bounds; 890 float bottom_edge_limit = window.bottom() - bounds; 891 892 // Bitwise manipulation in order to determine where on the screen the point 893 // lies. If more than one bit is turned on, then it is a corner where the two 894 // bit/edges intersect. Otherwise, if no bits are turned on, the point must be 895 // in the center of the screen. 896 int result = NO_EDGE; 897 if (point.x() < left_edge_limit) 898 result |= LEFT_EDGE; 899 if (point.x() > right_edge_limit) 900 result |= RIGHT_EDGE; 901 if (point.y() < top_edge_limit) 902 result |= TOP_EDGE; 903 if (point.y() > bottom_edge_limit) 904 result |= BOTTOM_EDGE; 905 return result; 906 } 907 908 void TouchExplorationController::DispatchShiftSearchKeyEvent( 909 const ui::KeyboardCode third_key) { 910 // In order to activate the shortcut shift+search+<arrow key> 911 // three KeyPressed events must be dispatched in succession along 912 // with three KeyReleased events. 913 914 ui::KeyEvent shift_down( 915 ui::ET_KEY_PRESSED, ui::VKEY_SHIFT, ui::EF_SHIFT_DOWN); 916 ui::KeyEvent search_down( 917 ui::ET_KEY_PRESSED, kChromeOSSearchKey, ui::EF_SHIFT_DOWN); 918 ui::KeyEvent third_key_down(ui::ET_KEY_PRESSED, third_key, ui::EF_SHIFT_DOWN); 919 920 ui::KeyEvent third_key_up(ui::ET_KEY_RELEASED, third_key, ui::EF_SHIFT_DOWN); 921 ui::KeyEvent search_up( 922 ui::ET_KEY_RELEASED, kChromeOSSearchKey, ui::EF_SHIFT_DOWN); 923 ui ::KeyEvent shift_up(ui::ET_KEY_RELEASED, ui::VKEY_SHIFT, ui::EF_NONE); 924 925 DispatchEvent(&shift_down); 926 DispatchEvent(&search_down); 927 DispatchEvent(&third_key_down); 928 DispatchEvent(&third_key_up); 929 DispatchEvent(&search_up); 930 DispatchEvent(&shift_up); 931 } 932 933 base::Closure TouchExplorationController::BindShiftSearchKeyEvent( 934 const ui::KeyboardCode third_key) { 935 return base::Bind(&TouchExplorationController::DispatchShiftSearchKeyEvent, 936 base::Unretained(this), 937 third_key); 938 } 939 940 void TouchExplorationController::DispatchKeyWithFlags( 941 const ui::KeyboardCode key, 942 int flags) { 943 ui::KeyEvent key_down(ui::ET_KEY_PRESSED, key, flags); 944 ui::KeyEvent key_up(ui::ET_KEY_RELEASED, key, flags); 945 DispatchEvent(&key_down); 946 DispatchEvent(&key_up); 947 if(VLOG_on_) { 948 VLOG(0) << "\nKey down: key code : " << key_down.key_code() 949 << ", flags: " << key_down.flags() 950 << "\nKey up: key code : " << key_up.key_code() 951 << ", flags: " << key_up.flags(); 952 } 953 } 954 955 base::Closure TouchExplorationController::BindKeyEventWithFlags( 956 const ui::KeyboardCode key, 957 int flags) { 958 return base::Bind(&TouchExplorationController::DispatchKeyWithFlags, 959 base::Unretained(this), 960 key, 961 flags); 962 } 963 964 scoped_ptr<ui::Event> TouchExplorationController::CreateMouseMoveEvent( 965 const gfx::PointF& location, 966 int flags) { 967 // The "synthesized" flag should be set on all events that don't have a 968 // backing native event. 969 flags |= ui::EF_IS_SYNTHESIZED; 970 971 // This flag is used to identify mouse move events that were generated from 972 // touch exploration in Chrome code. 973 flags |= ui::EF_TOUCH_ACCESSIBILITY; 974 975 // TODO(dmazzoni) http://crbug.com/391008 - get rid of this hack. 976 // This is a short-term workaround for the limitation that we're using 977 // the ChromeVox content script to process touch exploration events, but 978 // ChromeVox needs a way to distinguish between a real mouse move and a 979 // mouse move generated from touch exploration, so we have touch exploration 980 // pretend that the command key was down (which becomes the "meta" key in 981 // JavaScript). We can remove this hack when the ChromeVox content script 982 // goes away and native accessibility code sends a touch exploration 983 // event to the new ChromeVox background page via the automation api. 984 flags |= ui::EF_COMMAND_DOWN; 985 986 return scoped_ptr<ui::Event>( 987 new ui::MouseEvent(ui::ET_MOUSE_MOVED, location, location, flags, 0)); 988 } 989 990 void TouchExplorationController::EnterTouchToMouseMode() { 991 aura::client::CursorClient* cursor_client = 992 aura::client::GetCursorClient(root_window_); 993 if (cursor_client && !cursor_client->IsMouseEventsEnabled()) 994 cursor_client->EnableMouseEvents(); 995 if (cursor_client && cursor_client->IsCursorVisible()) 996 cursor_client->HideCursor(); 997 } 998 999 void TouchExplorationController::SetState(State new_state, 1000 const char* function_name) { 1001 state_ = new_state; 1002 VlogState(function_name); 1003 // These are the states the user can be in that will never result in a 1004 // gesture before the user returns to NO_FINGERS_DOWN. Therefore, if the 1005 // gesture provider still exists, it's reset to NULL until the user returns 1006 // to NO_FINGERS_DOWN. 1007 switch (new_state) { 1008 case SINGLE_TAP_RELEASED: 1009 case TOUCH_EXPLORE_RELEASED: 1010 case DOUBLE_TAP_PENDING: 1011 case TOUCH_RELEASE_PENDING: 1012 case TOUCH_EXPLORATION: 1013 case TOUCH_EXPLORE_SECOND_PRESS: 1014 case ONE_FINGER_PASSTHROUGH: 1015 case CORNER_PASSTHROUGH: 1016 case WAIT_FOR_NO_FINGERS: 1017 if (gesture_provider_.get()) 1018 gesture_provider_.reset(NULL); 1019 break; 1020 case NO_FINGERS_DOWN: 1021 gesture_provider_.reset(new GestureProviderAura(this)); 1022 if (sound_timer_.IsRunning()) 1023 sound_timer_.Stop(); 1024 tap_timer_.Stop(); 1025 break; 1026 case SINGLE_TAP_PRESSED: 1027 case GESTURE_IN_PROGRESS: 1028 case SLIDE_GESTURE: 1029 case TWO_FINGER_TAP: 1030 break; 1031 } 1032 } 1033 1034 void TouchExplorationController::VlogState(const char* function_name) { 1035 if (!VLOG_on_) 1036 return; 1037 if (prev_state_ == state_) 1038 return; 1039 prev_state_ = state_; 1040 const char* state_string = EnumStateToString(state_); 1041 VLOG(0) << "\n Function name: " << function_name 1042 << "\n State: " << state_string; 1043 } 1044 1045 void TouchExplorationController::VlogEvent(const ui::TouchEvent& touch_event, 1046 const char* function_name) { 1047 if (!VLOG_on_) 1048 return; 1049 1050 if (prev_event_ != NULL && 1051 prev_event_->type() == touch_event.type() && 1052 prev_event_->touch_id() == touch_event.touch_id()){ 1053 return; 1054 } 1055 // The above statement prevents events of the same type and id from being 1056 // printed in a row. However, if two fingers are down, they would both be 1057 // moving and alternating printing move events unless we check for this. 1058 if (prev_event_ != NULL && 1059 prev_event_->type() == ET_TOUCH_MOVED && 1060 touch_event.type() == ET_TOUCH_MOVED){ 1061 return; 1062 } 1063 1064 const std::string& type = touch_event.name(); 1065 const gfx::PointF& location = touch_event.location_f(); 1066 const int touch_id = touch_event.touch_id(); 1067 1068 VLOG(0) << "\n Function name: " << function_name 1069 << "\n Event Type: " << type 1070 << "\n Location: " << location.ToString() 1071 << "\n Touch ID: " << touch_id; 1072 prev_event_.reset(new TouchEvent(touch_event)); 1073 } 1074 1075 const char* TouchExplorationController::EnumStateToString(State state) { 1076 switch (state) { 1077 case NO_FINGERS_DOWN: 1078 return "NO_FINGERS_DOWN"; 1079 case SINGLE_TAP_PRESSED: 1080 return "SINGLE_TAP_PRESSED"; 1081 case SINGLE_TAP_RELEASED: 1082 return "SINGLE_TAP_RELEASED"; 1083 case TOUCH_EXPLORE_RELEASED: 1084 return "TOUCH_EXPLORE_RELEASED"; 1085 case DOUBLE_TAP_PENDING: 1086 return "DOUBLE_TAP_PENDING"; 1087 case TOUCH_RELEASE_PENDING: 1088 return "TOUCH_RELEASE_PENDING"; 1089 case TOUCH_EXPLORATION: 1090 return "TOUCH_EXPLORATION"; 1091 case GESTURE_IN_PROGRESS: 1092 return "GESTURE_IN_PROGRESS"; 1093 case TOUCH_EXPLORE_SECOND_PRESS: 1094 return "TOUCH_EXPLORE_SECOND_PRESS"; 1095 case CORNER_PASSTHROUGH: 1096 return "CORNER_PASSTHROUGH"; 1097 case SLIDE_GESTURE: 1098 return "SLIDE_GESTURE"; 1099 case ONE_FINGER_PASSTHROUGH: 1100 return "ONE_FINGER_PASSTHROUGH"; 1101 case WAIT_FOR_NO_FINGERS: 1102 return "WAIT_FOR_NO_FINGERS"; 1103 case TWO_FINGER_TAP: 1104 return "TWO_FINGER_TAP"; 1105 } 1106 return "Not a state"; 1107 } 1108 1109 // TODO(evy, lisayin) : Just call abstracted methods on the delegate (e.g. 1110 // Swipe(Direction direction, int num_fingers)), and add the DispatchXYZ 1111 // methods to the delegate. Avoid the middle step of dispatching keys at all, 1112 // and simply have ChromeVox/ChromeOS complete the required action. 1113 1114 void TouchExplorationController::InitializeSwipeGestureMaps() { 1115 // Gestures with one finger are used for navigation. 1116 left_swipe_gestures_[1] = BindShiftSearchKeyEvent(ui::VKEY_LEFT); 1117 right_swipe_gestures_[1] = BindShiftSearchKeyEvent(ui::VKEY_RIGHT); 1118 up_swipe_gestures_[1] = BindShiftSearchKeyEvent(ui::VKEY_UP); 1119 down_swipe_gestures_[1] = BindShiftSearchKeyEvent(ui::VKEY_DOWN); 1120 1121 // Gestures with two fingers. 1122 left_swipe_gestures_[2] = 1123 BindKeyEventWithFlags(ui::VKEY_BROWSER_BACK, ui::EF_NONE); 1124 right_swipe_gestures_[2] = 1125 BindKeyEventWithFlags(ui::VKEY_BROWSER_FORWARD, ui::EF_NONE); 1126 // Jump to top. 1127 up_swipe_gestures_[2] = BindShiftSearchKeyEvent(ui::VKEY_A); 1128 // Read from here. 1129 down_swipe_gestures_[2] = BindShiftSearchKeyEvent(ui::VKEY_R); 1130 1131 // Gestures with three fingers switch tabs left/right and scroll up/down. 1132 left_swipe_gestures_[3] = BindKeyEventWithFlags( 1133 ui::VKEY_TAB, ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN); 1134 right_swipe_gestures_[3] = 1135 BindKeyEventWithFlags(ui::VKEY_TAB, ui::EF_CONTROL_DOWN); 1136 up_swipe_gestures_[3] = BindKeyEventWithFlags(ui::VKEY_NEXT, ui::EF_NONE); 1137 down_swipe_gestures_[3] = BindKeyEventWithFlags(ui::VKEY_PRIOR, ui::EF_NONE); 1138 1139 // Gestures with four fingers should probably eventually be used for rare 1140 // needs that are hard to access through menus. 1141 // Note that brightness levels are here because they can be important for low 1142 // vision users. However, none of these mappings are permanent. 1143 left_swipe_gestures_[4] = 1144 BindKeyEventWithFlags(ui::VKEY_BRIGHTNESS_DOWN, ui::EF_NONE); 1145 right_swipe_gestures_[4] = 1146 BindKeyEventWithFlags(VKEY_BRIGHTNESS_UP, ui::EF_NONE); 1147 up_swipe_gestures_[4] = BindKeyEventWithFlags(VKEY_BROWSER_HOME, ui::EF_NONE); 1148 down_swipe_gestures_[4] = 1149 BindKeyEventWithFlags(VKEY_BROWSER_REFRESH, ui::EF_NONE); 1150 } 1151 1152 const float TouchExplorationController::GetSplitTapTouchSlop() { 1153 return gesture_detector_config_.touch_slop * 3; 1154 } 1155 1156 } // namespace ui 1157