MD Downloads: prevent search text from overlapping with the cancel search (X)
[chromium-blink-merge.git] / remoting / host / input_injector_win.cc
blob41972582b9d0f18fdf141df84feff81567319db4
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"
7 #include <windows.h>
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 "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"
22 namespace remoting {
24 namespace {
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.
29 INPUT input;
30 memset(&input, 0, sizeof(input));
31 input.type = INPUT_KEYBOARD;
32 input.ki.time = 0;
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
41 // shift.
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 {
59 public:
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.
74 void Start(
75 scoped_ptr<protocol::ClipboardStub> client_clipboard) override;
77 private:
78 // The actual implementation resides in InputInjectorWin::Core class.
79 class Core : public base::RefCountedThreadSafe<Core> {
80 public:
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);
96 void Stop();
98 private:
99 friend class base::RefCountedThreadSafe<Core>;
100 virtual ~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() {
127 core_->Stop();
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));
167 return;
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));
178 return;
181 HandleKey(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));
188 return;
191 HandleText(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));
198 return;
201 HandleMouse(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));
208 return;
211 HandleTouch(event);
214 void InputInjectorWin::Core::Start(
215 scoped_ptr<protocol::ClipboardStub> client_clipboard) {
216 if (!ui_task_runner_->BelongsToCurrentThread()) {
217 ui_task_runner_->PostTask(
218 FROM_HERE,
219 base::Bind(&Core::Start, this, base::Passed(&client_clipboard)));
220 return;
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));
230 return;
233 clipboard_.reset();
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);
246 int scancode =
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())
253 return;
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);
275 INPUT input;
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));
291 input.mi.dwFlags |=
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;
334 } else {
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);
349 } // namespace
351 // static
352 scoped_ptr<InputInjector> InputInjector::Create(
353 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
354 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
355 return make_scoped_ptr(
356 new InputInjectorWin(main_task_runner, ui_task_runner));
359 // static
360 bool InputInjector::SupportsTouchEvents() {
361 return TouchInjectorWinDelegate::Create();
364 } // namespace remoting