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