Home | History | Annotate | Download | only in host
      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/host/input_injector.h"
      6 
      7 #include <windows.h>
      8 
      9 #include "base/bind.h"
     10 #include "base/compiler_specific.h"
     11 #include "base/location.h"
     12 #include "base/memory/ref_counted.h"
     13 #include "base/single_thread_task_runner.h"
     14 #include "remoting/base/util.h"
     15 #include "remoting/host/clipboard.h"
     16 #include "remoting/proto/event.pb.h"
     17 // SkSize.h assumes that stdint.h-style types are already defined.
     18 #include "third_party/skia/include/core/SkTypes.h"
     19 #include "third_party/skia/include/core/SkSize.h"
     20 
     21 namespace remoting {
     22 
     23 namespace {
     24 
     25 using protocol::ClipboardEvent;
     26 using protocol::KeyEvent;
     27 using protocol::MouseEvent;
     28 
     29 // USB to XKB keycode map table.
     30 #define USB_KEYMAP(usb, xkb, win, mac) {usb, win}
     31 #include "ui/base/keycodes/usb_keycode_map.h"
     32 #undef USB_KEYMAP
     33 
     34 // A class to generate events on Windows.
     35 class InputInjectorWin : public InputInjector {
     36  public:
     37   InputInjectorWin(scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
     38                    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner);
     39   virtual ~InputInjectorWin();
     40 
     41   // ClipboardStub interface.
     42   virtual void InjectClipboardEvent(const ClipboardEvent& event) OVERRIDE;
     43 
     44   // InputStub interface.
     45   virtual void InjectKeyEvent(const KeyEvent& event) OVERRIDE;
     46   virtual void InjectMouseEvent(const MouseEvent& event) OVERRIDE;
     47 
     48   // InputInjector interface.
     49   virtual void Start(
     50       scoped_ptr<protocol::ClipboardStub> client_clipboard) OVERRIDE;
     51 
     52  private:
     53   // The actual implementation resides in InputInjectorWin::Core class.
     54   class Core : public base::RefCountedThreadSafe<Core> {
     55    public:
     56     Core(scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
     57          scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner);
     58 
     59     // Mirrors the ClipboardStub interface.
     60     void InjectClipboardEvent(const ClipboardEvent& event);
     61 
     62     // Mirrors the InputStub interface.
     63     void InjectKeyEvent(const KeyEvent& event);
     64     void InjectMouseEvent(const MouseEvent& event);
     65 
     66     // Mirrors the InputInjector interface.
     67     void Start(scoped_ptr<protocol::ClipboardStub> client_clipboard);
     68 
     69     void Stop();
     70 
     71    private:
     72     friend class base::RefCountedThreadSafe<Core>;
     73     virtual ~Core();
     74 
     75     void HandleKey(const KeyEvent& event);
     76     void HandleMouse(const MouseEvent& event);
     77 
     78     scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
     79     scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
     80     scoped_ptr<Clipboard> clipboard_;
     81 
     82     DISALLOW_COPY_AND_ASSIGN(Core);
     83   };
     84 
     85   scoped_refptr<Core> core_;
     86 
     87   DISALLOW_COPY_AND_ASSIGN(InputInjectorWin);
     88 };
     89 
     90 InputInjectorWin::InputInjectorWin(
     91     scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
     92     scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
     93   core_ = new Core(main_task_runner, ui_task_runner);
     94 }
     95 
     96 InputInjectorWin::~InputInjectorWin() {
     97   core_->Stop();
     98 }
     99 
    100 void InputInjectorWin::InjectClipboardEvent(const ClipboardEvent& event) {
    101   core_->InjectClipboardEvent(event);
    102 }
    103 
    104 void InputInjectorWin::InjectKeyEvent(const KeyEvent& event) {
    105   core_->InjectKeyEvent(event);
    106 }
    107 
    108 void InputInjectorWin::InjectMouseEvent(const MouseEvent& event) {
    109   core_->InjectMouseEvent(event);
    110 }
    111 
    112 void InputInjectorWin::Start(
    113     scoped_ptr<protocol::ClipboardStub> client_clipboard) {
    114   core_->Start(client_clipboard.Pass());
    115 }
    116 
    117 InputInjectorWin::Core::Core(
    118     scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
    119     scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner)
    120     : main_task_runner_(main_task_runner),
    121       ui_task_runner_(ui_task_runner),
    122       clipboard_(Clipboard::Create()) {
    123 }
    124 
    125 void InputInjectorWin::Core::InjectClipboardEvent(const ClipboardEvent& event) {
    126   if (!ui_task_runner_->BelongsToCurrentThread()) {
    127     ui_task_runner_->PostTask(
    128         FROM_HERE, base::Bind(&Core::InjectClipboardEvent, this, event));
    129     return;
    130   }
    131 
    132   // |clipboard_| will ignore unknown MIME-types, and verify the data's format.
    133   clipboard_->InjectClipboardEvent(event);
    134 }
    135 
    136 void InputInjectorWin::Core::InjectKeyEvent(const KeyEvent& event) {
    137   if (!main_task_runner_->BelongsToCurrentThread()) {
    138     main_task_runner_->PostTask(FROM_HERE,
    139                                 base::Bind(&Core::InjectKeyEvent, this, event));
    140     return;
    141   }
    142 
    143   HandleKey(event);
    144 }
    145 
    146 void InputInjectorWin::Core::InjectMouseEvent(const MouseEvent& event) {
    147   if (!main_task_runner_->BelongsToCurrentThread()) {
    148     main_task_runner_->PostTask(
    149         FROM_HERE, base::Bind(&Core::InjectMouseEvent, this, event));
    150     return;
    151   }
    152 
    153   HandleMouse(event);
    154 }
    155 
    156 void InputInjectorWin::Core::Start(
    157     scoped_ptr<protocol::ClipboardStub> client_clipboard) {
    158   if (!ui_task_runner_->BelongsToCurrentThread()) {
    159     ui_task_runner_->PostTask(
    160         FROM_HERE,
    161         base::Bind(&Core::Start, this, base::Passed(&client_clipboard)));
    162     return;
    163   }
    164 
    165   clipboard_->Start(client_clipboard.Pass());
    166 }
    167 
    168 void InputInjectorWin::Core::Stop() {
    169   if (!ui_task_runner_->BelongsToCurrentThread()) {
    170     ui_task_runner_->PostTask(FROM_HERE, base::Bind(&Core::Stop, this));
    171     return;
    172   }
    173 
    174   clipboard_->Stop();
    175 }
    176 
    177 InputInjectorWin::Core::~Core() {
    178 }
    179 
    180 void InputInjectorWin::Core::HandleKey(const KeyEvent& event) {
    181   // HostEventDispatcher should filter events missing the pressed field.
    182   if (!event.has_pressed() || !event.has_usb_keycode())
    183     return;
    184 
    185   // Reset the system idle suspend timeout.
    186   SetThreadExecutionState(ES_SYSTEM_REQUIRED);
    187 
    188   int scancode = UsbKeycodeToNativeKeycode(event.usb_keycode());
    189 
    190   VLOG(3) << "Converting USB keycode: " << std::hex << event.usb_keycode()
    191           << " to scancode: " << scancode << std::dec;
    192 
    193   // Ignore events which can't be mapped.
    194   if (scancode == InvalidNativeKeycode())
    195     return;
    196 
    197   // Populate the a Windows INPUT structure for the event.
    198   INPUT input;
    199   memset(&input, 0, sizeof(input));
    200   input.type = INPUT_KEYBOARD;
    201   input.ki.time = 0;
    202   input.ki.dwFlags = KEYEVENTF_SCANCODE;
    203   if (!event.pressed())
    204     input.ki.dwFlags |= KEYEVENTF_KEYUP;
    205 
    206   // Windows scancodes are only 8-bit, so store the low-order byte into the
    207   // event and set the extended flag if any high-order bits are set. The only
    208   // high-order values we should see are 0xE0 or 0xE1. The extended bit usually
    209   // distinguishes keys with the same meaning, e.g. left & right shift.
    210   input.ki.wScan = scancode & 0xFF;
    211   if ((scancode & 0xFF00) != 0x0000)
    212     input.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
    213 
    214   if (SendInput(1, &input, sizeof(INPUT)) == 0)
    215     LOG_GETLASTERROR(ERROR) << "Failed to inject a key event";
    216 }
    217 
    218 void InputInjectorWin::Core::HandleMouse(const MouseEvent& event) {
    219   // Reset the system idle suspend timeout.
    220   SetThreadExecutionState(ES_SYSTEM_REQUIRED);
    221 
    222   // TODO(garykac) Collapse mouse (x,y) and button events into a single
    223   // input event when possible.
    224   if (event.has_x() && event.has_y()) {
    225     int x = event.x();
    226     int y = event.y();
    227 
    228     INPUT input;
    229     input.type = INPUT_MOUSE;
    230     input.mi.time = 0;
    231     SkISize screen_size(SkISize::Make(GetSystemMetrics(SM_CXVIRTUALSCREEN),
    232                                       GetSystemMetrics(SM_CYVIRTUALSCREEN)));
    233     if ((screen_size.width() > 1) && (screen_size.height() > 1)) {
    234       x = std::max(0, std::min(screen_size.width(), x));
    235       y = std::max(0, std::min(screen_size.height(), y));
    236       input.mi.dx = static_cast<int>((x * 65535) / (screen_size.width() - 1));
    237       input.mi.dy = static_cast<int>((y * 65535) / (screen_size.height() - 1));
    238       input.mi.dwFlags =
    239           MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK;
    240       if (SendInput(1, &input, sizeof(INPUT)) == 0)
    241         LOG_GETLASTERROR(ERROR) << "Failed to inject a mouse move event";
    242     }
    243   }
    244 
    245   int wheel_delta_x = 0;
    246   int wheel_delta_y = 0;
    247   if (event.has_wheel_delta_x() && event.has_wheel_delta_y()) {
    248     wheel_delta_x = static_cast<int>(event.wheel_delta_x());
    249     wheel_delta_y = static_cast<int>(event.wheel_delta_y());
    250   }
    251 
    252   if (wheel_delta_x != 0 || wheel_delta_y != 0) {
    253     INPUT wheel;
    254     wheel.type = INPUT_MOUSE;
    255     wheel.mi.time = 0;
    256 
    257     if (wheel_delta_x != 0) {
    258       wheel.mi.mouseData = wheel_delta_x;
    259       wheel.mi.dwFlags = MOUSEEVENTF_HWHEEL;
    260       if (SendInput(1, &wheel, sizeof(INPUT)) == 0)
    261         LOG_GETLASTERROR(ERROR) << "Failed to inject a mouse wheel(x) event";
    262     }
    263     if (wheel_delta_y != 0) {
    264       wheel.mi.mouseData = wheel_delta_y;
    265       wheel.mi.dwFlags = MOUSEEVENTF_WHEEL;
    266       if (SendInput(1, &wheel, sizeof(INPUT)) == 0)
    267         LOG_GETLASTERROR(ERROR) << "Failed to inject a mouse wheel(y) event";
    268     }
    269   }
    270 
    271   if (event.has_button() && event.has_button_down()) {
    272     INPUT button_event;
    273     button_event.type = INPUT_MOUSE;
    274     button_event.mi.time = 0;
    275     button_event.mi.dx = 0;
    276     button_event.mi.dy = 0;
    277 
    278     MouseEvent::MouseButton button = event.button();
    279     bool down = event.button_down();
    280 
    281     // If the host is configured to swap left & right buttons, inject swapped
    282     // events to un-do that re-mapping.
    283     if (GetSystemMetrics(SM_SWAPBUTTON)) {
    284       if (button == MouseEvent::BUTTON_LEFT) {
    285         button = MouseEvent::BUTTON_RIGHT;
    286       } else if (button == MouseEvent::BUTTON_RIGHT) {
    287         button = MouseEvent::BUTTON_LEFT;
    288       }
    289     }
    290 
    291     if (button == MouseEvent::BUTTON_LEFT) {
    292       button_event.mi.dwFlags =
    293           down ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP;
    294     } else if (button == MouseEvent::BUTTON_MIDDLE) {
    295       button_event.mi.dwFlags =
    296           down ? MOUSEEVENTF_MIDDLEDOWN : MOUSEEVENTF_MIDDLEUP;
    297     } else if (button == MouseEvent::BUTTON_RIGHT) {
    298       button_event.mi.dwFlags =
    299           down ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP;
    300     } else {
    301       button_event.mi.dwFlags =
    302           down ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP;
    303     }
    304 
    305     if (SendInput(1, &button_event, sizeof(INPUT)) == 0)
    306       LOG_GETLASTERROR(ERROR) << "Failed to inject a mouse button event";
    307   }
    308 }
    309 
    310 }  // namespace
    311 
    312 scoped_ptr<InputInjector> InputInjector::Create(
    313     scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
    314     scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
    315   return scoped_ptr<InputInjector>(
    316       new InputInjectorWin(main_task_runner, ui_task_runner));
    317 }
    318 
    319 }  // namespace remoting
    320