Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/testing/selftests/hid/tests/test_mouse.py
26308 views
1
#!/bin/env python3
2
# SPDX-License-Identifier: GPL-2.0
3
# -*- coding: utf-8 -*-
4
#
5
# Copyright (c) 2017 Benjamin Tissoires <[email protected]>
6
# Copyright (c) 2017 Red Hat, Inc.
7
#
8
9
from . import base
10
import hidtools.hid
11
from hidtools.util import BusType
12
import libevdev
13
import logging
14
import pytest
15
16
logger = logging.getLogger("hidtools.test.mouse")
17
18
# workaround https://gitlab.freedesktop.org/libevdev/python-libevdev/issues/6
19
try:
20
libevdev.EV_REL.REL_WHEEL_HI_RES
21
except AttributeError:
22
libevdev.EV_REL.REL_WHEEL_HI_RES = libevdev.EV_REL.REL_0B
23
libevdev.EV_REL.REL_HWHEEL_HI_RES = libevdev.EV_REL.REL_0C
24
25
26
class InvalidHIDCommunication(Exception):
27
pass
28
29
30
class MouseData(object):
31
pass
32
33
34
class BaseMouse(base.UHIDTestDevice):
35
def __init__(self, rdesc, name=None, input_info=None):
36
assert rdesc is not None
37
super().__init__(name, "Mouse", input_info=input_info, rdesc=rdesc)
38
self.left = False
39
self.right = False
40
self.middle = False
41
42
def create_report(self, x, y, buttons=None, wheels=None, reportID=None):
43
"""
44
Return an input report for this device.
45
46
:param x: relative x
47
:param y: relative y
48
:param buttons: a (l, r, m) tuple of bools for the button states,
49
where ``None`` is "leave unchanged"
50
:param wheels: a single value for the vertical wheel or a (vertical, horizontal) tuple for
51
the two wheels
52
:param reportID: the numeric report ID for this report, if needed
53
"""
54
if buttons is not None:
55
left, right, middle = buttons
56
if left is not None:
57
self.left = left
58
if right is not None:
59
self.right = right
60
if middle is not None:
61
self.middle = middle
62
left = self.left
63
right = self.right
64
middle = self.middle
65
# Note: the BaseMouse doesn't actually have a wheel but the
66
# create_report magic only fills in those fields exist, so let's
67
# make this generic here.
68
wheel, acpan = 0, 0
69
if wheels is not None:
70
if isinstance(wheels, tuple):
71
wheel = wheels[0]
72
acpan = wheels[1]
73
else:
74
wheel = wheels
75
76
reportID = reportID or self.default_reportID
77
78
mouse = MouseData()
79
mouse.b1 = int(left)
80
mouse.b2 = int(right)
81
mouse.b3 = int(middle)
82
mouse.x = x
83
mouse.y = y
84
mouse.wheel = wheel
85
mouse.acpan = acpan
86
return super().create_report(mouse, reportID=reportID)
87
88
def event(self, x, y, buttons=None, wheels=None):
89
"""
90
Send an input event on the default report ID.
91
92
:param x: relative x
93
:param y: relative y
94
:param buttons: a (l, r, m) tuple of bools for the button states,
95
where ``None`` is "leave unchanged"
96
:param wheels: a single value for the vertical wheel or a (vertical, horizontal) tuple for
97
the two wheels
98
"""
99
r = self.create_report(x, y, buttons, wheels)
100
self.call_input_event(r)
101
return [r]
102
103
104
class ButtonMouse(BaseMouse):
105
# fmt: off
106
report_descriptor = [
107
0x05, 0x01, # .Usage Page (Generic Desktop) 0
108
0x09, 0x02, # .Usage (Mouse) 2
109
0xa1, 0x01, # .Collection (Application) 4
110
0x09, 0x02, # ..Usage (Mouse) 6
111
0xa1, 0x02, # ..Collection (Logical) 8
112
0x09, 0x01, # ...Usage (Pointer) 10
113
0xa1, 0x00, # ...Collection (Physical) 12
114
0x05, 0x09, # ....Usage Page (Button) 14
115
0x19, 0x01, # ....Usage Minimum (1) 16
116
0x29, 0x03, # ....Usage Maximum (3) 18
117
0x15, 0x00, # ....Logical Minimum (0) 20
118
0x25, 0x01, # ....Logical Maximum (1) 22
119
0x75, 0x01, # ....Report Size (1) 24
120
0x95, 0x03, # ....Report Count (3) 26
121
0x81, 0x02, # ....Input (Data,Var,Abs) 28
122
0x75, 0x05, # ....Report Size (5) 30
123
0x95, 0x01, # ....Report Count (1) 32
124
0x81, 0x03, # ....Input (Cnst,Var,Abs) 34
125
0x05, 0x01, # ....Usage Page (Generic Desktop) 36
126
0x09, 0x30, # ....Usage (X) 38
127
0x09, 0x31, # ....Usage (Y) 40
128
0x15, 0x81, # ....Logical Minimum (-127) 42
129
0x25, 0x7f, # ....Logical Maximum (127) 44
130
0x75, 0x08, # ....Report Size (8) 46
131
0x95, 0x02, # ....Report Count (2) 48
132
0x81, 0x06, # ....Input (Data,Var,Rel) 50
133
0xc0, # ...End Collection 52
134
0xc0, # ..End Collection 53
135
0xc0, # .End Collection 54
136
]
137
# fmt: on
138
139
def __init__(self, rdesc=report_descriptor, name=None, input_info=None):
140
super().__init__(rdesc, name, input_info)
141
142
def fake_report(self, x, y, buttons):
143
if buttons is not None:
144
left, right, middle = buttons
145
if left is None:
146
left = self.left
147
if right is None:
148
right = self.right
149
if middle is None:
150
middle = self.middle
151
else:
152
left = self.left
153
right = self.right
154
middle = self.middle
155
156
button_mask = sum(1 << i for i, b in enumerate([left, right, middle]) if b)
157
x = max(-127, min(127, x))
158
y = max(-127, min(127, y))
159
x = hidtools.util.to_twos_comp(x, 8)
160
y = hidtools.util.to_twos_comp(y, 8)
161
return [button_mask, x, y]
162
163
164
class WheelMouse(ButtonMouse):
165
# fmt: off
166
report_descriptor = [
167
0x05, 0x01, # Usage Page (Generic Desktop) 0
168
0x09, 0x02, # Usage (Mouse) 2
169
0xa1, 0x01, # Collection (Application) 4
170
0x05, 0x09, # .Usage Page (Button) 6
171
0x19, 0x01, # .Usage Minimum (1) 8
172
0x29, 0x03, # .Usage Maximum (3) 10
173
0x15, 0x00, # .Logical Minimum (0) 12
174
0x25, 0x01, # .Logical Maximum (1) 14
175
0x95, 0x03, # .Report Count (3) 16
176
0x75, 0x01, # .Report Size (1) 18
177
0x81, 0x02, # .Input (Data,Var,Abs) 20
178
0x95, 0x01, # .Report Count (1) 22
179
0x75, 0x05, # .Report Size (5) 24
180
0x81, 0x03, # .Input (Cnst,Var,Abs) 26
181
0x05, 0x01, # .Usage Page (Generic Desktop) 28
182
0x09, 0x01, # .Usage (Pointer) 30
183
0xa1, 0x00, # .Collection (Physical) 32
184
0x09, 0x30, # ..Usage (X) 34
185
0x09, 0x31, # ..Usage (Y) 36
186
0x15, 0x81, # ..Logical Minimum (-127) 38
187
0x25, 0x7f, # ..Logical Maximum (127) 40
188
0x75, 0x08, # ..Report Size (8) 42
189
0x95, 0x02, # ..Report Count (2) 44
190
0x81, 0x06, # ..Input (Data,Var,Rel) 46
191
0xc0, # .End Collection 48
192
0x09, 0x38, # .Usage (Wheel) 49
193
0x15, 0x81, # .Logical Minimum (-127) 51
194
0x25, 0x7f, # .Logical Maximum (127) 53
195
0x75, 0x08, # .Report Size (8) 55
196
0x95, 0x01, # .Report Count (1) 57
197
0x81, 0x06, # .Input (Data,Var,Rel) 59
198
0xc0, # End Collection 61
199
]
200
# fmt: on
201
202
def __init__(self, rdesc=report_descriptor, name=None, input_info=None):
203
super().__init__(rdesc, name, input_info)
204
self.wheel_multiplier = 1
205
206
207
class TwoWheelMouse(WheelMouse):
208
# fmt: off
209
report_descriptor = [
210
0x05, 0x01, # Usage Page (Generic Desktop) 0
211
0x09, 0x02, # Usage (Mouse) 2
212
0xa1, 0x01, # Collection (Application) 4
213
0x09, 0x01, # .Usage (Pointer) 6
214
0xa1, 0x00, # .Collection (Physical) 8
215
0x05, 0x09, # ..Usage Page (Button) 10
216
0x19, 0x01, # ..Usage Minimum (1) 12
217
0x29, 0x10, # ..Usage Maximum (16) 14
218
0x15, 0x00, # ..Logical Minimum (0) 16
219
0x25, 0x01, # ..Logical Maximum (1) 18
220
0x95, 0x10, # ..Report Count (16) 20
221
0x75, 0x01, # ..Report Size (1) 22
222
0x81, 0x02, # ..Input (Data,Var,Abs) 24
223
0x05, 0x01, # ..Usage Page (Generic Desktop) 26
224
0x16, 0x01, 0x80, # ..Logical Minimum (-32767) 28
225
0x26, 0xff, 0x7f, # ..Logical Maximum (32767) 31
226
0x75, 0x10, # ..Report Size (16) 34
227
0x95, 0x02, # ..Report Count (2) 36
228
0x09, 0x30, # ..Usage (X) 38
229
0x09, 0x31, # ..Usage (Y) 40
230
0x81, 0x06, # ..Input (Data,Var,Rel) 42
231
0x15, 0x81, # ..Logical Minimum (-127) 44
232
0x25, 0x7f, # ..Logical Maximum (127) 46
233
0x75, 0x08, # ..Report Size (8) 48
234
0x95, 0x01, # ..Report Count (1) 50
235
0x09, 0x38, # ..Usage (Wheel) 52
236
0x81, 0x06, # ..Input (Data,Var,Rel) 54
237
0x05, 0x0c, # ..Usage Page (Consumer Devices) 56
238
0x0a, 0x38, 0x02, # ..Usage (AC Pan) 58
239
0x95, 0x01, # ..Report Count (1) 61
240
0x81, 0x06, # ..Input (Data,Var,Rel) 63
241
0xc0, # .End Collection 65
242
0xc0, # End Collection 66
243
]
244
# fmt: on
245
246
def __init__(self, rdesc=report_descriptor, name=None, input_info=None):
247
super().__init__(rdesc, name, input_info)
248
self.hwheel_multiplier = 1
249
250
251
class MIDongleMIWirelessMouse(TwoWheelMouse):
252
# fmt: off
253
report_descriptor = [
254
0x05, 0x01, # Usage Page (Generic Desktop)
255
0x09, 0x02, # Usage (Mouse)
256
0xa1, 0x01, # Collection (Application)
257
0x85, 0x01, # .Report ID (1)
258
0x09, 0x01, # .Usage (Pointer)
259
0xa1, 0x00, # .Collection (Physical)
260
0x95, 0x05, # ..Report Count (5)
261
0x75, 0x01, # ..Report Size (1)
262
0x05, 0x09, # ..Usage Page (Button)
263
0x19, 0x01, # ..Usage Minimum (1)
264
0x29, 0x05, # ..Usage Maximum (5)
265
0x15, 0x00, # ..Logical Minimum (0)
266
0x25, 0x01, # ..Logical Maximum (1)
267
0x81, 0x02, # ..Input (Data,Var,Abs)
268
0x95, 0x01, # ..Report Count (1)
269
0x75, 0x03, # ..Report Size (3)
270
0x81, 0x01, # ..Input (Cnst,Arr,Abs)
271
0x75, 0x08, # ..Report Size (8)
272
0x95, 0x01, # ..Report Count (1)
273
0x05, 0x01, # ..Usage Page (Generic Desktop)
274
0x09, 0x38, # ..Usage (Wheel)
275
0x15, 0x81, # ..Logical Minimum (-127)
276
0x25, 0x7f, # ..Logical Maximum (127)
277
0x81, 0x06, # ..Input (Data,Var,Rel)
278
0x05, 0x0c, # ..Usage Page (Consumer Devices)
279
0x0a, 0x38, 0x02, # ..Usage (AC Pan)
280
0x95, 0x01, # ..Report Count (1)
281
0x81, 0x06, # ..Input (Data,Var,Rel)
282
0xc0, # .End Collection
283
0x85, 0x02, # .Report ID (2)
284
0x09, 0x01, # .Usage (Consumer Control)
285
0xa1, 0x00, # .Collection (Physical)
286
0x75, 0x0c, # ..Report Size (12)
287
0x95, 0x02, # ..Report Count (2)
288
0x05, 0x01, # ..Usage Page (Generic Desktop)
289
0x09, 0x30, # ..Usage (X)
290
0x09, 0x31, # ..Usage (Y)
291
0x16, 0x01, 0xf8, # ..Logical Minimum (-2047)
292
0x26, 0xff, 0x07, # ..Logical Maximum (2047)
293
0x81, 0x06, # ..Input (Data,Var,Rel)
294
0xc0, # .End Collection
295
0xc0, # End Collection
296
0x05, 0x0c, # Usage Page (Consumer Devices)
297
0x09, 0x01, # Usage (Consumer Control)
298
0xa1, 0x01, # Collection (Application)
299
0x85, 0x03, # .Report ID (3)
300
0x15, 0x00, # .Logical Minimum (0)
301
0x25, 0x01, # .Logical Maximum (1)
302
0x75, 0x01, # .Report Size (1)
303
0x95, 0x01, # .Report Count (1)
304
0x09, 0xcd, # .Usage (Play/Pause)
305
0x81, 0x06, # .Input (Data,Var,Rel)
306
0x0a, 0x83, 0x01, # .Usage (AL Consumer Control Config)
307
0x81, 0x06, # .Input (Data,Var,Rel)
308
0x09, 0xb5, # .Usage (Scan Next Track)
309
0x81, 0x06, # .Input (Data,Var,Rel)
310
0x09, 0xb6, # .Usage (Scan Previous Track)
311
0x81, 0x06, # .Input (Data,Var,Rel)
312
0x09, 0xea, # .Usage (Volume Down)
313
0x81, 0x06, # .Input (Data,Var,Rel)
314
0x09, 0xe9, # .Usage (Volume Up)
315
0x81, 0x06, # .Input (Data,Var,Rel)
316
0x0a, 0x25, 0x02, # .Usage (AC Forward)
317
0x81, 0x06, # .Input (Data,Var,Rel)
318
0x0a, 0x24, 0x02, # .Usage (AC Back)
319
0x81, 0x06, # .Input (Data,Var,Rel)
320
0xc0, # End Collection
321
]
322
# fmt: on
323
device_input_info = (BusType.USB, 0x2717, 0x003B)
324
device_name = "uhid test MI Dongle MI Wireless Mouse"
325
326
def __init__(
327
self, rdesc=report_descriptor, name=device_name, input_info=device_input_info
328
):
329
super().__init__(rdesc, name, input_info)
330
331
def event(self, x, y, buttons=None, wheels=None):
332
# this mouse spreads the relative pointer and the mouse buttons
333
# onto 2 distinct reports
334
rs = []
335
r = self.create_report(x, y, buttons, wheels, reportID=1)
336
self.call_input_event(r)
337
rs.append(r)
338
r = self.create_report(x, y, buttons, reportID=2)
339
self.call_input_event(r)
340
rs.append(r)
341
return rs
342
343
344
class ResolutionMultiplierMouse(TwoWheelMouse):
345
# fmt: off
346
report_descriptor = [
347
0x05, 0x01, # Usage Page (Generic Desktop) 83
348
0x09, 0x02, # Usage (Mouse) 85
349
0xa1, 0x01, # Collection (Application) 87
350
0x05, 0x01, # .Usage Page (Generic Desktop) 89
351
0x09, 0x02, # .Usage (Mouse) 91
352
0xa1, 0x02, # .Collection (Logical) 93
353
0x85, 0x11, # ..Report ID (17) 95
354
0x09, 0x01, # ..Usage (Pointer) 97
355
0xa1, 0x00, # ..Collection (Physical) 99
356
0x05, 0x09, # ...Usage Page (Button) 101
357
0x19, 0x01, # ...Usage Minimum (1) 103
358
0x29, 0x03, # ...Usage Maximum (3) 105
359
0x95, 0x03, # ...Report Count (3) 107
360
0x75, 0x01, # ...Report Size (1) 109
361
0x25, 0x01, # ...Logical Maximum (1) 111
362
0x81, 0x02, # ...Input (Data,Var,Abs) 113
363
0x95, 0x01, # ...Report Count (1) 115
364
0x81, 0x01, # ...Input (Cnst,Arr,Abs) 117
365
0x09, 0x05, # ...Usage (Vendor Usage 0x05) 119
366
0x81, 0x02, # ...Input (Data,Var,Abs) 121
367
0x95, 0x03, # ...Report Count (3) 123
368
0x81, 0x01, # ...Input (Cnst,Arr,Abs) 125
369
0x05, 0x01, # ...Usage Page (Generic Desktop) 127
370
0x09, 0x30, # ...Usage (X) 129
371
0x09, 0x31, # ...Usage (Y) 131
372
0x95, 0x02, # ...Report Count (2) 133
373
0x75, 0x08, # ...Report Size (8) 135
374
0x15, 0x81, # ...Logical Minimum (-127) 137
375
0x25, 0x7f, # ...Logical Maximum (127) 139
376
0x81, 0x06, # ...Input (Data,Var,Rel) 141
377
0xa1, 0x02, # ...Collection (Logical) 143
378
0x85, 0x12, # ....Report ID (18) 145
379
0x09, 0x48, # ....Usage (Resolution Multiplier) 147
380
0x95, 0x01, # ....Report Count (1) 149
381
0x75, 0x02, # ....Report Size (2) 151
382
0x15, 0x00, # ....Logical Minimum (0) 153
383
0x25, 0x01, # ....Logical Maximum (1) 155
384
0x35, 0x01, # ....Physical Minimum (1) 157
385
0x45, 0x04, # ....Physical Maximum (4) 159
386
0xb1, 0x02, # ....Feature (Data,Var,Abs) 161
387
0x35, 0x00, # ....Physical Minimum (0) 163
388
0x45, 0x00, # ....Physical Maximum (0) 165
389
0x75, 0x06, # ....Report Size (6) 167
390
0xb1, 0x01, # ....Feature (Cnst,Arr,Abs) 169
391
0x85, 0x11, # ....Report ID (17) 171
392
0x09, 0x38, # ....Usage (Wheel) 173
393
0x15, 0x81, # ....Logical Minimum (-127) 175
394
0x25, 0x7f, # ....Logical Maximum (127) 177
395
0x75, 0x08, # ....Report Size (8) 179
396
0x81, 0x06, # ....Input (Data,Var,Rel) 181
397
0xc0, # ...End Collection 183
398
0x05, 0x0c, # ...Usage Page (Consumer Devices) 184
399
0x75, 0x08, # ...Report Size (8) 186
400
0x0a, 0x38, 0x02, # ...Usage (AC Pan) 188
401
0x81, 0x06, # ...Input (Data,Var,Rel) 191
402
0xc0, # ..End Collection 193
403
0xc0, # .End Collection 194
404
0xc0, # End Collection 195
405
]
406
# fmt: on
407
408
def __init__(self, rdesc=report_descriptor, name=None, input_info=None):
409
super().__init__(rdesc, name, input_info)
410
self.default_reportID = 0x11
411
412
# Feature Report 12, multiplier Feature value must be set to 0b01,
413
# i.e. 1. We should extract that from the descriptor instead
414
# of hardcoding it here, but meanwhile this will do.
415
self.set_feature_report = [0x12, 0x1]
416
417
def set_report(self, req, rnum, rtype, data):
418
if rtype != self.UHID_FEATURE_REPORT:
419
raise InvalidHIDCommunication(f"Unexpected report type: {rtype}")
420
if rnum != 0x12:
421
raise InvalidHIDCommunication(f"Unexpected report number: {rnum}")
422
423
if data != self.set_feature_report:
424
raise InvalidHIDCommunication(
425
f"Unexpected data: {data}, expected {self.set_feature_report}"
426
)
427
428
self.wheel_multiplier = 4
429
430
return 0
431
432
433
class BadResolutionMultiplierMouse(ResolutionMultiplierMouse):
434
def set_report(self, req, rnum, rtype, data):
435
super().set_report(req, rnum, rtype, data)
436
437
self.wheel_multiplier = 1
438
self.hwheel_multiplier = 1
439
return 32 # EPIPE
440
441
442
class BadReportDescriptorMouse(BaseMouse):
443
"""
444
This "device" was one autogenerated by syzbot. There are a lot of issues in
445
it, and the most problematic is that it declares features that have no
446
size.
447
448
This leads to report->size being set to 0 and can mess up with usbhid
449
internals. Fortunately, uhid merely passes the incoming buffer, without
450
touching it so a buffer of size 0 will be translated to [] without
451
triggering a kernel oops.
452
453
Because the report descriptor is wrong, no input are created, and we need
454
to tweak a little bit the parameters to make it look correct.
455
"""
456
457
# fmt: off
458
report_descriptor = [
459
0x96, 0x01, 0x00, # Report Count (1) 0
460
0x06, 0x01, 0x00, # Usage Page (Generic Desktop) 3
461
# 0x03, 0x00, 0x00, 0x00, 0x00, # Ignored by the kernel somehow
462
0x2a, 0x90, 0xa0, # Usage Maximum (41104) 6
463
0x27, 0x00, 0x00, 0x00, 0x00, # Logical Maximum (0) 9
464
0xb3, 0x81, 0x3e, 0x25, 0x03, # Feature (Cnst,Arr,Abs,Vol) 14
465
0x1b, 0xdd, 0xe8, 0x40, 0x50, # Usage Minimum (1346431197) 19
466
0x3b, 0x5d, 0x8c, 0x3d, 0xda, # Designator Index 24
467
]
468
# fmt: on
469
470
def __init__(
471
self, rdesc=report_descriptor, name=None, input_info=(3, 0x045E, 0x07DA)
472
):
473
super().__init__(rdesc, name, input_info)
474
self.high_resolution_report_called = False
475
476
def get_evdev(self, application=None):
477
assert self._input_nodes is None
478
return (
479
"Ok" # should be a list or None, but both would fail, so abusing the system
480
)
481
482
def next_sync_events(self, application=None):
483
# there are no evdev nodes, so no events
484
return []
485
486
def is_ready(self):
487
# we wait for the SET_REPORT command to come
488
return self.high_resolution_report_called
489
490
def set_report(self, req, rnum, rtype, data):
491
if rtype != self.UHID_FEATURE_REPORT:
492
raise InvalidHIDCommunication(f"Unexpected report type: {rtype}")
493
if rnum != 0x0:
494
raise InvalidHIDCommunication(f"Unexpected report number: {rnum}")
495
496
if len(data) != 1:
497
raise InvalidHIDCommunication(f"Unexpected data: {data}, expected '[0]'")
498
499
self.high_resolution_report_called = True
500
501
return 0
502
503
504
class ResolutionMultiplierHWheelMouse(TwoWheelMouse):
505
# fmt: off
506
report_descriptor = [
507
0x05, 0x01, # Usage Page (Generic Desktop) 0
508
0x09, 0x02, # Usage (Mouse) 2
509
0xa1, 0x01, # Collection (Application) 4
510
0x05, 0x01, # .Usage Page (Generic Desktop) 6
511
0x09, 0x02, # .Usage (Mouse) 8
512
0xa1, 0x02, # .Collection (Logical) 10
513
0x85, 0x1a, # ..Report ID (26) 12
514
0x09, 0x01, # ..Usage (Pointer) 14
515
0xa1, 0x00, # ..Collection (Physical) 16
516
0x05, 0x09, # ...Usage Page (Button) 18
517
0x19, 0x01, # ...Usage Minimum (1) 20
518
0x29, 0x05, # ...Usage Maximum (5) 22
519
0x95, 0x05, # ...Report Count (5) 24
520
0x75, 0x01, # ...Report Size (1) 26
521
0x15, 0x00, # ...Logical Minimum (0) 28
522
0x25, 0x01, # ...Logical Maximum (1) 30
523
0x81, 0x02, # ...Input (Data,Var,Abs) 32
524
0x75, 0x03, # ...Report Size (3) 34
525
0x95, 0x01, # ...Report Count (1) 36
526
0x81, 0x01, # ...Input (Cnst,Arr,Abs) 38
527
0x05, 0x01, # ...Usage Page (Generic Desktop) 40
528
0x09, 0x30, # ...Usage (X) 42
529
0x09, 0x31, # ...Usage (Y) 44
530
0x95, 0x02, # ...Report Count (2) 46
531
0x75, 0x10, # ...Report Size (16) 48
532
0x16, 0x01, 0x80, # ...Logical Minimum (-32767) 50
533
0x26, 0xff, 0x7f, # ...Logical Maximum (32767) 53
534
0x81, 0x06, # ...Input (Data,Var,Rel) 56
535
0xa1, 0x02, # ...Collection (Logical) 58
536
0x85, 0x12, # ....Report ID (18) 60
537
0x09, 0x48, # ....Usage (Resolution Multiplier) 62
538
0x95, 0x01, # ....Report Count (1) 64
539
0x75, 0x02, # ....Report Size (2) 66
540
0x15, 0x00, # ....Logical Minimum (0) 68
541
0x25, 0x01, # ....Logical Maximum (1) 70
542
0x35, 0x01, # ....Physical Minimum (1) 72
543
0x45, 0x0c, # ....Physical Maximum (12) 74
544
0xb1, 0x02, # ....Feature (Data,Var,Abs) 76
545
0x85, 0x1a, # ....Report ID (26) 78
546
0x09, 0x38, # ....Usage (Wheel) 80
547
0x35, 0x00, # ....Physical Minimum (0) 82
548
0x45, 0x00, # ....Physical Maximum (0) 84
549
0x95, 0x01, # ....Report Count (1) 86
550
0x75, 0x10, # ....Report Size (16) 88
551
0x16, 0x01, 0x80, # ....Logical Minimum (-32767) 90
552
0x26, 0xff, 0x7f, # ....Logical Maximum (32767) 93
553
0x81, 0x06, # ....Input (Data,Var,Rel) 96
554
0xc0, # ...End Collection 98
555
0xa1, 0x02, # ...Collection (Logical) 99
556
0x85, 0x12, # ....Report ID (18) 101
557
0x09, 0x48, # ....Usage (Resolution Multiplier) 103
558
0x75, 0x02, # ....Report Size (2) 105
559
0x15, 0x00, # ....Logical Minimum (0) 107
560
0x25, 0x01, # ....Logical Maximum (1) 109
561
0x35, 0x01, # ....Physical Minimum (1) 111
562
0x45, 0x0c, # ....Physical Maximum (12) 113
563
0xb1, 0x02, # ....Feature (Data,Var,Abs) 115
564
0x35, 0x00, # ....Physical Minimum (0) 117
565
0x45, 0x00, # ....Physical Maximum (0) 119
566
0x75, 0x04, # ....Report Size (4) 121
567
0xb1, 0x01, # ....Feature (Cnst,Arr,Abs) 123
568
0x85, 0x1a, # ....Report ID (26) 125
569
0x05, 0x0c, # ....Usage Page (Consumer Devices) 127
570
0x95, 0x01, # ....Report Count (1) 129
571
0x75, 0x10, # ....Report Size (16) 131
572
0x16, 0x01, 0x80, # ....Logical Minimum (-32767) 133
573
0x26, 0xff, 0x7f, # ....Logical Maximum (32767) 136
574
0x0a, 0x38, 0x02, # ....Usage (AC Pan) 139
575
0x81, 0x06, # ....Input (Data,Var,Rel) 142
576
0xc0, # ...End Collection 144
577
0xc0, # ..End Collection 145
578
0xc0, # .End Collection 146
579
0xc0, # End Collection 147
580
]
581
# fmt: on
582
583
def __init__(self, rdesc=report_descriptor, name=None, input_info=None):
584
super().__init__(rdesc, name, input_info)
585
self.default_reportID = 0x1A
586
587
# Feature Report 12, multiplier Feature value must be set to 0b0101,
588
# i.e. 5. We should extract that from the descriptor instead
589
# of hardcoding it here, but meanwhile this will do.
590
self.set_feature_report = [0x12, 0x5]
591
592
def set_report(self, req, rnum, rtype, data):
593
super().set_report(req, rnum, rtype, data)
594
595
self.wheel_multiplier = 12
596
self.hwheel_multiplier = 12
597
598
return 0
599
600
601
class BaseTest:
602
class TestMouse(base.BaseTestCase.TestUhid):
603
def test_buttons(self):
604
"""check for button reliability."""
605
uhdev = self.uhdev
606
evdev = uhdev.get_evdev()
607
syn_event = self.syn_event
608
609
r = uhdev.event(0, 0, (None, True, None))
610
expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 1)
611
events = uhdev.next_sync_events()
612
self.debug_reports(r, uhdev, events)
613
self.assertInputEventsIn((syn_event, expected_event), events)
614
assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 1
615
616
r = uhdev.event(0, 0, (None, False, None))
617
expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 0)
618
events = uhdev.next_sync_events()
619
self.debug_reports(r, uhdev, events)
620
self.assertInputEventsIn((syn_event, expected_event), events)
621
assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 0
622
623
r = uhdev.event(0, 0, (None, None, True))
624
expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_MIDDLE, 1)
625
events = uhdev.next_sync_events()
626
self.debug_reports(r, uhdev, events)
627
self.assertInputEventsIn((syn_event, expected_event), events)
628
assert evdev.value[libevdev.EV_KEY.BTN_MIDDLE] == 1
629
630
r = uhdev.event(0, 0, (None, None, False))
631
expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_MIDDLE, 0)
632
events = uhdev.next_sync_events()
633
self.debug_reports(r, uhdev, events)
634
self.assertInputEventsIn((syn_event, expected_event), events)
635
assert evdev.value[libevdev.EV_KEY.BTN_MIDDLE] == 0
636
637
r = uhdev.event(0, 0, (True, None, None))
638
expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 1)
639
events = uhdev.next_sync_events()
640
self.debug_reports(r, uhdev, events)
641
self.assertInputEventsIn((syn_event, expected_event), events)
642
assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 1
643
644
r = uhdev.event(0, 0, (False, None, None))
645
expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 0)
646
events = uhdev.next_sync_events()
647
self.debug_reports(r, uhdev, events)
648
self.assertInputEventsIn((syn_event, expected_event), events)
649
assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 0
650
651
r = uhdev.event(0, 0, (True, True, None))
652
expected_event0 = libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 1)
653
expected_event1 = libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 1)
654
events = uhdev.next_sync_events()
655
self.debug_reports(r, uhdev, events)
656
self.assertInputEventsIn(
657
(syn_event, expected_event0, expected_event1), events
658
)
659
assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 1
660
assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 1
661
662
r = uhdev.event(0, 0, (False, None, None))
663
expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 0)
664
events = uhdev.next_sync_events()
665
self.debug_reports(r, uhdev, events)
666
self.assertInputEventsIn((syn_event, expected_event), events)
667
assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 1
668
assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 0
669
670
r = uhdev.event(0, 0, (None, False, None))
671
expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 0)
672
events = uhdev.next_sync_events()
673
self.debug_reports(r, uhdev, events)
674
self.assertInputEventsIn((syn_event, expected_event), events)
675
assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 0
676
assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 0
677
678
def test_relative(self):
679
"""Check for relative events."""
680
uhdev = self.uhdev
681
682
syn_event = self.syn_event
683
684
r = uhdev.event(0, -1)
685
expected_event = libevdev.InputEvent(libevdev.EV_REL.REL_Y, -1)
686
events = uhdev.next_sync_events()
687
self.debug_reports(r, uhdev, events)
688
self.assertInputEvents((syn_event, expected_event), events)
689
690
r = uhdev.event(1, 0)
691
expected_event = libevdev.InputEvent(libevdev.EV_REL.REL_X, 1)
692
events = uhdev.next_sync_events()
693
self.debug_reports(r, uhdev, events)
694
self.assertInputEvents((syn_event, expected_event), events)
695
696
r = uhdev.event(-1, 2)
697
expected_event0 = libevdev.InputEvent(libevdev.EV_REL.REL_X, -1)
698
expected_event1 = libevdev.InputEvent(libevdev.EV_REL.REL_Y, 2)
699
events = uhdev.next_sync_events()
700
self.debug_reports(r, uhdev, events)
701
self.assertInputEvents(
702
(syn_event, expected_event0, expected_event1), events
703
)
704
705
706
class TestSimpleMouse(BaseTest.TestMouse):
707
def create_device(self):
708
return ButtonMouse()
709
710
def test_rdesc(self):
711
"""Check that the testsuite actually manages to format the
712
reports according to the report descriptors.
713
No kernel device is used here"""
714
uhdev = self.uhdev
715
716
event = (0, 0, (None, None, None))
717
assert uhdev.fake_report(*event) == uhdev.create_report(*event)
718
719
event = (0, 0, (None, True, None))
720
assert uhdev.fake_report(*event) == uhdev.create_report(*event)
721
722
event = (0, 0, (True, True, None))
723
assert uhdev.fake_report(*event) == uhdev.create_report(*event)
724
725
event = (0, 0, (False, False, False))
726
assert uhdev.fake_report(*event) == uhdev.create_report(*event)
727
728
event = (1, 0, (True, False, True))
729
assert uhdev.fake_report(*event) == uhdev.create_report(*event)
730
731
event = (-1, 0, (True, False, True))
732
assert uhdev.fake_report(*event) == uhdev.create_report(*event)
733
734
event = (-5, 5, (True, False, True))
735
assert uhdev.fake_report(*event) == uhdev.create_report(*event)
736
737
event = (-127, 127, (True, False, True))
738
assert uhdev.fake_report(*event) == uhdev.create_report(*event)
739
740
event = (0, -128, (True, False, True))
741
with pytest.raises(hidtools.hid.RangeError):
742
uhdev.create_report(*event)
743
744
745
class TestWheelMouse(BaseTest.TestMouse):
746
def create_device(self):
747
return WheelMouse()
748
749
def is_wheel_highres(self, uhdev):
750
evdev = uhdev.get_evdev()
751
assert evdev.has(libevdev.EV_REL.REL_WHEEL)
752
return evdev.has(libevdev.EV_REL.REL_WHEEL_HI_RES)
753
754
def test_wheel(self):
755
uhdev = self.uhdev
756
757
# check if the kernel is high res wheel compatible
758
high_res_wheel = self.is_wheel_highres(uhdev)
759
760
syn_event = self.syn_event
761
# The Resolution Multiplier is applied to the HID reports, so we
762
# need to pre-multiply too.
763
mult = uhdev.wheel_multiplier
764
765
r = uhdev.event(0, 0, wheels=1 * mult)
766
expected = [syn_event]
767
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, 1))
768
if high_res_wheel:
769
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, 120))
770
events = uhdev.next_sync_events()
771
self.debug_reports(r, uhdev, events)
772
self.assertInputEvents(expected, events)
773
774
r = uhdev.event(0, 0, wheels=-1 * mult)
775
expected = [syn_event]
776
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, -1))
777
if high_res_wheel:
778
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, -120))
779
events = uhdev.next_sync_events()
780
self.debug_reports(r, uhdev, events)
781
self.assertInputEvents(expected, events)
782
783
r = uhdev.event(-1, 2, wheels=3 * mult)
784
expected = [syn_event]
785
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, -1))
786
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, 2))
787
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, 3))
788
if high_res_wheel:
789
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, 360))
790
events = uhdev.next_sync_events()
791
self.debug_reports(r, uhdev, events)
792
self.assertInputEvents(expected, events)
793
794
795
class TestTwoWheelMouse(TestWheelMouse):
796
def create_device(self):
797
return TwoWheelMouse()
798
799
def is_hwheel_highres(self, uhdev):
800
evdev = uhdev.get_evdev()
801
assert evdev.has(libevdev.EV_REL.REL_HWHEEL)
802
return evdev.has(libevdev.EV_REL.REL_HWHEEL_HI_RES)
803
804
def test_ac_pan(self):
805
uhdev = self.uhdev
806
807
# check if the kernel is high res wheel compatible
808
high_res_wheel = self.is_wheel_highres(uhdev)
809
high_res_hwheel = self.is_hwheel_highres(uhdev)
810
assert high_res_wheel == high_res_hwheel
811
812
syn_event = self.syn_event
813
# The Resolution Multiplier is applied to the HID reports, so we
814
# need to pre-multiply too.
815
hmult = uhdev.hwheel_multiplier
816
vmult = uhdev.wheel_multiplier
817
818
r = uhdev.event(0, 0, wheels=(0, 1 * hmult))
819
expected = [syn_event]
820
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, 1))
821
if high_res_hwheel:
822
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 120))
823
events = uhdev.next_sync_events()
824
self.debug_reports(r, uhdev, events)
825
self.assertInputEvents(expected, events)
826
827
r = uhdev.event(0, 0, wheels=(0, -1 * hmult))
828
expected = [syn_event]
829
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, -1))
830
if high_res_hwheel:
831
expected.append(
832
libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, -120)
833
)
834
events = uhdev.next_sync_events()
835
self.debug_reports(r, uhdev, events)
836
self.assertInputEvents(expected, events)
837
838
r = uhdev.event(-1, 2, wheels=(0, 3 * hmult))
839
expected = [syn_event]
840
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, -1))
841
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, 2))
842
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, 3))
843
if high_res_hwheel:
844
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 360))
845
events = uhdev.next_sync_events()
846
self.debug_reports(r, uhdev, events)
847
self.assertInputEvents(expected, events)
848
849
r = uhdev.event(-1, 2, wheels=(-3 * vmult, 4 * hmult))
850
expected = [syn_event]
851
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, -1))
852
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, 2))
853
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, -3))
854
if high_res_wheel:
855
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, -360))
856
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, 4))
857
if high_res_wheel:
858
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 480))
859
events = uhdev.next_sync_events()
860
self.debug_reports(r, uhdev, events)
861
self.assertInputEvents(expected, events)
862
863
864
class TestResolutionMultiplierMouse(TestTwoWheelMouse):
865
def create_device(self):
866
return ResolutionMultiplierMouse()
867
868
def is_wheel_highres(self, uhdev):
869
high_res = super().is_wheel_highres(uhdev)
870
871
if not high_res:
872
# the kernel doesn't seem to support the high res wheel mice,
873
# make sure we haven't triggered the feature
874
assert uhdev.wheel_multiplier == 1
875
876
return high_res
877
878
def test_resolution_multiplier_wheel(self):
879
uhdev = self.uhdev
880
881
if not self.is_wheel_highres(uhdev):
882
pytest.skip("Kernel not compatible, we can not trigger the conditions")
883
884
assert uhdev.wheel_multiplier > 1
885
assert 120 % uhdev.wheel_multiplier == 0
886
887
def test_wheel_with_multiplier(self):
888
uhdev = self.uhdev
889
890
if not self.is_wheel_highres(uhdev):
891
pytest.skip("Kernel not compatible, we can not trigger the conditions")
892
893
assert uhdev.wheel_multiplier > 1
894
895
syn_event = self.syn_event
896
mult = uhdev.wheel_multiplier
897
898
r = uhdev.event(0, 0, wheels=1)
899
expected = [syn_event]
900
expected.append(
901
libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, 120 / mult)
902
)
903
events = uhdev.next_sync_events()
904
self.debug_reports(r, uhdev, events)
905
self.assertInputEvents(expected, events)
906
907
r = uhdev.event(0, 0, wheels=-1)
908
expected = [syn_event]
909
expected.append(
910
libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, -120 / mult)
911
)
912
events = uhdev.next_sync_events()
913
self.debug_reports(r, uhdev, events)
914
self.assertInputEvents(expected, events)
915
916
expected = [syn_event]
917
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, 1))
918
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, -2))
919
expected.append(
920
libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, 120 / mult)
921
)
922
923
for _ in range(mult - 1):
924
r = uhdev.event(1, -2, wheels=1)
925
events = uhdev.next_sync_events()
926
self.debug_reports(r, uhdev, events)
927
self.assertInputEvents(expected, events)
928
929
r = uhdev.event(1, -2, wheels=1)
930
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, 1))
931
events = uhdev.next_sync_events()
932
self.debug_reports(r, uhdev, events)
933
self.assertInputEvents(expected, events)
934
935
936
class TestBadResolutionMultiplierMouse(TestTwoWheelMouse):
937
def create_device(self):
938
return BadResolutionMultiplierMouse()
939
940
def is_wheel_highres(self, uhdev):
941
high_res = super().is_wheel_highres(uhdev)
942
943
assert uhdev.wheel_multiplier == 1
944
945
return high_res
946
947
def test_resolution_multiplier_wheel(self):
948
uhdev = self.uhdev
949
950
assert uhdev.wheel_multiplier == 1
951
952
953
class TestResolutionMultiplierHWheelMouse(TestResolutionMultiplierMouse):
954
def create_device(self):
955
return ResolutionMultiplierHWheelMouse()
956
957
def is_hwheel_highres(self, uhdev):
958
high_res = super().is_hwheel_highres(uhdev)
959
960
if not high_res:
961
# the kernel doesn't seem to support the high res wheel mice,
962
# make sure we haven't triggered the feature
963
assert uhdev.hwheel_multiplier == 1
964
965
return high_res
966
967
def test_resolution_multiplier_ac_pan(self):
968
uhdev = self.uhdev
969
970
if not self.is_hwheel_highres(uhdev):
971
pytest.skip("Kernel not compatible, we can not trigger the conditions")
972
973
assert uhdev.hwheel_multiplier > 1
974
assert 120 % uhdev.hwheel_multiplier == 0
975
976
def test_ac_pan_with_multiplier(self):
977
uhdev = self.uhdev
978
979
if not self.is_hwheel_highres(uhdev):
980
pytest.skip("Kernel not compatible, we can not trigger the conditions")
981
982
assert uhdev.hwheel_multiplier > 1
983
984
syn_event = self.syn_event
985
hmult = uhdev.hwheel_multiplier
986
987
r = uhdev.event(0, 0, wheels=(0, 1))
988
expected = [syn_event]
989
expected.append(
990
libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 120 / hmult)
991
)
992
events = uhdev.next_sync_events()
993
self.debug_reports(r, uhdev, events)
994
self.assertInputEvents(expected, events)
995
996
r = uhdev.event(0, 0, wheels=(0, -1))
997
expected = [syn_event]
998
expected.append(
999
libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, -120 / hmult)
1000
)
1001
events = uhdev.next_sync_events()
1002
self.debug_reports(r, uhdev, events)
1003
self.assertInputEvents(expected, events)
1004
1005
expected = [syn_event]
1006
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, 1))
1007
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, -2))
1008
expected.append(
1009
libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 120 / hmult)
1010
)
1011
1012
for _ in range(hmult - 1):
1013
r = uhdev.event(1, -2, wheels=(0, 1))
1014
events = uhdev.next_sync_events()
1015
self.debug_reports(r, uhdev, events)
1016
self.assertInputEvents(expected, events)
1017
1018
r = uhdev.event(1, -2, wheels=(0, 1))
1019
expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, 1))
1020
events = uhdev.next_sync_events()
1021
self.debug_reports(r, uhdev, events)
1022
self.assertInputEvents(expected, events)
1023
1024
1025
class TestMiMouse(TestWheelMouse):
1026
def create_device(self):
1027
return MIDongleMIWirelessMouse()
1028
1029
def assertInputEvents(self, expected_events, effective_events):
1030
# Buttons and x/y are spread over two HID reports, so we can get two
1031
# event frames for this device.
1032
remaining = self.assertInputEventsIn(expected_events, effective_events)
1033
try:
1034
remaining.remove(libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT, 0))
1035
except ValueError:
1036
# If there's no SYN_REPORT in the list, continue and let the
1037
# assert below print out the real error
1038
pass
1039
assert remaining == []
1040
1041
1042
class TestBadReportDescriptorMouse(base.BaseTestCase.TestUhid):
1043
def create_device(self):
1044
return BadReportDescriptorMouse()
1045
1046
def assertName(self, uhdev):
1047
pass
1048
1049