blob: 1a3b8fad4447ffd8eab56e8e7c1be57745109896 [file]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/chromeos/touch_exploration_controller.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "ui/aura/client/cursor_client.h"
#include "ui/aura/window.h"
#include "ui/aura/window_event_dispatcher.h"
#include "ui/aura/window_tree_host.h"
#include "ui/events/event.h"
#include "ui/events/event_processor.h"
#define VLOG_STATE() if (VLOG_IS_ON(0)) VlogState(__func__)
#define VLOG_EVENT(event) if (VLOG_IS_ON(0)) VlogEvent(event, __func__)
namespace ui {
namespace {
// In ChromeOS, VKEY_LWIN is synonymous for the search key.
const ui::KeyboardCode kChromeOSSearchKey = ui::VKEY_LWIN;
} // namespace
TouchExplorationController::TouchExplorationController(
aura::Window* root_window)
: root_window_(root_window),
state_(NO_FINGERS_DOWN),
event_handler_for_testing_(NULL),
gesture_provider_(this),
prev_state_(NO_FINGERS_DOWN),
VLOG_on_(true) {
CHECK(root_window);
root_window->GetHost()->GetEventSource()->AddEventRewriter(this);
}
TouchExplorationController::~TouchExplorationController() {
root_window_->GetHost()->GetEventSource()->RemoveEventRewriter(this);
}
ui::EventRewriteStatus TouchExplorationController::RewriteEvent(
const ui::Event& event,
scoped_ptr<ui::Event>* rewritten_event) {
if (!event.IsTouchEvent()) {
if (event.IsKeyEvent()) {
const ui::KeyEvent& key_event = static_cast<const ui::KeyEvent&>(event);
VLOG(0) << "\nKeyboard event: " << key_event.name()
<< "\n Key code: " << key_event.key_code()
<< ", Flags: " << key_event.flags()
<< ", Is char: " << key_event.is_char();
}
return ui::EVENT_REWRITE_CONTINUE;
}
const ui::TouchEvent& touch_event = static_cast<const ui::TouchEvent&>(event);
// If the tap timer should have fired by now but hasn't, run it now and
// stop the timer. This is important so that behavior is consistent with
// the timestamps of the events, and not dependent on the granularity of
// the timer.
if (tap_timer_.IsRunning() &&
touch_event.time_stamp() - initial_press_->time_stamp() >
gesture_detector_config_.double_tap_timeout) {
tap_timer_.Stop();
OnTapTimerFired();
// Note: this may change the state. We should now continue and process
// this event under this new state.
}
const ui::EventType type = touch_event.type();
const gfx::PointF& location = touch_event.location_f();
const int touch_id = touch_event.touch_id();
// Always update touch ids and touch locations, so we can use those
// no matter what state we're in.
if (type == ui::ET_TOUCH_PRESSED) {
current_touch_ids_.push_back(touch_id);
touch_locations_.insert(std::pair<int, gfx::PointF>(touch_id, location));
} else if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) {
std::vector<int>::iterator it = std::find(
current_touch_ids_.begin(), current_touch_ids_.end(), touch_id);
// Can happen if touch exploration is enabled while fingers were down.
if (it == current_touch_ids_.end())
return ui::EVENT_REWRITE_CONTINUE;
current_touch_ids_.erase(it);
touch_locations_.erase(touch_id);
} else if (type == ui::ET_TOUCH_MOVED) {
std::vector<int>::iterator it = std::find(
current_touch_ids_.begin(), current_touch_ids_.end(), touch_id);
// Can happen if touch exploration is enabled while fingers were down.
if (it == current_touch_ids_.end())
return ui::EVENT_REWRITE_CONTINUE;
touch_locations_[*it] = location;
}
VLOG_STATE();
VLOG_EVENT(touch_event);
// The rest of the processing depends on what state we're in.
switch(state_) {
case NO_FINGERS_DOWN:
return InNoFingersDown(touch_event, rewritten_event);
case SINGLE_TAP_PRESSED:
return InSingleTapPressed(touch_event, rewritten_event);
case SINGLE_TAP_RELEASED:
case TOUCH_EXPLORE_RELEASED:
return InSingleTapOrTouchExploreReleased(touch_event, rewritten_event);
case DOUBLE_TAP_PRESSED:
return InDoubleTapPressed(touch_event, rewritten_event);
case TOUCH_EXPLORATION:
return InTouchExploration(touch_event, rewritten_event);
case GESTURE_IN_PROGRESS:
return InGestureInProgress(touch_event, rewritten_event);
case TOUCH_EXPLORE_SECOND_PRESS:
return InTouchExploreSecondPress(touch_event, rewritten_event);
case TWO_TO_ONE_FINGER:
return InTwoToOneFinger(touch_event, rewritten_event);
case PASSTHROUGH:
return InPassthrough(touch_event, rewritten_event);
case WAIT_FOR_RELEASE:
return InWaitForRelease(touch_event, rewritten_event);
}
NOTREACHED();
return ui::EVENT_REWRITE_CONTINUE;
}
ui::EventRewriteStatus TouchExplorationController::NextDispatchEvent(
const ui::Event& last_event, scoped_ptr<ui::Event>* new_event) {
NOTREACHED();
return ui::EVENT_REWRITE_CONTINUE;
}
ui::EventRewriteStatus TouchExplorationController::InNoFingersDown(
const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event) {
const ui::EventType type = event.type();
if (type == ui::ET_TOUCH_PRESSED) {
initial_press_.reset(new TouchEvent(event));
last_unused_finger_event_.reset(new TouchEvent(event));
tap_timer_.Start(FROM_HERE,
gesture_detector_config_.double_tap_timeout,
this,
&TouchExplorationController::OnTapTimerFired);
gesture_provider_.OnTouchEvent(event);
gesture_provider_.OnTouchEventAck(false);
ProcessGestureEvents();
state_ = SINGLE_TAP_PRESSED;
VLOG_STATE();
return ui::EVENT_REWRITE_DISCARD;
}
NOTREACHED() << "Unexpected event type received: " << event.name();;
return ui::EVENT_REWRITE_CONTINUE;
}
ui::EventRewriteStatus TouchExplorationController::InSingleTapPressed(
const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event) {
const ui::EventType type = event.type();
if (type == ui::ET_TOUCH_PRESSED) {
// Adding a second finger within the timeout period switches to
// passing through every event from the second finger and none form the
// first. The event from the first finger is still saved in initial_press_.
state_ = TWO_TO_ONE_FINGER;
last_two_to_one_.reset(new TouchEvent(event));
rewritten_event->reset(new ui::TouchEvent(ui::ET_TOUCH_PRESSED,
event.location(),
event.touch_id(),
event.time_stamp()));
(*rewritten_event)->set_flags(event.flags());
return EVENT_REWRITE_REWRITTEN;
} else if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) {
DCHECK_EQ(0U, current_touch_ids_.size());
state_ = SINGLE_TAP_RELEASED;
VLOG_STATE();
return EVENT_REWRITE_DISCARD;
} else if (type == ui::ET_TOUCH_MOVED) {
float distance = (event.location() - initial_press_->location()).Length();
// If the user does not move far enough from the original position, then the
// resulting movement should not be considered to be a deliberate gesture or
// touch exploration.
if (distance <= gesture_detector_config_.touch_slop)
return EVENT_REWRITE_DISCARD;
float delta_time =
(event.time_stamp() - initial_press_->time_stamp()).InSecondsF();
float velocity = distance / delta_time;
VLOG(0) << "\n Delta time: " << delta_time
<< "\n Distance: " << distance
<< "\n Velocity of click: " << velocity
<< "\n Minimum swipe velocity: "
<< gesture_detector_config_.minimum_swipe_velocity;
// If the user moves fast enough from the initial touch location, start
// gesture detection. Otherwise, jump to the touch exploration mode early.
if (velocity > gesture_detector_config_.minimum_swipe_velocity) {
state_ = GESTURE_IN_PROGRESS;
VLOG_STATE();
return InGestureInProgress(event, rewritten_event);
}
EnterTouchToMouseMode();
state_ = TOUCH_EXPLORATION;
VLOG_STATE();
return InTouchExploration(event, rewritten_event);
}
NOTREACHED() << "Unexpected event type received: " << event.name();;
return ui::EVENT_REWRITE_CONTINUE;
}
ui::EventRewriteStatus
TouchExplorationController::InSingleTapOrTouchExploreReleased(
const ui::TouchEvent& event,
scoped_ptr<ui::Event>* rewritten_event) {
const ui::EventType type = event.type();
// If there is more than one finger down, then discard to wait until only one
// finger is or no fingers are down.
if (current_touch_ids_.size() > 1) {
state_ = WAIT_FOR_RELEASE;
return ui::EVENT_REWRITE_DISCARD;
}
// If there is no touch exploration yet, discard.
if (!last_touch_exploration_ || type == ui::ET_TOUCH_RELEASED) {
if (current_touch_ids_.size() == 0) {
ResetToNoFingersDown();
}
return ui::EVENT_REWRITE_DISCARD;
}
if (type == ui::ET_TOUCH_PRESSED) {
// This is the second tap in a double-tap (or double tap-hold).
// Rewrite at location of last touch exploration.
rewritten_event->reset(
new ui::TouchEvent(ui::ET_TOUCH_PRESSED,
last_touch_exploration_->location(),
event.touch_id(),
event.time_stamp()));
(*rewritten_event)->set_flags(event.flags());
state_ = DOUBLE_TAP_PRESSED;
VLOG_STATE();
return ui::EVENT_REWRITE_REWRITTEN;
} else if (type == ui::ET_TOUCH_RELEASED && !last_touch_exploration_) {
// If the previous press was discarded, we need to also handle its
// release.
if (current_touch_ids_.size() == 0) {
ResetToNoFingersDown();
}
return ui::EVENT_REWRITE_DISCARD;
} else if (type == ui::ET_TOUCH_MOVED){
return ui::EVENT_REWRITE_DISCARD;
}
NOTREACHED() << "Unexpected event type received: " << event.name();
return ui::EVENT_REWRITE_CONTINUE;
}
ui::EventRewriteStatus TouchExplorationController::InDoubleTapPressed(
const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event) {
const ui::EventType type = event.type();
if (type == ui::ET_TOUCH_PRESSED) {
return ui::EVENT_REWRITE_DISCARD;
} else if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) {
if (current_touch_ids_.size() != 0)
return EVENT_REWRITE_DISCARD;
// Rewrite release at location of last touch exploration with the same
// id as the previous press.
rewritten_event->reset(
new ui::TouchEvent(ui::ET_TOUCH_RELEASED,
last_touch_exploration_->location(),
initial_press_->touch_id(),
event.time_stamp()));
(*rewritten_event)->set_flags(event.flags());
ResetToNoFingersDown();
return ui::EVENT_REWRITE_REWRITTEN;
} else if (type == ui::ET_TOUCH_MOVED) {
return ui::EVENT_REWRITE_DISCARD;
}
NOTREACHED() << "Unexpected event type received: " << event.name();
return ui::EVENT_REWRITE_CONTINUE;
}
ui::EventRewriteStatus TouchExplorationController::InTouchExploration(
const ui::TouchEvent& event,
scoped_ptr<ui::Event>* rewritten_event) {
const ui::EventType type = event.type();
if (type == ui::ET_TOUCH_PRESSED) {
// Handle split-tap.
initial_press_.reset(new TouchEvent(event));
if (tap_timer_.IsRunning())
tap_timer_.Stop();
rewritten_event->reset(
new ui::TouchEvent(ui::ET_TOUCH_PRESSED,
last_touch_exploration_->location(),
event.touch_id(),
event.time_stamp()));
(*rewritten_event)->set_flags(event.flags());
state_ = TOUCH_EXPLORE_SECOND_PRESS;
VLOG_STATE();
return ui::EVENT_REWRITE_REWRITTEN;
} else if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) {
initial_press_.reset(new TouchEvent(event));
tap_timer_.Start(FROM_HERE,
gesture_detector_config_.double_tap_timeout,
this,
&TouchExplorationController::OnTapTimerFired);
state_ = TOUCH_EXPLORE_RELEASED;
VLOG_STATE();
} else if (type != ui::ET_TOUCH_MOVED) {
NOTREACHED() << "Unexpected event type received: " << event.name();
return ui::EVENT_REWRITE_CONTINUE;
}
// Rewrite as a mouse-move event.
*rewritten_event = CreateMouseMoveEvent(event.location(), event.flags());
last_touch_exploration_.reset(new TouchEvent(event));
return ui::EVENT_REWRITE_REWRITTEN;
}
ui::EventRewriteStatus TouchExplorationController::InGestureInProgress(
const ui::TouchEvent& event,
scoped_ptr<ui::Event>* rewritten_event) {
ui::EventType type = event.type();
// If additional fingers are added before a swipe gesture has been registered,
// then the state will no longer be GESTURE_IN_PROGRESS.
if (type == ui::ET_TOUCH_PRESSED ||
event.touch_id() != initial_press_->touch_id()) {
if (tap_timer_.IsRunning())
tap_timer_.Stop();
// Discard any pending gestures.
ignore_result(gesture_provider_.GetAndResetPendingGestures());
state_ = TWO_TO_ONE_FINGER;
last_two_to_one_.reset(new TouchEvent(event));
rewritten_event->reset(new ui::TouchEvent(ui::ET_TOUCH_PRESSED,
event.location(),
event.touch_id(),
event.time_stamp()));
(*rewritten_event)->set_flags(event.flags());
return EVENT_REWRITE_REWRITTEN;
}
// There should not be more than one finger down.
DCHECK(current_touch_ids_.size() <= 1);
if (type == ui::ET_TOUCH_MOVED) {
gesture_provider_.OnTouchEvent(event);
gesture_provider_.OnTouchEventAck(false);
}
if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) {
gesture_provider_.OnTouchEvent(event);
gesture_provider_.OnTouchEventAck(false);
if (current_touch_ids_.size() == 0)
ResetToNoFingersDown();
}
ProcessGestureEvents();
return ui::EVENT_REWRITE_DISCARD;
}
ui::EventRewriteStatus TouchExplorationController::InTwoToOneFinger(
const ui::TouchEvent& event,
scoped_ptr<ui::Event>* rewritten_event) {
// The user should only ever be in TWO_TO_ONE_FINGER with two fingers down.
// If the user added or removed a finger, the state is changed.
ui::EventType type = event.type();
if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) {
DCHECK(current_touch_ids_.size() == 1);
// Stop passing through the second finger and go to the wait state.
if (current_touch_ids_.size() == 1) {
rewritten_event->reset(new ui::TouchEvent(ui::ET_TOUCH_RELEASED,
last_two_to_one_->location(),
last_two_to_one_->touch_id(),
event.time_stamp()));
(*rewritten_event)->set_flags(event.flags());
state_ = WAIT_FOR_RELEASE;
return ui::EVENT_REWRITE_REWRITTEN;
}
} else if (type == ui::ET_TOUCH_PRESSED) {
DCHECK(current_touch_ids_.size() == 3);
// If a third finger is pressed, we are now going into passthrough mode
// and now need to dispatch the first finger into a press, as well as the
// recent press.
if (current_touch_ids_.size() == 3){
state_ = PASSTHROUGH;
scoped_ptr<ui::TouchEvent> first_finger_press;
first_finger_press.reset(
new ui::TouchEvent(ui::ET_TOUCH_PRESSED,
last_unused_finger_event_->location(),
last_unused_finger_event_->touch_id(),
event.time_stamp()));
DispatchEvent(first_finger_press.get());
rewritten_event->reset(new ui::TouchEvent(ui::ET_TOUCH_PRESSED,
event.location(),
event.touch_id(),
event.time_stamp()));
(*rewritten_event)->set_flags(event.flags());
return ui::EVENT_REWRITE_REWRITTEN;
}
} else if (type == ui::ET_TOUCH_MOVED) {
DCHECK(current_touch_ids_.size() == 2);
// The first finger should have no events pass through, but for a proper
// conversion to passthrough, the press of the initial finger should
// be updated.
if (event.touch_id() == last_unused_finger_event_->touch_id()) {
last_unused_finger_event_.reset(new TouchEvent(event));
return ui::EVENT_REWRITE_DISCARD;
}
if (event.touch_id() == last_two_to_one_->touch_id()) {
last_two_to_one_.reset(new TouchEvent(event));
rewritten_event->reset(new ui::TouchEvent(ui::ET_TOUCH_MOVED,
event.location(),
event.touch_id(),
event.time_stamp()));
(*rewritten_event)->set_flags(event.flags());
return ui::EVENT_REWRITE_REWRITTEN;
}
}
NOTREACHED() << "Unexpected event type received: " << event.name();
return ui::EVENT_REWRITE_CONTINUE;
}
ui::EventRewriteStatus TouchExplorationController::InPassthrough(
const ui::TouchEvent& event,
scoped_ptr<ui::Event>* rewritten_event) {
ui::EventType type = event.type();
if (!(type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED ||
type == ui::ET_TOUCH_MOVED || type == ui::ET_TOUCH_PRESSED)) {
NOTREACHED() << "Unexpected event type received: " << event.name();
return ui::EVENT_REWRITE_CONTINUE;
}
rewritten_event->reset(new ui::TouchEvent(
type, event.location(), event.touch_id(), event.time_stamp()));
(*rewritten_event)->set_flags(event.flags());
if (current_touch_ids_.size() == 0) {
ResetToNoFingersDown();
}
return ui::EVENT_REWRITE_REWRITTEN;
}
ui::EventRewriteStatus TouchExplorationController::InTouchExploreSecondPress(
const ui::TouchEvent& event,
scoped_ptr<ui::Event>* rewritten_event) {
ui::EventType type = event.type();
gfx::PointF location = event.location_f();
if (type == ui::ET_TOUCH_PRESSED) {
return ui::EVENT_REWRITE_DISCARD;
} else if (type == ui::ET_TOUCH_MOVED) {
// Currently this is a discard, but could be something like rotor
// in the future.
return ui::EVENT_REWRITE_DISCARD;
} else if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) {
// If the touch exploration finger is lifted, there is no option to return
// to touch explore anymore. The remaining finger acts as a pending
// tap or long tap for the last touch explore location.
if (event.touch_id() == last_touch_exploration_->touch_id()){
state_ = DOUBLE_TAP_PRESSED;
VLOG_STATE();
return EVENT_REWRITE_DISCARD;
}
// Continue to release the touch only if the touch explore finger is the
// only finger remaining.
if (current_touch_ids_.size() != 1)
return EVENT_REWRITE_DISCARD;
// Rewrite at location of last touch exploration.
rewritten_event->reset(
new ui::TouchEvent(ui::ET_TOUCH_RELEASED,
last_touch_exploration_->location(),
initial_press_->touch_id(),
event.time_stamp()));
(*rewritten_event)->set_flags(event.flags());
state_ = TOUCH_EXPLORATION;
EnterTouchToMouseMode();
VLOG_STATE();
return ui::EVENT_REWRITE_REWRITTEN;
}
NOTREACHED() << "Unexpected event type received: " << event.name();
return ui::EVENT_REWRITE_CONTINUE;
}
ui::EventRewriteStatus TouchExplorationController::InWaitForRelease(
const ui::TouchEvent& event,
scoped_ptr<ui::Event>* rewritten_event) {
ui::EventType type = event.type();
if (!(type == ui::ET_TOUCH_PRESSED || type == ui::ET_TOUCH_MOVED ||
type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED)) {
NOTREACHED() << "Unexpected event type received: " << event.name();
return ui::EVENT_REWRITE_CONTINUE;
}
if (current_touch_ids_.size() == 0) {
state_ = NO_FINGERS_DOWN;
VLOG_STATE();
ResetToNoFingersDown();
}
return EVENT_REWRITE_DISCARD;
}
void TouchExplorationController::OnTapTimerFired() {
switch (state_) {
case SINGLE_TAP_RELEASED:
ResetToNoFingersDown();
break;
case TOUCH_EXPLORE_RELEASED:
ResetToNoFingersDown();
last_touch_exploration_.reset(new TouchEvent(*initial_press_));
return;
case SINGLE_TAP_PRESSED:
case GESTURE_IN_PROGRESS:
// Discard any pending gestures.
ignore_result(gesture_provider_.GetAndResetPendingGestures());
EnterTouchToMouseMode();
state_ = TOUCH_EXPLORATION;
VLOG_STATE();
break;
default:
return;
}
scoped_ptr<ui::Event> mouse_move =
CreateMouseMoveEvent(initial_press_->location(), initial_press_->flags());
DispatchEvent(mouse_move.get());
last_touch_exploration_.reset(new TouchEvent(*initial_press_));
}
void TouchExplorationController::DispatchEvent(ui::Event* event) {
if (event_handler_for_testing_) {
event_handler_for_testing_->OnEvent(event);
return;
}
ui::EventDispatchDetails result ALLOW_UNUSED =
root_window_->GetHost()->dispatcher()->OnEventFromSource(event);
}
void TouchExplorationController::OnGestureEvent(ui::GestureEvent* gesture) {
CHECK(gesture->IsGestureEvent());
VLOG(0) << " \n Gesture Triggered: " << gesture->name();
if (gesture->type() == ui::ET_GESTURE_SWIPE) {
if (tap_timer_.IsRunning())
tap_timer_.Stop();
OnSwipeEvent(gesture);
return;
}
}
void TouchExplorationController::ProcessGestureEvents() {
scoped_ptr<ScopedVector<ui::GestureEvent> > gestures(
gesture_provider_.GetAndResetPendingGestures());
if (gestures) {
for (ScopedVector<GestureEvent>::iterator i = gestures->begin();
i != gestures->end();
++i) {
OnGestureEvent(*i);
}
}
}
void TouchExplorationController::OnSwipeEvent(ui::GestureEvent* swipe_gesture) {
// A swipe gesture contains details for the direction in which the swipe
// occurred.
GestureEventDetails event_details = swipe_gesture->details();
if (event_details.swipe_left()) {
DispatchShiftSearchKeyEvent(ui::VKEY_LEFT);
return;
} else if (event_details.swipe_right()) {
DispatchShiftSearchKeyEvent(ui::VKEY_RIGHT);
return;
} else if (event_details.swipe_up()) {
DispatchShiftSearchKeyEvent(ui::VKEY_UP);
return;
} else if (event_details.swipe_down()) {
DispatchShiftSearchKeyEvent(ui::VKEY_DOWN);
return;
}
}
void TouchExplorationController::DispatchShiftSearchKeyEvent(
const ui::KeyboardCode direction) {
// In order to activate the shortcut shift+search+<arrow key>
// three KeyPressed events must be dispatched in succession along
// with three KeyReleased events.
ui::KeyEvent shift_down = ui::KeyEvent(
ui::ET_KEY_PRESSED, ui::VKEY_SHIFT, ui::EF_SHIFT_DOWN, false);
ui::KeyEvent search_down = ui::KeyEvent(
ui::ET_KEY_PRESSED, kChromeOSSearchKey, ui::EF_SHIFT_DOWN, false);
ui::KeyEvent direction_down =
ui::KeyEvent(ui::ET_KEY_PRESSED, direction, ui::EF_SHIFT_DOWN, false);
ui::KeyEvent direction_up =
ui::KeyEvent(ui::ET_KEY_RELEASED, direction, ui::EF_SHIFT_DOWN, false);
ui::KeyEvent search_up = ui::KeyEvent(
ui::ET_KEY_RELEASED, kChromeOSSearchKey, ui::EF_SHIFT_DOWN, false);
ui::KeyEvent shift_up =
ui::KeyEvent(ui::ET_KEY_RELEASED, ui::VKEY_SHIFT, ui::EF_NONE, false);
DispatchEvent(&shift_down);
DispatchEvent(&search_down);
DispatchEvent(&direction_down);
DispatchEvent(&direction_up);
DispatchEvent(&search_up);
DispatchEvent(&shift_up);
}
scoped_ptr<ui::Event> TouchExplorationController::CreateMouseMoveEvent(
const gfx::PointF& location,
int flags) {
return scoped_ptr<ui::Event>(
new ui::MouseEvent(
ui::ET_MOUSE_MOVED,
location,
location,
flags | ui::EF_IS_SYNTHESIZED | ui::EF_TOUCH_ACCESSIBILITY,
0));
}
void TouchExplorationController::EnterTouchToMouseMode() {
aura::client::CursorClient* cursor_client =
aura::client::GetCursorClient(root_window_);
if (cursor_client && !cursor_client->IsMouseEventsEnabled())
cursor_client->EnableMouseEvents();
if (cursor_client && cursor_client->IsCursorVisible())
cursor_client->HideCursor();
}
void TouchExplorationController::ResetToNoFingersDown() {
state_ = NO_FINGERS_DOWN;
VLOG_STATE();
if (tap_timer_.IsRunning())
tap_timer_.Stop();
}
void TouchExplorationController::VlogState(const char* function_name) {
if (!VLOG_on_)
return;
if (prev_state_ == state_)
return;
prev_state_ = state_;
const char* state_string = EnumStateToString(state_);
VLOG(0) << "\n Function name: " << function_name
<< "\n State: " << state_string;
}
void TouchExplorationController::VlogEvent(const ui::TouchEvent& touch_event,
const char* function_name) {
if (!VLOG_on_)
return;
CHECK(touch_event.IsTouchEvent());
if (prev_event_ != NULL &&
prev_event_->type() == touch_event.type() &&
prev_event_->touch_id() == touch_event.touch_id()){
return;
}
// The above statement prevents events of the same type and id from being
// printed in a row. However, if two fingers are down, they would both be
// moving and alternating printing move events unless we check for this.
if (prev_event_ != NULL &&
prev_event_->type() == ET_TOUCH_MOVED &&
touch_event.type() == ET_TOUCH_MOVED){
return;
}
const std::string& type = touch_event.name();
const gfx::PointF& location = touch_event.location_f();
const int touch_id = touch_event.touch_id();
VLOG(0) << "\n Function name: " << function_name
<< "\n Event Type: " << type
<< "\n Location: " << location.ToString()
<< "\n Touch ID: " << touch_id;
prev_event_.reset(new TouchEvent(touch_event));
}
const char* TouchExplorationController::EnumStateToString(State state) {
switch (state) {
case NO_FINGERS_DOWN:
return "NO_FINGERS_DOWN";
case SINGLE_TAP_PRESSED:
return "SINGLE_TAP_PRESSED";
case SINGLE_TAP_RELEASED:
return "SINGLE_TAP_RELEASED";
case TOUCH_EXPLORE_RELEASED:
return "TOUCH_EXPLORE_RELEASED";
case DOUBLE_TAP_PRESSED:
return "DOUBLE_TAP_PRESSED";
case TOUCH_EXPLORATION:
return "TOUCH_EXPLORATION";
case GESTURE_IN_PROGRESS:
return "GESTURE_IN_PROGRESS";
case TOUCH_EXPLORE_SECOND_PRESS:
return "TOUCH_EXPLORE_SECOND_PRESS";
case TWO_TO_ONE_FINGER:
return "TWO_TO_ONE_FINGER";
case PASSTHROUGH:
return "PASSTHROUGH";
case WAIT_FOR_RELEASE:
return "WAIT_FOR_RELEASE";
}
return "Not a state";
}
} // namespace ui