pepper_input_handler.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
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
17namespace remoting {
18
19PepperInputHandler::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
34PepperInputHandler::~PepperInputHandler() {}
35
36// Helper function to get the USB key code using the Dev InputEvent interface.
37uint32_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
46bool 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
199void PepperInputHandler::AllowMouseLock() {
200  DCHECK_EQ(mouse_lock_state_, MouseLockDisallowed);
201  mouse_lock_state_ = MouseLockOff;
202}
203
204void PepperInputHandler::DidChangeFocus(bool has_focus) {
205  has_focus_ = has_focus;
206  if (has_focus_)
207    RequestMouseLock();
208}
209
210void 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
222void PepperInputHandler::MouseLockLost() {
223  DCHECK(mouse_lock_state_ == MouseLockOn ||
224         mouse_lock_state_ == MouseLockCancelling);
225
226  mouse_lock_state_ = MouseLockOff;
227  UpdateMouseCursor();
228}
229
230void 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
246void 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
275void 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
291void 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