[SyncFS] Build indexes from FileTracker entries on disk.
[chromium-blink-merge.git] / ui / chromeos / touch_exploration_controller.cc
blob1a3b8fad4447ffd8eab56e8e7c1be57745109896
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.
5 #include "ui/chromeos/touch_exploration_controller.h"
7 #include "base/logging.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "ui/aura/client/cursor_client.h"
10 #include "ui/aura/window.h"
11 #include "ui/aura/window_event_dispatcher.h"
12 #include "ui/aura/window_tree_host.h"
13 #include "ui/events/event.h"
14 #include "ui/events/event_processor.h"
16 #define VLOG_STATE() if (VLOG_IS_ON(0)) VlogState(__func__)
17 #define VLOG_EVENT(event) if (VLOG_IS_ON(0)) VlogEvent(event, __func__)
19 namespace ui {
21 namespace {
22 // In ChromeOS, VKEY_LWIN is synonymous for the search key.
23 const ui::KeyboardCode kChromeOSSearchKey = ui::VKEY_LWIN;
24 } // namespace
26 TouchExplorationController::TouchExplorationController(
27 aura::Window* root_window)
28 : root_window_(root_window),
29 state_(NO_FINGERS_DOWN),
30 event_handler_for_testing_(NULL),
31 gesture_provider_(this),
32 prev_state_(NO_FINGERS_DOWN),
33 VLOG_on_(true) {
34 CHECK(root_window);
35 root_window->GetHost()->GetEventSource()->AddEventRewriter(this);
38 TouchExplorationController::~TouchExplorationController() {
39 root_window_->GetHost()->GetEventSource()->RemoveEventRewriter(this);
42 ui::EventRewriteStatus TouchExplorationController::RewriteEvent(
43 const ui::Event& event,
44 scoped_ptr<ui::Event>* rewritten_event) {
45 if (!event.IsTouchEvent()) {
46 if (event.IsKeyEvent()) {
47 const ui::KeyEvent& key_event = static_cast<const ui::KeyEvent&>(event);
48 VLOG(0) << "\nKeyboard event: " << key_event.name()
49 << "\n Key code: " << key_event.key_code()
50 << ", Flags: " << key_event.flags()
51 << ", Is char: " << key_event.is_char();
53 return ui::EVENT_REWRITE_CONTINUE;
55 const ui::TouchEvent& touch_event = static_cast<const ui::TouchEvent&>(event);
57 // If the tap timer should have fired by now but hasn't, run it now and
58 // stop the timer. This is important so that behavior is consistent with
59 // the timestamps of the events, and not dependent on the granularity of
60 // the timer.
61 if (tap_timer_.IsRunning() &&
62 touch_event.time_stamp() - initial_press_->time_stamp() >
63 gesture_detector_config_.double_tap_timeout) {
64 tap_timer_.Stop();
65 OnTapTimerFired();
66 // Note: this may change the state. We should now continue and process
67 // this event under this new state.
70 const ui::EventType type = touch_event.type();
71 const gfx::PointF& location = touch_event.location_f();
72 const int touch_id = touch_event.touch_id();
74 // Always update touch ids and touch locations, so we can use those
75 // no matter what state we're in.
76 if (type == ui::ET_TOUCH_PRESSED) {
77 current_touch_ids_.push_back(touch_id);
78 touch_locations_.insert(std::pair<int, gfx::PointF>(touch_id, location));
79 } else if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) {
80 std::vector<int>::iterator it = std::find(
81 current_touch_ids_.begin(), current_touch_ids_.end(), touch_id);
83 // Can happen if touch exploration is enabled while fingers were down.
84 if (it == current_touch_ids_.end())
85 return ui::EVENT_REWRITE_CONTINUE;
87 current_touch_ids_.erase(it);
88 touch_locations_.erase(touch_id);
89 } else if (type == ui::ET_TOUCH_MOVED) {
90 std::vector<int>::iterator it = std::find(
91 current_touch_ids_.begin(), current_touch_ids_.end(), touch_id);
93 // Can happen if touch exploration is enabled while fingers were down.
94 if (it == current_touch_ids_.end())
95 return ui::EVENT_REWRITE_CONTINUE;
97 touch_locations_[*it] = location;
99 VLOG_STATE();
100 VLOG_EVENT(touch_event);
101 // The rest of the processing depends on what state we're in.
102 switch(state_) {
103 case NO_FINGERS_DOWN:
104 return InNoFingersDown(touch_event, rewritten_event);
105 case SINGLE_TAP_PRESSED:
106 return InSingleTapPressed(touch_event, rewritten_event);
107 case SINGLE_TAP_RELEASED:
108 case TOUCH_EXPLORE_RELEASED:
109 return InSingleTapOrTouchExploreReleased(touch_event, rewritten_event);
110 case DOUBLE_TAP_PRESSED:
111 return InDoubleTapPressed(touch_event, rewritten_event);
112 case TOUCH_EXPLORATION:
113 return InTouchExploration(touch_event, rewritten_event);
114 case GESTURE_IN_PROGRESS:
115 return InGestureInProgress(touch_event, rewritten_event);
116 case TOUCH_EXPLORE_SECOND_PRESS:
117 return InTouchExploreSecondPress(touch_event, rewritten_event);
118 case TWO_TO_ONE_FINGER:
119 return InTwoToOneFinger(touch_event, rewritten_event);
120 case PASSTHROUGH:
121 return InPassthrough(touch_event, rewritten_event);
122 case WAIT_FOR_RELEASE:
123 return InWaitForRelease(touch_event, rewritten_event);
125 NOTREACHED();
126 return ui::EVENT_REWRITE_CONTINUE;
129 ui::EventRewriteStatus TouchExplorationController::NextDispatchEvent(
130 const ui::Event& last_event, scoped_ptr<ui::Event>* new_event) {
131 NOTREACHED();
132 return ui::EVENT_REWRITE_CONTINUE;
135 ui::EventRewriteStatus TouchExplorationController::InNoFingersDown(
136 const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event) {
137 const ui::EventType type = event.type();
138 if (type == ui::ET_TOUCH_PRESSED) {
139 initial_press_.reset(new TouchEvent(event));
140 last_unused_finger_event_.reset(new TouchEvent(event));
141 tap_timer_.Start(FROM_HERE,
142 gesture_detector_config_.double_tap_timeout,
143 this,
144 &TouchExplorationController::OnTapTimerFired);
145 gesture_provider_.OnTouchEvent(event);
146 gesture_provider_.OnTouchEventAck(false);
147 ProcessGestureEvents();
148 state_ = SINGLE_TAP_PRESSED;
149 VLOG_STATE();
150 return ui::EVENT_REWRITE_DISCARD;
152 NOTREACHED() << "Unexpected event type received: " << event.name();;
153 return ui::EVENT_REWRITE_CONTINUE;
156 ui::EventRewriteStatus TouchExplorationController::InSingleTapPressed(
157 const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event) {
158 const ui::EventType type = event.type();
160 if (type == ui::ET_TOUCH_PRESSED) {
161 // Adding a second finger within the timeout period switches to
162 // passing through every event from the second finger and none form the
163 // first. The event from the first finger is still saved in initial_press_.
164 state_ = TWO_TO_ONE_FINGER;
165 last_two_to_one_.reset(new TouchEvent(event));
166 rewritten_event->reset(new ui::TouchEvent(ui::ET_TOUCH_PRESSED,
167 event.location(),
168 event.touch_id(),
169 event.time_stamp()));
170 (*rewritten_event)->set_flags(event.flags());
171 return EVENT_REWRITE_REWRITTEN;
172 } else if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) {
173 DCHECK_EQ(0U, current_touch_ids_.size());
174 state_ = SINGLE_TAP_RELEASED;
175 VLOG_STATE();
176 return EVENT_REWRITE_DISCARD;
177 } else if (type == ui::ET_TOUCH_MOVED) {
178 float distance = (event.location() - initial_press_->location()).Length();
179 // If the user does not move far enough from the original position, then the
180 // resulting movement should not be considered to be a deliberate gesture or
181 // touch exploration.
182 if (distance <= gesture_detector_config_.touch_slop)
183 return EVENT_REWRITE_DISCARD;
185 float delta_time =
186 (event.time_stamp() - initial_press_->time_stamp()).InSecondsF();
187 float velocity = distance / delta_time;
188 VLOG(0) << "\n Delta time: " << delta_time
189 << "\n Distance: " << distance
190 << "\n Velocity of click: " << velocity
191 << "\n Minimum swipe velocity: "
192 << gesture_detector_config_.minimum_swipe_velocity;
194 // If the user moves fast enough from the initial touch location, start
195 // gesture detection. Otherwise, jump to the touch exploration mode early.
196 if (velocity > gesture_detector_config_.minimum_swipe_velocity) {
197 state_ = GESTURE_IN_PROGRESS;
198 VLOG_STATE();
199 return InGestureInProgress(event, rewritten_event);
201 EnterTouchToMouseMode();
202 state_ = TOUCH_EXPLORATION;
203 VLOG_STATE();
204 return InTouchExploration(event, rewritten_event);
206 NOTREACHED() << "Unexpected event type received: " << event.name();;
207 return ui::EVENT_REWRITE_CONTINUE;
210 ui::EventRewriteStatus
211 TouchExplorationController::InSingleTapOrTouchExploreReleased(
212 const ui::TouchEvent& event,
213 scoped_ptr<ui::Event>* rewritten_event) {
214 const ui::EventType type = event.type();
215 // If there is more than one finger down, then discard to wait until only one
216 // finger is or no fingers are down.
217 if (current_touch_ids_.size() > 1) {
218 state_ = WAIT_FOR_RELEASE;
219 return ui::EVENT_REWRITE_DISCARD;
221 // If there is no touch exploration yet, discard.
222 if (!last_touch_exploration_ || type == ui::ET_TOUCH_RELEASED) {
223 if (current_touch_ids_.size() == 0) {
224 ResetToNoFingersDown();
226 return ui::EVENT_REWRITE_DISCARD;
229 if (type == ui::ET_TOUCH_PRESSED) {
230 // This is the second tap in a double-tap (or double tap-hold).
231 // Rewrite at location of last touch exploration.
232 rewritten_event->reset(
233 new ui::TouchEvent(ui::ET_TOUCH_PRESSED,
234 last_touch_exploration_->location(),
235 event.touch_id(),
236 event.time_stamp()));
237 (*rewritten_event)->set_flags(event.flags());
238 state_ = DOUBLE_TAP_PRESSED;
239 VLOG_STATE();
240 return ui::EVENT_REWRITE_REWRITTEN;
241 } else if (type == ui::ET_TOUCH_RELEASED && !last_touch_exploration_) {
242 // If the previous press was discarded, we need to also handle its
243 // release.
244 if (current_touch_ids_.size() == 0) {
245 ResetToNoFingersDown();
247 return ui::EVENT_REWRITE_DISCARD;
248 } else if (type == ui::ET_TOUCH_MOVED){
249 return ui::EVENT_REWRITE_DISCARD;
251 NOTREACHED() << "Unexpected event type received: " << event.name();
252 return ui::EVENT_REWRITE_CONTINUE;
255 ui::EventRewriteStatus TouchExplorationController::InDoubleTapPressed(
256 const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event) {
257 const ui::EventType type = event.type();
258 if (type == ui::ET_TOUCH_PRESSED) {
259 return ui::EVENT_REWRITE_DISCARD;
260 } else if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) {
261 if (current_touch_ids_.size() != 0)
262 return EVENT_REWRITE_DISCARD;
264 // Rewrite release at location of last touch exploration with the same
265 // id as the previous press.
266 rewritten_event->reset(
267 new ui::TouchEvent(ui::ET_TOUCH_RELEASED,
268 last_touch_exploration_->location(),
269 initial_press_->touch_id(),
270 event.time_stamp()));
271 (*rewritten_event)->set_flags(event.flags());
272 ResetToNoFingersDown();
273 return ui::EVENT_REWRITE_REWRITTEN;
274 } else if (type == ui::ET_TOUCH_MOVED) {
275 return ui::EVENT_REWRITE_DISCARD;
277 NOTREACHED() << "Unexpected event type received: " << event.name();
278 return ui::EVENT_REWRITE_CONTINUE;
281 ui::EventRewriteStatus TouchExplorationController::InTouchExploration(
282 const ui::TouchEvent& event,
283 scoped_ptr<ui::Event>* rewritten_event) {
284 const ui::EventType type = event.type();
285 if (type == ui::ET_TOUCH_PRESSED) {
286 // Handle split-tap.
287 initial_press_.reset(new TouchEvent(event));
288 if (tap_timer_.IsRunning())
289 tap_timer_.Stop();
290 rewritten_event->reset(
291 new ui::TouchEvent(ui::ET_TOUCH_PRESSED,
292 last_touch_exploration_->location(),
293 event.touch_id(),
294 event.time_stamp()));
295 (*rewritten_event)->set_flags(event.flags());
296 state_ = TOUCH_EXPLORE_SECOND_PRESS;
297 VLOG_STATE();
298 return ui::EVENT_REWRITE_REWRITTEN;
299 } else if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) {
300 initial_press_.reset(new TouchEvent(event));
301 tap_timer_.Start(FROM_HERE,
302 gesture_detector_config_.double_tap_timeout,
303 this,
304 &TouchExplorationController::OnTapTimerFired);
305 state_ = TOUCH_EXPLORE_RELEASED;
306 VLOG_STATE();
307 } else if (type != ui::ET_TOUCH_MOVED) {
308 NOTREACHED() << "Unexpected event type received: " << event.name();
309 return ui::EVENT_REWRITE_CONTINUE;
312 // Rewrite as a mouse-move event.
313 *rewritten_event = CreateMouseMoveEvent(event.location(), event.flags());
314 last_touch_exploration_.reset(new TouchEvent(event));
315 return ui::EVENT_REWRITE_REWRITTEN;
318 ui::EventRewriteStatus TouchExplorationController::InGestureInProgress(
319 const ui::TouchEvent& event,
320 scoped_ptr<ui::Event>* rewritten_event) {
321 ui::EventType type = event.type();
322 // If additional fingers are added before a swipe gesture has been registered,
323 // then the state will no longer be GESTURE_IN_PROGRESS.
324 if (type == ui::ET_TOUCH_PRESSED ||
325 event.touch_id() != initial_press_->touch_id()) {
326 if (tap_timer_.IsRunning())
327 tap_timer_.Stop();
328 // Discard any pending gestures.
329 ignore_result(gesture_provider_.GetAndResetPendingGestures());
330 state_ = TWO_TO_ONE_FINGER;
331 last_two_to_one_.reset(new TouchEvent(event));
332 rewritten_event->reset(new ui::TouchEvent(ui::ET_TOUCH_PRESSED,
333 event.location(),
334 event.touch_id(),
335 event.time_stamp()));
336 (*rewritten_event)->set_flags(event.flags());
337 return EVENT_REWRITE_REWRITTEN;
340 // There should not be more than one finger down.
341 DCHECK(current_touch_ids_.size() <= 1);
342 if (type == ui::ET_TOUCH_MOVED) {
343 gesture_provider_.OnTouchEvent(event);
344 gesture_provider_.OnTouchEventAck(false);
346 if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) {
347 gesture_provider_.OnTouchEvent(event);
348 gesture_provider_.OnTouchEventAck(false);
349 if (current_touch_ids_.size() == 0)
350 ResetToNoFingersDown();
353 ProcessGestureEvents();
354 return ui::EVENT_REWRITE_DISCARD;
357 ui::EventRewriteStatus TouchExplorationController::InTwoToOneFinger(
358 const ui::TouchEvent& event,
359 scoped_ptr<ui::Event>* rewritten_event) {
360 // The user should only ever be in TWO_TO_ONE_FINGER with two fingers down.
361 // If the user added or removed a finger, the state is changed.
362 ui::EventType type = event.type();
363 if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) {
364 DCHECK(current_touch_ids_.size() == 1);
365 // Stop passing through the second finger and go to the wait state.
366 if (current_touch_ids_.size() == 1) {
367 rewritten_event->reset(new ui::TouchEvent(ui::ET_TOUCH_RELEASED,
368 last_two_to_one_->location(),
369 last_two_to_one_->touch_id(),
370 event.time_stamp()));
371 (*rewritten_event)->set_flags(event.flags());
372 state_ = WAIT_FOR_RELEASE;
373 return ui::EVENT_REWRITE_REWRITTEN;
375 } else if (type == ui::ET_TOUCH_PRESSED) {
376 DCHECK(current_touch_ids_.size() == 3);
377 // If a third finger is pressed, we are now going into passthrough mode
378 // and now need to dispatch the first finger into a press, as well as the
379 // recent press.
380 if (current_touch_ids_.size() == 3){
381 state_ = PASSTHROUGH;
382 scoped_ptr<ui::TouchEvent> first_finger_press;
383 first_finger_press.reset(
384 new ui::TouchEvent(ui::ET_TOUCH_PRESSED,
385 last_unused_finger_event_->location(),
386 last_unused_finger_event_->touch_id(),
387 event.time_stamp()));
388 DispatchEvent(first_finger_press.get());
389 rewritten_event->reset(new ui::TouchEvent(ui::ET_TOUCH_PRESSED,
390 event.location(),
391 event.touch_id(),
392 event.time_stamp()));
393 (*rewritten_event)->set_flags(event.flags());
394 return ui::EVENT_REWRITE_REWRITTEN;
396 } else if (type == ui::ET_TOUCH_MOVED) {
397 DCHECK(current_touch_ids_.size() == 2);
398 // The first finger should have no events pass through, but for a proper
399 // conversion to passthrough, the press of the initial finger should
400 // be updated.
401 if (event.touch_id() == last_unused_finger_event_->touch_id()) {
402 last_unused_finger_event_.reset(new TouchEvent(event));
403 return ui::EVENT_REWRITE_DISCARD;
405 if (event.touch_id() == last_two_to_one_->touch_id()) {
406 last_two_to_one_.reset(new TouchEvent(event));
407 rewritten_event->reset(new ui::TouchEvent(ui::ET_TOUCH_MOVED,
408 event.location(),
409 event.touch_id(),
410 event.time_stamp()));
411 (*rewritten_event)->set_flags(event.flags());
412 return ui::EVENT_REWRITE_REWRITTEN;
415 NOTREACHED() << "Unexpected event type received: " << event.name();
416 return ui::EVENT_REWRITE_CONTINUE;
419 ui::EventRewriteStatus TouchExplorationController::InPassthrough(
420 const ui::TouchEvent& event,
421 scoped_ptr<ui::Event>* rewritten_event) {
422 ui::EventType type = event.type();
424 if (!(type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED ||
425 type == ui::ET_TOUCH_MOVED || type == ui::ET_TOUCH_PRESSED)) {
426 NOTREACHED() << "Unexpected event type received: " << event.name();
427 return ui::EVENT_REWRITE_CONTINUE;
430 rewritten_event->reset(new ui::TouchEvent(
431 type, event.location(), event.touch_id(), event.time_stamp()));
432 (*rewritten_event)->set_flags(event.flags());
434 if (current_touch_ids_.size() == 0) {
435 ResetToNoFingersDown();
438 return ui::EVENT_REWRITE_REWRITTEN;
441 ui::EventRewriteStatus TouchExplorationController::InTouchExploreSecondPress(
442 const ui::TouchEvent& event,
443 scoped_ptr<ui::Event>* rewritten_event) {
444 ui::EventType type = event.type();
445 gfx::PointF location = event.location_f();
446 if (type == ui::ET_TOUCH_PRESSED) {
447 return ui::EVENT_REWRITE_DISCARD;
448 } else if (type == ui::ET_TOUCH_MOVED) {
449 // Currently this is a discard, but could be something like rotor
450 // in the future.
451 return ui::EVENT_REWRITE_DISCARD;
452 } else if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) {
453 // If the touch exploration finger is lifted, there is no option to return
454 // to touch explore anymore. The remaining finger acts as a pending
455 // tap or long tap for the last touch explore location.
456 if (event.touch_id() == last_touch_exploration_->touch_id()){
457 state_ = DOUBLE_TAP_PRESSED;
458 VLOG_STATE();
459 return EVENT_REWRITE_DISCARD;
462 // Continue to release the touch only if the touch explore finger is the
463 // only finger remaining.
464 if (current_touch_ids_.size() != 1)
465 return EVENT_REWRITE_DISCARD;
467 // Rewrite at location of last touch exploration.
468 rewritten_event->reset(
469 new ui::TouchEvent(ui::ET_TOUCH_RELEASED,
470 last_touch_exploration_->location(),
471 initial_press_->touch_id(),
472 event.time_stamp()));
473 (*rewritten_event)->set_flags(event.flags());
474 state_ = TOUCH_EXPLORATION;
475 EnterTouchToMouseMode();
476 VLOG_STATE();
477 return ui::EVENT_REWRITE_REWRITTEN;
479 NOTREACHED() << "Unexpected event type received: " << event.name();
480 return ui::EVENT_REWRITE_CONTINUE;
483 ui::EventRewriteStatus TouchExplorationController::InWaitForRelease(
484 const ui::TouchEvent& event,
485 scoped_ptr<ui::Event>* rewritten_event) {
486 ui::EventType type = event.type();
487 if (!(type == ui::ET_TOUCH_PRESSED || type == ui::ET_TOUCH_MOVED ||
488 type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED)) {
489 NOTREACHED() << "Unexpected event type received: " << event.name();
490 return ui::EVENT_REWRITE_CONTINUE;
492 if (current_touch_ids_.size() == 0) {
493 state_ = NO_FINGERS_DOWN;
494 VLOG_STATE();
495 ResetToNoFingersDown();
497 return EVENT_REWRITE_DISCARD;
500 void TouchExplorationController::OnTapTimerFired() {
501 switch (state_) {
502 case SINGLE_TAP_RELEASED:
503 ResetToNoFingersDown();
504 break;
505 case TOUCH_EXPLORE_RELEASED:
506 ResetToNoFingersDown();
507 last_touch_exploration_.reset(new TouchEvent(*initial_press_));
508 return;
509 case SINGLE_TAP_PRESSED:
510 case GESTURE_IN_PROGRESS:
511 // Discard any pending gestures.
512 ignore_result(gesture_provider_.GetAndResetPendingGestures());
513 EnterTouchToMouseMode();
514 state_ = TOUCH_EXPLORATION;
515 VLOG_STATE();
516 break;
517 default:
518 return;
520 scoped_ptr<ui::Event> mouse_move =
521 CreateMouseMoveEvent(initial_press_->location(), initial_press_->flags());
522 DispatchEvent(mouse_move.get());
523 last_touch_exploration_.reset(new TouchEvent(*initial_press_));
526 void TouchExplorationController::DispatchEvent(ui::Event* event) {
527 if (event_handler_for_testing_) {
528 event_handler_for_testing_->OnEvent(event);
529 return;
531 ui::EventDispatchDetails result ALLOW_UNUSED =
532 root_window_->GetHost()->dispatcher()->OnEventFromSource(event);
535 void TouchExplorationController::OnGestureEvent(ui::GestureEvent* gesture) {
536 CHECK(gesture->IsGestureEvent());
537 VLOG(0) << " \n Gesture Triggered: " << gesture->name();
538 if (gesture->type() == ui::ET_GESTURE_SWIPE) {
539 if (tap_timer_.IsRunning())
540 tap_timer_.Stop();
541 OnSwipeEvent(gesture);
542 return;
546 void TouchExplorationController::ProcessGestureEvents() {
547 scoped_ptr<ScopedVector<ui::GestureEvent> > gestures(
548 gesture_provider_.GetAndResetPendingGestures());
549 if (gestures) {
550 for (ScopedVector<GestureEvent>::iterator i = gestures->begin();
551 i != gestures->end();
552 ++i) {
553 OnGestureEvent(*i);
558 void TouchExplorationController::OnSwipeEvent(ui::GestureEvent* swipe_gesture) {
559 // A swipe gesture contains details for the direction in which the swipe
560 // occurred.
561 GestureEventDetails event_details = swipe_gesture->details();
562 if (event_details.swipe_left()) {
563 DispatchShiftSearchKeyEvent(ui::VKEY_LEFT);
564 return;
565 } else if (event_details.swipe_right()) {
566 DispatchShiftSearchKeyEvent(ui::VKEY_RIGHT);
567 return;
568 } else if (event_details.swipe_up()) {
569 DispatchShiftSearchKeyEvent(ui::VKEY_UP);
570 return;
571 } else if (event_details.swipe_down()) {
572 DispatchShiftSearchKeyEvent(ui::VKEY_DOWN);
573 return;
577 void TouchExplorationController::DispatchShiftSearchKeyEvent(
578 const ui::KeyboardCode direction) {
579 // In order to activate the shortcut shift+search+<arrow key>
580 // three KeyPressed events must be dispatched in succession along
581 // with three KeyReleased events.
582 ui::KeyEvent shift_down = ui::KeyEvent(
583 ui::ET_KEY_PRESSED, ui::VKEY_SHIFT, ui::EF_SHIFT_DOWN, false);
584 ui::KeyEvent search_down = ui::KeyEvent(
585 ui::ET_KEY_PRESSED, kChromeOSSearchKey, ui::EF_SHIFT_DOWN, false);
586 ui::KeyEvent direction_down =
587 ui::KeyEvent(ui::ET_KEY_PRESSED, direction, ui::EF_SHIFT_DOWN, false);
589 ui::KeyEvent direction_up =
590 ui::KeyEvent(ui::ET_KEY_RELEASED, direction, ui::EF_SHIFT_DOWN, false);
591 ui::KeyEvent search_up = ui::KeyEvent(
592 ui::ET_KEY_RELEASED, kChromeOSSearchKey, ui::EF_SHIFT_DOWN, false);
593 ui::KeyEvent shift_up =
594 ui::KeyEvent(ui::ET_KEY_RELEASED, ui::VKEY_SHIFT, ui::EF_NONE, false);
596 DispatchEvent(&shift_down);
597 DispatchEvent(&search_down);
598 DispatchEvent(&direction_down);
599 DispatchEvent(&direction_up);
600 DispatchEvent(&search_up);
601 DispatchEvent(&shift_up);
604 scoped_ptr<ui::Event> TouchExplorationController::CreateMouseMoveEvent(
605 const gfx::PointF& location,
606 int flags) {
607 return scoped_ptr<ui::Event>(
608 new ui::MouseEvent(
609 ui::ET_MOUSE_MOVED,
610 location,
611 location,
612 flags | ui::EF_IS_SYNTHESIZED | ui::EF_TOUCH_ACCESSIBILITY,
613 0));
616 void TouchExplorationController::EnterTouchToMouseMode() {
617 aura::client::CursorClient* cursor_client =
618 aura::client::GetCursorClient(root_window_);
619 if (cursor_client && !cursor_client->IsMouseEventsEnabled())
620 cursor_client->EnableMouseEvents();
621 if (cursor_client && cursor_client->IsCursorVisible())
622 cursor_client->HideCursor();
625 void TouchExplorationController::ResetToNoFingersDown() {
626 state_ = NO_FINGERS_DOWN;
627 VLOG_STATE();
628 if (tap_timer_.IsRunning())
629 tap_timer_.Stop();
632 void TouchExplorationController::VlogState(const char* function_name) {
633 if (!VLOG_on_)
634 return;
635 if (prev_state_ == state_)
636 return;
637 prev_state_ = state_;
638 const char* state_string = EnumStateToString(state_);
639 VLOG(0) << "\n Function name: " << function_name
640 << "\n State: " << state_string;
643 void TouchExplorationController::VlogEvent(const ui::TouchEvent& touch_event,
644 const char* function_name) {
645 if (!VLOG_on_)
646 return;
648 CHECK(touch_event.IsTouchEvent());
649 if (prev_event_ != NULL &&
650 prev_event_->type() == touch_event.type() &&
651 prev_event_->touch_id() == touch_event.touch_id()){
652 return;
654 // The above statement prevents events of the same type and id from being
655 // printed in a row. However, if two fingers are down, they would both be
656 // moving and alternating printing move events unless we check for this.
657 if (prev_event_ != NULL &&
658 prev_event_->type() == ET_TOUCH_MOVED &&
659 touch_event.type() == ET_TOUCH_MOVED){
660 return;
662 const std::string& type = touch_event.name();
663 const gfx::PointF& location = touch_event.location_f();
664 const int touch_id = touch_event.touch_id();
666 VLOG(0) << "\n Function name: " << function_name
667 << "\n Event Type: " << type
668 << "\n Location: " << location.ToString()
669 << "\n Touch ID: " << touch_id;
670 prev_event_.reset(new TouchEvent(touch_event));
673 const char* TouchExplorationController::EnumStateToString(State state) {
674 switch (state) {
675 case NO_FINGERS_DOWN:
676 return "NO_FINGERS_DOWN";
677 case SINGLE_TAP_PRESSED:
678 return "SINGLE_TAP_PRESSED";
679 case SINGLE_TAP_RELEASED:
680 return "SINGLE_TAP_RELEASED";
681 case TOUCH_EXPLORE_RELEASED:
682 return "TOUCH_EXPLORE_RELEASED";
683 case DOUBLE_TAP_PRESSED:
684 return "DOUBLE_TAP_PRESSED";
685 case TOUCH_EXPLORATION:
686 return "TOUCH_EXPLORATION";
687 case GESTURE_IN_PROGRESS:
688 return "GESTURE_IN_PROGRESS";
689 case TOUCH_EXPLORE_SECOND_PRESS:
690 return "TOUCH_EXPLORE_SECOND_PRESS";
691 case TWO_TO_ONE_FINGER:
692 return "TWO_TO_ONE_FINGER";
693 case PASSTHROUGH:
694 return "PASSTHROUGH";
695 case WAIT_FOR_RELEASE:
696 return "WAIT_FOR_RELEASE";
698 return "Not a state";
701 } // namespace ui