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