Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / ui / chromeos / touch_exploration_controller.cc
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 "ui/aura/client/cursor_client.h"
9 #include "ui/aura/window.h"
10 #include "ui/aura/window_tree_host.h"
11 #include "ui/events/event.h"
12
13 namespace ui {
14
15 TouchExplorationController::TouchExplorationController(
16     aura::Window* root_window)
17         : root_window_(root_window) {
18   CHECK(root_window);
19   root_window->GetHost()->GetEventSource()->AddEventRewriter(this);
20 }
21
22 TouchExplorationController::~TouchExplorationController() {
23   root_window_->GetHost()->GetEventSource()->RemoveEventRewriter(this);
24 }
25
26 ui::EventRewriteStatus TouchExplorationController::RewriteEvent(
27     const ui::Event& event,
28     scoped_ptr<ui::Event>* rewritten_event) {
29   if (!event.IsTouchEvent())
30     return ui::EVENT_REWRITE_CONTINUE;
31
32   const ui::TouchEvent& touch_event =
33       static_cast<const ui::TouchEvent&>(event);
34   const ui::EventType type = touch_event.type();
35   const gfx::PointF& location = touch_event.location_f();
36   const int touch_id = touch_event.touch_id();
37   const int flags = touch_event.flags();
38
39   if (type == ui::ET_TOUCH_PRESSED) {
40     touch_ids_.push_back(touch_id);
41     touch_locations_.insert(std::pair<int, gfx::PointF>(touch_id, location));
42     // If this is the first and only finger touching - rewrite the touch as a
43     // mouse move. Otherwise let the it go through as is.
44     if (touch_ids_.size() == 1) {
45       *rewritten_event = CreateMouseMoveEvent(location, flags);
46       EnterTouchToMouseMode();
47       return ui::EVENT_REWRITE_REWRITTEN;
48     }
49     return ui::EVENT_REWRITE_CONTINUE;
50   } else if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) {
51     std::vector<int>::iterator it =
52         std::find(touch_ids_.begin(), touch_ids_.end(), touch_id);
53     // We may fail to find the finger if the exploration mode was turned on
54     // while the user had some fingers touching the screen. We simply ignore
55     // those fingers for the purposes of event transformation.
56     if (it == touch_ids_.end())
57       return ui::EVENT_REWRITE_CONTINUE;
58     const bool first_finger_released = it == touch_ids_.begin();
59     touch_ids_.erase(it);
60     int num_erased = touch_locations_.erase(touch_id);
61     DCHECK_EQ(num_erased, 1);
62     const int num_fingers_remaining = touch_ids_.size();
63
64     if (num_fingers_remaining == 0) {
65       *rewritten_event = CreateMouseMoveEvent(location, flags);
66       return ui::EVENT_REWRITE_REWRITTEN;
67     }
68
69     // If we are left with one finger - enter the mouse move mode.
70     const bool enter_mouse_move_mode = num_fingers_remaining == 1;
71
72     if (!enter_mouse_move_mode && !first_finger_released) {
73       // No special handling needed.
74       return ui::EVENT_REWRITE_CONTINUE;
75     }
76
77     // If the finger which was released was the first one, - we need to rewrite
78     // the release event as a release of the was second / now first finger.
79     // This is the finger which will now be getting "substracted".
80     if (first_finger_released) {
81       int rewritten_release_id = touch_ids_[0];
82       const gfx::PointF& rewritten_release_location =
83           touch_locations_[rewritten_release_id];
84       ui::TouchEvent* rewritten_release_event = new ui::TouchEvent(
85           ui::ET_TOUCH_RELEASED,
86           rewritten_release_location,
87           rewritten_release_id,
88           event.time_stamp());
89       rewritten_release_event->set_flags(touch_event.flags());
90       rewritten_event->reset(rewritten_release_event);
91     } else if (enter_mouse_move_mode) {
92       // Dispatch the release event as is.
93       // TODO(mfomitchev): We can get rid of this clause once we have
94       // EVENT_REWRITE_DISPATCH_ANOTHER working without having to set
95       // rewritten_event.
96       rewritten_event->reset(new ui::TouchEvent(touch_event));
97     }
98
99     if (enter_mouse_move_mode) {
100       // Since we are entering the mouse move mode - also dispatch a mouse move
101       // event at the location of the one remaining finger. (num_fingers == 1)
102       const gfx::PointF& mouse_move_location = touch_locations_[touch_ids_[0]];
103       next_dispatch_event_ =
104           CreateMouseMoveEvent(mouse_move_location, flags).Pass();
105       return ui::EVENT_REWRITE_DISPATCH_ANOTHER;
106     }
107     return ui::EVENT_REWRITE_REWRITTEN;
108   } else if (type == ui::ET_TOUCH_MOVED) {
109     std::vector<int>::iterator it =
110         std::find(touch_ids_.begin(), touch_ids_.end(), touch_id);
111     // We may fail to find the finger if the exploration mode was turned on
112     // while the user had some fingers touching the screen. We simply ignore
113     // those fingers for the purposes of event transformation.
114     if (it == touch_ids_.end())
115       return ui::EVENT_REWRITE_CONTINUE;
116     touch_locations_[*it] = location;
117     if (touch_ids_.size() == 1) {
118       // Touch moves are rewritten as mouse moves when there's only one finger
119       // touching the screen.
120       *rewritten_event = CreateMouseMoveEvent(location, flags).Pass();
121       return ui::EVENT_REWRITE_REWRITTEN;
122     }
123     if (touch_id == touch_ids_.front()) {
124       // Touch moves of the first finger are discarded when there's more than
125       // one finger touching.
126       return ui::EVENT_REWRITE_DISCARD;
127     }
128     return ui::EVENT_REWRITE_CONTINUE;
129   }
130   NOTREACHED() << "Unexpected event type received.";
131   return ui::EVENT_REWRITE_CONTINUE;
132 }
133
134 ui::EventRewriteStatus TouchExplorationController::NextDispatchEvent(
135     const ui::Event& last_event,
136     scoped_ptr<ui::Event>* new_event) {
137   CHECK(next_dispatch_event_);
138   DCHECK(last_event.IsTouchEvent());
139   *new_event = next_dispatch_event_.Pass();
140   // Enter the mouse move mode if needed
141   if ((*new_event)->IsMouseEvent())
142     EnterTouchToMouseMode();
143   return ui::EVENT_REWRITE_REWRITTEN;
144 }
145
146 scoped_ptr<ui::Event> TouchExplorationController::CreateMouseMoveEvent(
147     const gfx::PointF& location,
148     int flags) {
149   return scoped_ptr<ui::Event>(
150       new ui::MouseEvent(ui::ET_MOUSE_MOVED,
151                          location,
152                          location,
153                          flags | ui::EF_IS_SYNTHESIZED | ui::EF_FROM_TOUCH,
154                          0));
155 }
156
157 void TouchExplorationController::EnterTouchToMouseMode() {
158   aura::client::CursorClient* cursor_client =
159       aura::client::GetCursorClient(root_window_);
160   if (cursor_client && !cursor_client->IsMouseEventsEnabled())
161     cursor_client->EnableMouseEvents();
162   if (cursor_client && cursor_client->IsCursorVisible())
163     cursor_client->HideCursor();
164 }
165
166 }  // namespace ui