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   ui::KeycodeConverter* key_converter = ui::KeycodeConverter::GetInstance();
     43   return key_converter->CodeToUsbKeycode(codestr.c_str());
     44 }
     45 
     46 bool 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 
    199 void PepperInputHandler::AllowMouseLock() {
    200   DCHECK_EQ(mouse_lock_state_, MouseLockDisallowed);
    201   mouse_lock_state_ = MouseLockOff;
    202 }
    203 
    204 void PepperInputHandler::DidChangeFocus(bool has_focus) {
    205   has_focus_ = has_focus;
    206   if (has_focus_)
    207     RequestMouseLock();
    208 }
    209 
    210 void 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 
    222 void PepperInputHandler::MouseLockLost() {
    223   DCHECK(mouse_lock_state_ == MouseLockOn ||
    224          mouse_lock_state_ == MouseLockCancelling);
    225 
    226   mouse_lock_state_ = MouseLockOff;
    227   UpdateMouseCursor();
    228 }
    229 
    230 void 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 
    246 void 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 
    275 void 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 
    291 void 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