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 make_scoped_ptr(
329 new InputInjectorWin(main_task_runner
, ui_task_runner
));
332 } // namespace remoting