• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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