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 "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"
15 TouchExplorationController::TouchExplorationController(
16 aura::Window
* root_window
)
17 : root_window_(root_window
) {
19 root_window
->GetHost()->GetEventSource()->AddEventRewriter(this);
22 TouchExplorationController::~TouchExplorationController() {
23 root_window_
->GetHost()->GetEventSource()->RemoveEventRewriter(this);
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
;
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();
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
;
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();
60 int num_erased
= touch_locations_
.erase(touch_id
);
61 DCHECK_EQ(num_erased
, 1);
62 const int num_fingers_remaining
= touch_ids_
.size();
64 if (num_fingers_remaining
== 0) {
65 *rewritten_event
= CreateMouseMoveEvent(location
, flags
);
66 return ui::EVENT_REWRITE_REWRITTEN
;
69 // If we are left with one finger - enter the mouse move mode.
70 const bool enter_mouse_move_mode
= num_fingers_remaining
== 1;
72 if (!enter_mouse_move_mode
&& !first_finger_released
) {
73 // No special handling needed.
74 return ui::EVENT_REWRITE_CONTINUE
;
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
,
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
96 rewritten_event
->reset(new ui::TouchEvent(touch_event
));
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
;
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
;
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
;
128 return ui::EVENT_REWRITE_CONTINUE
;
130 NOTREACHED() << "Unexpected event type received.";
131 return ui::EVENT_REWRITE_CONTINUE
;
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
;
146 scoped_ptr
<ui::Event
> TouchExplorationController::CreateMouseMoveEvent(
147 const gfx::PointF
& location
,
149 return scoped_ptr
<ui::Event
>(
150 new ui::MouseEvent(ui::ET_MOUSE_MOVED
,
153 flags
| ui::EF_IS_SYNTHESIZED
| ui::EF_FROM_TOUCH
,
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();