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
;
54 using protocol::TouchEvent
;
56 // A class to generate events on Windows.
57 class InputInjectorWin
: public InputInjector
{
59 InputInjectorWin(scoped_refptr
<base::SingleThreadTaskRunner
> main_task_runner
,
60 scoped_refptr
<base::SingleThreadTaskRunner
> ui_task_runner
);
61 virtual ~InputInjectorWin();
63 // ClipboardStub interface.
64 virtual void InjectClipboardEvent(const ClipboardEvent
& event
) override
;
66 // InputStub interface.
67 virtual void InjectKeyEvent(const KeyEvent
& event
) override
;
68 virtual void InjectTextEvent(const TextEvent
& event
) override
;
69 virtual void InjectMouseEvent(const MouseEvent
& event
) override
;
70 virtual void InjectTouchEvent(const TouchEvent
& event
) override
;
72 // InputInjector interface.
74 scoped_ptr
<protocol::ClipboardStub
> client_clipboard
) override
;
77 // The actual implementation resides in InputInjectorWin::Core class.
78 class Core
: public base::RefCountedThreadSafe
<Core
> {
80 Core(scoped_refptr
<base::SingleThreadTaskRunner
> main_task_runner
,
81 scoped_refptr
<base::SingleThreadTaskRunner
> ui_task_runner
);
83 // Mirrors the ClipboardStub interface.
84 void InjectClipboardEvent(const ClipboardEvent
& event
);
86 // Mirrors the InputStub interface.
87 void InjectKeyEvent(const KeyEvent
& event
);
88 void InjectTextEvent(const TextEvent
& event
);
89 void InjectMouseEvent(const MouseEvent
& event
);
91 // Mirrors the InputInjector interface.
92 void Start(scoped_ptr
<protocol::ClipboardStub
> client_clipboard
);
97 friend class base::RefCountedThreadSafe
<Core
>;
100 void HandleKey(const KeyEvent
& event
);
101 void HandleText(const TextEvent
& event
);
102 void HandleMouse(const MouseEvent
& event
);
104 scoped_refptr
<base::SingleThreadTaskRunner
> main_task_runner_
;
105 scoped_refptr
<base::SingleThreadTaskRunner
> ui_task_runner_
;
106 scoped_ptr
<Clipboard
> clipboard_
;
108 DISALLOW_COPY_AND_ASSIGN(Core
);
111 scoped_refptr
<Core
> core_
;
113 DISALLOW_COPY_AND_ASSIGN(InputInjectorWin
);
116 InputInjectorWin::InputInjectorWin(
117 scoped_refptr
<base::SingleThreadTaskRunner
> main_task_runner
,
118 scoped_refptr
<base::SingleThreadTaskRunner
> ui_task_runner
) {
119 core_
= new Core(main_task_runner
, ui_task_runner
);
122 InputInjectorWin::~InputInjectorWin() {
126 void InputInjectorWin::InjectClipboardEvent(const ClipboardEvent
& event
) {
127 core_
->InjectClipboardEvent(event
);
130 void InputInjectorWin::InjectKeyEvent(const KeyEvent
& event
) {
131 core_
->InjectKeyEvent(event
);
134 void InputInjectorWin::InjectTextEvent(const TextEvent
& event
) {
135 core_
->InjectTextEvent(event
);
138 void InputInjectorWin::InjectMouseEvent(const MouseEvent
& event
) {
139 core_
->InjectMouseEvent(event
);
142 void InputInjectorWin::InjectTouchEvent(const TouchEvent
& event
) {
143 NOTIMPLEMENTED() << "Raw touch event injection not implemented for Windows.";
146 void InputInjectorWin::Start(
147 scoped_ptr
<protocol::ClipboardStub
> client_clipboard
) {
148 core_
->Start(client_clipboard
.Pass());
151 InputInjectorWin::Core::Core(
152 scoped_refptr
<base::SingleThreadTaskRunner
> main_task_runner
,
153 scoped_refptr
<base::SingleThreadTaskRunner
> ui_task_runner
)
154 : main_task_runner_(main_task_runner
),
155 ui_task_runner_(ui_task_runner
),
156 clipboard_(Clipboard::Create()) {
159 void InputInjectorWin::Core::InjectClipboardEvent(const ClipboardEvent
& event
) {
160 if (!ui_task_runner_
->BelongsToCurrentThread()) {
161 ui_task_runner_
->PostTask(
162 FROM_HERE
, base::Bind(&Core::InjectClipboardEvent
, this, event
));
166 // |clipboard_| will ignore unknown MIME-types, and verify the data's format.
167 clipboard_
->InjectClipboardEvent(event
);
170 void InputInjectorWin::Core::InjectKeyEvent(const KeyEvent
& event
) {
171 if (!main_task_runner_
->BelongsToCurrentThread()) {
172 main_task_runner_
->PostTask(FROM_HERE
,
173 base::Bind(&Core::InjectKeyEvent
, this, event
));
180 void InputInjectorWin::Core::InjectTextEvent(const TextEvent
& event
) {
181 if (!main_task_runner_
->BelongsToCurrentThread()) {
182 main_task_runner_
->PostTask(
183 FROM_HERE
, base::Bind(&Core::InjectTextEvent
, this, event
));
190 void InputInjectorWin::Core::InjectMouseEvent(const MouseEvent
& event
) {
191 if (!main_task_runner_
->BelongsToCurrentThread()) {
192 main_task_runner_
->PostTask(
193 FROM_HERE
, base::Bind(&Core::InjectMouseEvent
, this, event
));
200 void InputInjectorWin::Core::Start(
201 scoped_ptr
<protocol::ClipboardStub
> client_clipboard
) {
202 if (!ui_task_runner_
->BelongsToCurrentThread()) {
203 ui_task_runner_
->PostTask(
205 base::Bind(&Core::Start
, this, base::Passed(&client_clipboard
)));
209 clipboard_
->Start(client_clipboard
.Pass());
212 void InputInjectorWin::Core::Stop() {
213 if (!ui_task_runner_
->BelongsToCurrentThread()) {
214 ui_task_runner_
->PostTask(FROM_HERE
, base::Bind(&Core::Stop
, this));
221 InputInjectorWin::Core::~Core() {}
223 void InputInjectorWin::Core::HandleKey(const KeyEvent
& event
) {
224 // HostEventDispatcher should filter events missing the pressed field.
225 DCHECK(event
.has_pressed() && event
.has_usb_keycode());
227 // Reset the system idle suspend timeout.
228 SetThreadExecutionState(ES_SYSTEM_REQUIRED
);
231 ui::KeycodeConverter::UsbKeycodeToNativeKeycode(event
.usb_keycode());
232 VLOG(3) << "Converting USB keycode: " << std::hex
<< event
.usb_keycode()
233 << " to scancode: " << scancode
<< std::dec
;
235 // Ignore events which can't be mapped.
236 if (scancode
== ui::KeycodeConverter::InvalidNativeKeycode())
239 uint32_t flags
= KEYEVENTF_SCANCODE
| (event
.pressed() ? 0 : KEYEVENTF_KEYUP
);
240 SendKeyboardInput(flags
, scancode
);
243 void InputInjectorWin::Core::HandleText(const TextEvent
& event
) {
244 // HostEventDispatcher should filter events missing the pressed field.
245 DCHECK(event
.has_text());
247 base::string16 text
= base::UTF8ToUTF16(event
.text());
248 for (base::string16::const_iterator it
= text
.begin();
249 it
!= text
.end(); ++it
) {
250 SendKeyboardInput(KEYEVENTF_UNICODE
, *it
);
251 SendKeyboardInput(KEYEVENTF_UNICODE
| KEYEVENTF_KEYUP
, *it
);
255 void InputInjectorWin::Core::HandleMouse(const MouseEvent
& event
) {
256 // Reset the system idle suspend timeout.
257 SetThreadExecutionState(ES_SYSTEM_REQUIRED
);
260 memset(&input
, 0, sizeof(input
));
261 input
.type
= INPUT_MOUSE
;
263 if (event
.has_delta_x() && event
.has_delta_y()) {
264 input
.mi
.dx
= event
.delta_x();
265 input
.mi
.dy
= event
.delta_y();
266 input
.mi
.dwFlags
|= MOUSEEVENTF_MOVE
| MOUSEEVENTF_VIRTUALDESK
;
267 } else if (event
.has_x() && event
.has_y()) {
268 int width
= GetSystemMetrics(SM_CXVIRTUALSCREEN
);
269 int height
= GetSystemMetrics(SM_CYVIRTUALSCREEN
);
270 if (width
> 1 && height
> 1) {
271 int x
= std::max(0, std::min(width
, event
.x()));
272 int y
= std::max(0, std::min(height
, event
.y()));
273 input
.mi
.dx
= static_cast<int>((x
* 65535) / (width
- 1));
274 input
.mi
.dy
= static_cast<int>((y
* 65535) / (height
- 1));
276 MOUSEEVENTF_MOVE
| MOUSEEVENTF_ABSOLUTE
| MOUSEEVENTF_VIRTUALDESK
;
280 int wheel_delta_x
= 0;
281 int wheel_delta_y
= 0;
282 if (event
.has_wheel_delta_x() && event
.has_wheel_delta_y()) {
283 wheel_delta_x
= static_cast<int>(event
.wheel_delta_x());
284 wheel_delta_y
= static_cast<int>(event
.wheel_delta_y());
287 if (wheel_delta_x
!= 0 || wheel_delta_y
!= 0) {
288 if (wheel_delta_x
!= 0) {
289 input
.mi
.mouseData
= wheel_delta_x
;
290 input
.mi
.dwFlags
|= MOUSEEVENTF_HWHEEL
;
292 if (wheel_delta_y
!= 0) {
293 input
.mi
.mouseData
= wheel_delta_y
;
294 input
.mi
.dwFlags
|= MOUSEEVENTF_WHEEL
;
298 if (event
.has_button() && event
.has_button_down()) {
299 MouseEvent::MouseButton button
= event
.button();
300 bool down
= event
.button_down();
302 // If the host is configured to swap left & right buttons, inject swapped
303 // events to un-do that re-mapping.
304 if (GetSystemMetrics(SM_SWAPBUTTON
)) {
305 if (button
== MouseEvent::BUTTON_LEFT
) {
306 button
= MouseEvent::BUTTON_RIGHT
;
307 } else if (button
== MouseEvent::BUTTON_RIGHT
) {
308 button
= MouseEvent::BUTTON_LEFT
;
312 if (button
== MouseEvent::BUTTON_LEFT
) {
313 input
.mi
.dwFlags
|= down
? MOUSEEVENTF_LEFTDOWN
: MOUSEEVENTF_LEFTUP
;
314 } else if (button
== MouseEvent::BUTTON_MIDDLE
) {
315 input
.mi
.dwFlags
|= down
? MOUSEEVENTF_MIDDLEDOWN
: MOUSEEVENTF_MIDDLEUP
;
316 } else if (button
== MouseEvent::BUTTON_RIGHT
) {
317 input
.mi
.dwFlags
|= down
? MOUSEEVENTF_RIGHTDOWN
: MOUSEEVENTF_RIGHTUP
;
319 input
.mi
.dwFlags
|= down
? MOUSEEVENTF_LEFTDOWN
: MOUSEEVENTF_LEFTUP
;
323 if (input
.mi
.dwFlags
) {
324 if (SendInput(1, &input
, sizeof(INPUT
)) == 0)
325 PLOG(ERROR
) << "Failed to inject a mouse event";
331 scoped_ptr
<InputInjector
> InputInjector::Create(
332 scoped_refptr
<base::SingleThreadTaskRunner
> main_task_runner
,
333 scoped_refptr
<base::SingleThreadTaskRunner
> ui_task_runner
) {
334 return make_scoped_ptr(
335 new InputInjectorWin(main_task_runner
, ui_task_runner
));
338 } // namespace remoting