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/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 
     17 namespace remoting {
     18 
     19 PepperInputHandler::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 
     34 PepperInputHandler::~PepperInputHandler() {}
     35 
     36 // Helper function to get the USB key code using the Dev InputEvent interface.
     37 uint32_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   return ui::KeycodeConverter::CodeToUsbKeycode(codestr.c_str());
     43 }
     44 
     45 bool PepperInputHandler::HandleInputEvent(const pp::InputEvent& event) {
     46   switch (event.GetType()) {
     47     case PP_INPUTEVENT_TYPE_CONTEXTMENU: {
     48       // We need to return true here or else we'll get a local (plugin) context
     49       // menu instead of the mouseup event for the right click.
     50       return true;
     51     }
     52 
     53     case PP_INPUTEVENT_TYPE_KEYDOWN:
     54     case PP_INPUTEVENT_TYPE_KEYUP: {
     55       pp::KeyboardInputEvent pp_key_event(event);
     56       uint32_t modifiers = event.GetModifiers();
     57       uint32_t lock_states = 0;
     58 
     59       if (modifiers & PP_INPUTEVENT_MODIFIER_CAPSLOCKKEY)
     60         lock_states |= protocol::KeyEvent::LOCK_STATES_CAPSLOCK;
     61 
     62       if (modifiers & PP_INPUTEVENT_MODIFIER_NUMLOCKKEY)
     63         lock_states |= protocol::KeyEvent::LOCK_STATES_NUMLOCK;
     64 
     65       protocol::KeyEvent key_event;
     66       key_event.set_usb_keycode(GetUsbKeyCode(pp_key_event));
     67       key_event.set_pressed(event.GetType() == PP_INPUTEVENT_TYPE_KEYDOWN);
     68       key_event.set_lock_states(lock_states);
     69 
     70       if (input_stub_)
     71         input_stub_->InjectKeyEvent(key_event);
     72       return true;
     73     }
     74 
     75     case PP_INPUTEVENT_TYPE_MOUSEDOWN:
     76     case PP_INPUTEVENT_TYPE_MOUSEUP: {
     77       if (!has_focus_ && !send_mouse_input_when_unfocused_)
     78         return false;
     79 
     80       pp::MouseInputEvent pp_mouse_event(event);
     81       protocol::MouseEvent mouse_event;
     82       switch (pp_mouse_event.GetButton()) {
     83         case PP_INPUTEVENT_MOUSEBUTTON_LEFT:
     84           mouse_event.set_button(protocol::MouseEvent::BUTTON_LEFT);
     85           break;
     86         case PP_INPUTEVENT_MOUSEBUTTON_MIDDLE:
     87           mouse_event.set_button(protocol::MouseEvent::BUTTON_MIDDLE);
     88           break;
     89         case PP_INPUTEVENT_MOUSEBUTTON_RIGHT:
     90           mouse_event.set_button(protocol::MouseEvent::BUTTON_RIGHT);
     91           break;
     92         case PP_INPUTEVENT_MOUSEBUTTON_NONE:
     93           break;
     94       }
     95       if (mouse_event.has_button()) {
     96         bool is_down = (event.GetType() == PP_INPUTEVENT_TYPE_MOUSEDOWN);
     97         mouse_event.set_button_down(is_down);
     98         mouse_event.set_x(pp_mouse_event.GetPosition().x());
     99         mouse_event.set_y(pp_mouse_event.GetPosition().y());
    100 
    101         // Add relative movement if the mouse is locked.
    102         if (mouse_lock_state_ == MouseLockOn) {
    103           pp::Point delta = pp_mouse_event.GetMovement();
    104           mouse_event.set_delta_x(delta.x());
    105           mouse_event.set_delta_y(delta.y());
    106         }
    107 
    108         if (input_stub_)
    109           input_stub_->InjectMouseEvent(mouse_event);
    110       }
    111       return true;
    112     }
    113 
    114     case PP_INPUTEVENT_TYPE_MOUSEMOVE:
    115     case PP_INPUTEVENT_TYPE_MOUSEENTER:
    116     case PP_INPUTEVENT_TYPE_MOUSELEAVE: {
    117       if (!has_focus_ && !send_mouse_input_when_unfocused_)
    118         return false;
    119 
    120       pp::MouseInputEvent pp_mouse_event(event);
    121       protocol::MouseEvent mouse_event;
    122       mouse_event.set_x(pp_mouse_event.GetPosition().x());
    123       mouse_event.set_y(pp_mouse_event.GetPosition().y());
    124 
    125       // Add relative movement if the mouse is locked.
    126       if (mouse_lock_state_ == MouseLockOn) {
    127         pp::Point delta = pp_mouse_event.GetMovement();
    128         mouse_event.set_delta_x(delta.x());
    129         mouse_event.set_delta_y(delta.y());
    130       }
    131 
    132       if (input_stub_)
    133         input_stub_->InjectMouseEvent(mouse_event);
    134       return true;
    135     }
    136 
    137     case PP_INPUTEVENT_TYPE_WHEEL: {
    138       if (!has_focus_ && !send_mouse_input_when_unfocused_)
    139         return false;
    140 
    141       pp::WheelInputEvent pp_wheel_event(event);
    142 
    143       // Don't handle scroll-by-page events, for now.
    144       if (pp_wheel_event.GetScrollByPage())
    145         return false;
    146 
    147       // Add this event to our accumulated sub-pixel deltas and clicks.
    148       pp::FloatPoint delta = pp_wheel_event.GetDelta();
    149       wheel_delta_x_ += delta.x();
    150       wheel_delta_y_ += delta.y();
    151       pp::FloatPoint ticks = pp_wheel_event.GetTicks();
    152       wheel_ticks_x_ += ticks.x();
    153       wheel_ticks_y_ += ticks.y();
    154 
    155       // If there is at least a pixel's movement, emit an event. We don't
    156       // ever expect to accumulate one tick's worth of scrolling without
    157       // accumulating a pixel's worth at the same time, so this is safe.
    158       int delta_x = static_cast<int>(wheel_delta_x_);
    159       int delta_y = static_cast<int>(wheel_delta_y_);
    160       if (delta_x != 0 || delta_y != 0) {
    161         wheel_delta_x_ -= delta_x;
    162         wheel_delta_y_ -= delta_y;
    163         protocol::MouseEvent mouse_event;
    164         mouse_event.set_wheel_delta_x(delta_x);
    165         mouse_event.set_wheel_delta_y(delta_y);
    166 
    167         // Always include the ticks in the event, even if insufficient pixel
    168         // scrolling has accumulated for a single tick. This informs hosts
    169         // that can't inject pixel-based scroll events that the client will
    170         // accumulate them into tick-based scrolling, which gives a better
    171         // overall experience than trying to do this host-side.
    172         int ticks_x = static_cast<int>(wheel_ticks_x_);
    173         int ticks_y = static_cast<int>(wheel_ticks_y_);
    174         wheel_ticks_x_ -= ticks_x;
    175         wheel_ticks_y_ -= ticks_y;
    176         mouse_event.set_wheel_ticks_x(ticks_x);
    177         mouse_event.set_wheel_ticks_y(ticks_y);
    178 
    179         if (input_stub_)
    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