1 // Copyright (c) 2012 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 "remoting/client/plugin/pepper_input_handler.h"
6
7 #include "base/logging.h"
8 #include "ppapi/cpp/image_data.h"
9 #include "ppapi/cpp/input_event.h"
10 #include "ppapi/cpp/module_impl.h"
11 #include "ppapi/cpp/mouse_cursor.h"
12 #include "ppapi/cpp/point.h"
13 #include "ppapi/cpp/var.h"
14 #include "remoting/proto/event.pb.h"
15 #include "ui/events/keycodes/dom4/keycode_converter.h"
16
17 namespace remoting {
18
PepperInputHandler(pp::Instance * instance)19 PepperInputHandler::PepperInputHandler(
20 pp::Instance* instance)
21 : pp::MouseLock(instance),
22 instance_(instance),
23 input_stub_(NULL),
24 callback_factory_(this),
25 has_focus_(false),
26 send_mouse_input_when_unfocused_(false),
27 mouse_lock_state_(MouseLockDisallowed),
28 wheel_delta_x_(0),
29 wheel_delta_y_(0),
30 wheel_ticks_x_(0),
31 wheel_ticks_y_(0) {
32 }
33
~PepperInputHandler()34 PepperInputHandler::~PepperInputHandler() {}
35
36 // Helper function to get the USB key code using the Dev InputEvent interface.
GetUsbKeyCode(pp::KeyboardInputEvent pp_key_event)37 uint32_t GetUsbKeyCode(pp::KeyboardInputEvent pp_key_event) {
38 // Get the DOM3 |code| as a string.
39 std::string codestr = pp_key_event.GetCode().AsString();
40
41 // Convert the |code| string into a USB keycode.
42 ui::KeycodeConverter* key_converter = ui::KeycodeConverter::GetInstance();
43 return key_converter->CodeToUsbKeycode(codestr.c_str());
44 }
45
HandleInputEvent(const pp::InputEvent & event)46 bool PepperInputHandler::HandleInputEvent(const pp::InputEvent& event) {
47 switch (event.GetType()) {
48 case PP_INPUTEVENT_TYPE_CONTEXTMENU: {
49 // We need to return true here or else we'll get a local (plugin) context
50 // menu instead of the mouseup event for the right click.
51 return true;
52 }
53
54 case PP_INPUTEVENT_TYPE_KEYDOWN:
55 case PP_INPUTEVENT_TYPE_KEYUP: {
56 pp::KeyboardInputEvent pp_key_event(event);
57 uint32_t modifiers = event.GetModifiers();
58 uint32_t lock_states = 0;
59
60 if (modifiers & PP_INPUTEVENT_MODIFIER_CAPSLOCKKEY)
61 lock_states |= protocol::KeyEvent::LOCK_STATES_CAPSLOCK;
62
63 if (modifiers & PP_INPUTEVENT_MODIFIER_NUMLOCKKEY)
64 lock_states |= protocol::KeyEvent::LOCK_STATES_NUMLOCK;
65
66 protocol::KeyEvent key_event;
67 key_event.set_usb_keycode(GetUsbKeyCode(pp_key_event));
68 key_event.set_pressed(event.GetType() == PP_INPUTEVENT_TYPE_KEYDOWN);
69 key_event.set_lock_states(lock_states);
70
71 if (input_stub_)
72 input_stub_->InjectKeyEvent(key_event);
73 return true;
74 }
75
76 case PP_INPUTEVENT_TYPE_MOUSEDOWN:
77 case PP_INPUTEVENT_TYPE_MOUSEUP: {
78 if (!has_focus_ && !send_mouse_input_when_unfocused_)
79 return false;
80
81 pp::MouseInputEvent pp_mouse_event(event);
82 protocol::MouseEvent mouse_event;
83 switch (pp_mouse_event.GetButton()) {
84 case PP_INPUTEVENT_MOUSEBUTTON_LEFT:
85 mouse_event.set_button(protocol::MouseEvent::BUTTON_LEFT);
86 break;
87 case PP_INPUTEVENT_MOUSEBUTTON_MIDDLE:
88 mouse_event.set_button(protocol::MouseEvent::BUTTON_MIDDLE);
89 break;
90 case PP_INPUTEVENT_MOUSEBUTTON_RIGHT:
91 mouse_event.set_button(protocol::MouseEvent::BUTTON_RIGHT);
92 break;
93 case PP_INPUTEVENT_MOUSEBUTTON_NONE:
94 break;
95 }
96 if (mouse_event.has_button()) {
97 bool is_down = (event.GetType() == PP_INPUTEVENT_TYPE_MOUSEDOWN);
98 mouse_event.set_button_down(is_down);
99 mouse_event.set_x(pp_mouse_event.GetPosition().x());
100 mouse_event.set_y(pp_mouse_event.GetPosition().y());
101
102 // Add relative movement if the mouse is locked.
103 if (mouse_lock_state_ == MouseLockOn) {
104 pp::Point delta = pp_mouse_event.GetMovement();
105 mouse_event.set_delta_x(delta.x());
106 mouse_event.set_delta_y(delta.y());
107 }
108
109 if (input_stub_)
110 input_stub_->InjectMouseEvent(mouse_event);
111 }
112 return true;
113 }
114
115 case PP_INPUTEVENT_TYPE_MOUSEMOVE:
116 case PP_INPUTEVENT_TYPE_MOUSEENTER:
117 case PP_INPUTEVENT_TYPE_MOUSELEAVE: {
118 if (!has_focus_ && !send_mouse_input_when_unfocused_)
119 return false;
120
121 pp::MouseInputEvent pp_mouse_event(event);
122 protocol::MouseEvent mouse_event;
123 mouse_event.set_x(pp_mouse_event.GetPosition().x());
124 mouse_event.set_y(pp_mouse_event.GetPosition().y());
125
126 // Add relative movement if the mouse is locked.
127 if (mouse_lock_state_ == MouseLockOn) {
128 pp::Point delta = pp_mouse_event.GetMovement();
129 mouse_event.set_delta_x(delta.x());
130 mouse_event.set_delta_y(delta.y());
131 }
132
133 if (input_stub_)
134 input_stub_->InjectMouseEvent(mouse_event);
135 return true;
136 }
137
138 case PP_INPUTEVENT_TYPE_WHEEL: {
139 if (!has_focus_ && !send_mouse_input_when_unfocused_)
140 return false;
141
142 pp::WheelInputEvent pp_wheel_event(event);
143
144 // Don't handle scroll-by-page events, for now.
145 if (pp_wheel_event.GetScrollByPage())
146 return false;
147
148 // Add this event to our accumulated sub-pixel deltas and clicks.
149 pp::FloatPoint delta = pp_wheel_event.GetDelta();
150 wheel_delta_x_ += delta.x();
151 wheel_delta_y_ += delta.y();
152 pp::FloatPoint ticks = pp_wheel_event.GetTicks();
153 wheel_ticks_x_ += ticks.x();
154 wheel_ticks_y_ += ticks.y();
155
156 // If there is at least a pixel's movement, emit an event. We don't
157 // ever expect to accumulate one tick's worth of scrolling without
158 // accumulating a pixel's worth at the same time, so this is safe.
159 int delta_x = static_cast<int>(wheel_delta_x_);
160 int delta_y = static_cast<int>(wheel_delta_y_);
161 if (delta_x != 0 || delta_y != 0) {
162 wheel_delta_x_ -= delta_x;
163 wheel_delta_y_ -= delta_y;
164 protocol::MouseEvent mouse_event;
165 mouse_event.set_wheel_delta_x(delta_x);
166 mouse_event.set_wheel_delta_y(delta_y);
167
168 // Always include the ticks in the event, even if insufficient pixel
169 // scrolling has accumulated for a single tick. This informs hosts
170 // that can't inject pixel-based scroll events that the client will
171 // accumulate them into tick-based scrolling, which gives a better
172 // overall experience than trying to do this host-side.
173 int ticks_x = static_cast<int>(wheel_ticks_x_);
174 int ticks_y = static_cast<int>(wheel_ticks_y_);
175 wheel_ticks_x_ -= ticks_x;
176 wheel_ticks_y_ -= ticks_y;
177 mouse_event.set_wheel_ticks_x(ticks_x);
178 mouse_event.set_wheel_ticks_y(ticks_y);
179
180 if (input_stub_)
181 input_stub_->InjectMouseEvent(mouse_event);
182 }
183 return true;
184 }
185
186 case PP_INPUTEVENT_TYPE_CHAR:
187 // Consume but ignore character input events.
188 return true;
189
190 default: {
191 VLOG(0) << "Unhandled input event: " << event.GetType();
192 break;
193 }
194 }
195
196 return false;
197 }
198
AllowMouseLock()199 void PepperInputHandler::AllowMouseLock() {
200 DCHECK_EQ(mouse_lock_state_, MouseLockDisallowed);
201 mouse_lock_state_ = MouseLockOff;
202 }
203
DidChangeFocus(bool has_focus)204 void PepperInputHandler::DidChangeFocus(bool has_focus) {
205 has_focus_ = has_focus;
206 if (has_focus_)
207 RequestMouseLock();
208 }
209
SetMouseCursor(scoped_ptr<pp::ImageData> image,const pp::Point & hotspot)210 void PepperInputHandler::SetMouseCursor(scoped_ptr<pp::ImageData> image,
211 const pp::Point& hotspot) {
212 cursor_image_ = image.Pass();
213 cursor_hotspot_ = hotspot;
214
215 if (mouse_lock_state_ != MouseLockDisallowed && !cursor_image_) {
216 RequestMouseLock();
217 } else {
218 CancelMouseLock();
219 }
220 }
221
MouseLockLost()222 void PepperInputHandler::MouseLockLost() {
223 DCHECK(mouse_lock_state_ == MouseLockOn ||
224 mouse_lock_state_ == MouseLockCancelling);
225
226 mouse_lock_state_ = MouseLockOff;
227 UpdateMouseCursor();
228 }
229
RequestMouseLock()230 void PepperInputHandler::RequestMouseLock() {
231 // Request mouse lock only if the plugin is focused, the host-supplied cursor
232 // is empty and no callback is pending.
233 if (has_focus_ && !cursor_image_ && mouse_lock_state_ == MouseLockOff) {
234 pp::CompletionCallback callback =
235 callback_factory_.NewCallback(&PepperInputHandler::OnMouseLocked);
236 int result = pp::MouseLock::LockMouse(callback);
237 DCHECK_EQ(result, PP_OK_COMPLETIONPENDING);
238
239 // Hide cursor to avoid it becoming a black square (see crbug.com/285809).
240 pp::MouseCursor::SetCursor(instance_, PP_MOUSECURSOR_TYPE_NONE);
241
242 mouse_lock_state_ = MouseLockRequestPending;
243 }
244 }
245
CancelMouseLock()246 void PepperInputHandler::CancelMouseLock() {
247 switch (mouse_lock_state_) {
248 case MouseLockDisallowed:
249 case MouseLockOff:
250 UpdateMouseCursor();
251 break;
252
253 case MouseLockCancelling:
254 break;
255
256 case MouseLockRequestPending:
257 // The mouse lock request is pending. Delay UnlockMouse() call until
258 // the callback is called.
259 mouse_lock_state_ = MouseLockCancelling;
260 break;
261
262 case MouseLockOn:
263 pp::MouseLock::UnlockMouse();
264
265 // Note that mouse-lock has been cancelled. We will continue to receive
266 // locked events until MouseLockLost() is called back.
267 mouse_lock_state_ = MouseLockCancelling;
268 break;
269
270 default:
271 NOTREACHED();
272 }
273 }
274
UpdateMouseCursor()275 void PepperInputHandler::UpdateMouseCursor() {
276 DCHECK(mouse_lock_state_ == MouseLockDisallowed ||
277 mouse_lock_state_ == MouseLockOff);
278
279 if (cursor_image_) {
280 pp::MouseCursor::SetCursor(instance_, PP_MOUSECURSOR_TYPE_CUSTOM,
281 *cursor_image_,
282 cursor_hotspot_);
283 } else {
284 // If there is no cursor shape stored, either because the host never
285 // supplied one, or we were previously in mouse-lock mode, then use
286 // a standard arrow pointer.
287 pp::MouseCursor::SetCursor(instance_, PP_MOUSECURSOR_TYPE_POINTER);
288 }
289 }
290
OnMouseLocked(int error)291 void PepperInputHandler::OnMouseLocked(int error) {
292 DCHECK(mouse_lock_state_ == MouseLockRequestPending ||
293 mouse_lock_state_ == MouseLockCancelling);
294
295 bool should_cancel = (mouse_lock_state_ == MouseLockCancelling);
296
297 // See if the operation succeeded.
298 if (error == PP_OK) {
299 mouse_lock_state_ = MouseLockOn;
300 } else {
301 mouse_lock_state_ = MouseLockOff;
302 UpdateMouseCursor();
303 }
304
305 // Cancel as needed.
306 if (should_cancel)
307 CancelMouseLock();
308 }
309
310 } // namespace remoting
311