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