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 "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"
20
21namespace remoting {
22
23namespace {
24
25// Helper used to call SendInput() API.
26void 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;
34
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;
44  }
45
46  if (SendInput(1, &input, sizeof(INPUT)) == 0)
47    PLOG(ERROR) << "Failed to inject a key event";
48}
49
50using protocol::ClipboardEvent;
51using protocol::KeyEvent;
52using protocol::TextEvent;
53using protocol::MouseEvent;
54
55// A class to generate events on Windows.
56class InputInjectorWin : public InputInjector {
57 public:
58  InputInjectorWin(scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
59                   scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner);
60  virtual ~InputInjectorWin();
61
62  // ClipboardStub interface.
63  virtual void InjectClipboardEvent(const ClipboardEvent& event) OVERRIDE;
64
65  // InputStub interface.
66  virtual void InjectKeyEvent(const KeyEvent& event) OVERRIDE;
67  virtual void InjectTextEvent(const TextEvent& event) OVERRIDE;
68  virtual void InjectMouseEvent(const MouseEvent& event) OVERRIDE;
69
70  // InputInjector interface.
71  virtual void Start(
72      scoped_ptr<protocol::ClipboardStub> client_clipboard) OVERRIDE;
73
74 private:
75  // The actual implementation resides in InputInjectorWin::Core class.
76  class Core : public base::RefCountedThreadSafe<Core> {
77   public:
78    Core(scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
79         scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner);
80
81    // Mirrors the ClipboardStub interface.
82    void InjectClipboardEvent(const ClipboardEvent& event);
83
84    // Mirrors the InputStub interface.
85    void InjectKeyEvent(const KeyEvent& event);
86    void InjectTextEvent(const TextEvent& event);
87    void InjectMouseEvent(const MouseEvent& event);
88
89    // Mirrors the InputInjector interface.
90    void Start(scoped_ptr<protocol::ClipboardStub> client_clipboard);
91
92    void Stop();
93
94   private:
95    friend class base::RefCountedThreadSafe<Core>;
96    virtual ~Core();
97
98    void HandleKey(const KeyEvent& event);
99    void HandleText(const TextEvent& event);
100    void HandleMouse(const MouseEvent& event);
101
102    scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
103    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
104    scoped_ptr<Clipboard> clipboard_;
105
106    DISALLOW_COPY_AND_ASSIGN(Core);
107  };
108
109  scoped_refptr<Core> core_;
110
111  DISALLOW_COPY_AND_ASSIGN(InputInjectorWin);
112};
113
114InputInjectorWin::InputInjectorWin(
115    scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
116    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
117  core_ = new Core(main_task_runner, ui_task_runner);
118}
119
120InputInjectorWin::~InputInjectorWin() {
121  core_->Stop();
122}
123
124void InputInjectorWin::InjectClipboardEvent(const ClipboardEvent& event) {
125  core_->InjectClipboardEvent(event);
126}
127
128void InputInjectorWin::InjectKeyEvent(const KeyEvent& event) {
129  core_->InjectKeyEvent(event);
130}
131
132void InputInjectorWin::InjectTextEvent(const TextEvent& event) {
133  core_->InjectTextEvent(event);
134}
135
136void InputInjectorWin::InjectMouseEvent(const MouseEvent& event) {
137  core_->InjectMouseEvent(event);
138}
139
140void InputInjectorWin::Start(
141    scoped_ptr<protocol::ClipboardStub> client_clipboard) {
142  core_->Start(client_clipboard.Pass());
143}
144
145InputInjectorWin::Core::Core(
146    scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
147    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner)
148    : main_task_runner_(main_task_runner),
149      ui_task_runner_(ui_task_runner),
150      clipboard_(Clipboard::Create()) {
151}
152
153void InputInjectorWin::Core::InjectClipboardEvent(const ClipboardEvent& event) {
154  if (!ui_task_runner_->BelongsToCurrentThread()) {
155    ui_task_runner_->PostTask(
156        FROM_HERE, base::Bind(&Core::InjectClipboardEvent, this, event));
157    return;
158  }
159
160  // |clipboard_| will ignore unknown MIME-types, and verify the data's format.
161  clipboard_->InjectClipboardEvent(event);
162}
163
164void InputInjectorWin::Core::InjectKeyEvent(const KeyEvent& event) {
165  if (!main_task_runner_->BelongsToCurrentThread()) {
166    main_task_runner_->PostTask(FROM_HERE,
167                                base::Bind(&Core::InjectKeyEvent, this, event));
168    return;
169  }
170
171  HandleKey(event);
172}
173
174void InputInjectorWin::Core::InjectTextEvent(const TextEvent& event) {
175  if (!main_task_runner_->BelongsToCurrentThread()) {
176    main_task_runner_->PostTask(
177        FROM_HERE, base::Bind(&Core::InjectTextEvent, this, event));
178    return;
179  }
180
181  HandleText(event);
182}
183
184void InputInjectorWin::Core::InjectMouseEvent(const MouseEvent& event) {
185  if (!main_task_runner_->BelongsToCurrentThread()) {
186    main_task_runner_->PostTask(
187        FROM_HERE, base::Bind(&Core::InjectMouseEvent, this, event));
188    return;
189  }
190
191  HandleMouse(event);
192}
193
194void InputInjectorWin::Core::Start(
195    scoped_ptr<protocol::ClipboardStub> client_clipboard) {
196  if (!ui_task_runner_->BelongsToCurrentThread()) {
197    ui_task_runner_->PostTask(
198        FROM_HERE,
199        base::Bind(&Core::Start, this, base::Passed(&client_clipboard)));
200    return;
201  }
202
203  clipboard_->Start(client_clipboard.Pass());
204}
205
206void InputInjectorWin::Core::Stop() {
207  if (!ui_task_runner_->BelongsToCurrentThread()) {
208    ui_task_runner_->PostTask(FROM_HERE, base::Bind(&Core::Stop, this));
209    return;
210  }
211
212  clipboard_->Stop();
213}
214
215InputInjectorWin::Core::~Core() {}
216
217void InputInjectorWin::Core::HandleKey(const KeyEvent& event) {
218  // HostEventDispatcher should filter events missing the pressed field.
219  DCHECK(event.has_pressed() && event.has_usb_keycode());
220
221  // Reset the system idle suspend timeout.
222  SetThreadExecutionState(ES_SYSTEM_REQUIRED);
223
224  int scancode =
225      ui::KeycodeConverter::UsbKeycodeToNativeKeycode(event.usb_keycode());
226  VLOG(3) << "Converting USB keycode: " << std::hex << event.usb_keycode()
227          << " to scancode: " << scancode << std::dec;
228
229  // Ignore events which can't be mapped.
230  if (scancode == ui::KeycodeConverter::InvalidNativeKeycode())
231    return;
232
233  uint32_t flags = KEYEVENTF_SCANCODE | (event.pressed() ? 0 : KEYEVENTF_KEYUP);
234  SendKeyboardInput(flags, scancode);
235}
236
237void InputInjectorWin::Core::HandleText(const TextEvent& event) {
238  // HostEventDispatcher should filter events missing the pressed field.
239  DCHECK(event.has_text());
240
241  base::string16 text = base::UTF8ToUTF16(event.text());
242  for (base::string16::const_iterator it = text.begin();
243       it != text.end(); ++it)  {
244    SendKeyboardInput(KEYEVENTF_UNICODE, *it);
245    SendKeyboardInput(KEYEVENTF_UNICODE | KEYEVENTF_KEYUP, *it);
246  }
247}
248
249void InputInjectorWin::Core::HandleMouse(const MouseEvent& event) {
250  // Reset the system idle suspend timeout.
251  SetThreadExecutionState(ES_SYSTEM_REQUIRED);
252
253  INPUT input;
254  memset(&input, 0, sizeof(input));
255  input.type = INPUT_MOUSE;
256
257  if (event.has_delta_x() && event.has_delta_y()) {
258    input.mi.dx = event.delta_x();
259    input.mi.dy = event.delta_y();
260    input.mi.dwFlags |= MOUSEEVENTF_MOVE | MOUSEEVENTF_VIRTUALDESK;
261  } else if (event.has_x() && event.has_y()) {
262    int width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
263    int height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
264    if (width > 1 && height > 1) {
265      int x = std::max(0, std::min(width, event.x()));
266      int y = std::max(0, std::min(height, event.y()));
267      input.mi.dx = static_cast<int>((x * 65535) / (width - 1));
268      input.mi.dy = static_cast<int>((y * 65535) / (height - 1));
269      input.mi.dwFlags |=
270          MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK;
271    }
272  }
273
274  int wheel_delta_x = 0;
275  int wheel_delta_y = 0;
276  if (event.has_wheel_delta_x() && event.has_wheel_delta_y()) {
277    wheel_delta_x = static_cast<int>(event.wheel_delta_x());
278    wheel_delta_y = static_cast<int>(event.wheel_delta_y());
279  }
280
281  if (wheel_delta_x != 0 || wheel_delta_y != 0) {
282    if (wheel_delta_x != 0) {
283      input.mi.mouseData = wheel_delta_x;
284      input.mi.dwFlags |= MOUSEEVENTF_HWHEEL;
285    }
286    if (wheel_delta_y != 0) {
287      input.mi.mouseData = wheel_delta_y;
288      input.mi.dwFlags |= MOUSEEVENTF_WHEEL;
289    }
290  }
291
292  if (event.has_button() && event.has_button_down()) {
293    MouseEvent::MouseButton button = event.button();
294    bool down = event.button_down();
295
296    // If the host is configured to swap left & right buttons, inject swapped
297    // events to un-do that re-mapping.
298    if (GetSystemMetrics(SM_SWAPBUTTON)) {
299      if (button == MouseEvent::BUTTON_LEFT) {
300        button = MouseEvent::BUTTON_RIGHT;
301      } else if (button == MouseEvent::BUTTON_RIGHT) {
302        button = MouseEvent::BUTTON_LEFT;
303      }
304    }
305
306    if (button == MouseEvent::BUTTON_LEFT) {
307      input.mi.dwFlags |= down ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP;
308    } else if (button == MouseEvent::BUTTON_MIDDLE) {
309      input.mi.dwFlags |= down ? MOUSEEVENTF_MIDDLEDOWN : MOUSEEVENTF_MIDDLEUP;
310    } else if (button == MouseEvent::BUTTON_RIGHT) {
311      input.mi.dwFlags |= down ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP;
312    } else {
313      input.mi.dwFlags |= down ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP;
314    }
315  }
316
317  if (input.mi.dwFlags) {
318    if (SendInput(1, &input, sizeof(INPUT)) == 0)
319      PLOG(ERROR) << "Failed to inject a mouse event";
320  }
321}
322
323}  // namespace
324
325scoped_ptr<InputInjector> InputInjector::Create(
326    scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
327    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
328  return scoped_ptr<InputInjector>(
329      new InputInjectorWin(main_task_runner, ui_task_runner));
330}
331
332}  // namespace remoting
333