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/host/touch_injector_win.h"
19 #include "remoting/proto/event.pb.h"
20 #include "ui/events/keycodes/dom/keycode_converter.h"
26 // Helper used to call SendInput() API.
27 void SendKeyboardInput(uint32_t flags
, uint16_t scancode
) {
28 // Populate a Windows INPUT structure for the event.
30 memset(&input
, 0, sizeof(input
));
31 input
.type
= INPUT_KEYBOARD
;
33 input
.ki
.dwFlags
= flags
;
34 input
.ki
.wScan
= scancode
;
36 if ((flags
& KEYEVENTF_UNICODE
) == 0) {
37 // Windows scancodes are only 8-bit, so store the low-order byte into the
38 // event and set the extended flag if any high-order bits are set. The only
39 // high-order values we should see are 0xE0 or 0xE1. The extended bit
40 // usually distinguishes keys with the same meaning, e.g. left & right
42 input
.ki
.wScan
&= 0xFF;
43 if ((scancode
& 0xFF00) != 0x0000)
44 input
.ki
.dwFlags
|= KEYEVENTF_EXTENDEDKEY
;
47 if (SendInput(1, &input
, sizeof(INPUT
)) == 0)
48 PLOG(ERROR
) << "Failed to inject a key event";
51 using protocol::ClipboardEvent
;
52 using protocol::KeyEvent
;
53 using protocol::TextEvent
;
54 using protocol::MouseEvent
;
55 using protocol::TouchEvent
;
57 // A class to generate events on Windows.
58 class InputInjectorWin
: public InputInjector
{
60 InputInjectorWin(scoped_refptr
<base::SingleThreadTaskRunner
> main_task_runner
,
61 scoped_refptr
<base::SingleThreadTaskRunner
> ui_task_runner
);
62 ~InputInjectorWin() override
;
64 // ClipboardStub interface.
65 void InjectClipboardEvent(const ClipboardEvent
& event
) override
;
67 // InputStub interface.
68 void InjectKeyEvent(const KeyEvent
& event
) override
;
69 void InjectTextEvent(const TextEvent
& event
) override
;
70 void InjectMouseEvent(const MouseEvent
& event
) override
;
71 void InjectTouchEvent(const TouchEvent
& event
) override
;
73 // InputInjector interface.
75 scoped_ptr
<protocol::ClipboardStub
> client_clipboard
) override
;
78 // The actual implementation resides in InputInjectorWin::Core class.
79 class Core
: public base::RefCountedThreadSafe
<Core
> {
81 Core(scoped_refptr
<base::SingleThreadTaskRunner
> main_task_runner
,
82 scoped_refptr
<base::SingleThreadTaskRunner
> ui_task_runner
);
84 // Mirrors the ClipboardStub interface.
85 void InjectClipboardEvent(const ClipboardEvent
& event
);
87 // Mirrors the InputStub interface.
88 void InjectKeyEvent(const KeyEvent
& event
);
89 void InjectTextEvent(const TextEvent
& event
);
90 void InjectMouseEvent(const MouseEvent
& event
);
91 void InjectTouchEvent(const TouchEvent
& event
);
93 // Mirrors the InputInjector interface.
94 void Start(scoped_ptr
<protocol::ClipboardStub
> client_clipboard
);
99 friend class base::RefCountedThreadSafe
<Core
>;
102 void HandleKey(const KeyEvent
& event
);
103 void HandleText(const TextEvent
& event
);
104 void HandleMouse(const MouseEvent
& event
);
105 void HandleTouch(const TouchEvent
& event
);
107 scoped_refptr
<base::SingleThreadTaskRunner
> main_task_runner_
;
108 scoped_refptr
<base::SingleThreadTaskRunner
> ui_task_runner_
;
109 scoped_ptr
<Clipboard
> clipboard_
;
110 TouchInjectorWin touch_injector_
;
112 DISALLOW_COPY_AND_ASSIGN(Core
);
115 scoped_refptr
<Core
> core_
;
117 DISALLOW_COPY_AND_ASSIGN(InputInjectorWin
);
120 InputInjectorWin::InputInjectorWin(
121 scoped_refptr
<base::SingleThreadTaskRunner
> main_task_runner
,
122 scoped_refptr
<base::SingleThreadTaskRunner
> ui_task_runner
) {
123 core_
= new Core(main_task_runner
, ui_task_runner
);
126 InputInjectorWin::~InputInjectorWin() {
130 void InputInjectorWin::InjectClipboardEvent(const ClipboardEvent
& event
) {
131 core_
->InjectClipboardEvent(event
);
134 void InputInjectorWin::InjectKeyEvent(const KeyEvent
& event
) {
135 core_
->InjectKeyEvent(event
);
138 void InputInjectorWin::InjectTextEvent(const TextEvent
& event
) {
139 core_
->InjectTextEvent(event
);
142 void InputInjectorWin::InjectMouseEvent(const MouseEvent
& event
) {
143 core_
->InjectMouseEvent(event
);
146 void InputInjectorWin::InjectTouchEvent(const TouchEvent
& event
) {
147 core_
->InjectTouchEvent(event
);
150 void InputInjectorWin::Start(
151 scoped_ptr
<protocol::ClipboardStub
> client_clipboard
) {
152 core_
->Start(client_clipboard
.Pass());
155 InputInjectorWin::Core::Core(
156 scoped_refptr
<base::SingleThreadTaskRunner
> main_task_runner
,
157 scoped_refptr
<base::SingleThreadTaskRunner
> ui_task_runner
)
158 : main_task_runner_(main_task_runner
),
159 ui_task_runner_(ui_task_runner
),
160 clipboard_(Clipboard::Create()) {
163 void InputInjectorWin::Core::InjectClipboardEvent(const ClipboardEvent
& event
) {
164 if (!ui_task_runner_
->BelongsToCurrentThread()) {
165 ui_task_runner_
->PostTask(
166 FROM_HERE
, base::Bind(&Core::InjectClipboardEvent
, this, event
));
170 // |clipboard_| will ignore unknown MIME-types, and verify the data's format.
171 clipboard_
->InjectClipboardEvent(event
);
174 void InputInjectorWin::Core::InjectKeyEvent(const KeyEvent
& event
) {
175 if (!main_task_runner_
->BelongsToCurrentThread()) {
176 main_task_runner_
->PostTask(FROM_HERE
,
177 base::Bind(&Core::InjectKeyEvent
, this, event
));
184 void InputInjectorWin::Core::InjectTextEvent(const TextEvent
& event
) {
185 if (!main_task_runner_
->BelongsToCurrentThread()) {
186 main_task_runner_
->PostTask(
187 FROM_HERE
, base::Bind(&Core::InjectTextEvent
, this, event
));
194 void InputInjectorWin::Core::InjectMouseEvent(const MouseEvent
& event
) {
195 if (!main_task_runner_
->BelongsToCurrentThread()) {
196 main_task_runner_
->PostTask(
197 FROM_HERE
, base::Bind(&Core::InjectMouseEvent
, this, event
));
204 void InputInjectorWin::Core::InjectTouchEvent(const TouchEvent
& event
) {
205 if (!main_task_runner_
->BelongsToCurrentThread()) {
206 main_task_runner_
->PostTask(
207 FROM_HERE
, base::Bind(&Core::InjectTouchEvent
, this, event
));
214 void InputInjectorWin::Core::Start(
215 scoped_ptr
<protocol::ClipboardStub
> client_clipboard
) {
216 if (!ui_task_runner_
->BelongsToCurrentThread()) {
217 ui_task_runner_
->PostTask(
219 base::Bind(&Core::Start
, this, base::Passed(&client_clipboard
)));
223 clipboard_
->Start(client_clipboard
.Pass());
224 touch_injector_
.Init();
227 void InputInjectorWin::Core::Stop() {
228 if (!ui_task_runner_
->BelongsToCurrentThread()) {
229 ui_task_runner_
->PostTask(FROM_HERE
, base::Bind(&Core::Stop
, this));
234 touch_injector_
.Deinitialize();
237 InputInjectorWin::Core::~Core() {}
239 void InputInjectorWin::Core::HandleKey(const KeyEvent
& event
) {
240 // HostEventDispatcher should filter events missing the pressed field.
241 DCHECK(event
.has_pressed() && event
.has_usb_keycode());
243 // Reset the system idle suspend timeout.
244 SetThreadExecutionState(ES_SYSTEM_REQUIRED
);
247 ui::KeycodeConverter::UsbKeycodeToNativeKeycode(event
.usb_keycode());
248 VLOG(3) << "Converting USB keycode: " << std::hex
<< event
.usb_keycode()
249 << " to scancode: " << scancode
<< std::dec
;
251 // Ignore events which can't be mapped.
252 if (scancode
== ui::KeycodeConverter::InvalidNativeKeycode())
255 uint32_t flags
= KEYEVENTF_SCANCODE
| (event
.pressed() ? 0 : KEYEVENTF_KEYUP
);
256 SendKeyboardInput(flags
, scancode
);
259 void InputInjectorWin::Core::HandleText(const TextEvent
& event
) {
260 // HostEventDispatcher should filter events missing the pressed field.
261 DCHECK(event
.has_text());
263 base::string16 text
= base::UTF8ToUTF16(event
.text());
264 for (base::string16::const_iterator it
= text
.begin();
265 it
!= text
.end(); ++it
) {
266 SendKeyboardInput(KEYEVENTF_UNICODE
, *it
);
267 SendKeyboardInput(KEYEVENTF_UNICODE
| KEYEVENTF_KEYUP
, *it
);
271 void InputInjectorWin::Core::HandleMouse(const MouseEvent
& event
) {
272 // Reset the system idle suspend timeout.
273 SetThreadExecutionState(ES_SYSTEM_REQUIRED
);
276 memset(&input
, 0, sizeof(input
));
277 input
.type
= INPUT_MOUSE
;
279 if (event
.has_delta_x() && event
.has_delta_y()) {
280 input
.mi
.dx
= event
.delta_x();
281 input
.mi
.dy
= event
.delta_y();
282 input
.mi
.dwFlags
|= MOUSEEVENTF_MOVE
| MOUSEEVENTF_VIRTUALDESK
;
283 } else if (event
.has_x() && event
.has_y()) {
284 int width
= GetSystemMetrics(SM_CXVIRTUALSCREEN
);
285 int height
= GetSystemMetrics(SM_CYVIRTUALSCREEN
);
286 if (width
> 1 && height
> 1) {
287 int x
= std::max(0, std::min(width
, event
.x()));
288 int y
= std::max(0, std::min(height
, event
.y()));
289 input
.mi
.dx
= static_cast<int>((x
* 65535) / (width
- 1));
290 input
.mi
.dy
= static_cast<int>((y
* 65535) / (height
- 1));
292 MOUSEEVENTF_MOVE
| MOUSEEVENTF_ABSOLUTE
| MOUSEEVENTF_VIRTUALDESK
;
296 int wheel_delta_x
= 0;
297 int wheel_delta_y
= 0;
298 if (event
.has_wheel_delta_x() && event
.has_wheel_delta_y()) {
299 wheel_delta_x
= static_cast<int>(event
.wheel_delta_x());
300 wheel_delta_y
= static_cast<int>(event
.wheel_delta_y());
303 if (wheel_delta_x
!= 0 || wheel_delta_y
!= 0) {
304 if (wheel_delta_x
!= 0) {
305 input
.mi
.mouseData
= wheel_delta_x
;
306 input
.mi
.dwFlags
|= MOUSEEVENTF_HWHEEL
;
308 if (wheel_delta_y
!= 0) {
309 input
.mi
.mouseData
= wheel_delta_y
;
310 input
.mi
.dwFlags
|= MOUSEEVENTF_WHEEL
;
314 if (event
.has_button() && event
.has_button_down()) {
315 MouseEvent::MouseButton button
= event
.button();
316 bool down
= event
.button_down();
318 // If the host is configured to swap left & right buttons, inject swapped
319 // events to un-do that re-mapping.
320 if (GetSystemMetrics(SM_SWAPBUTTON
)) {
321 if (button
== MouseEvent::BUTTON_LEFT
) {
322 button
= MouseEvent::BUTTON_RIGHT
;
323 } else if (button
== MouseEvent::BUTTON_RIGHT
) {
324 button
= MouseEvent::BUTTON_LEFT
;
328 if (button
== MouseEvent::BUTTON_LEFT
) {
329 input
.mi
.dwFlags
|= down
? MOUSEEVENTF_LEFTDOWN
: MOUSEEVENTF_LEFTUP
;
330 } else if (button
== MouseEvent::BUTTON_MIDDLE
) {
331 input
.mi
.dwFlags
|= down
? MOUSEEVENTF_MIDDLEDOWN
: MOUSEEVENTF_MIDDLEUP
;
332 } else if (button
== MouseEvent::BUTTON_RIGHT
) {
333 input
.mi
.dwFlags
|= down
? MOUSEEVENTF_RIGHTDOWN
: MOUSEEVENTF_RIGHTUP
;
335 input
.mi
.dwFlags
|= down
? MOUSEEVENTF_LEFTDOWN
: MOUSEEVENTF_LEFTUP
;
339 if (input
.mi
.dwFlags
) {
340 if (SendInput(1, &input
, sizeof(INPUT
)) == 0)
341 PLOG(ERROR
) << "Failed to inject a mouse event";
345 void InputInjectorWin::Core::HandleTouch(const TouchEvent
& event
) {
346 touch_injector_
.InjectTouchEvent(event
);
351 scoped_ptr
<InputInjector
> InputInjector::Create(
352 scoped_refptr
<base::SingleThreadTaskRunner
> main_task_runner
,
353 scoped_refptr
<base::SingleThreadTaskRunner
> ui_task_runner
) {
354 return make_scoped_ptr(
355 new InputInjectorWin(main_task_runner
, ui_task_runner
));
358 } // namespace remoting