1 // Copyright 2015 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/touch_injector_win.h"
6
7 #include <stddef.h>
8 #include <stdint.h>
9
10 #include <map>
11 #include <utility>
12
13 #include "base/stl_util.h"
14 #include "remoting/proto/event.pb.h"
15 #include "testing/gmock/include/gmock/gmock.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17
18 using ::testing::_;
19 using ::testing::AtLeast;
20 using ::testing::InSequence;
21 using ::testing::ExpectationSet;
22 using ::testing::Return;
23
24 namespace remoting {
25
26 using protocol::TouchEvent;
27 using protocol::TouchEventPoint;
28
29 namespace {
30
31 // Maps touch pointer ID to expected flags [start, move, end, cancel] listed
32 // below.
33 typedef std::map<uint32_t, uint32_t> IdFlagMap;
34
35 const uint32_t kStartFlag =
36 POINTER_FLAG_INRANGE | POINTER_FLAG_INCONTACT | POINTER_FLAG_DOWN;
37
38 const uint32_t kMoveFlag =
39 POINTER_FLAG_INRANGE | POINTER_FLAG_INCONTACT | POINTER_FLAG_UPDATE;
40
41 const uint32_t kEndFlag = POINTER_FLAG_UP;
42
43 const uint32_t kCancelFlag = POINTER_FLAG_UP | POINTER_FLAG_CANCELED;
44
45 MATCHER_P(EqualsSinglePointerTouchInfo, expected, "") {
46 return arg->touchMask == expected.touchMask &&
47 arg->rcContact.left == expected.rcContact.left &&
48 arg->rcContact.top == expected.rcContact.top &&
49 arg->rcContact.right == expected.rcContact.right &&
50 arg->rcContact.bottom == expected.rcContact.bottom &&
51 arg->orientation == expected.orientation &&
52 arg->pressure == expected.pressure &&
53 arg->pointerInfo.pointerType == expected.pointerInfo.pointerType &&
54 arg->pointerInfo.pointerId == expected.pointerInfo.pointerId &&
55 arg->pointerInfo.ptPixelLocation.x ==
56 expected.pointerInfo.ptPixelLocation.x &&
57 arg->pointerInfo.ptPixelLocation.y ==
58 expected.pointerInfo.ptPixelLocation.y;
59 }
60
61 // Make sure that every touch point has the right flag (pointerFlags).
62 MATCHER_P(EqualsPointerTouchInfoFlag, id_to_flag_map, "") {
63 for (size_t i = 0; i < id_to_flag_map.size(); ++i) {
64 const POINTER_TOUCH_INFO* touch_info = arg + i;
65 const uint32_t id = touch_info->pointerInfo.pointerId;
66 if (!base::Contains(id_to_flag_map, id))
67 return false;
68
69 if (id_to_flag_map.find(id)->second != touch_info->pointerInfo.pointerFlags)
70 return false;
71 }
72 return true;
73 }
74
75 class TouchInjectorWinDelegateMock : public TouchInjectorWinDelegate {
76 public:
TouchInjectorWinDelegateMock()77 TouchInjectorWinDelegateMock()
78 : TouchInjectorWinDelegate(nullptr, nullptr, nullptr) {}
~TouchInjectorWinDelegateMock()79 ~TouchInjectorWinDelegateMock() override {}
80
81 MOCK_METHOD2(InitializeTouchInjection, BOOL(UINT32 max_count, DWORD dw_mode));
82 MOCK_METHOD2(InjectTouchInput,
83 DWORD(UINT32 count, const POINTER_TOUCH_INFO* contacts));
84 };
85
86 } // namespace
87
88 // A test to make sure that the touch event is converted correctly to
89 // POINTER_TOUCH_INFO.
TEST(TouchInjectorWinTest,CheckConversionWithPressure)90 TEST(TouchInjectorWinTest, CheckConversionWithPressure) {
91 std::unique_ptr<TouchInjectorWinDelegateMock> delegate_mock(
92 new ::testing::StrictMock<TouchInjectorWinDelegateMock>());
93
94 TouchEvent event;
95 event.set_event_type(TouchEvent::TOUCH_POINT_START);
96 TouchEventPoint* point = event.add_touch_points();
97 point->set_id(1234u);
98 point->set_x(321.0f);
99 point->set_y(123.0f);
100 point->set_radius_x(10.0f);
101 point->set_radius_y(20.0f);
102 point->set_pressure(0.5f);
103 point->set_angle(45.0f);
104
105 POINTER_TOUCH_INFO expected_touch_info;
106 expected_touch_info.touchMask =
107 TOUCH_MASK_CONTACTAREA | TOUCH_MASK_ORIENTATION | TOUCH_MASK_PRESSURE;
108 expected_touch_info.rcContact.left = 311;
109 expected_touch_info.rcContact.top = 103;
110 expected_touch_info.rcContact.right = 331;
111 expected_touch_info.rcContact.bottom = 143;
112 expected_touch_info.orientation = 0;
113 expected_touch_info.pressure = 512;
114 expected_touch_info.orientation = 45;
115
116 expected_touch_info.pointerInfo.pointerType = PT_TOUCH;
117 expected_touch_info.pointerInfo.pointerId = 1234u;
118 expected_touch_info.pointerInfo.ptPixelLocation.x = 321;
119 expected_touch_info.pointerInfo.ptPixelLocation.y = 123;
120
121 InSequence s;
122 EXPECT_CALL(*delegate_mock, InitializeTouchInjection(_, _))
123 .WillOnce(Return(1));
124 EXPECT_CALL(
125 *delegate_mock,
126 InjectTouchInput(1, EqualsSinglePointerTouchInfo(expected_touch_info)))
127 .WillOnce(Return(1));
128
129 // Check pressure clamping as well.
130 expected_touch_info.pressure = 1024; // Max
131 EXPECT_CALL(
132 *delegate_mock,
133 InjectTouchInput(1, EqualsSinglePointerTouchInfo(expected_touch_info)))
134 .WillOnce(Return(1));
135
136 expected_touch_info.pressure = 0; // Min
137 EXPECT_CALL(
138 *delegate_mock,
139 InjectTouchInput(1, EqualsSinglePointerTouchInfo(expected_touch_info)))
140 .WillOnce(Return(1));
141
142 TouchInjectorWin injector;
143 injector.SetInjectorDelegateForTest(std::move(delegate_mock));
144 EXPECT_TRUE(injector.Init());
145 injector.InjectTouchEvent(event);
146
147 // Change to MOVE so that there still only one point.
148 event.set_event_type(TouchEvent::TOUCH_POINT_MOVE);
149 point->set_pressure(2.0f);
150 injector.InjectTouchEvent(event);
151
152 point->set_pressure(-3.0f);
153 injector.InjectTouchEvent(event);
154 }
155
156 // Some devices don't detect pressure. This test is a conversion check for
157 // such devices.
TEST(TouchInjectorWinTest,CheckConversionNoPressure)158 TEST(TouchInjectorWinTest, CheckConversionNoPressure) {
159 std::unique_ptr<TouchInjectorWinDelegateMock> delegate_mock(
160 new ::testing::StrictMock<TouchInjectorWinDelegateMock>());
161
162 TouchEvent event;
163 event.set_event_type(TouchEvent::TOUCH_POINT_START);
164 TouchEventPoint* point = event.add_touch_points();
165 point->set_id(1234u);
166 point->set_x(321.0f);
167 point->set_y(123.0f);
168 point->set_radius_x(10.0f);
169 point->set_radius_y(20.0f);
170 point->set_angle(45.0f);
171
172 POINTER_TOUCH_INFO expected_touch_info;
173 expected_touch_info.touchMask =
174 TOUCH_MASK_CONTACTAREA | TOUCH_MASK_ORIENTATION;
175 expected_touch_info.rcContact.left = 311;
176 expected_touch_info.rcContact.top = 103;
177 expected_touch_info.rcContact.right = 331;
178 expected_touch_info.rcContact.bottom = 143;
179 expected_touch_info.orientation = 0;
180 expected_touch_info.pressure = 0;
181 expected_touch_info.orientation = 45;
182
183 expected_touch_info.pointerInfo.pointerType = PT_TOUCH;
184 expected_touch_info.pointerInfo.pointerId = 1234u;
185 expected_touch_info.pointerInfo.ptPixelLocation.x = 321;
186 expected_touch_info.pointerInfo.ptPixelLocation.y = 123;
187
188 InSequence s;
189 EXPECT_CALL(*delegate_mock, InitializeTouchInjection(_, _))
190 .WillOnce(Return(1));
191 EXPECT_CALL(
192 *delegate_mock,
193 InjectTouchInput(1, EqualsSinglePointerTouchInfo(expected_touch_info)))
194 .WillOnce(Return(1));
195
196 TouchInjectorWin injector;
197 injector.SetInjectorDelegateForTest(std::move(delegate_mock));
198 EXPECT_TRUE(injector.Init());
199 injector.InjectTouchEvent(event);
200 }
201
202 // If initialization fails, it should not call any touch injection functions.
TEST(TouchInjectorWinTest,InitFailed)203 TEST(TouchInjectorWinTest, InitFailed) {
204 std::unique_ptr<TouchInjectorWinDelegateMock> delegate_mock(
205 new ::testing::StrictMock<TouchInjectorWinDelegateMock>());
206
207 TouchEvent event;
208 event.set_event_type(TouchEvent::TOUCH_POINT_START);
209
210 InSequence s;
211 EXPECT_CALL(*delegate_mock, InitializeTouchInjection(_, _))
212 .WillOnce(Return(0));
213 EXPECT_CALL(*delegate_mock, InjectTouchInput(_, _)).Times(0);
214
215 TouchInjectorWin injector;
216 injector.SetInjectorDelegateForTest(std::move(delegate_mock));
217 EXPECT_FALSE(injector.Init());
218 injector.InjectTouchEvent(event);
219 }
220
221 // Deinitialize and initialize should clean the state.
TEST(TouchInjectorWinTest,Reinitialize)222 TEST(TouchInjectorWinTest, Reinitialize) {
223 std::unique_ptr<TouchInjectorWinDelegateMock>
224 delegate_mock_before_deinitialize(
225 new ::testing::StrictMock<TouchInjectorWinDelegateMock>());
226 std::unique_ptr<TouchInjectorWinDelegateMock>
227 delegate_mock_after_deinitialize(
228 new ::testing::StrictMock<TouchInjectorWinDelegateMock>());
229
230 TouchEvent first_event;
231 first_event.set_event_type(TouchEvent::TOUCH_POINT_START);
232 TouchEventPoint* point0 = first_event.add_touch_points();
233 point0->set_id(0u);
234
235 TouchEvent second_event;
236 second_event.set_event_type(TouchEvent::TOUCH_POINT_START);
237 TouchEventPoint* point1 = second_event.add_touch_points();
238 point1->set_id(1u);
239
240 InSequence s;
241 EXPECT_CALL(*delegate_mock_before_deinitialize,
242 InitializeTouchInjection(_, _)).WillOnce(Return(1));
243
244 IdFlagMap id_to_flags;
245 id_to_flags[0u] = kStartFlag;
246 EXPECT_CALL(
247 *delegate_mock_before_deinitialize,
248 InjectTouchInput(1, EqualsPointerTouchInfoFlag(id_to_flags)))
249 .WillOnce(Return(1));
250
251 EXPECT_CALL(*delegate_mock_after_deinitialize,
252 InitializeTouchInjection(_, _)).WillOnce(Return(1));
253
254 // After deinitializing and then initializing, previous touch points should be
255 // gone.
256 id_to_flags.clear();
257 id_to_flags[1u] = kStartFlag;
258 EXPECT_CALL(
259 *delegate_mock_after_deinitialize,
260 InjectTouchInput(1, EqualsPointerTouchInfoFlag(id_to_flags)))
261 .WillOnce(Return(1));
262
263 TouchInjectorWin injector;
264 injector.SetInjectorDelegateForTest(
265 std::move(delegate_mock_before_deinitialize));
266
267 EXPECT_TRUE(injector.Init());
268 injector.InjectTouchEvent(first_event);
269 injector.Deinitialize();
270
271 injector.SetInjectorDelegateForTest(
272 std::move(delegate_mock_after_deinitialize));
273 EXPECT_TRUE(injector.Init());
274 injector.InjectTouchEvent(second_event);
275 }
276
277 // Make sure that the flag is set to kStartFlag.
TEST(TouchInjectorWinTest,StartTouchPoint)278 TEST(TouchInjectorWinTest, StartTouchPoint) {
279 std::unique_ptr<TouchInjectorWinDelegateMock> delegate_mock(
280 new ::testing::StrictMock<TouchInjectorWinDelegateMock>());
281
282 TouchEvent event;
283 event.set_event_type(TouchEvent::TOUCH_POINT_START);
284 TouchEventPoint* point = event.add_touch_points();
285 point->set_id(0u);
286
287 InSequence s;
288 EXPECT_CALL(*delegate_mock, InitializeTouchInjection(_, _))
289 .WillOnce(Return(1));
290
291 IdFlagMap id_to_flags;
292 id_to_flags[0u] = kStartFlag;
293 EXPECT_CALL(
294 *delegate_mock,
295 InjectTouchInput(1, EqualsPointerTouchInfoFlag(id_to_flags)))
296 .WillOnce(Return(1));
297
298 TouchInjectorWin injector;
299 injector.SetInjectorDelegateForTest(std::move(delegate_mock));
300 EXPECT_TRUE(injector.Init());
301 injector.InjectTouchEvent(event);
302 }
303
304 // Start a point and then move, make sure the flag is set to kMoveFlag.
TEST(TouchInjectorWinTest,MoveTouchPoint)305 TEST(TouchInjectorWinTest, MoveTouchPoint) {
306 std::unique_ptr<TouchInjectorWinDelegateMock> delegate_mock(
307 new ::testing::StrictMock<TouchInjectorWinDelegateMock>());
308
309 TouchEvent event;
310 event.set_event_type(TouchEvent::TOUCH_POINT_START);
311 TouchEventPoint* point = event.add_touch_points();
312 point->set_id(0u);
313
314
315 InSequence s;
316 EXPECT_CALL(*delegate_mock, InitializeTouchInjection(_, _))
317 .WillOnce(Return(1));
318
319 IdFlagMap id_to_flags;
320 id_to_flags[0u] = kStartFlag;
321 EXPECT_CALL(
322 *delegate_mock,
323 InjectTouchInput(1, EqualsPointerTouchInfoFlag(id_to_flags)))
324 .WillOnce(Return(1));
325
326 id_to_flags[0u] = kMoveFlag;
327 EXPECT_CALL(
328 *delegate_mock,
329 InjectTouchInput(1, EqualsPointerTouchInfoFlag(id_to_flags)))
330 .WillOnce(Return(1));
331
332 TouchInjectorWin injector;
333 injector.SetInjectorDelegateForTest(std::move(delegate_mock));
334 EXPECT_TRUE(injector.Init());
335 injector.InjectTouchEvent(event);
336 event.set_event_type(TouchEvent::TOUCH_POINT_MOVE);
337 injector.InjectTouchEvent(event);
338 }
339
340 // Start a point and then move, make sure the flag is set to kEndFlag.
TEST(TouchInjectorWinTest,EndTouchPoint)341 TEST(TouchInjectorWinTest, EndTouchPoint) {
342 std::unique_ptr<TouchInjectorWinDelegateMock> delegate_mock(
343 new ::testing::StrictMock<TouchInjectorWinDelegateMock>());
344
345 TouchEvent event;
346 event.set_event_type(TouchEvent::TOUCH_POINT_START);
347 TouchEventPoint* point = event.add_touch_points();
348 point->set_id(0u);
349
350 InSequence s;
351 EXPECT_CALL(*delegate_mock, InitializeTouchInjection(_, _))
352 .WillOnce(Return(1));
353
354 IdFlagMap id_to_flags;
355 id_to_flags[0u] = kStartFlag;
356 EXPECT_CALL(
357 *delegate_mock,
358 InjectTouchInput(1, EqualsPointerTouchInfoFlag(id_to_flags)))
359 .WillOnce(Return(1));
360
361 id_to_flags[0u] = kEndFlag;
362 EXPECT_CALL(
363 *delegate_mock,
364 InjectTouchInput(1, EqualsPointerTouchInfoFlag(id_to_flags)))
365 .WillOnce(Return(1));
366
367 TouchInjectorWin injector;
368 injector.SetInjectorDelegateForTest(std::move(delegate_mock));
369 EXPECT_TRUE(injector.Init());
370 injector.InjectTouchEvent(event);
371 event.set_event_type(TouchEvent::TOUCH_POINT_END);
372 injector.InjectTouchEvent(event);
373 }
374
375 // Start a point and then move, make sure the flag is set to kCancelFlag.
TEST(TouchInjectorWinTest,CancelTouchPoint)376 TEST(TouchInjectorWinTest, CancelTouchPoint) {
377 std::unique_ptr<TouchInjectorWinDelegateMock> delegate_mock(
378 new ::testing::StrictMock<TouchInjectorWinDelegateMock>());
379
380 TouchEvent event;
381 event.set_event_type(TouchEvent::TOUCH_POINT_START);
382 TouchEventPoint* point = event.add_touch_points();
383 point->set_id(0u);
384
385 InSequence s;
386 EXPECT_CALL(*delegate_mock, InitializeTouchInjection(_, _))
387 .WillOnce(Return(1));
388
389 IdFlagMap id_to_flags;
390 id_to_flags[0u] = kStartFlag;
391 EXPECT_CALL(
392 *delegate_mock,
393 InjectTouchInput(1, EqualsPointerTouchInfoFlag(id_to_flags)))
394 .WillOnce(Return(1));
395
396 id_to_flags[0u] = kCancelFlag;
397 EXPECT_CALL(
398 *delegate_mock,
399 InjectTouchInput(1, EqualsPointerTouchInfoFlag(id_to_flags)))
400 .WillOnce(Return(1));
401
402 TouchInjectorWin injector;
403 injector.SetInjectorDelegateForTest(std::move(delegate_mock));
404 EXPECT_TRUE(injector.Init());
405 injector.InjectTouchEvent(event);
406 event.set_event_type(TouchEvent::TOUCH_POINT_CANCEL);
407 injector.InjectTouchEvent(event);
408 }
409
410 // Note that points that haven't changed should be injected as MOVE.
411 // This tests:
412 // 1. Start first touch point.
413 // 2. Start second touch point.
414 // 3. Move both touch points.
415 // 4. Start third touch point.
416 // 5. End second touch point.
417 // 6. Cancel remaining (first and third) touch points.
TEST(TouchInjectorWinTest,MultiTouch)418 TEST(TouchInjectorWinTest, MultiTouch) {
419 std::unique_ptr<TouchInjectorWinDelegateMock> delegate_mock(
420 new ::testing::StrictMock<TouchInjectorWinDelegateMock>());
421
422 InSequence s;
423 EXPECT_CALL(*delegate_mock, InitializeTouchInjection(_, _))
424 .WillOnce(Return(1));
425
426 IdFlagMap id_to_flags;
427 id_to_flags[0u] = kStartFlag;
428 EXPECT_CALL(
429 *delegate_mock,
430 InjectTouchInput(1, EqualsPointerTouchInfoFlag(id_to_flags)))
431 .WillOnce(Return(1));
432
433 id_to_flags[0u] = kMoveFlag;
434 id_to_flags[1u] = kStartFlag;
435 EXPECT_CALL(
436 *delegate_mock,
437 InjectTouchInput(2, EqualsPointerTouchInfoFlag(id_to_flags)))
438 .WillOnce(Return(1));
439
440 id_to_flags[0u] = kMoveFlag;
441 id_to_flags[1u] = kMoveFlag;
442 EXPECT_CALL(
443 *delegate_mock,
444 InjectTouchInput(2, EqualsPointerTouchInfoFlag(id_to_flags)))
445 .WillOnce(Return(1));
446
447 id_to_flags[0u] = kMoveFlag;
448 id_to_flags[1u] = kMoveFlag;
449 id_to_flags[2u] = kStartFlag;
450 EXPECT_CALL(
451 *delegate_mock,
452 InjectTouchInput(3, EqualsPointerTouchInfoFlag(id_to_flags)))
453 .WillOnce(Return(1));
454
455 id_to_flags[0u] = kMoveFlag;
456 id_to_flags[1u] = kEndFlag;
457 id_to_flags[2u] = kMoveFlag;
458 EXPECT_CALL(
459 *delegate_mock,
460 InjectTouchInput(3, EqualsPointerTouchInfoFlag(id_to_flags)))
461 .WillOnce(Return(1));
462
463 id_to_flags.erase(1u);
464 id_to_flags[0u] = kCancelFlag;
465 id_to_flags[2u] = kCancelFlag;
466 EXPECT_CALL(
467 *delegate_mock,
468 InjectTouchInput(2, EqualsPointerTouchInfoFlag(id_to_flags)))
469 .WillOnce(Return(1));
470
471 TouchInjectorWin injector;
472 injector.SetInjectorDelegateForTest(std::move(delegate_mock));
473 EXPECT_TRUE(injector.Init());
474
475 // Start first touch point.
476 TouchEvent first_touch_start;
477 first_touch_start.set_event_type(TouchEvent::TOUCH_POINT_START);
478 TouchEventPoint* point0 = first_touch_start.add_touch_points();
479 point0->set_id(0u);
480 injector.InjectTouchEvent(first_touch_start);
481
482 // Add second touch point.
483 TouchEvent second_touch_start;
484 second_touch_start.set_event_type(TouchEvent::TOUCH_POINT_START);
485 TouchEventPoint* point1 = second_touch_start.add_touch_points();
486 point1->set_id(1u);
487 injector.InjectTouchEvent(second_touch_start);
488
489 // Move both touch points.
490 TouchEvent move_both;
491 move_both.set_event_type(TouchEvent::TOUCH_POINT_MOVE);
492 point0 = second_touch_start.add_touch_points();
493 point1 = second_touch_start.add_touch_points();
494 point0->set_id(0u);
495 point1->set_id(1u);
496 injector.InjectTouchEvent(move_both);
497
498 // Add another.
499 TouchEvent third_touch_start;
500 third_touch_start.set_event_type(TouchEvent::TOUCH_POINT_START);
501 TouchEventPoint* point2 = third_touch_start.add_touch_points();
502 point2->set_id(2u);
503 injector.InjectTouchEvent(third_touch_start);
504
505 // Release second touch point.
506 TouchEvent release_second;
507 release_second.set_event_type(TouchEvent::TOUCH_POINT_END);
508 point1 = release_second.add_touch_points();
509 point1->set_id(1u);
510 injector.InjectTouchEvent(release_second);
511
512 // Cancel the remaining two points.
513 TouchEvent cancel_rest;
514 cancel_rest.set_event_type(TouchEvent::TOUCH_POINT_CANCEL);
515 point0 = cancel_rest.add_touch_points();
516 point0->set_id(0u);
517 point2 = cancel_rest.add_touch_points();
518 point2->set_id(2u);
519 injector.InjectTouchEvent(cancel_rest);
520 }
521
522 } // namespace remoting
523