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