Home | History | Annotate | Download | only in plugin
      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 
     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 
     35 PepperInputHandler::~PepperInputHandler() {
     36 }
     37 
     38 // Helper function to get the USB key code using the Dev InputEvent interface.
     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 
     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 
    198 void PepperInputHandler::AllowMouseLock() {
    199   DCHECK_EQ(mouse_lock_state_, MouseLockDisallowed);
    200   mouse_lock_state_ = MouseLockOff;
    201 }
    202 
    203 void PepperInputHandler::DidChangeFocus(bool has_focus) {
    204   has_focus_ = has_focus;
    205   if (has_focus_)
    206     RequestMouseLock();
    207 }
    208 
    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 
    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 
    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 
    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 
    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 
    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