Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(183)

Side by Side Diff: remoting/host/input_injector_win.cc

Issue 1865543003: Update input_injector_win.cc to support injecting wheel-x and wheel-y events together (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "remoting/host/input_injector.h" 5 #include "remoting/host/input_injector.h"
6 6
7 #include <stdint.h> 7 #include <stdint.h>
8 #include <windows.h> 8 #include <windows.h>
9 9
10 #include <utility> 10 #include <utility>
11 #include <vector>
11 12
12 #include "base/bind.h" 13 #include "base/bind.h"
13 #include "base/compiler_specific.h" 14 #include "base/compiler_specific.h"
14 #include "base/location.h" 15 #include "base/location.h"
15 #include "base/macros.h" 16 #include "base/macros.h"
16 #include "base/memory/ref_counted.h" 17 #include "base/memory/ref_counted.h"
17 #include "base/single_thread_task_runner.h" 18 #include "base/single_thread_task_runner.h"
18 #include "base/strings/string16.h" 19 #include "base/strings/string16.h"
19 #include "base/strings/utf_string_conversions.h" 20 #include "base/strings/utf_string_conversions.h"
20 #include "remoting/base/util.h" 21 #include "remoting/base/util.h"
21 #include "remoting/host/clipboard.h" 22 #include "remoting/host/clipboard.h"
22 #include "remoting/host/touch_injector_win.h" 23 #include "remoting/host/touch_injector_win.h"
23 #include "remoting/proto/event.pb.h" 24 #include "remoting/proto/event.pb.h"
24 #include "ui/events/keycodes/dom/keycode_converter.h" 25 #include "ui/events/keycodes/dom/keycode_converter.h"
25 26
26 namespace remoting { 27 namespace remoting {
27 28
29 using protocol::ClipboardEvent;
30 using protocol::KeyEvent;
31 using protocol::TextEvent;
32 using protocol::MouseEvent;
33 using protocol::TouchEvent;
34 using std::vector;
Sergey Ulanov 2016/04/06 18:25:31 Please just refer to std::vector<> everywhere belo
Hzj_jie 2016/04/06 19:56:34 Yes, I can do it, but why do we prefer removing th
Sergey Ulanov 2016/04/06 20:42:20 We don't use using statement for STL types anywher
Hzj_jie 2016/04/06 21:41:05 Done.
35
28 namespace { 36 namespace {
29 37
30 // Helper used to call SendInput() API. 38 // Helper used to call SendInput() API.
31 void SendKeyboardInput(uint32_t flags, uint16_t scancode) { 39 void SendKeyboardInput(uint32_t flags, uint16_t scancode) {
32 // Populate a Windows INPUT structure for the event. 40 // Populate a Windows INPUT structure for the event.
33 INPUT input; 41 INPUT input;
34 memset(&input, 0, sizeof(input)); 42 memset(&input, 0, sizeof(input));
35 input.type = INPUT_KEYBOARD; 43 input.type = INPUT_KEYBOARD;
36 input.ki.time = 0; 44 input.ki.time = 0;
37 input.ki.dwFlags = flags; 45 input.ki.dwFlags = flags;
38 input.ki.wScan = scancode; 46 input.ki.wScan = scancode;
39 47
40 if ((flags & KEYEVENTF_UNICODE) == 0) { 48 if ((flags & KEYEVENTF_UNICODE) == 0) {
41 // Windows scancodes are only 8-bit, so store the low-order byte into the 49 // Windows scancodes are only 8-bit, so store the low-order byte into the
42 // event and set the extended flag if any high-order bits are set. The only 50 // event and set the extended flag if any high-order bits are set. The only
43 // high-order values we should see are 0xE0 or 0xE1. The extended bit 51 // high-order values we should see are 0xE0 or 0xE1. The extended bit
44 // usually distinguishes keys with the same meaning, e.g. left & right 52 // usually distinguishes keys with the same meaning, e.g. left & right
45 // shift. 53 // shift.
46 input.ki.wScan &= 0xFF; 54 input.ki.wScan &= 0xFF;
47 if ((scancode & 0xFF00) != 0x0000) 55 if ((scancode & 0xFF00) != 0x0000)
48 input.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY; 56 input.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
49 } 57 }
50 58
51 if (SendInput(1, &input, sizeof(INPUT)) == 0) 59 if (SendInput(1, &input, sizeof(INPUT)) == 0)
52 PLOG(ERROR) << "Failed to inject a key event"; 60 PLOG(ERROR) << "Failed to inject a key event";
53 } 61 }
54 62
55 using protocol::ClipboardEvent; 63 // Parse move related operations from the input MouseEvent, and insert the
56 using protocol::KeyEvent; 64 // result into output.
57 using protocol::TextEvent; 65 void ParseMouseMoveEvent(const MouseEvent& event, vector<INPUT>* output) {
58 using protocol::MouseEvent; 66 DCHECK(output != nullptr);
Sergey Ulanov 2016/04/06 18:25:31 This should be DCHECK(output), but I don't think t
Hzj_jie 2016/04/06 19:56:34 Similar as above, why do we prefer to use, say, 'i
Sergey Ulanov 2016/04/06 20:42:20 Mainly for consistency with all other code. In thi
Hzj_jie 2016/04/06 21:41:05 Done.
59 using protocol::TouchEvent; 67 INPUT input{};
Sergey Ulanov 2016/04/06 18:25:31 C++11 initialization syntax is currently banned, s
Hzj_jie 2016/04/06 19:56:34 Done.
68 input.type = INPUT_MOUSE;
69
70 if (event.has_delta_x() && event.has_delta_y()) {
71 input.mi.dx = event.delta_x();
72 input.mi.dy = event.delta_y();
73 input.mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_VIRTUALDESK;
74 } else if (event.has_x() && event.has_y()) {
75 int width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
76 int height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
77 if (width > 1 && height > 1) {
78 int x = std::max(0, std::min(width, event.x()));
79 int y = std::max(0, std::min(height, event.y()));
80 input.mi.dx = static_cast<int>((x * 65535) / (width - 1));
81 input.mi.dy = static_cast<int>((y * 65535) / (height - 1));
82 input.mi.dwFlags =
83 MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK;
84 }
85 } else {
86 return;
87 }
88
89 output->push_back(std::move(input));
Sergey Ulanov 2016/04/06 18:25:31 INPUT is a just a struct. I don't think you need s
Hzj_jie 2016/04/06 19:56:34 Since it's a trivial type, a move constructor is a
Sergey Ulanov 2016/04/06 20:42:20 Interesting. How can it be optimized? push_back()
Hzj_jie 2016/04/06 21:41:05 Sorry, I did not express my opinion clear. I do no
90 }
91
92 // Parse click related operations from the input MouseEvent, and insert the
93 // result into output.
94 void ParseMouseClickEvent(const MouseEvent& event, vector<INPUT>* output) {
95 DCHECK(output != nullptr);
96
97 if (event.has_button() && event.has_button_down()) {
98 INPUT input{};
99 input.type = INPUT_MOUSE;
100
101 MouseEvent::MouseButton button = event.button();
102 bool down = event.button_down();
103
104 // If the host is configured to swap left & right buttons, inject swapped
105 // events to un-do that re-mapping.
106 if (GetSystemMetrics(SM_SWAPBUTTON)) {
107 if (button == MouseEvent::BUTTON_LEFT) {
108 button = MouseEvent::BUTTON_RIGHT;
109 } else if (button == MouseEvent::BUTTON_RIGHT) {
110 button = MouseEvent::BUTTON_LEFT;
111 }
112 }
113
114 if (button == MouseEvent::BUTTON_MIDDLE) {
115 input.mi.dwFlags = down ? MOUSEEVENTF_MIDDLEDOWN : MOUSEEVENTF_MIDDLEUP;
116 } else if (button == MouseEvent::BUTTON_RIGHT) {
117 input.mi.dwFlags = down ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP;
118 } else {
119 input.mi.dwFlags = down ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP;
120 }
121
122 output->push_back(std::move(input));
123 }
124 }
125
126 // Parse wheel related operations from the input MouseEvent, and insert the
127 // result into output.
128 void ParseMouseWheelEvent(const MouseEvent& event, vector<INPUT>* output) {
129 DCHECK(output != nullptr);
130
131 if (event.has_wheel_delta_x()) {
132 int delta = static_cast<int>(event.wheel_delta_x());
133 if (delta != 0) {
134 INPUT input{};
135 input.type = INPUT_MOUSE;
136 input.mi.mouseData = delta;
137 // According to MSDN, if dwFlags does not contain MOUSEEVENTF_WHEEL, the
138 // event should have mouseData = 0. Without MOUSEDEVENTF_WHEEL, mouseData
139 // > 0 will cause this INPUT to be invalid.
Sergey Ulanov 2016/04/06 18:25:31 This comment is confusing. Maybe just say that bot
Hzj_jie 2016/04/06 19:56:34 Updated. The original implementation has two issu
140 input.mi.dwFlags = MOUSEEVENTF_HWHEEL | MOUSEEVENTF_WHEEL;
141 output->push_back(std::move(input));
142 }
143 }
144
145 if (event.has_wheel_delta_y()) {
146 int delta = static_cast<int>(event.wheel_delta_y());
147 if (delta != 0) {
148 INPUT input{};
149 input.type = INPUT_MOUSE;
150 input.mi.mouseData = delta;
151 input.mi.dwFlags = MOUSEEVENTF_WHEEL;
152 output->push_back(std::move(input));
153 }
154 }
155 }
60 156
61 // A class to generate events on Windows. 157 // A class to generate events on Windows.
62 class InputInjectorWin : public InputInjector { 158 class InputInjectorWin : public InputInjector {
63 public: 159 public:
64 InputInjectorWin(scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, 160 InputInjectorWin(scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
65 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner); 161 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner);
66 ~InputInjectorWin() override; 162 ~InputInjectorWin() override;
67 163
68 // ClipboardStub interface. 164 // ClipboardStub interface.
69 void InjectClipboardEvent(const ClipboardEvent& event) override; 165 void InjectClipboardEvent(const ClipboardEvent& event) override;
(...skipping 199 matching lines...) Expand 10 before | Expand all | Expand 10 after
269 it != text.end(); ++it) { 365 it != text.end(); ++it) {
270 SendKeyboardInput(KEYEVENTF_UNICODE, *it); 366 SendKeyboardInput(KEYEVENTF_UNICODE, *it);
271 SendKeyboardInput(KEYEVENTF_UNICODE | KEYEVENTF_KEYUP, *it); 367 SendKeyboardInput(KEYEVENTF_UNICODE | KEYEVENTF_KEYUP, *it);
272 } 368 }
273 } 369 }
274 370
275 void InputInjectorWin::Core::HandleMouse(const MouseEvent& event) { 371 void InputInjectorWin::Core::HandleMouse(const MouseEvent& event) {
276 // Reset the system idle suspend timeout. 372 // Reset the system idle suspend timeout.
277 SetThreadExecutionState(ES_SYSTEM_REQUIRED); 373 SetThreadExecutionState(ES_SYSTEM_REQUIRED);
278 374
279 INPUT input; 375 vector<INPUT> inputs;
280 memset(&input, 0, sizeof(input)); 376 ParseMouseMoveEvent(event, &inputs);
281 input.type = INPUT_MOUSE; 377 ParseMouseClickEvent(event, &inputs);
378 ParseMouseWheelEvent(event, &inputs);
282 379
283 if (event.has_delta_x() && event.has_delta_y()) { 380 if (!inputs.empty()) {
284 input.mi.dx = event.delta_x(); 381 if (SendInput(inputs.size(), inputs.data(), sizeof(INPUT)) != inputs.size())
285 input.mi.dy = event.delta_y();
286 input.mi.dwFlags |= MOUSEEVENTF_MOVE | MOUSEEVENTF_VIRTUALDESK;
287 } else if (event.has_x() && event.has_y()) {
288 int width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
289 int height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
290 if (width > 1 && height > 1) {
291 int x = std::max(0, std::min(width, event.x()));
292 int y = std::max(0, std::min(height, event.y()));
293 input.mi.dx = static_cast<int>((x * 65535) / (width - 1));
294 input.mi.dy = static_cast<int>((y * 65535) / (height - 1));
295 input.mi.dwFlags |=
296 MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK;
297 }
298 }
299
300 int wheel_delta_x = 0;
301 int wheel_delta_y = 0;
302 if (event.has_wheel_delta_x() && event.has_wheel_delta_y()) {
303 wheel_delta_x = static_cast<int>(event.wheel_delta_x());
304 wheel_delta_y = static_cast<int>(event.wheel_delta_y());
305 }
306
307 if (wheel_delta_x != 0 || wheel_delta_y != 0) {
308 if (wheel_delta_x != 0) {
309 input.mi.mouseData = wheel_delta_x;
310 input.mi.dwFlags |= MOUSEEVENTF_HWHEEL;
311 }
312 if (wheel_delta_y != 0) {
313 input.mi.mouseData = wheel_delta_y;
314 input.mi.dwFlags |= MOUSEEVENTF_WHEEL;
315 }
316 }
317
318 if (event.has_button() && event.has_button_down()) {
319 MouseEvent::MouseButton button = event.button();
320 bool down = event.button_down();
321
322 // If the host is configured to swap left & right buttons, inject swapped
323 // events to un-do that re-mapping.
324 if (GetSystemMetrics(SM_SWAPBUTTON)) {
325 if (button == MouseEvent::BUTTON_LEFT) {
326 button = MouseEvent::BUTTON_RIGHT;
327 } else if (button == MouseEvent::BUTTON_RIGHT) {
328 button = MouseEvent::BUTTON_LEFT;
329 }
330 }
331
332 if (button == MouseEvent::BUTTON_LEFT) {
333 input.mi.dwFlags |= down ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP;
334 } else if (button == MouseEvent::BUTTON_MIDDLE) {
335 input.mi.dwFlags |= down ? MOUSEEVENTF_MIDDLEDOWN : MOUSEEVENTF_MIDDLEUP;
336 } else if (button == MouseEvent::BUTTON_RIGHT) {
337 input.mi.dwFlags |= down ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP;
338 } else {
339 input.mi.dwFlags |= down ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP;
340 }
341 }
342
343 if (input.mi.dwFlags) {
344 if (SendInput(1, &input, sizeof(INPUT)) == 0)
345 PLOG(ERROR) << "Failed to inject a mouse event"; 382 PLOG(ERROR) << "Failed to inject a mouse event";
346 } 383 }
347 } 384 }
348 385
349 void InputInjectorWin::Core::HandleTouch(const TouchEvent& event) { 386 void InputInjectorWin::Core::HandleTouch(const TouchEvent& event) {
350 touch_injector_.InjectTouchEvent(event); 387 touch_injector_.InjectTouchEvent(event);
351 } 388 }
352 389
353 } // namespace 390 } // namespace
354 391
355 // static 392 // static
356 scoped_ptr<InputInjector> InputInjector::Create( 393 scoped_ptr<InputInjector> InputInjector::Create(
357 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, 394 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
358 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) { 395 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
359 return make_scoped_ptr( 396 return make_scoped_ptr(
360 new InputInjectorWin(main_task_runner, ui_task_runner)); 397 new InputInjectorWin(main_task_runner, ui_task_runner));
361 } 398 }
362 399
363 // static 400 // static
364 bool InputInjector::SupportsTouchEvents() { 401 bool InputInjector::SupportsTouchEvents() {
365 return TouchInjectorWinDelegate::Create() != nullptr; 402 return TouchInjectorWinDelegate::Create() != nullptr;
366 } 403 }
367 404
368 } // namespace remoting 405 } // namespace remoting
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698