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