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
InputInjectorWin(scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner)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
~InputInjectorWin()89 InputInjectorWin::~InputInjectorWin() {
90 core_->Stop();
91 }
92
InjectClipboardEvent(const ClipboardEvent & event)93 void InputInjectorWin::InjectClipboardEvent(const ClipboardEvent& event) {
94 core_->InjectClipboardEvent(event);
95 }
96
InjectKeyEvent(const KeyEvent & event)97 void InputInjectorWin::InjectKeyEvent(const KeyEvent& event) {
98 core_->InjectKeyEvent(event);
99 }
100
InjectMouseEvent(const MouseEvent & event)101 void InputInjectorWin::InjectMouseEvent(const MouseEvent& event) {
102 core_->InjectMouseEvent(event);
103 }
104
Start(scoped_ptr<protocol::ClipboardStub> client_clipboard)105 void InputInjectorWin::Start(
106 scoped_ptr<protocol::ClipboardStub> client_clipboard) {
107 core_->Start(client_clipboard.Pass());
108 }
109
Core(scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner)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
InjectClipboardEvent(const ClipboardEvent & event)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
InjectKeyEvent(const KeyEvent & event)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
InjectMouseEvent(const MouseEvent & event)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
Start(scoped_ptr<protocol::ClipboardStub> client_clipboard)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
Stop()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
~Core()170 InputInjectorWin::Core::~Core() {
171 }
172
HandleKey(const KeyEvent & event)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
HandleMouse(const MouseEvent & event)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
Create(scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner)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