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