blob: 430266a73177666039c9512ddbf3c8adcd11d8ae [file] [log] [blame]
[email protected]c2b374402012-03-12 19:23:071// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]cb3b1f9312010-06-07 19:58:232// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]0b7e4282011-04-04 22:44:115#include "remoting/host/event_executor.h"
[email protected]cb3b1f9312010-06-07 19:58:236
7#include <windows.h>
[email protected]6fd3d6a12010-11-16 19:53:358
[email protected]b25ff3b2011-09-13 18:17:309#include "base/bind.h"
[email protected]0b7e4282011-04-04 22:44:1110#include "base/compiler_specific.h"
[email protected]6fd3d6a12010-11-16 19:53:3511#include "base/message_loop.h"
[email protected]98f99a02012-05-16 18:03:3212#include "base/message_loop_proxy.h"
[email protected]411fd1d32010-08-31 15:56:0513#include "remoting/host/capturer.h"
[email protected]6d17db92012-05-11 17:03:1414#include "remoting/host/clipboard.h"
[email protected]6fd3d6a12010-11-16 19:53:3515#include "remoting/proto/event.pb.h"
[email protected]b6d81262011-01-13 17:36:0916#include "ui/base/keycodes/keyboard_codes.h"
[email protected]cb3b1f9312010-06-07 19:58:2317
18namespace remoting {
19
[email protected]1ab57f02012-04-05 20:17:5320namespace {
21
[email protected]e265ad72012-03-16 17:28:0322using protocol::ClipboardEvent;
[email protected]2c229682010-12-02 20:23:3523using protocol::KeyEvent;
[email protected]e265ad72012-03-16 17:28:0324using protocol::MouseEvent;
[email protected]2c229682010-12-02 20:23:3525
[email protected]1a6258f2012-03-15 21:16:4726// USB to XKB keycode map table.
27#define USB_KEYMAP(usb, xkb, win, mac) {usb, win}
[email protected]1a6258f2012-03-15 21:16:4728#include "remoting/host/usb_keycode_map.h"
29#undef USB_KEYMAP
30
[email protected]0b7e4282011-04-04 22:44:1131// A class to generate events on Windows.
32class EventExecutorWin : public EventExecutor {
33 public:
[email protected]98f99a02012-05-16 18:03:3234 EventExecutorWin(MessageLoop* message_loop,
35 base::MessageLoopProxy* ui_loop,
36 Capturer* capturer);
[email protected]0b7e4282011-04-04 22:44:1137 virtual ~EventExecutorWin() {}
38
[email protected]e265ad72012-03-16 17:28:0339 // ClipboardStub interface.
40 virtual void InjectClipboardEvent(const ClipboardEvent& event) OVERRIDE;
41
42 // InputStub interface.
[email protected]b25ff3b2011-09-13 18:17:3043 virtual void InjectKeyEvent(const KeyEvent& event) OVERRIDE;
44 virtual void InjectMouseEvent(const MouseEvent& event) OVERRIDE;
[email protected]0b7e4282011-04-04 22:44:1145
[email protected]6d17db92012-05-11 17:03:1446 // EventExecutor interface.
47 virtual void OnSessionStarted() OVERRIDE;
48 virtual void OnSessionFinished() OVERRIDE;
49
[email protected]0b7e4282011-04-04 22:44:1150 private:
[email protected]eb8d92e2012-04-04 03:33:4351 HKL GetForegroundKeyboardLayout();
[email protected]b25ff3b2011-09-13 18:17:3052 void HandleKey(const KeyEvent& event);
53 void HandleMouse(const MouseEvent& event);
[email protected]0b7e4282011-04-04 22:44:1154
[email protected]1a6cdf42011-05-18 07:41:0155 MessageLoop* message_loop_;
[email protected]98f99a02012-05-16 18:03:3256 base::MessageLoopProxy* ui_loop_;
[email protected]0b7e4282011-04-04 22:44:1157 Capturer* capturer_;
[email protected]6d17db92012-05-11 17:03:1458 scoped_ptr<Clipboard> clipboard_;
[email protected]0b7e4282011-04-04 22:44:1159
60 DISALLOW_COPY_AND_ASSIGN(EventExecutorWin);
61};
62
[email protected]1a6cdf42011-05-18 07:41:0163EventExecutorWin::EventExecutorWin(MessageLoop* message_loop,
[email protected]98f99a02012-05-16 18:03:3264 base::MessageLoopProxy* ui_loop,
[email protected]0b7e4282011-04-04 22:44:1165 Capturer* capturer)
[email protected]65f86872010-11-29 21:56:1266 : message_loop_(message_loop),
[email protected]98f99a02012-05-16 18:03:3267 ui_loop_(ui_loop),
[email protected]6d17db92012-05-11 17:03:1468 capturer_(capturer),
69 clipboard_(Clipboard::Create()) {
[email protected]cb3b1f9312010-06-07 19:58:2370}
71
[email protected]e265ad72012-03-16 17:28:0372void EventExecutorWin::InjectClipboardEvent(const ClipboardEvent& event) {
[email protected]98f99a02012-05-16 18:03:3273 if (!ui_loop_->BelongsToCurrentThread()) {
74 ui_loop_->PostTask(
[email protected]6d17db92012-05-11 17:03:1475 FROM_HERE,
76 base::Bind(&EventExecutorWin::InjectClipboardEvent,
77 base::Unretained(this),
78 event));
79 return;
80 }
81
82 clipboard_->InjectClipboardEvent(event);
[email protected]e265ad72012-03-16 17:28:0383}
84
[email protected]b25ff3b2011-09-13 18:17:3085void EventExecutorWin::InjectKeyEvent(const KeyEvent& event) {
[email protected]6fd3d6a12010-11-16 19:53:3586 if (MessageLoop::current() != message_loop_) {
87 message_loop_->PostTask(
88 FROM_HERE,
[email protected]b25ff3b2011-09-13 18:17:3089 base::Bind(&EventExecutorWin::InjectKeyEvent, base::Unretained(this),
90 event));
[email protected]6fd3d6a12010-11-16 19:53:3591 return;
[email protected]cb3b1f9312010-06-07 19:58:2392 }
[email protected]825c4282011-04-21 23:46:5793
[email protected]65f86872010-11-29 21:56:1294 HandleKey(event);
[email protected]cb3b1f9312010-06-07 19:58:2395}
96
[email protected]b25ff3b2011-09-13 18:17:3097void EventExecutorWin::InjectMouseEvent(const MouseEvent& event) {
[email protected]6fd3d6a12010-11-16 19:53:3598 if (MessageLoop::current() != message_loop_) {
99 message_loop_->PostTask(
100 FROM_HERE,
[email protected]b25ff3b2011-09-13 18:17:30101 base::Bind(&EventExecutorWin::InjectMouseEvent, base::Unretained(this),
102 event));
[email protected]6fd3d6a12010-11-16 19:53:35103 return;
104 }
[email protected]825c4282011-04-21 23:46:57105
[email protected]65f86872010-11-29 21:56:12106 HandleMouse(event);
[email protected]6fd3d6a12010-11-16 19:53:35107}
108
[email protected]6d17db92012-05-11 17:03:14109void EventExecutorWin::OnSessionStarted() {
[email protected]98f99a02012-05-16 18:03:32110 if (!ui_loop_->BelongsToCurrentThread()) {
111 ui_loop_->PostTask(
[email protected]6d17db92012-05-11 17:03:14112 FROM_HERE,
113 base::Bind(&EventExecutorWin::OnSessionStarted,
114 base::Unretained(this)));
115 return;
116 }
117
[email protected]98f99a02012-05-16 18:03:32118 clipboard_->Start();
[email protected]6d17db92012-05-11 17:03:14119}
120
121void EventExecutorWin::OnSessionFinished() {
[email protected]98f99a02012-05-16 18:03:32122 if (!ui_loop_->BelongsToCurrentThread()) {
123 ui_loop_->PostTask(
[email protected]6d17db92012-05-11 17:03:14124 FROM_HERE,
125 base::Bind(&EventExecutorWin::OnSessionFinished,
126 base::Unretained(this)));
127 return;
128 }
129
[email protected]98f99a02012-05-16 18:03:32130 clipboard_->Stop();
[email protected]6d17db92012-05-11 17:03:14131}
132
[email protected]eb8d92e2012-04-04 03:33:43133HKL EventExecutorWin::GetForegroundKeyboardLayout() {
134 HKL layout = 0;
135
136 // Can return NULL if a window is losing focus.
137 HWND foreground = GetForegroundWindow();
138 if (foreground) {
139 // Can return 0 if the window no longer exists.
140 DWORD thread_id = GetWindowThreadProcessId(foreground, 0);
141 if (thread_id) {
142 // Can return 0 if the thread no longer exists, or if we're
143 // running on Windows Vista and the window is a command-prompt.
144 layout = GetKeyboardLayout(thread_id);
145 }
146 }
147
148 // If we couldn't determine a layout then use the system default.
149 if (!layout) {
150 SystemParametersInfo(SPI_GETDEFAULTINPUTLANG, 0, &layout, 0);
151 }
152
153 return layout;
154}
155
[email protected]b25ff3b2011-09-13 18:17:30156void EventExecutorWin::HandleKey(const KeyEvent& event) {
[email protected]1ab57f02012-04-05 20:17:53157 // HostEventDispatcher should filter events missing the pressed field.
158 DCHECK(event.has_pressed());
159
[email protected]6aaebcf2012-04-03 05:34:27160 // Reset the system idle suspend timeout.
161 SetThreadExecutionState(ES_SYSTEM_REQUIRED);
[email protected]3fb793182010-10-15 22:07:20162
[email protected]eb8d92e2012-04-04 03:33:43163 // The mapping between scancodes and VKEY values depends on the foreground
164 // window's current keyboard layout.
165 HKL layout = GetForegroundKeyboardLayout();
166
[email protected]6d42bdce2012-04-24 22:21:14167 // Populate the a Windows INPUT structure for the event.
168 INPUT input;
169 memset(&input, 0, sizeof(input));
170 input.type = INPUT_KEYBOARD;
171 input.ki.time = 0;
172 input.ki.dwFlags = event.pressed() ? 0 : KEYEVENTF_KEYUP;
173
[email protected]1ab57f02012-04-05 20:17:53174 int scancode = kInvalidKeycode;
175 if (event.has_usb_keycode()) {
[email protected]eb8d92e2012-04-04 03:33:43176 // If the event contains a USB-style code, map to a Windows scancode, and
[email protected]6d42bdce2012-04-24 22:21:14177 // set a flag to have Windows look up the corresponding VK code.
178 input.ki.dwFlags |= KEYEVENTF_SCANCODE;
[email protected]1ab57f02012-04-05 20:17:53179 scancode = UsbKeycodeToNativeKeycode(event.usb_keycode());
[email protected]1ab57f02012-04-05 20:17:53180 VLOG(3) << "Converting USB keycode: " << std::hex << event.usb_keycode()
[email protected]6d42bdce2012-04-24 22:21:14181 << " to scancode: " << scancode << std::dec;
[email protected]1a6258f2012-03-15 21:16:47182 } else {
[email protected]6d42bdce2012-04-24 22:21:14183 // If the event provides only a VKEY then use it, and map to the scancode.
184 input.ki.wVk = event.keycode();
185 scancode = MapVirtualKeyEx(event.keycode(), MAPVK_VK_TO_VSC_EX, layout);
[email protected]1ab57f02012-04-05 20:17:53186 VLOG(3) << "Converting VKEY: " << std::hex << event.keycode()
187 << " to scancode: " << scancode << std::dec;
[email protected]1a6258f2012-03-15 21:16:47188 }
189
[email protected]1ab57f02012-04-05 20:17:53190 // Ignore events with no VK- or USB-keycode, or which can't be mapped.
191 if (scancode == kInvalidKeycode)
[email protected]1a6258f2012-03-15 21:16:47192 return;
[email protected]3fb793182010-10-15 22:07:20193
[email protected]6d42bdce2012-04-24 22:21:14194 // Windows scancodes are only 8-bit, so store the low-order byte into the
195 // event and set the extended flag if any high-order bits are set. The only
196 // high-order values we should see are 0xE0 or 0xE1. The extended bit usually
197 // distinguishes keys with the same meaning, e.g. left & right shift.
198 input.ki.wScan = scancode & 0xFF;
199 if ((scancode & 0xFF00) != 0x0000) {
[email protected]3fb793182010-10-15 22:07:20200 input.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
201 }
202
[email protected]5ae4da12012-03-17 20:03:57203 if (SendInput(1, &input, sizeof(INPUT)) == 0) {
204 LOG_GETLASTERROR(ERROR) << "Failed to inject a key event";
205 }
[email protected]e5b9fca92010-08-27 21:19:07206}
207
[email protected]b25ff3b2011-09-13 18:17:30208void EventExecutorWin::HandleMouse(const MouseEvent& event) {
[email protected]6aaebcf2012-04-03 05:34:27209 // Reset the system idle suspend timeout.
210 SetThreadExecutionState(ES_SYSTEM_REQUIRED);
211
[email protected]65f86872010-11-29 21:56:12212 // TODO(garykac) Collapse mouse (x,y) and button events into a single
213 // input event when possible.
[email protected]b25ff3b2011-09-13 18:17:30214 if (event.has_x() && event.has_y()) {
215 int x = event.x();
216 int y = event.y();
[email protected]65f86872010-11-29 21:56:12217
218 INPUT input;
219 input.type = INPUT_MOUSE;
220 input.mi.time = 0;
[email protected]bcad2682011-09-30 20:35:26221 SkISize screen_size = capturer_->size_most_recent();
[email protected]b691a102011-10-12 08:54:56222 if ((screen_size.width() > 1) && (screen_size.height() > 1)) {
223 input.mi.dx = static_cast<int>((x * 65535) / (screen_size.width() - 1));
224 input.mi.dy = static_cast<int>((y * 65535) / (screen_size.height() - 1));
[email protected]e1a6c462011-03-04 12:15:31225 input.mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE;
[email protected]5ae4da12012-03-17 20:03:57226 if (SendInput(1, &input, sizeof(INPUT)) == 0) {
227 LOG_GETLASTERROR(ERROR) << "Failed to inject a mouse move event";
228 }
[email protected]e1a6c462011-03-04 12:15:31229 }
[email protected]65f86872010-11-29 21:56:12230 }
231
[email protected]b25ff3b2011-09-13 18:17:30232 if (event.has_wheel_offset_x() && event.has_wheel_offset_y()) {
[email protected]65f86872010-11-29 21:56:12233 INPUT wheel;
234 wheel.type = INPUT_MOUSE;
235 wheel.mi.time = 0;
236
[email protected]b25ff3b2011-09-13 18:17:30237 int dx = event.wheel_offset_x();
238 int dy = event.wheel_offset_y();
[email protected]65f86872010-11-29 21:56:12239
240 if (dx != 0) {
[email protected]cd0099212011-10-17 06:37:25241 wheel.mi.mouseData = dx * WHEEL_DELTA;
[email protected]65f86872010-11-29 21:56:12242 wheel.mi.dwFlags = MOUSEEVENTF_HWHEEL;
[email protected]5ae4da12012-03-17 20:03:57243 if (SendInput(1, &wheel, sizeof(INPUT)) == 0) {
244 LOG_GETLASTERROR(ERROR) << "Failed to inject a mouse wheel(x) event";
245 }
[email protected]65f86872010-11-29 21:56:12246 }
247 if (dy != 0) {
[email protected]cd0099212011-10-17 06:37:25248 wheel.mi.mouseData = dy * WHEEL_DELTA;
[email protected]65f86872010-11-29 21:56:12249 wheel.mi.dwFlags = MOUSEEVENTF_WHEEL;
[email protected]5ae4da12012-03-17 20:03:57250 if (SendInput(1, &wheel, sizeof(INPUT)) == 0) {
251 LOG_GETLASTERROR(ERROR) << "Failed to inject a mouse wheel(y) event";
252 }
[email protected]65f86872010-11-29 21:56:12253 }
254 }
255
[email protected]b25ff3b2011-09-13 18:17:30256 if (event.has_button() && event.has_button_down()) {
[email protected]65f86872010-11-29 21:56:12257 INPUT button_event;
258 button_event.type = INPUT_MOUSE;
259 button_event.mi.time = 0;
260 button_event.mi.dx = 0;
261 button_event.mi.dy = 0;
262
[email protected]b25ff3b2011-09-13 18:17:30263 MouseEvent::MouseButton button = event.button();
264 bool down = event.button_down();
[email protected]2c229682010-12-02 20:23:35265 if (button == MouseEvent::BUTTON_LEFT) {
[email protected]65f86872010-11-29 21:56:12266 button_event.mi.dwFlags =
267 down ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP;
[email protected]2c229682010-12-02 20:23:35268 } else if (button == MouseEvent::BUTTON_MIDDLE) {
[email protected]65f86872010-11-29 21:56:12269 button_event.mi.dwFlags =
270 down ? MOUSEEVENTF_MIDDLEDOWN : MOUSEEVENTF_MIDDLEUP;
[email protected]2c229682010-12-02 20:23:35271 } else if (button == MouseEvent::BUTTON_RIGHT) {
[email protected]65f86872010-11-29 21:56:12272 button_event.mi.dwFlags =
273 down ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP;
274 } else {
275 button_event.mi.dwFlags =
276 down ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP;
277 }
278
[email protected]5ae4da12012-03-17 20:03:57279 if (SendInput(1, &button_event, sizeof(INPUT)) == 0) {
280 LOG_GETLASTERROR(ERROR) << "Failed to inject a mouse button event";
281 }
[email protected]65f86872010-11-29 21:56:12282 }
283}
284
[email protected]0b7e4282011-04-04 22:44:11285} // namespace
286
[email protected]98f99a02012-05-16 18:03:32287scoped_ptr<EventExecutor> EventExecutor::Create(MessageLoop* message_loop,
288 base::MessageLoopProxy* ui_loop,
289 Capturer* capturer) {
[email protected]6d17db92012-05-11 17:03:14290 return scoped_ptr<EventExecutor>(
[email protected]98f99a02012-05-16 18:03:32291 new EventExecutorWin(message_loop, ui_loop, capturer));
[email protected]0b7e4282011-04-04 22:44:11292}
293
[email protected]cb3b1f9312010-06-07 19:58:23294} // namespace remoting