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.
5 #include "remoting/host/input_injector.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"
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.
29 memset(&input, 0, sizeof(input));
30 input.type = INPUT_KEYBOARD;
32 input.ki.dwFlags = flags;
33 input.ki.wScan = scancode;
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
41 input.ki.wScan &= 0xFF;
42 if ((scancode & 0xFF00) != 0x0000)
43 input.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
46 if (SendInput(1, &input, sizeof(INPUT)) == 0)
47 PLOG(ERROR) << "Failed to inject a key event";
50 using protocol::ClipboardEvent;
51 using protocol::KeyEvent;
52 using protocol::TextEvent;
53 using protocol::MouseEvent;
55 // A class to generate events on Windows.
56 class InputInjectorWin : public InputInjector {
58 InputInjectorWin(scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
59 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner);
60 virtual ~InputInjectorWin();
62 // ClipboardStub interface.
63 virtual void InjectClipboardEvent(const ClipboardEvent& event) OVERRIDE;
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;
70 // InputInjector interface.
72 scoped_ptr<protocol::ClipboardStub> client_clipboard) OVERRIDE;
75 // The actual implementation resides in InputInjectorWin::Core class.
76 class Core : public base::RefCountedThreadSafe<Core> {
78 Core(scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
79 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner);
81 // Mirrors the ClipboardStub interface.
82 void InjectClipboardEvent(const ClipboardEvent& event);
84 // Mirrors the InputStub interface.
85 void InjectKeyEvent(const KeyEvent& event);
86 void InjectTextEvent(const TextEvent& event);
87 void InjectMouseEvent(const MouseEvent& event);
89 // Mirrors the InputInjector interface.
90 void Start(scoped_ptr<protocol::ClipboardStub> client_clipboard);
95 friend class base::RefCountedThreadSafe<Core>;
98 void HandleKey(const KeyEvent& event);
99 void HandleText(const TextEvent& event);
100 void HandleMouse(const MouseEvent& event);
102 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
103 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
104 scoped_ptr<Clipboard> clipboard_;
106 DISALLOW_COPY_AND_ASSIGN(Core);
109 scoped_refptr<Core> core_;
111 DISALLOW_COPY_AND_ASSIGN(InputInjectorWin);
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);
120 InputInjectorWin::~InputInjectorWin() {
124 void InputInjectorWin::InjectClipboardEvent(const ClipboardEvent& event) {
125 core_->InjectClipboardEvent(event);
128 void InputInjectorWin::InjectKeyEvent(const KeyEvent& event) {
129 core_->InjectKeyEvent(event);
132 void InputInjectorWin::InjectTextEvent(const TextEvent& event) {
133 core_->InjectTextEvent(event);
136 void InputInjectorWin::InjectMouseEvent(const MouseEvent& event) {
137 core_->InjectMouseEvent(event);
140 void InputInjectorWin::Start(
141 scoped_ptr<protocol::ClipboardStub> client_clipboard) {
142 core_->Start(client_clipboard.Pass());
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()) {
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));
160 // |clipboard_| will ignore unknown MIME-types, and verify the data's format.
161 clipboard_->InjectClipboardEvent(event);
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));
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));
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));
194 void InputInjectorWin::Core::Start(
195 scoped_ptr<protocol::ClipboardStub> client_clipboard) {
196 if (!ui_task_runner_->BelongsToCurrentThread()) {
197 ui_task_runner_->PostTask(
199 base::Bind(&Core::Start, this, base::Passed(&client_clipboard)));
203 clipboard_->Start(client_clipboard.Pass());
206 void InputInjectorWin::Core::Stop() {
207 if (!ui_task_runner_->BelongsToCurrentThread()) {
208 ui_task_runner_->PostTask(FROM_HERE, base::Bind(&Core::Stop, this));
215 InputInjectorWin::Core::~Core() {}
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());
221 // Reset the system idle suspend timeout.
222 SetThreadExecutionState(ES_SYSTEM_REQUIRED);
225 ui::KeycodeConverter::UsbKeycodeToNativeKeycode(event.usb_keycode());
226 VLOG(3) << "Converting USB keycode: " << std::hex << event.usb_keycode()
227 << " to scancode: " << scancode << std::dec;
229 // Ignore events which can't be mapped.
230 if (scancode == ui::KeycodeConverter::InvalidNativeKeycode())
233 uint32_t flags = KEYEVENTF_SCANCODE | (event.pressed() ? 0 : KEYEVENTF_KEYUP);
234 SendKeyboardInput(flags, scancode);
237 void InputInjectorWin::Core::HandleText(const TextEvent& event) {
238 // HostEventDispatcher should filter events missing the pressed field.
239 DCHECK(event.has_text());
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);
249 void InputInjectorWin::Core::HandleMouse(const MouseEvent& event) {
250 // Reset the system idle suspend timeout.
251 SetThreadExecutionState(ES_SYSTEM_REQUIRED);
254 memset(&input, 0, sizeof(input));
255 input.type = INPUT_MOUSE;
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));
270 MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK;
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());
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;
286 if (wheel_delta_y != 0) {
287 input.mi.mouseData = wheel_delta_y;
288 input.mi.dwFlags |= MOUSEEVENTF_WHEEL;
292 if (event.has_button() && event.has_button_down()) {
293 MouseEvent::MouseButton button = event.button();
294 bool down = event.button_down();
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;
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;
313 input.mi.dwFlags |= down ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP;
317 if (input.mi.dwFlags) {
318 if (SendInput(1, &input, sizeof(INPUT)) == 0)
319 PLOG(ERROR) << "Failed to inject a mouse event";
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));
332 } // namespace remoting