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