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/input_event.h" 10 #include "ppapi/cpp/module_impl.h" 11 #include "ppapi/cpp/point.h" 12 #include "remoting/proto/event.pb.h" 13 14 namespace remoting { 15 16 PepperInputHandler::PepperInputHandler(protocol::InputStub* input_stub) 17 : input_stub_(input_stub), 18 wheel_delta_x_(0), 19 wheel_delta_y_(0), 20 wheel_ticks_x_(0), 21 wheel_ticks_y_(0) { 22 } 23 24 PepperInputHandler::~PepperInputHandler() { 25 } 26 27 // Helper function to get the USB key code using the Dev InputEvent interface. 28 uint32_t GetUsbKeyCode(pp::KeyboardInputEvent pp_key_event) { 29 const PPB_KeyboardInputEvent_Dev* key_event_interface = 30 reinterpret_cast<const PPB_KeyboardInputEvent_Dev*>( 31 pp::Module::Get()->GetBrowserInterface( 32 PPB_KEYBOARD_INPUT_EVENT_DEV_INTERFACE)); 33 if (!key_event_interface) 34 return 0; 35 return key_event_interface->GetUsbKeyCode(pp_key_event.pp_resource()); 36 } 37 38 bool PepperInputHandler::HandleInputEvent(const pp::InputEvent& event) { 39 switch (event.GetType()) { 40 case PP_INPUTEVENT_TYPE_CONTEXTMENU: { 41 // We need to return true here or else we'll get a local (plugin) context 42 // menu instead of the mouseup event for the right click. 43 return true; 44 } 45 46 case PP_INPUTEVENT_TYPE_KEYDOWN: 47 case PP_INPUTEVENT_TYPE_KEYUP: { 48 pp::KeyboardInputEvent pp_key_event(event); 49 uint32_t modifiers = event.GetModifiers(); 50 uint32_t lock_states = 0; 51 52 if (modifiers & PP_INPUTEVENT_MODIFIER_CAPSLOCKKEY) 53 lock_states |= protocol::KeyEvent::LOCK_STATES_CAPSLOCK; 54 55 if (modifiers & PP_INPUTEVENT_MODIFIER_NUMLOCKKEY) 56 lock_states |= protocol::KeyEvent::LOCK_STATES_NUMLOCK; 57 58 protocol::KeyEvent key_event; 59 key_event.set_usb_keycode(GetUsbKeyCode(pp_key_event)); 60 key_event.set_pressed(event.GetType() == PP_INPUTEVENT_TYPE_KEYDOWN); 61 key_event.set_lock_states(lock_states); 62 63 input_stub_->InjectKeyEvent(key_event); 64 return true; 65 } 66 67 case PP_INPUTEVENT_TYPE_MOUSEDOWN: 68 case PP_INPUTEVENT_TYPE_MOUSEUP: { 69 pp::MouseInputEvent pp_mouse_event(event); 70 protocol::MouseEvent mouse_event; 71 switch (pp_mouse_event.GetButton()) { 72 case PP_INPUTEVENT_MOUSEBUTTON_LEFT: 73 mouse_event.set_button(protocol::MouseEvent::BUTTON_LEFT); 74 break; 75 case PP_INPUTEVENT_MOUSEBUTTON_MIDDLE: 76 mouse_event.set_button(protocol::MouseEvent::BUTTON_MIDDLE); 77 break; 78 case PP_INPUTEVENT_MOUSEBUTTON_RIGHT: 79 mouse_event.set_button(protocol::MouseEvent::BUTTON_RIGHT); 80 break; 81 case PP_INPUTEVENT_MOUSEBUTTON_NONE: 82 break; 83 } 84 if (mouse_event.has_button()) { 85 bool is_down = (event.GetType() == PP_INPUTEVENT_TYPE_MOUSEDOWN); 86 mouse_event.set_button_down(is_down); 87 mouse_event.set_x(pp_mouse_event.GetPosition().x()); 88 mouse_event.set_y(pp_mouse_event.GetPosition().y()); 89 input_stub_->InjectMouseEvent(mouse_event); 90 } 91 return true; 92 } 93 94 case PP_INPUTEVENT_TYPE_MOUSEMOVE: 95 case PP_INPUTEVENT_TYPE_MOUSEENTER: 96 case PP_INPUTEVENT_TYPE_MOUSELEAVE: { 97 pp::MouseInputEvent pp_mouse_event(event); 98 protocol::MouseEvent mouse_event; 99 mouse_event.set_x(pp_mouse_event.GetPosition().x()); 100 mouse_event.set_y(pp_mouse_event.GetPosition().y()); 101 input_stub_->InjectMouseEvent(mouse_event); 102 return true; 103 } 104 105 case PP_INPUTEVENT_TYPE_WHEEL: { 106 pp::WheelInputEvent pp_wheel_event(event); 107 108 // Don't handle scroll-by-page events, for now. 109 if (pp_wheel_event.GetScrollByPage()) 110 return false; 111 112 // Add this event to our accumulated sub-pixel deltas and clicks. 113 pp::FloatPoint delta = pp_wheel_event.GetDelta(); 114 wheel_delta_x_ += delta.x(); 115 wheel_delta_y_ += delta.y(); 116 pp::FloatPoint ticks = pp_wheel_event.GetTicks(); 117 wheel_ticks_x_ += ticks.x(); 118 wheel_ticks_y_ += ticks.y(); 119 120 // If there is at least a pixel's movement, emit an event. We don't 121 // ever expect to accumulate one tick's worth of scrolling without 122 // accumulating a pixel's worth at the same time, so this is safe. 123 int delta_x = static_cast<int>(wheel_delta_x_); 124 int delta_y = static_cast<int>(wheel_delta_y_); 125 if (delta_x != 0 || delta_y != 0) { 126 wheel_delta_x_ -= delta_x; 127 wheel_delta_y_ -= delta_y; 128 protocol::MouseEvent mouse_event; 129 mouse_event.set_wheel_delta_x(delta_x); 130 mouse_event.set_wheel_delta_y(delta_y); 131 132 // Always include the ticks in the event, even if insufficient pixel 133 // scrolling has accumulated for a single tick. This informs hosts 134 // that can't inject pixel-based scroll events that the client will 135 // accumulate them into tick-based scrolling, which gives a better 136 // overall experience than trying to do this host-side. 137 int ticks_x = static_cast<int>(wheel_ticks_x_); 138 int ticks_y = static_cast<int>(wheel_ticks_y_); 139 wheel_ticks_x_ -= ticks_x; 140 wheel_ticks_y_ -= ticks_y; 141 mouse_event.set_wheel_ticks_x(ticks_x); 142 mouse_event.set_wheel_ticks_y(ticks_y); 143 144 input_stub_->InjectMouseEvent(mouse_event); 145 } 146 return true; 147 } 148 149 case PP_INPUTEVENT_TYPE_CHAR: 150 // Consume but ignore character input events. 151 return true; 152 153 default: { 154 LOG(INFO) << "Unhandled input event: " << event.GetType(); 155 break; 156 } 157 } 158 159 return false; 160 } 161 162 } // namespace remoting 163