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// SkSize.h assumes that stdint.h-style types are already defined.
18#include "third_party/skia/include/core/SkTypes.h"
19#include "third_party/skia/include/core/SkSize.h"
20
21namespace remoting {
22
23namespace {
24
25using protocol::ClipboardEvent;
26using protocol::KeyEvent;
27using protocol::MouseEvent;
28
29// USB to XKB keycode map table.
30#define USB_KEYMAP(usb, xkb, win, mac) {usb, win}
31#include "ui/base/keycodes/usb_keycode_map.h"
32#undef USB_KEYMAP
33
34// A class to generate events on Windows.
35class InputInjectorWin : public InputInjector {
36 public:
37  InputInjectorWin(scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
38                   scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner);
39  virtual ~InputInjectorWin();
40
41  // ClipboardStub interface.
42  virtual void InjectClipboardEvent(const ClipboardEvent& event) OVERRIDE;
43
44  // InputStub interface.
45  virtual void InjectKeyEvent(const KeyEvent& event) OVERRIDE;
46  virtual void InjectMouseEvent(const MouseEvent& event) OVERRIDE;
47
48  // InputInjector interface.
49  virtual void Start(
50      scoped_ptr<protocol::ClipboardStub> client_clipboard) OVERRIDE;
51
52 private:
53  // The actual implementation resides in InputInjectorWin::Core class.
54  class Core : public base::RefCountedThreadSafe<Core> {
55   public:
56    Core(scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
57         scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner);
58
59    // Mirrors the ClipboardStub interface.
60    void InjectClipboardEvent(const ClipboardEvent& event);
61
62    // Mirrors the InputStub interface.
63    void InjectKeyEvent(const KeyEvent& event);
64    void InjectMouseEvent(const MouseEvent& event);
65
66    // Mirrors the InputInjector interface.
67    void Start(scoped_ptr<protocol::ClipboardStub> client_clipboard);
68
69    void Stop();
70
71   private:
72    friend class base::RefCountedThreadSafe<Core>;
73    virtual ~Core();
74
75    void HandleKey(const KeyEvent& event);
76    void HandleMouse(const MouseEvent& event);
77
78    scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
79    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
80    scoped_ptr<Clipboard> clipboard_;
81
82    DISALLOW_COPY_AND_ASSIGN(Core);
83  };
84
85  scoped_refptr<Core> core_;
86
87  DISALLOW_COPY_AND_ASSIGN(InputInjectorWin);
88};
89
90InputInjectorWin::InputInjectorWin(
91    scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
92    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
93  core_ = new Core(main_task_runner, ui_task_runner);
94}
95
96InputInjectorWin::~InputInjectorWin() {
97  core_->Stop();
98}
99
100void InputInjectorWin::InjectClipboardEvent(const ClipboardEvent& event) {
101  core_->InjectClipboardEvent(event);
102}
103
104void InputInjectorWin::InjectKeyEvent(const KeyEvent& event) {
105  core_->InjectKeyEvent(event);
106}
107
108void InputInjectorWin::InjectMouseEvent(const MouseEvent& event) {
109  core_->InjectMouseEvent(event);
110}
111
112void InputInjectorWin::Start(
113    scoped_ptr<protocol::ClipboardStub> client_clipboard) {
114  core_->Start(client_clipboard.Pass());
115}
116
117InputInjectorWin::Core::Core(
118    scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
119    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner)
120    : main_task_runner_(main_task_runner),
121      ui_task_runner_(ui_task_runner),
122      clipboard_(Clipboard::Create()) {
123}
124
125void InputInjectorWin::Core::InjectClipboardEvent(const ClipboardEvent& event) {
126  if (!ui_task_runner_->BelongsToCurrentThread()) {
127    ui_task_runner_->PostTask(
128        FROM_HERE, base::Bind(&Core::InjectClipboardEvent, this, event));
129    return;
130  }
131
132  // |clipboard_| will ignore unknown MIME-types, and verify the data's format.
133  clipboard_->InjectClipboardEvent(event);
134}
135
136void InputInjectorWin::Core::InjectKeyEvent(const KeyEvent& event) {
137  if (!main_task_runner_->BelongsToCurrentThread()) {
138    main_task_runner_->PostTask(FROM_HERE,
139                                base::Bind(&Core::InjectKeyEvent, this, event));
140    return;
141  }
142
143  HandleKey(event);
144}
145
146void InputInjectorWin::Core::InjectMouseEvent(const MouseEvent& event) {
147  if (!main_task_runner_->BelongsToCurrentThread()) {
148    main_task_runner_->PostTask(
149        FROM_HERE, base::Bind(&Core::InjectMouseEvent, this, event));
150    return;
151  }
152
153  HandleMouse(event);
154}
155
156void InputInjectorWin::Core::Start(
157    scoped_ptr<protocol::ClipboardStub> client_clipboard) {
158  if (!ui_task_runner_->BelongsToCurrentThread()) {
159    ui_task_runner_->PostTask(
160        FROM_HERE,
161        base::Bind(&Core::Start, this, base::Passed(&client_clipboard)));
162    return;
163  }
164
165  clipboard_->Start(client_clipboard.Pass());
166}
167
168void InputInjectorWin::Core::Stop() {
169  if (!ui_task_runner_->BelongsToCurrentThread()) {
170    ui_task_runner_->PostTask(FROM_HERE, base::Bind(&Core::Stop, this));
171    return;
172  }
173
174  clipboard_->Stop();
175}
176
177InputInjectorWin::Core::~Core() {
178}
179
180void InputInjectorWin::Core::HandleKey(const KeyEvent& event) {
181  // HostEventDispatcher should filter events missing the pressed field.
182  if (!event.has_pressed() || !event.has_usb_keycode())
183    return;
184
185  // Reset the system idle suspend timeout.
186  SetThreadExecutionState(ES_SYSTEM_REQUIRED);
187
188  int scancode = UsbKeycodeToNativeKeycode(event.usb_keycode());
189
190  VLOG(3) << "Converting USB keycode: " << std::hex << event.usb_keycode()
191          << " to scancode: " << scancode << std::dec;
192
193  // Ignore events which can't be mapped.
194  if (scancode == InvalidNativeKeycode())
195    return;
196
197  // Populate the a Windows INPUT structure for the event.
198  INPUT input;
199  memset(&input, 0, sizeof(input));
200  input.type = INPUT_KEYBOARD;
201  input.ki.time = 0;
202  input.ki.dwFlags = KEYEVENTF_SCANCODE;
203  if (!event.pressed())
204    input.ki.dwFlags |= KEYEVENTF_KEYUP;
205
206  // Windows scancodes are only 8-bit, so store the low-order byte into the
207  // event and set the extended flag if any high-order bits are set. The only
208  // high-order values we should see are 0xE0 or 0xE1. The extended bit usually
209  // distinguishes keys with the same meaning, e.g. left & right shift.
210  input.ki.wScan = scancode & 0xFF;
211  if ((scancode & 0xFF00) != 0x0000)
212    input.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
213
214  if (SendInput(1, &input, sizeof(INPUT)) == 0)
215    LOG_GETLASTERROR(ERROR) << "Failed to inject a key event";
216}
217
218void InputInjectorWin::Core::HandleMouse(const MouseEvent& event) {
219  // Reset the system idle suspend timeout.
220  SetThreadExecutionState(ES_SYSTEM_REQUIRED);
221
222  // TODO(garykac) Collapse mouse (x,y) and button events into a single
223  // input event when possible.
224  if (event.has_x() && event.has_y()) {
225    int x = event.x();
226    int y = event.y();
227
228    INPUT input;
229    input.type = INPUT_MOUSE;
230    input.mi.time = 0;
231    SkISize screen_size(SkISize::Make(GetSystemMetrics(SM_CXVIRTUALSCREEN),
232                                      GetSystemMetrics(SM_CYVIRTUALSCREEN)));
233    if ((screen_size.width() > 1) && (screen_size.height() > 1)) {
234      x = std::max(0, std::min(screen_size.width(), x));
235      y = std::max(0, std::min(screen_size.height(), y));
236      input.mi.dx = static_cast<int>((x * 65535) / (screen_size.width() - 1));
237      input.mi.dy = static_cast<int>((y * 65535) / (screen_size.height() - 1));
238      input.mi.dwFlags =
239          MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK;
240      if (SendInput(1, &input, sizeof(INPUT)) == 0)
241        LOG_GETLASTERROR(ERROR) << "Failed to inject a mouse move event";
242    }
243  }
244
245  int wheel_delta_x = 0;
246  int wheel_delta_y = 0;
247  if (event.has_wheel_delta_x() && event.has_wheel_delta_y()) {
248    wheel_delta_x = static_cast<int>(event.wheel_delta_x());
249    wheel_delta_y = static_cast<int>(event.wheel_delta_y());
250  }
251
252  if (wheel_delta_x != 0 || wheel_delta_y != 0) {
253    INPUT wheel;
254    wheel.type = INPUT_MOUSE;
255    wheel.mi.time = 0;
256
257    if (wheel_delta_x != 0) {
258      wheel.mi.mouseData = wheel_delta_x;
259      wheel.mi.dwFlags = MOUSEEVENTF_HWHEEL;
260      if (SendInput(1, &wheel, sizeof(INPUT)) == 0)
261        LOG_GETLASTERROR(ERROR) << "Failed to inject a mouse wheel(x) event";
262    }
263    if (wheel_delta_y != 0) {
264      wheel.mi.mouseData = wheel_delta_y;
265      wheel.mi.dwFlags = MOUSEEVENTF_WHEEL;
266      if (SendInput(1, &wheel, sizeof(INPUT)) == 0)
267        LOG_GETLASTERROR(ERROR) << "Failed to inject a mouse wheel(y) event";
268    }
269  }
270
271  if (event.has_button() && event.has_button_down()) {
272    INPUT button_event;
273    button_event.type = INPUT_MOUSE;
274    button_event.mi.time = 0;
275    button_event.mi.dx = 0;
276    button_event.mi.dy = 0;
277
278    MouseEvent::MouseButton button = event.button();
279    bool down = event.button_down();
280
281    // If the host is configured to swap left & right buttons, inject swapped
282    // events to un-do that re-mapping.
283    if (GetSystemMetrics(SM_SWAPBUTTON)) {
284      if (button == MouseEvent::BUTTON_LEFT) {
285        button = MouseEvent::BUTTON_RIGHT;
286      } else if (button == MouseEvent::BUTTON_RIGHT) {
287        button = MouseEvent::BUTTON_LEFT;
288      }
289    }
290
291    if (button == MouseEvent::BUTTON_LEFT) {
292      button_event.mi.dwFlags =
293          down ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP;
294    } else if (button == MouseEvent::BUTTON_MIDDLE) {
295      button_event.mi.dwFlags =
296          down ? MOUSEEVENTF_MIDDLEDOWN : MOUSEEVENTF_MIDDLEUP;
297    } else if (button == MouseEvent::BUTTON_RIGHT) {
298      button_event.mi.dwFlags =
299          down ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP;
300    } else {
301      button_event.mi.dwFlags =
302          down ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP;
303    }
304
305    if (SendInput(1, &button_event, sizeof(INPUT)) == 0)
306      LOG_GETLASTERROR(ERROR) << "Failed to inject a mouse button event";
307  }
308}
309
310}  // namespace
311
312scoped_ptr<InputInjector> InputInjector::Create(
313    scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
314    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
315  return scoped_ptr<InputInjector>(
316      new InputInjectorWin(main_task_runner, ui_task_runner));
317}
318
319}  // namespace remoting
320