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__)
22 // In ChromeOS, VKEY_LWIN is synonymous for the search key.
23 const ui::KeyboardCode kChromeOSSearchKey
= ui::VKEY_LWIN
;
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
),
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
61 if (tap_timer_
.IsRunning() &&
62 touch_event
.time_stamp() - initial_press_
->time_stamp() >
63 gesture_detector_config_
.double_tap_timeout
) {
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
;
100 VLOG_EVENT(touch_event
);
101 // The rest of the processing depends on what state we're in.
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
);
121 return InPassthrough(touch_event
, rewritten_event
);
122 case WAIT_FOR_RELEASE
:
123 return InWaitForRelease(touch_event
, rewritten_event
);
126 return ui::EVENT_REWRITE_CONTINUE
;
129 ui::EventRewriteStatus
TouchExplorationController::NextDispatchEvent(
130 const ui::Event
& last_event
, scoped_ptr
<ui::Event
>* new_event
) {
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
,
144 &TouchExplorationController::OnTapTimerFired
);
145 gesture_provider_
.OnTouchEvent(event
);
146 gesture_provider_
.OnTouchEventAck(false);
147 ProcessGestureEvents();
148 state_
= SINGLE_TAP_PRESSED
;
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
,
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
;
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
;
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
;
199 return InGestureInProgress(event
, rewritten_event
);
201 EnterTouchToMouseMode();
202 state_
= TOUCH_EXPLORATION
;
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(),
236 event
.time_stamp()));
237 (*rewritten_event
)->set_flags(event
.flags());
238 state_
= DOUBLE_TAP_PRESSED
;
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
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
) {
287 initial_press_
.reset(new TouchEvent(event
));
288 if (tap_timer_
.IsRunning())
290 rewritten_event
->reset(
291 new ui::TouchEvent(ui::ET_TOUCH_PRESSED
,
292 last_touch_exploration_
->location(),
294 event
.time_stamp()));
295 (*rewritten_event
)->set_flags(event
.flags());
296 state_
= TOUCH_EXPLORE_SECOND_PRESS
;
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
,
304 &TouchExplorationController::OnTapTimerFired
);
305 state_
= TOUCH_EXPLORE_RELEASED
;
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())
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
,
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
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
,
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
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
,
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
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
;
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();
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
;
495 ResetToNoFingersDown();
497 return EVENT_REWRITE_DISCARD
;
500 void TouchExplorationController::OnTapTimerFired() {
502 case SINGLE_TAP_RELEASED
:
503 ResetToNoFingersDown();
505 case TOUCH_EXPLORE_RELEASED
:
506 ResetToNoFingersDown();
507 last_touch_exploration_
.reset(new TouchEvent(*initial_press_
));
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
;
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
);
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())
541 OnSwipeEvent(gesture
);
546 void TouchExplorationController::ProcessGestureEvents() {
547 scoped_ptr
<ScopedVector
<ui::GestureEvent
> > gestures(
548 gesture_provider_
.GetAndResetPendingGestures());
550 for (ScopedVector
<GestureEvent
>::iterator i
= gestures
->begin();
551 i
!= gestures
->end();
558 void TouchExplorationController::OnSwipeEvent(ui::GestureEvent
* swipe_gesture
) {
559 // A swipe gesture contains details for the direction in which the swipe
561 GestureEventDetails event_details
= swipe_gesture
->details();
562 if (event_details
.swipe_left()) {
563 DispatchShiftSearchKeyEvent(ui::VKEY_LEFT
);
565 } else if (event_details
.swipe_right()) {
566 DispatchShiftSearchKeyEvent(ui::VKEY_RIGHT
);
568 } else if (event_details
.swipe_up()) {
569 DispatchShiftSearchKeyEvent(ui::VKEY_UP
);
571 } else if (event_details
.swipe_down()) {
572 DispatchShiftSearchKeyEvent(ui::VKEY_DOWN
);
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
,
607 return scoped_ptr
<ui::Event
>(
612 flags
| ui::EF_IS_SYNTHESIZED
| ui::EF_TOUCH_ACCESSIBILITY
,
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
;
628 if (tap_timer_
.IsRunning())
632 void TouchExplorationController::VlogState(const char* function_name
) {
635 if (prev_state_
== state_
)
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
) {
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()){
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
){
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
) {
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";
694 return "PASSTHROUGH";
695 case WAIT_FOR_RELEASE
:
696 return "WAIT_FOR_RELEASE";
698 return "Not a state";