ozone: evdev: Sync caps lock LED state to evdev
[chromium-blink-merge.git] / remoting / host / input_injector_win.cc
blob698c6862f68c4f76e317def9ae16293c4fc7b04d
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/proto/event.pb.h"
19 #include "ui/events/keycodes/dom4/keycode_converter.h"
21 namespace remoting {
23 namespace {
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.
28 INPUT input;
29 memset(&input, 0, sizeof(input));
30 input.type = INPUT_KEYBOARD;
31 input.ki.time = 0;
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
40 // shift.
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 {
58 public:
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.
73 virtual void Start(
74 scoped_ptr<protocol::ClipboardStub> client_clipboard) override;
76 private:
77 // The actual implementation resides in InputInjectorWin::Core class.
78 class Core : public base::RefCountedThreadSafe<Core> {
79 public:
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);
94 void Stop();
96 private:
97 friend class base::RefCountedThreadSafe<Core>;
98 virtual ~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() {
123 core_->Stop();
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));
163 return;
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));
174 return;
177 HandleKey(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));
184 return;
187 HandleText(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));
194 return;
197 HandleMouse(event);
200 void InputInjectorWin::Core::Start(
201 scoped_ptr<protocol::ClipboardStub> client_clipboard) {
202 if (!ui_task_runner_->BelongsToCurrentThread()) {
203 ui_task_runner_->PostTask(
204 FROM_HERE,
205 base::Bind(&Core::Start, this, base::Passed(&client_clipboard)));
206 return;
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));
215 return;
218 clipboard_.reset();
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);
230 int scancode =
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())
237 return;
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);
259 INPUT input;
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));
275 input.mi.dwFlags |=
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;
318 } else {
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";
329 } // namespace
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