Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/testing/selftests/hid/tests/test_tablet.py
48890 views
1
#!/bin/env python3
2
# SPDX-License-Identifier: GPL-2.0
3
# -*- coding: utf-8 -*-
4
#
5
# Copyright (c) 2021 Benjamin Tissoires <[email protected]>
6
# Copyright (c) 2021 Red Hat, Inc.
7
#
8
9
from . import base
10
import copy
11
from enum import Enum
12
from hidtools.util import BusType
13
from .base import HidBpf
14
import libevdev
15
import logging
16
import pytest
17
from typing import Dict, List, Optional, Tuple
18
19
logger = logging.getLogger("hidtools.test.tablet")
20
21
22
class BtnTouch(Enum):
23
"""Represents whether the BTN_TOUCH event is set to True or False"""
24
25
DOWN = True
26
UP = False
27
28
29
class ToolType(Enum):
30
PEN = libevdev.EV_KEY.BTN_TOOL_PEN
31
RUBBER = libevdev.EV_KEY.BTN_TOOL_RUBBER
32
33
34
class BtnPressed(Enum):
35
"""Represents whether a button is pressed on the stylus"""
36
37
PRIMARY_PRESSED = libevdev.EV_KEY.BTN_STYLUS
38
SECONDARY_PRESSED = libevdev.EV_KEY.BTN_STYLUS2
39
THIRD_PRESSED = libevdev.EV_KEY.BTN_STYLUS3
40
41
42
class PenState(Enum):
43
"""Pen states according to Microsoft reference:
44
https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states
45
46
We extend it with the various buttons when we need to check them.
47
"""
48
49
PEN_IS_OUT_OF_RANGE = BtnTouch.UP, None, False
50
PEN_IS_IN_RANGE = BtnTouch.UP, ToolType.PEN, False
51
PEN_IS_IN_RANGE_WITH_BUTTON = BtnTouch.UP, ToolType.PEN, True
52
PEN_IS_IN_CONTACT = BtnTouch.DOWN, ToolType.PEN, False
53
PEN_IS_IN_CONTACT_WITH_BUTTON = BtnTouch.DOWN, ToolType.PEN, True
54
PEN_IS_IN_RANGE_WITH_ERASING_INTENT = BtnTouch.UP, ToolType.RUBBER, False
55
PEN_IS_IN_RANGE_WITH_ERASING_INTENT_WITH_BUTTON = BtnTouch.UP, ToolType.RUBBER, True
56
PEN_IS_ERASING = BtnTouch.DOWN, ToolType.RUBBER, False
57
PEN_IS_ERASING_WITH_BUTTON = BtnTouch.DOWN, ToolType.RUBBER, True
58
59
def __init__(
60
self, touch: BtnTouch, tool: Optional[ToolType], button: Optional[bool]
61
):
62
self.touch = touch # type: ignore
63
self.tool = tool # type: ignore
64
self.button = button # type: ignore
65
66
@classmethod
67
def from_evdev(cls, evdev, test_button) -> "PenState":
68
touch = BtnTouch(evdev.value[libevdev.EV_KEY.BTN_TOUCH])
69
tool = None
70
button = False
71
if (
72
evdev.value[libevdev.EV_KEY.BTN_TOOL_RUBBER]
73
and not evdev.value[libevdev.EV_KEY.BTN_TOOL_PEN]
74
):
75
tool = ToolType(libevdev.EV_KEY.BTN_TOOL_RUBBER)
76
elif (
77
evdev.value[libevdev.EV_KEY.BTN_TOOL_PEN]
78
and not evdev.value[libevdev.EV_KEY.BTN_TOOL_RUBBER]
79
):
80
tool = ToolType(libevdev.EV_KEY.BTN_TOOL_PEN)
81
elif (
82
evdev.value[libevdev.EV_KEY.BTN_TOOL_PEN]
83
or evdev.value[libevdev.EV_KEY.BTN_TOOL_RUBBER]
84
):
85
raise ValueError("2 tools are not allowed")
86
87
# we take only the provided button into account
88
if test_button is not None:
89
button = bool(evdev.value[test_button.value])
90
91
# the kernel tends to insert an EV_SYN once removing the tool, so
92
# the button will be released after
93
if tool is None:
94
button = False
95
96
return cls((touch, tool, button)) # type: ignore
97
98
def apply(
99
self, events: List[libevdev.InputEvent], strict: bool, test_button: BtnPressed
100
) -> "PenState":
101
if libevdev.EV_SYN.SYN_REPORT in events:
102
raise ValueError("EV_SYN is in the event sequence")
103
touch = self.touch
104
touch_found = False
105
tool = self.tool
106
tool_found = False
107
button = self.button
108
button_found = False
109
110
for ev in events:
111
if ev == libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH):
112
if touch_found:
113
raise ValueError(f"duplicated BTN_TOUCH in {events}")
114
touch_found = True
115
touch = BtnTouch(ev.value)
116
elif ev in (
117
libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN),
118
libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_RUBBER),
119
):
120
if tool_found:
121
raise ValueError(f"duplicated BTN_TOOL_* in {events}")
122
tool_found = True
123
tool = ToolType(ev.code) if ev.value else None
124
elif test_button is not None and ev in (test_button.value,):
125
if button_found:
126
raise ValueError(f"duplicated BTN_STYLUS* in {events}")
127
button_found = True
128
button = bool(ev.value)
129
130
# the kernel tends to insert an EV_SYN once removing the tool, so
131
# the button will be released after
132
if tool is None:
133
button = False
134
135
new_state = PenState((touch, tool, button)) # type: ignore
136
if strict:
137
assert (
138
new_state in self.valid_transitions()
139
), f"moving from {self} to {new_state} is forbidden"
140
else:
141
assert (
142
new_state in self.historically_tolerated_transitions()
143
), f"moving from {self} to {new_state} is forbidden"
144
145
return new_state
146
147
def valid_transitions(self) -> Tuple["PenState", ...]:
148
"""Following the state machine in the URL above.
149
150
Note that those transitions are from the evdev point of view, not HID"""
151
if self == PenState.PEN_IS_OUT_OF_RANGE:
152
return (
153
PenState.PEN_IS_OUT_OF_RANGE,
154
PenState.PEN_IS_IN_RANGE,
155
PenState.PEN_IS_IN_RANGE_WITH_BUTTON,
156
PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
157
PenState.PEN_IS_IN_CONTACT,
158
PenState.PEN_IS_IN_CONTACT_WITH_BUTTON,
159
PenState.PEN_IS_ERASING,
160
)
161
162
if self == PenState.PEN_IS_IN_RANGE:
163
return (
164
PenState.PEN_IS_IN_RANGE,
165
PenState.PEN_IS_IN_RANGE_WITH_BUTTON,
166
PenState.PEN_IS_OUT_OF_RANGE,
167
PenState.PEN_IS_IN_CONTACT,
168
)
169
170
if self == PenState.PEN_IS_IN_CONTACT:
171
return (
172
PenState.PEN_IS_IN_CONTACT,
173
PenState.PEN_IS_IN_CONTACT_WITH_BUTTON,
174
PenState.PEN_IS_IN_RANGE,
175
)
176
177
if self == PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT:
178
return (
179
PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
180
PenState.PEN_IS_OUT_OF_RANGE,
181
PenState.PEN_IS_ERASING,
182
)
183
184
if self == PenState.PEN_IS_ERASING:
185
return (
186
PenState.PEN_IS_ERASING,
187
PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
188
)
189
190
if self == PenState.PEN_IS_IN_RANGE_WITH_BUTTON:
191
return (
192
PenState.PEN_IS_IN_RANGE_WITH_BUTTON,
193
PenState.PEN_IS_IN_RANGE,
194
PenState.PEN_IS_OUT_OF_RANGE,
195
PenState.PEN_IS_IN_CONTACT_WITH_BUTTON,
196
)
197
198
if self == PenState.PEN_IS_IN_CONTACT_WITH_BUTTON:
199
return (
200
PenState.PEN_IS_IN_CONTACT_WITH_BUTTON,
201
PenState.PEN_IS_IN_CONTACT,
202
PenState.PEN_IS_IN_RANGE_WITH_BUTTON,
203
)
204
205
return tuple()
206
207
def historically_tolerated_transitions(self) -> Tuple["PenState", ...]:
208
"""Following the state machine in the URL above, with a couple of addition
209
for skipping the in-range state, due to historical reasons.
210
211
Note that those transitions are from the evdev point of view, not HID"""
212
if self == PenState.PEN_IS_OUT_OF_RANGE:
213
return (
214
PenState.PEN_IS_OUT_OF_RANGE,
215
PenState.PEN_IS_IN_RANGE,
216
PenState.PEN_IS_IN_RANGE_WITH_BUTTON,
217
PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
218
PenState.PEN_IS_IN_CONTACT,
219
PenState.PEN_IS_IN_CONTACT_WITH_BUTTON,
220
PenState.PEN_IS_ERASING,
221
)
222
223
if self == PenState.PEN_IS_IN_RANGE:
224
return (
225
PenState.PEN_IS_IN_RANGE,
226
PenState.PEN_IS_IN_RANGE_WITH_BUTTON,
227
PenState.PEN_IS_OUT_OF_RANGE,
228
PenState.PEN_IS_IN_CONTACT,
229
)
230
231
if self == PenState.PEN_IS_IN_CONTACT:
232
return (
233
PenState.PEN_IS_IN_CONTACT,
234
PenState.PEN_IS_IN_CONTACT_WITH_BUTTON,
235
PenState.PEN_IS_IN_RANGE,
236
PenState.PEN_IS_OUT_OF_RANGE,
237
)
238
239
if self == PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT:
240
return (
241
PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
242
PenState.PEN_IS_OUT_OF_RANGE,
243
PenState.PEN_IS_ERASING,
244
)
245
246
if self == PenState.PEN_IS_ERASING:
247
return (
248
PenState.PEN_IS_ERASING,
249
PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
250
PenState.PEN_IS_OUT_OF_RANGE,
251
)
252
253
if self == PenState.PEN_IS_IN_RANGE_WITH_BUTTON:
254
return (
255
PenState.PEN_IS_IN_RANGE_WITH_BUTTON,
256
PenState.PEN_IS_IN_RANGE,
257
PenState.PEN_IS_OUT_OF_RANGE,
258
PenState.PEN_IS_IN_CONTACT_WITH_BUTTON,
259
)
260
261
if self == PenState.PEN_IS_IN_CONTACT_WITH_BUTTON:
262
return (
263
PenState.PEN_IS_IN_CONTACT_WITH_BUTTON,
264
PenState.PEN_IS_IN_CONTACT,
265
PenState.PEN_IS_IN_RANGE_WITH_BUTTON,
266
PenState.PEN_IS_OUT_OF_RANGE,
267
)
268
269
return tuple()
270
271
@staticmethod
272
def legal_transitions() -> Dict[str, Tuple["PenState", ...]]:
273
"""This is the first half of the Windows Pen Implementation state machine:
274
we don't have Invert nor Erase bits, so just move in/out-of-range or proximity.
275
https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states
276
"""
277
return {
278
"in-range": (PenState.PEN_IS_IN_RANGE,),
279
"in-range -> out-of-range": (
280
PenState.PEN_IS_IN_RANGE,
281
PenState.PEN_IS_OUT_OF_RANGE,
282
),
283
"in-range -> touch": (PenState.PEN_IS_IN_RANGE, PenState.PEN_IS_IN_CONTACT),
284
"in-range -> touch -> release": (
285
PenState.PEN_IS_IN_RANGE,
286
PenState.PEN_IS_IN_CONTACT,
287
PenState.PEN_IS_IN_RANGE,
288
),
289
"in-range -> touch -> release -> out-of-range": (
290
PenState.PEN_IS_IN_RANGE,
291
PenState.PEN_IS_IN_CONTACT,
292
PenState.PEN_IS_IN_RANGE,
293
PenState.PEN_IS_OUT_OF_RANGE,
294
),
295
}
296
297
@staticmethod
298
def legal_transitions_with_invert() -> Dict[str, Tuple["PenState", ...]]:
299
"""This is the second half of the Windows Pen Implementation state machine:
300
we now have Invert and Erase bits, so move in/out or proximity with the intend
301
to erase.
302
https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states
303
"""
304
return {
305
"hover-erasing": (PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,),
306
"hover-erasing -> out-of-range": (
307
PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
308
PenState.PEN_IS_OUT_OF_RANGE,
309
),
310
"hover-erasing -> erase": (
311
PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
312
PenState.PEN_IS_ERASING,
313
),
314
"hover-erasing -> erase -> release": (
315
PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
316
PenState.PEN_IS_ERASING,
317
PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
318
),
319
"hover-erasing -> erase -> release -> out-of-range": (
320
PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
321
PenState.PEN_IS_ERASING,
322
PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
323
PenState.PEN_IS_OUT_OF_RANGE,
324
),
325
"hover-erasing -> in-range": (
326
PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
327
PenState.PEN_IS_IN_RANGE,
328
),
329
"in-range -> hover-erasing": (
330
PenState.PEN_IS_IN_RANGE,
331
PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
332
),
333
}
334
335
@staticmethod
336
def legal_transitions_with_button() -> Dict[str, Tuple["PenState", ...]]:
337
"""We revisit the Windows Pen Implementation state machine:
338
we now have a button.
339
"""
340
return {
341
"hover-button": (PenState.PEN_IS_IN_RANGE_WITH_BUTTON,),
342
"hover-button -> out-of-range": (
343
PenState.PEN_IS_IN_RANGE_WITH_BUTTON,
344
PenState.PEN_IS_OUT_OF_RANGE,
345
),
346
"in-range -> button-press": (
347
PenState.PEN_IS_IN_RANGE,
348
PenState.PEN_IS_IN_RANGE_WITH_BUTTON,
349
),
350
"in-range -> button-press -> button-release": (
351
PenState.PEN_IS_IN_RANGE,
352
PenState.PEN_IS_IN_RANGE_WITH_BUTTON,
353
PenState.PEN_IS_IN_RANGE,
354
),
355
"in-range -> touch -> button-press -> button-release": (
356
PenState.PEN_IS_IN_RANGE,
357
PenState.PEN_IS_IN_CONTACT,
358
PenState.PEN_IS_IN_CONTACT_WITH_BUTTON,
359
PenState.PEN_IS_IN_CONTACT,
360
),
361
"in-range -> touch -> button-press -> release -> button-release": (
362
PenState.PEN_IS_IN_RANGE,
363
PenState.PEN_IS_IN_CONTACT,
364
PenState.PEN_IS_IN_CONTACT_WITH_BUTTON,
365
PenState.PEN_IS_IN_RANGE_WITH_BUTTON,
366
PenState.PEN_IS_IN_RANGE,
367
),
368
"in-range -> button-press -> touch -> release -> button-release": (
369
PenState.PEN_IS_IN_RANGE,
370
PenState.PEN_IS_IN_RANGE_WITH_BUTTON,
371
PenState.PEN_IS_IN_CONTACT_WITH_BUTTON,
372
PenState.PEN_IS_IN_RANGE_WITH_BUTTON,
373
PenState.PEN_IS_IN_RANGE,
374
),
375
"in-range -> button-press -> touch -> button-release -> release": (
376
PenState.PEN_IS_IN_RANGE,
377
PenState.PEN_IS_IN_RANGE_WITH_BUTTON,
378
PenState.PEN_IS_IN_CONTACT_WITH_BUTTON,
379
PenState.PEN_IS_IN_CONTACT,
380
PenState.PEN_IS_IN_RANGE,
381
),
382
}
383
384
@staticmethod
385
def tolerated_transitions() -> Dict[str, Tuple["PenState", ...]]:
386
"""This is not adhering to the Windows Pen Implementation state machine
387
but we should expect the kernel to behave properly, mostly for historical
388
reasons."""
389
return {
390
"direct-in-contact": (PenState.PEN_IS_IN_CONTACT,),
391
"direct-in-contact -> out-of-range": (
392
PenState.PEN_IS_IN_CONTACT,
393
PenState.PEN_IS_OUT_OF_RANGE,
394
),
395
}
396
397
@staticmethod
398
def tolerated_transitions_with_invert() -> Dict[str, Tuple["PenState", ...]]:
399
"""This is the second half of the Windows Pen Implementation state machine:
400
we now have Invert and Erase bits, so move in/out or proximity with the intend
401
to erase.
402
https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states
403
"""
404
return {
405
"direct-erase": (PenState.PEN_IS_ERASING,),
406
"direct-erase -> out-of-range": (
407
PenState.PEN_IS_ERASING,
408
PenState.PEN_IS_OUT_OF_RANGE,
409
),
410
}
411
412
@staticmethod
413
def broken_transitions() -> Dict[str, Tuple["PenState", ...]]:
414
"""Those tests are definitely not part of the Windows specification.
415
However, a half broken device might export those transitions.
416
For example, a pen that has the eraser button might wobble between
417
touching and erasing if the tablet doesn't enforce the Windows
418
state machine."""
419
return {
420
"in-range -> touch -> erase -> hover-erase": (
421
PenState.PEN_IS_IN_RANGE,
422
PenState.PEN_IS_IN_CONTACT,
423
PenState.PEN_IS_ERASING,
424
PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
425
),
426
"in-range -> erase -> hover-erase": (
427
PenState.PEN_IS_IN_RANGE,
428
PenState.PEN_IS_ERASING,
429
PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
430
),
431
"hover-erase -> erase -> touch -> in-range": (
432
PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
433
PenState.PEN_IS_ERASING,
434
PenState.PEN_IS_IN_CONTACT,
435
PenState.PEN_IS_IN_RANGE,
436
),
437
"hover-erase -> touch -> in-range": (
438
PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
439
PenState.PEN_IS_IN_CONTACT,
440
PenState.PEN_IS_IN_RANGE,
441
),
442
"touch -> erase -> touch -> erase": (
443
PenState.PEN_IS_IN_CONTACT,
444
PenState.PEN_IS_ERASING,
445
PenState.PEN_IS_IN_CONTACT,
446
PenState.PEN_IS_ERASING,
447
),
448
}
449
450
451
class Pen(object):
452
def __init__(self, x, y):
453
self.x = x
454
self.y = y
455
self.distance = -10
456
self.tipswitch = False
457
self.tippressure = 15
458
self.azimuth = 0
459
self.inrange = False
460
self.width = 10
461
self.height = 10
462
self.barrelswitch = False
463
self.secondarybarrelswitch = False
464
self.invert = False
465
self.eraser = False
466
self.xtilt = 1
467
self.ytilt = 1
468
self.twist = 1
469
self._old_values = None
470
self.current_state = None
471
472
def restore(self):
473
if self._old_values is not None:
474
for i in [
475
"x",
476
"y",
477
"distance",
478
"tippressure",
479
"azimuth",
480
"width",
481
"height",
482
"twist",
483
"xtilt",
484
"ytilt",
485
]:
486
setattr(self, i, getattr(self._old_values, i))
487
488
def backup(self):
489
self._old_values = copy.copy(self)
490
491
def __assert_axis(self, evdev, axis, value):
492
if (
493
axis == libevdev.EV_KEY.BTN_TOOL_RUBBER
494
and evdev.value[libevdev.EV_KEY.BTN_TOOL_RUBBER] is None
495
):
496
return
497
498
assert (
499
evdev.value[axis] == value
500
), f"assert evdev.value[{axis}] ({evdev.value[axis]}) != {value}"
501
502
def assert_expected_input_events(self, evdev, button):
503
assert evdev.value[libevdev.EV_ABS.ABS_X] == self.x
504
assert evdev.value[libevdev.EV_ABS.ABS_Y] == self.y
505
506
# assert no other buttons than the tested ones are set
507
buttons = [
508
BtnPressed.PRIMARY_PRESSED,
509
BtnPressed.SECONDARY_PRESSED,
510
BtnPressed.THIRD_PRESSED,
511
]
512
if button is not None:
513
buttons.remove(button)
514
for b in buttons:
515
assert evdev.value[b.value] is None or evdev.value[b.value] == False
516
517
assert self.current_state == PenState.from_evdev(evdev, button)
518
519
520
class PenDigitizer(base.UHIDTestDevice):
521
def __init__(
522
self,
523
name,
524
rdesc_str=None,
525
rdesc=None,
526
application="Pen",
527
physical="Stylus",
528
input_info=(BusType.USB, 1, 2),
529
evdev_name_suffix=None,
530
):
531
super().__init__(name, application, rdesc_str, rdesc, input_info)
532
self.physical = physical
533
self.cur_application = application
534
if evdev_name_suffix is not None:
535
self.name += evdev_name_suffix
536
537
self.fields = []
538
for r in self.parsed_rdesc.input_reports.values():
539
if r.application_name == self.application:
540
physicals = [f.physical_name for f in r]
541
if self.physical not in physicals and None not in physicals:
542
continue
543
self.fields = [f.usage_name for f in r]
544
545
def move_to(self, pen, state, button):
546
# fill in the previous values
547
if pen.current_state == PenState.PEN_IS_OUT_OF_RANGE:
548
pen.restore()
549
550
print(f"\n *** pen is moving to {state} ***")
551
552
if state == PenState.PEN_IS_OUT_OF_RANGE:
553
pen.backup()
554
pen.x = 0
555
pen.y = 0
556
pen.tipswitch = False
557
pen.tippressure = 0
558
pen.azimuth = 0
559
pen.distance = 0
560
pen.inrange = False
561
pen.width = 0
562
pen.height = 0
563
pen.invert = False
564
pen.eraser = False
565
pen.xtilt = 0
566
pen.ytilt = 0
567
pen.twist = 0
568
pen.barrelswitch = False
569
pen.secondarybarrelswitch = False
570
elif state == PenState.PEN_IS_IN_RANGE:
571
pen.tipswitch = False
572
pen.inrange = True
573
pen.invert = False
574
pen.eraser = False
575
pen.barrelswitch = False
576
pen.secondarybarrelswitch = False
577
elif state == PenState.PEN_IS_IN_CONTACT:
578
pen.tipswitch = True
579
pen.inrange = True
580
pen.invert = False
581
pen.eraser = False
582
pen.barrelswitch = False
583
pen.secondarybarrelswitch = False
584
elif state == PenState.PEN_IS_IN_RANGE_WITH_BUTTON:
585
pen.tipswitch = False
586
pen.inrange = True
587
pen.invert = False
588
pen.eraser = False
589
assert button is not None
590
pen.barrelswitch = button == BtnPressed.PRIMARY_PRESSED
591
pen.secondarybarrelswitch = button == BtnPressed.SECONDARY_PRESSED
592
elif state == PenState.PEN_IS_IN_CONTACT_WITH_BUTTON:
593
pen.tipswitch = True
594
pen.inrange = True
595
pen.invert = False
596
pen.eraser = False
597
assert button is not None
598
pen.barrelswitch = button == BtnPressed.PRIMARY_PRESSED
599
pen.secondarybarrelswitch = button == BtnPressed.SECONDARY_PRESSED
600
elif state == PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT:
601
pen.tipswitch = False
602
pen.inrange = True
603
pen.invert = True
604
pen.eraser = False
605
pen.barrelswitch = False
606
pen.secondarybarrelswitch = False
607
elif state == PenState.PEN_IS_ERASING:
608
pen.tipswitch = False
609
pen.inrange = True
610
pen.invert = False
611
pen.eraser = True
612
pen.barrelswitch = False
613
pen.secondarybarrelswitch = False
614
615
pen.current_state = state
616
617
def event(self, pen, button):
618
rs = []
619
r = self.create_report(application=self.cur_application, data=pen)
620
self.call_input_event(r)
621
rs.append(r)
622
return rs
623
624
def get_report(self, req, rnum, rtype):
625
if rtype != self.UHID_FEATURE_REPORT:
626
return (1, [])
627
628
rdesc = None
629
for v in self.parsed_rdesc.feature_reports.values():
630
if v.report_ID == rnum:
631
rdesc = v
632
633
if rdesc is None:
634
return (1, [])
635
636
return (1, [])
637
638
def set_report(self, req, rnum, rtype, data):
639
if rtype != self.UHID_FEATURE_REPORT:
640
return 1
641
642
rdesc = None
643
for v in self.parsed_rdesc.feature_reports.values():
644
if v.report_ID == rnum:
645
rdesc = v
646
647
if rdesc is None:
648
return 1
649
650
return 1
651
652
653
class BaseTest:
654
class TestTablet(base.BaseTestCase.TestUhid):
655
def create_device(self):
656
raise Exception("please reimplement me in subclasses")
657
658
def post(self, uhdev, pen, test_button):
659
r = uhdev.event(pen, test_button)
660
events = uhdev.next_sync_events()
661
self.debug_reports(r, uhdev, events)
662
return events
663
664
def validate_transitions(
665
self, from_state, pen, evdev, events, allow_intermediate_states, button
666
):
667
# check that the final state is correct
668
pen.assert_expected_input_events(evdev, button)
669
670
state = from_state
671
672
# check that the transitions are valid
673
sync_events = []
674
while libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT) in events:
675
# split the first EV_SYN from the list
676
idx = events.index(libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT))
677
sync_events = events[:idx]
678
events = events[idx + 1 :]
679
680
# now check for a valid transition
681
state = state.apply(sync_events, not allow_intermediate_states, button)
682
683
if events:
684
state = state.apply(sync_events, not allow_intermediate_states, button)
685
686
def _test_states(
687
self, state_list, scribble, allow_intermediate_states, button=None
688
):
689
"""Internal method to test against a list of
690
transition between states.
691
state_list is a list of PenState objects
692
scribble is a boolean which tells if we need
693
to wobble a little the X,Y coordinates of the pen
694
between each state transition."""
695
uhdev = self.uhdev
696
evdev = uhdev.get_evdev()
697
698
cur_state = PenState.PEN_IS_OUT_OF_RANGE
699
700
p = Pen(50, 60)
701
uhdev.move_to(p, PenState.PEN_IS_OUT_OF_RANGE, button)
702
events = self.post(uhdev, p, button)
703
self.validate_transitions(
704
cur_state, p, evdev, events, allow_intermediate_states, button
705
)
706
707
cur_state = p.current_state
708
709
for state in state_list:
710
if scribble and cur_state != PenState.PEN_IS_OUT_OF_RANGE:
711
p.x += 1
712
p.y -= 1
713
events = self.post(uhdev, p, button)
714
self.validate_transitions(
715
cur_state, p, evdev, events, allow_intermediate_states, button
716
)
717
assert len(events) >= 3 # X, Y, SYN
718
uhdev.move_to(p, state, button)
719
if scribble and state != PenState.PEN_IS_OUT_OF_RANGE:
720
p.x += 1
721
p.y -= 1
722
events = self.post(uhdev, p, button)
723
self.validate_transitions(
724
cur_state, p, evdev, events, allow_intermediate_states, button
725
)
726
cur_state = p.current_state
727
728
@pytest.mark.parametrize("scribble", [True, False], ids=["scribble", "static"])
729
@pytest.mark.parametrize(
730
"state_list",
731
[pytest.param(v, id=k) for k, v in PenState.legal_transitions().items()],
732
)
733
def test_valid_pen_states(self, state_list, scribble):
734
"""This is the first half of the Windows Pen Implementation state machine:
735
we don't have Invert nor Erase bits, so just move in/out-of-range or proximity.
736
https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states
737
"""
738
self._test_states(state_list, scribble, allow_intermediate_states=False)
739
740
@pytest.mark.parametrize("scribble", [True, False], ids=["scribble", "static"])
741
@pytest.mark.parametrize(
742
"state_list",
743
[
744
pytest.param(v, id=k)
745
for k, v in PenState.tolerated_transitions().items()
746
],
747
)
748
def test_tolerated_pen_states(self, state_list, scribble):
749
"""This is not adhering to the Windows Pen Implementation state machine
750
but we should expect the kernel to behave properly, mostly for historical
751
reasons."""
752
self._test_states(state_list, scribble, allow_intermediate_states=True)
753
754
@pytest.mark.skip_if_uhdev(
755
lambda uhdev: "Barrel Switch" not in uhdev.fields,
756
"Device not compatible, missing Barrel Switch usage",
757
)
758
@pytest.mark.parametrize("scribble", [True, False], ids=["scribble", "static"])
759
@pytest.mark.parametrize(
760
"state_list",
761
[
762
pytest.param(v, id=k)
763
for k, v in PenState.legal_transitions_with_button().items()
764
],
765
)
766
def test_valid_primary_button_pen_states(self, state_list, scribble):
767
"""Rework the transition state machine by adding the primary button."""
768
self._test_states(
769
state_list,
770
scribble,
771
allow_intermediate_states=False,
772
button=BtnPressed.PRIMARY_PRESSED,
773
)
774
775
@pytest.mark.skip_if_uhdev(
776
lambda uhdev: "Secondary Barrel Switch" not in uhdev.fields,
777
"Device not compatible, missing Secondary Barrel Switch usage",
778
)
779
@pytest.mark.parametrize("scribble", [True, False], ids=["scribble", "static"])
780
@pytest.mark.parametrize(
781
"state_list",
782
[
783
pytest.param(v, id=k)
784
for k, v in PenState.legal_transitions_with_button().items()
785
],
786
)
787
def test_valid_secondary_button_pen_states(self, state_list, scribble):
788
"""Rework the transition state machine by adding the secondary button."""
789
self._test_states(
790
state_list,
791
scribble,
792
allow_intermediate_states=False,
793
button=BtnPressed.SECONDARY_PRESSED,
794
)
795
796
@pytest.mark.skip_if_uhdev(
797
lambda uhdev: "Third Barrel Switch" not in uhdev.fields,
798
"Device not compatible, missing Third Barrel Switch usage",
799
)
800
@pytest.mark.parametrize("scribble", [True, False], ids=["scribble", "static"])
801
@pytest.mark.parametrize(
802
"state_list",
803
[
804
pytest.param(v, id=k)
805
for k, v in PenState.legal_transitions_with_button().items()
806
],
807
)
808
def test_valid_third_button_pen_states(self, state_list, scribble):
809
"""Rework the transition state machine by adding the secondary button."""
810
self._test_states(
811
state_list,
812
scribble,
813
allow_intermediate_states=False,
814
button=BtnPressed.THIRD_PRESSED,
815
)
816
817
@pytest.mark.skip_if_uhdev(
818
lambda uhdev: "Invert" not in uhdev.fields,
819
"Device not compatible, missing Invert usage",
820
)
821
@pytest.mark.parametrize("scribble", [True, False], ids=["scribble", "static"])
822
@pytest.mark.parametrize(
823
"state_list",
824
[
825
pytest.param(v, id=k)
826
for k, v in PenState.legal_transitions_with_invert().items()
827
],
828
)
829
def test_valid_invert_pen_states(self, state_list, scribble):
830
"""This is the second half of the Windows Pen Implementation state machine:
831
we now have Invert and Erase bits, so move in/out or proximity with the intend
832
to erase.
833
https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states
834
"""
835
self._test_states(state_list, scribble, allow_intermediate_states=False)
836
837
@pytest.mark.skip_if_uhdev(
838
lambda uhdev: "Invert" not in uhdev.fields,
839
"Device not compatible, missing Invert usage",
840
)
841
@pytest.mark.parametrize("scribble", [True, False], ids=["scribble", "static"])
842
@pytest.mark.parametrize(
843
"state_list",
844
[
845
pytest.param(v, id=k)
846
for k, v in PenState.tolerated_transitions_with_invert().items()
847
],
848
)
849
def test_tolerated_invert_pen_states(self, state_list, scribble):
850
"""This is the second half of the Windows Pen Implementation state machine:
851
we now have Invert and Erase bits, so move in/out or proximity with the intend
852
to erase.
853
https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states
854
"""
855
self._test_states(state_list, scribble, allow_intermediate_states=True)
856
857
@pytest.mark.skip_if_uhdev(
858
lambda uhdev: "Invert" not in uhdev.fields,
859
"Device not compatible, missing Invert usage",
860
)
861
@pytest.mark.parametrize("scribble", [True, False], ids=["scribble", "static"])
862
@pytest.mark.parametrize(
863
"state_list",
864
[pytest.param(v, id=k) for k, v in PenState.broken_transitions().items()],
865
)
866
def test_tolerated_broken_pen_states(self, state_list, scribble):
867
"""Those tests are definitely not part of the Windows specification.
868
However, a half broken device might export those transitions.
869
For example, a pen that has the eraser button might wobble between
870
touching and erasing if the tablet doesn't enforce the Windows
871
state machine."""
872
self._test_states(state_list, scribble, allow_intermediate_states=True)
873
874
@pytest.mark.skip_if_uhdev(
875
lambda uhdev: "Z" not in uhdev.fields,
876
"Device not compatible, missing Z usage",
877
)
878
@pytest.mark.parametrize("scribble", [True, False], ids=["scribble", "static"])
879
@pytest.mark.parametrize(
880
"state_list",
881
[pytest.param(v, id=k) for k, v in PenState.legal_transitions().items()],
882
)
883
def test_z_reported_as_distance(self, state_list, scribble):
884
"""Verify stylus Z values are reported as ABS_DISTANCE."""
885
self._test_states(state_list, scribble, allow_intermediate_states=False)
886
887
uhdev = self.uhdev
888
evdev = uhdev.get_evdev()
889
p = Pen(0, 0)
890
uhdev.move_to(p, PenState.PEN_IS_OUT_OF_RANGE, None)
891
p = Pen(100, 200)
892
uhdev.move_to(p, PenState.PEN_IS_IN_RANGE, None)
893
p.distance = -1
894
events = self.post(uhdev, p, None)
895
assert evdev.value[libevdev.EV_ABS.ABS_DISTANCE] == -1
896
897
898
class GXTP_pen(PenDigitizer):
899
def event(self, pen, test_button):
900
if not hasattr(self, "prev_tip_state"):
901
self.prev_tip_state = False
902
903
internal_pen = copy.copy(pen)
904
905
# bug in the controller: when the pen touches the
906
# surface, in-range stays to 1, but when
907
# the pen moves in-range gets reverted to 0
908
if pen.tipswitch and self.prev_tip_state:
909
internal_pen.inrange = False
910
911
self.prev_tip_state = pen.tipswitch
912
913
# another bug in the controller: when the pen is
914
# inverted, invert is set to 1, but as soon as
915
# the pen touches the surface, eraser is correctly
916
# set to 1 but invert is released
917
if pen.eraser:
918
internal_pen.invert = False
919
920
return super().event(internal_pen, test_button)
921
922
923
class USIPen(PenDigitizer):
924
pass
925
926
927
class XPPen_ArtistPro16Gen2_28bd_095b(PenDigitizer):
928
"""
929
Pen with two buttons and a rubber end, but which reports
930
the second button as an eraser
931
"""
932
933
def __init__(
934
self,
935
name,
936
rdesc_str=None,
937
rdesc=None,
938
application="Pen",
939
physical="Stylus",
940
input_info=(BusType.USB, 0x28BD, 0x095B),
941
evdev_name_suffix=None,
942
):
943
super().__init__(
944
name, rdesc_str, rdesc, application, physical, input_info, evdev_name_suffix
945
)
946
self.fields.append("Secondary Barrel Switch")
947
948
def move_to(self, pen, state, button):
949
# fill in the previous values
950
if pen.current_state == PenState.PEN_IS_OUT_OF_RANGE:
951
pen.restore()
952
953
print(f"\n *** pen is moving to {state} ***")
954
955
if state == PenState.PEN_IS_OUT_OF_RANGE:
956
pen.backup()
957
pen.x = 0
958
pen.y = 0
959
pen.tipswitch = False
960
pen.tippressure = 0
961
pen.azimuth = 0
962
pen.inrange = False
963
pen.width = 0
964
pen.height = 0
965
pen.invert = False
966
pen.eraser = False
967
pen.xtilt = 0
968
pen.ytilt = 0
969
pen.twist = 0
970
pen.barrelswitch = False
971
elif state == PenState.PEN_IS_IN_RANGE:
972
pen.tipswitch = False
973
pen.inrange = True
974
pen.invert = False
975
pen.eraser = False
976
pen.barrelswitch = False
977
elif state == PenState.PEN_IS_IN_CONTACT:
978
pen.tipswitch = True
979
pen.inrange = True
980
pen.invert = False
981
pen.eraser = False
982
pen.barrelswitch = False
983
elif state == PenState.PEN_IS_IN_RANGE_WITH_BUTTON:
984
pen.tipswitch = False
985
pen.inrange = True
986
pen.invert = False
987
assert button is not None
988
pen.barrelswitch = button == BtnPressed.PRIMARY_PRESSED
989
pen.eraser = button == BtnPressed.SECONDARY_PRESSED
990
elif state == PenState.PEN_IS_IN_CONTACT_WITH_BUTTON:
991
pen.tipswitch = True
992
pen.inrange = True
993
pen.invert = False
994
assert button is not None
995
pen.barrelswitch = button == BtnPressed.PRIMARY_PRESSED
996
pen.eraser = button == BtnPressed.SECONDARY_PRESSED
997
elif state == PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT:
998
pen.tipswitch = False
999
pen.inrange = True
1000
pen.invert = True
1001
pen.eraser = False
1002
pen.barrelswitch = False
1003
elif state == PenState.PEN_IS_ERASING:
1004
pen.tipswitch = True
1005
pen.inrange = True
1006
pen.invert = True
1007
pen.eraser = False
1008
pen.barrelswitch = False
1009
1010
pen.current_state = state
1011
1012
def event(self, pen, test_button):
1013
import math
1014
1015
pen_copy = copy.copy(pen)
1016
width = 13.567
1017
height = 8.480
1018
tip_height = 0.055677699
1019
hx = tip_height * (32767 / width)
1020
hy = tip_height * (32767 / height)
1021
if pen_copy.xtilt != 0:
1022
pen_copy.x += round(hx * math.sin(math.radians(pen_copy.xtilt)))
1023
if pen_copy.ytilt != 0:
1024
pen_copy.y += round(hy * math.sin(math.radians(pen_copy.ytilt)))
1025
1026
return super().event(pen_copy, test_button)
1027
1028
1029
class XPPen_Artist24_28bd_093a(PenDigitizer):
1030
"""
1031
Pen that reports secondary barrel switch through eraser
1032
"""
1033
1034
def __init__(
1035
self,
1036
name,
1037
rdesc_str=None,
1038
rdesc=None,
1039
application="Pen",
1040
physical="Stylus",
1041
input_info=(BusType.USB, 0x28BD, 0x093A),
1042
evdev_name_suffix=None,
1043
):
1044
super().__init__(
1045
name, rdesc_str, rdesc, application, physical, input_info, evdev_name_suffix
1046
)
1047
self.fields.append("Secondary Barrel Switch")
1048
self.previous_state = PenState.PEN_IS_OUT_OF_RANGE
1049
1050
def move_to(self, pen, state, button, debug=True):
1051
# fill in the previous values
1052
if pen.current_state == PenState.PEN_IS_OUT_OF_RANGE:
1053
pen.restore()
1054
1055
if debug:
1056
print(f"\n *** pen is moving to {state} ***")
1057
1058
if state == PenState.PEN_IS_OUT_OF_RANGE:
1059
pen.backup()
1060
pen.tipswitch = False
1061
pen.tippressure = 0
1062
pen.azimuth = 0
1063
pen.inrange = False
1064
pen.width = 0
1065
pen.height = 0
1066
pen.invert = False
1067
pen.eraser = False
1068
pen.xtilt = 0
1069
pen.ytilt = 0
1070
pen.twist = 0
1071
pen.barrelswitch = False
1072
elif state == PenState.PEN_IS_IN_RANGE:
1073
pen.tipswitch = False
1074
pen.inrange = True
1075
pen.invert = False
1076
pen.eraser = False
1077
pen.barrelswitch = False
1078
elif state == PenState.PEN_IS_IN_CONTACT:
1079
pen.tipswitch = True
1080
pen.inrange = True
1081
pen.invert = False
1082
pen.eraser = False
1083
pen.barrelswitch = False
1084
elif state == PenState.PEN_IS_IN_RANGE_WITH_BUTTON:
1085
pen.tipswitch = False
1086
pen.inrange = True
1087
pen.invert = False
1088
assert button is not None
1089
pen.barrelswitch = button == BtnPressed.PRIMARY_PRESSED
1090
pen.eraser = button == BtnPressed.SECONDARY_PRESSED
1091
elif state == PenState.PEN_IS_IN_CONTACT_WITH_BUTTON:
1092
pen.tipswitch = True
1093
pen.inrange = True
1094
pen.invert = False
1095
assert button is not None
1096
pen.barrelswitch = button == BtnPressed.PRIMARY_PRESSED
1097
pen.eraser = button == BtnPressed.SECONDARY_PRESSED
1098
1099
pen.current_state = state
1100
1101
def send_intermediate_state(self, pen, state, button):
1102
intermediate_pen = copy.copy(pen)
1103
self.move_to(intermediate_pen, state, button, debug=False)
1104
return super().event(intermediate_pen, button)
1105
1106
def event(self, pen, button):
1107
rs = []
1108
1109
# the pen reliably sends in-range events in a normal case (non emulation of eraser mode)
1110
if self.previous_state == PenState.PEN_IS_IN_CONTACT:
1111
if pen.current_state == PenState.PEN_IS_OUT_OF_RANGE:
1112
rs.extend(
1113
self.send_intermediate_state(pen, PenState.PEN_IS_IN_RANGE, button)
1114
)
1115
1116
if button == BtnPressed.SECONDARY_PRESSED:
1117
if self.previous_state == PenState.PEN_IS_IN_RANGE:
1118
if pen.current_state == PenState.PEN_IS_IN_RANGE_WITH_BUTTON:
1119
rs.extend(
1120
self.send_intermediate_state(
1121
pen, PenState.PEN_IS_OUT_OF_RANGE, button
1122
)
1123
)
1124
1125
if self.previous_state == PenState.PEN_IS_IN_RANGE_WITH_BUTTON:
1126
if pen.current_state == PenState.PEN_IS_IN_RANGE:
1127
rs.extend(
1128
self.send_intermediate_state(
1129
pen, PenState.PEN_IS_OUT_OF_RANGE, button
1130
)
1131
)
1132
1133
if self.previous_state == PenState.PEN_IS_IN_CONTACT:
1134
if pen.current_state == PenState.PEN_IS_IN_CONTACT_WITH_BUTTON:
1135
rs.extend(
1136
self.send_intermediate_state(
1137
pen, PenState.PEN_IS_OUT_OF_RANGE, button
1138
)
1139
)
1140
rs.extend(
1141
self.send_intermediate_state(
1142
pen, PenState.PEN_IS_IN_RANGE_WITH_BUTTON, button
1143
)
1144
)
1145
1146
if self.previous_state == PenState.PEN_IS_IN_CONTACT_WITH_BUTTON:
1147
if pen.current_state == PenState.PEN_IS_IN_CONTACT:
1148
rs.extend(
1149
self.send_intermediate_state(
1150
pen, PenState.PEN_IS_OUT_OF_RANGE, button
1151
)
1152
)
1153
rs.extend(
1154
self.send_intermediate_state(
1155
pen, PenState.PEN_IS_IN_RANGE, button
1156
)
1157
)
1158
1159
rs.extend(super().event(pen, button))
1160
self.previous_state = pen.current_state
1161
return rs
1162
1163
1164
class Huion_Kamvas_Pro_19_256c_006b(PenDigitizer):
1165
"""
1166
Pen that reports secondary barrel switch through secondary TipSwtich
1167
and 3rd button through Invert
1168
"""
1169
1170
def __init__(
1171
self,
1172
name,
1173
rdesc_str=None,
1174
rdesc=None,
1175
application="Stylus",
1176
physical=None,
1177
input_info=(BusType.USB, 0x256C, 0x006B),
1178
evdev_name_suffix=None,
1179
):
1180
super().__init__(
1181
name, rdesc_str, rdesc, application, physical, input_info, evdev_name_suffix
1182
)
1183
self.fields.append("Secondary Barrel Switch")
1184
self.fields.append("Third Barrel Switch")
1185
self.previous_state = PenState.PEN_IS_OUT_OF_RANGE
1186
1187
def move_to(self, pen, state, button, debug=True):
1188
# fill in the previous values
1189
if pen.current_state == PenState.PEN_IS_OUT_OF_RANGE:
1190
pen.restore()
1191
1192
if debug:
1193
print(f"\n *** pen is moving to {state} ***")
1194
1195
if state == PenState.PEN_IS_OUT_OF_RANGE:
1196
pen.backup()
1197
pen.tipswitch = False
1198
pen.tippressure = 0
1199
pen.azimuth = 0
1200
pen.inrange = False
1201
pen.width = 0
1202
pen.height = 0
1203
pen.invert = False
1204
pen.eraser = False
1205
pen.xtilt = 0
1206
pen.ytilt = 0
1207
pen.twist = 0
1208
pen.barrelswitch = False
1209
pen.secondarytipswitch = False
1210
elif state == PenState.PEN_IS_IN_RANGE:
1211
pen.tipswitch = False
1212
pen.inrange = True
1213
pen.invert = False
1214
pen.eraser = False
1215
pen.barrelswitch = False
1216
pen.secondarytipswitch = False
1217
elif state == PenState.PEN_IS_IN_CONTACT:
1218
pen.tipswitch = True
1219
pen.inrange = True
1220
pen.invert = False
1221
pen.eraser = False
1222
pen.barrelswitch = False
1223
pen.secondarytipswitch = False
1224
elif state == PenState.PEN_IS_IN_RANGE_WITH_BUTTON:
1225
pen.tipswitch = False
1226
pen.inrange = True
1227
pen.eraser = False
1228
assert button is not None
1229
pen.barrelswitch = button == BtnPressed.PRIMARY_PRESSED
1230
pen.secondarytipswitch = button == BtnPressed.SECONDARY_PRESSED
1231
pen.invert = button == BtnPressed.THIRD_PRESSED
1232
elif state == PenState.PEN_IS_IN_CONTACT_WITH_BUTTON:
1233
pen.tipswitch = True
1234
pen.inrange = True
1235
pen.eraser = False
1236
assert button is not None
1237
pen.barrelswitch = button == BtnPressed.PRIMARY_PRESSED
1238
pen.secondarytipswitch = button == BtnPressed.SECONDARY_PRESSED
1239
pen.invert = button == BtnPressed.THIRD_PRESSED
1240
elif state == PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT:
1241
pen.tipswitch = False
1242
pen.inrange = True
1243
pen.invert = True
1244
pen.eraser = False
1245
pen.barrelswitch = False
1246
pen.secondarytipswitch = False
1247
elif state == PenState.PEN_IS_ERASING:
1248
pen.tipswitch = False
1249
pen.inrange = True
1250
pen.invert = False
1251
pen.eraser = True
1252
pen.barrelswitch = False
1253
pen.secondarytipswitch = False
1254
1255
pen.current_state = state
1256
1257
def call_input_event(self, report):
1258
if report[0] == 0x0A:
1259
# ensures the original second Eraser usage is null
1260
report[1] &= 0xDF
1261
1262
# ensures the original last bit is equal to bit 6 (In Range)
1263
if report[1] & 0x40:
1264
report[1] |= 0x80
1265
1266
super().call_input_event(report)
1267
1268
def send_intermediate_state(self, pen, state, test_button):
1269
intermediate_pen = copy.copy(pen)
1270
self.move_to(intermediate_pen, state, test_button, debug=False)
1271
return super().event(intermediate_pen, test_button)
1272
1273
def event(self, pen, button):
1274
rs = []
1275
1276
# it's not possible to go between eraser mode or not without
1277
# going out-of-prox: the eraser mode is activated by presenting
1278
# the tail of the pen
1279
if self.previous_state in (
1280
PenState.PEN_IS_IN_RANGE,
1281
PenState.PEN_IS_IN_RANGE_WITH_BUTTON,
1282
PenState.PEN_IS_IN_CONTACT,
1283
PenState.PEN_IS_IN_CONTACT_WITH_BUTTON,
1284
) and pen.current_state in (
1285
PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
1286
PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT_WITH_BUTTON,
1287
PenState.PEN_IS_ERASING,
1288
PenState.PEN_IS_ERASING_WITH_BUTTON,
1289
):
1290
rs.extend(
1291
self.send_intermediate_state(pen, PenState.PEN_IS_OUT_OF_RANGE, button)
1292
)
1293
1294
# same than above except from eraser to normal
1295
if self.previous_state in (
1296
PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
1297
PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT_WITH_BUTTON,
1298
PenState.PEN_IS_ERASING,
1299
PenState.PEN_IS_ERASING_WITH_BUTTON,
1300
) and pen.current_state in (
1301
PenState.PEN_IS_IN_RANGE,
1302
PenState.PEN_IS_IN_RANGE_WITH_BUTTON,
1303
PenState.PEN_IS_IN_CONTACT,
1304
PenState.PEN_IS_IN_CONTACT_WITH_BUTTON,
1305
):
1306
rs.extend(
1307
self.send_intermediate_state(pen, PenState.PEN_IS_OUT_OF_RANGE, button)
1308
)
1309
1310
if self.previous_state == PenState.PEN_IS_OUT_OF_RANGE:
1311
if pen.current_state == PenState.PEN_IS_IN_RANGE_WITH_BUTTON:
1312
rs.extend(
1313
self.send_intermediate_state(pen, PenState.PEN_IS_IN_RANGE, button)
1314
)
1315
1316
rs.extend(super().event(pen, button))
1317
self.previous_state = pen.current_state
1318
return rs
1319
1320
1321
class Wacom_2d1f_014b(PenDigitizer):
1322
"""
1323
Pen that reports distance values with HID_GD_Z usage.
1324
"""
1325
def __init__(
1326
self,
1327
name,
1328
rdesc_str=None,
1329
rdesc=None,
1330
application="Pen",
1331
physical="Stylus",
1332
input_info=(BusType.USB, 0x2D1F, 0x014B),
1333
evdev_name_suffix=None,
1334
):
1335
super().__init__(
1336
name, rdesc_str, rdesc, application, physical, input_info, evdev_name_suffix
1337
)
1338
1339
def match_evdev_rule(self, application, evdev):
1340
# there are 2 nodes created by the device, only one matters
1341
return evdev.name.endswith("Stylus")
1342
1343
def event(self, pen, test_button):
1344
# this device reports the distance through Z
1345
pen.z = pen.distance
1346
1347
return super().event(pen, test_button)
1348
1349
1350
################################################################################
1351
#
1352
# Windows 7 compatible devices
1353
#
1354
################################################################################
1355
# class TestEgalax_capacitive_0eef_7224(BaseTest.TestTablet):
1356
# def create_device(self):
1357
# return PenDigitizer('uhid test egalax-capacitive_0eef_7224',
1358
# rdesc='05 0d 09 04 a1 01 85 04 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 15 00 25 01 81 02 09 51 75 05 95 01 16 00 00 26 10 00 81 02 09 47 75 01 95 01 15 00 25 01 81 02 05 01 09 30 75 10 95 01 55 0d 65 33 35 00 46 34 49 26 ff 7f 81 02 09 31 75 10 95 01 55 0d 65 33 35 00 46 37 29 26 ff 7f 81 02 05 0d 09 55 25 08 75 08 95 01 b1 02 c0 c0 05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 16 00 00 26 ff 0f 36 00 00 46 ff 0f 66 00 00 75 10 95 02 81 02 c0 c0 06 00 ff 09 01 a1 01 09 01 15 00 26 ff 00 85 03 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0 05 0d 09 04 a1 01 85 02 09 20 a1 00 09 42 09 32 15 00 25 01 95 02 75 01 81 02 95 06 75 01 81 03 05 01 09 30 75 10 95 01 a4 55 0d 65 33 36 00 00 46 34 49 16 00 00 26 ff 0f 81 02 09 31 16 00 00 26 ff 0f 36 00 00 46 37 29 81 02 b4 c0 c0 05 0d 09 0e a1 01 85 05 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0',
1359
# input_info=(BusType.USB, 0x0eef, 0x7224),
1360
# evdev_name_suffix=' Touchscreen')
1361
#
1362
#
1363
# class TestEgalax_capacitive_0eef_72fa(BaseTest.TestTablet):
1364
# def create_device(self):
1365
# return PenDigitizer('uhid test egalax-capacitive_0eef_72fa',
1366
# rdesc='05 0d 09 04 a1 01 85 04 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 15 00 25 01 81 02 09 51 75 05 95 01 16 00 00 26 10 00 81 02 09 47 75 01 95 01 15 00 25 01 81 02 05 01 09 30 75 10 95 01 55 0d 65 33 35 00 46 72 22 26 ff 7f 81 02 09 31 75 10 95 01 55 0d 65 33 35 00 46 87 13 26 ff 7f 81 02 05 0d 09 55 25 08 75 08 95 01 b1 02 c0 c0 05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 16 00 00 26 ff 0f 36 00 00 46 ff 0f 66 00 00 75 10 95 02 81 02 c0 c0 06 00 ff 09 01 a1 01 09 01 15 00 26 ff 00 85 03 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0 05 0d 09 04 a1 01 85 02 09 20 a1 00 09 42 09 32 15 00 25 01 95 02 75 01 81 02 95 06 75 01 81 03 05 01 09 30 75 10 95 01 a4 55 0d 65 33 36 00 00 46 72 22 16 00 00 26 ff 0f 81 02 09 31 16 00 00 26 ff 0f 36 00 00 46 87 13 81 02 b4 c0 c0 05 0d 09 0e a1 01 85 05 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0',
1367
# input_info=(BusType.USB, 0x0eef, 0x72fa),
1368
# evdev_name_suffix=' Touchscreen')
1369
#
1370
#
1371
# class TestEgalax_capacitive_0eef_7336(BaseTest.TestTablet):
1372
# def create_device(self):
1373
# return PenDigitizer('uhid test egalax-capacitive_0eef_7336',
1374
# rdesc='05 0d 09 04 a1 01 85 04 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 15 00 25 01 81 02 09 51 75 05 95 01 16 00 00 26 10 00 81 02 09 47 75 01 95 01 15 00 25 01 81 02 05 01 09 30 75 10 95 01 55 0d 65 33 35 00 46 c1 20 26 ff 7f 81 02 09 31 75 10 95 01 55 0d 65 33 35 00 46 c2 18 26 ff 7f 81 02 05 0d 09 55 25 08 75 08 95 01 b1 02 c0 c0 05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 16 00 00 26 ff 0f 36 00 00 46 ff 0f 66 00 00 75 10 95 02 81 02 c0 c0 06 00 ff 09 01 a1 01 09 01 15 00 26 ff 00 85 03 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0 05 0d 09 04 a1 01 85 02 09 20 a1 00 09 42 09 32 15 00 25 01 95 02 75 01 81 02 95 06 75 01 81 03 05 01 09 30 75 10 95 01 a4 55 0d 65 33 36 00 00 46 c1 20 16 00 00 26 ff 0f 81 02 09 31 16 00 00 26 ff 0f 36 00 00 46 c2 18 81 02 b4 c0 c0 05 0d 09 0e a1 01 85 05 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0',
1375
# input_info=(BusType.USB, 0x0eef, 0x7336),
1376
# evdev_name_suffix=' Touchscreen')
1377
#
1378
#
1379
# class TestEgalax_capacitive_0eef_7337(BaseTest.TestTablet):
1380
# def create_device(self):
1381
# return PenDigitizer('uhid test egalax-capacitive_0eef_7337',
1382
# rdesc='05 0d 09 04 a1 01 85 04 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 15 00 25 01 81 02 09 51 75 05 95 01 16 00 00 26 10 00 81 02 09 47 75 01 95 01 15 00 25 01 81 02 05 01 09 30 75 10 95 01 55 0d 65 33 35 00 46 ae 17 26 ff 7f 81 02 09 31 75 10 95 01 55 0d 65 33 35 00 46 c3 0e 26 ff 7f 81 02 05 0d 09 55 25 08 75 08 95 01 b1 02 c0 c0 05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 16 00 00 26 ff 0f 36 00 00 46 ff 0f 66 00 00 75 10 95 02 81 02 c0 c0 06 00 ff 09 01 a1 01 09 01 15 00 26 ff 00 85 03 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0 05 0d 09 04 a1 01 85 02 09 20 a1 00 09 42 09 32 15 00 25 01 95 02 75 01 81 02 95 06 75 01 81 03 05 01 09 30 75 10 95 01 a4 55 0d 65 33 36 00 00 46 ae 17 16 00 00 26 ff 0f 81 02 09 31 16 00 00 26 ff 0f 36 00 00 46 c3 0e 81 02 b4 c0 c0 05 0d 09 0e a1 01 85 05 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0',
1383
# input_info=(BusType.USB, 0x0eef, 0x7337),
1384
# evdev_name_suffix=' Touchscreen')
1385
#
1386
#
1387
# class TestEgalax_capacitive_0eef_7349(BaseTest.TestTablet):
1388
# def create_device(self):
1389
# return PenDigitizer('uhid test egalax-capacitive_0eef_7349',
1390
# rdesc='05 0d 09 04 a1 01 85 04 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 15 00 25 01 81 02 09 51 75 05 95 01 16 00 00 26 10 00 81 02 09 47 75 01 95 01 15 00 25 01 81 02 05 01 09 30 75 10 95 01 55 0d 65 33 35 00 46 34 49 26 ff 7f 81 02 09 31 75 10 95 01 55 0d 65 33 35 00 46 37 29 26 ff 7f 81 02 05 0d 09 55 25 08 75 08 95 01 b1 02 c0 c0 05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 16 00 00 26 ff 0f 36 00 00 46 ff 0f 66 00 00 75 10 95 02 81 02 c0 c0 06 00 ff 09 01 a1 01 09 01 15 00 26 ff 00 85 03 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0 05 0d 09 04 a1 01 85 02 09 20 a1 00 09 42 09 32 15 00 25 01 95 02 75 01 81 02 95 06 75 01 81 03 05 01 09 30 75 10 95 01 a4 55 0d 65 33 36 00 00 46 34 49 16 00 00 26 ff 0f 81 02 09 31 16 00 00 26 ff 0f 36 00 00 46 37 29 81 02 b4 c0 c0 05 0d 09 0e a1 01 85 05 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0',
1391
# input_info=(BusType.USB, 0x0eef, 0x7349),
1392
# evdev_name_suffix=' Touchscreen')
1393
#
1394
#
1395
# class TestEgalax_capacitive_0eef_73f4(BaseTest.TestTablet):
1396
# def create_device(self):
1397
# return PenDigitizer('uhid test egalax-capacitive_0eef_73f4',
1398
# rdesc='05 0d 09 04 a1 01 85 04 09 22 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 09 32 15 00 25 01 81 02 09 51 75 05 95 01 16 00 00 26 10 00 81 02 09 47 75 01 95 01 15 00 25 01 81 02 05 01 09 30 75 10 95 01 55 0d 65 33 35 00 46 96 4e 26 ff 7f 81 02 09 31 75 10 95 01 55 0d 65 33 35 00 46 23 2c 26 ff 7f 81 02 05 0d 09 55 25 08 75 08 95 01 b1 02 c0 c0 05 01 09 01 a1 01 85 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 01 75 06 81 01 05 01 09 30 09 31 16 00 00 26 ff 0f 36 00 00 46 ff 0f 66 00 00 75 10 95 02 81 02 c0 c0 06 00 ff 09 01 a1 01 09 01 15 00 26 ff 00 85 03 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0 05 0d 09 04 a1 01 85 02 09 20 a1 00 09 42 09 32 15 00 25 01 95 02 75 01 81 02 95 06 75 01 81 03 05 01 09 30 75 10 95 01 a4 55 0d 65 33 36 00 00 46 96 4e 16 00 00 26 ff 0f 81 02 09 31 16 00 00 26 ff 0f 36 00 00 46 23 2c 81 02 b4 c0 c0 05 0d 09 0e a1 01 85 05 09 22 a1 00 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0',
1399
# input_info=(BusType.USB, 0x0eef, 0x73f4),
1400
# evdev_name_suffix=' Touchscreen')
1401
#
1402
# bogus: BTN_TOOL_PEN is not emitted
1403
# class TestIrtouch_6615_0070(BaseTest.TestTablet):
1404
# def create_device(self):
1405
# return PenDigitizer('uhid test irtouch_6615_0070',
1406
# rdesc='05 01 09 02 a1 01 85 10 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 95 02 75 01 81 02 95 06 81 03 05 01 09 30 09 31 15 00 26 ff 7f 75 10 95 02 81 02 c0 c0 05 0d 09 04 a1 01 85 30 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 51 75 08 95 01 81 02 05 01 09 30 26 ff 7f 55 0f 65 11 35 00 46 51 02 75 10 95 01 81 02 09 31 35 00 46 73 01 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 09 51 75 08 95 01 81 02 05 01 09 30 26 ff 7f 55 0f 65 11 35 00 46 51 02 75 10 95 01 81 02 09 31 35 00 46 73 01 81 02 c0 05 0d 09 54 15 00 26 02 00 75 08 95 01 81 02 85 03 09 55 15 00 26 ff 00 75 08 95 01 b1 02 c0 05 0d 09 0e a1 01 85 02 09 52 09 53 15 00 26 ff 00 75 08 95 02 b1 02 c0 05 0d 09 02 a1 01 85 20 09 20 a1 00 09 42 15 00 25 01 75 01 95 01 81 02 95 07 81 03 05 01 09 30 26 ff 7f 55 0f 65 11 35 00 46 51 02 75 10 95 01 81 02 09 31 35 00 46 73 01 81 02 85 01 06 00 ff 09 01 75 08 95 01 b1 02 c0 c0',
1407
# input_info=(BusType.USB, 0x6615, 0x0070))
1408
1409
1410
class TestNexio_1870_0100(BaseTest.TestTablet):
1411
def create_device(self):
1412
return PenDigitizer(
1413
"uhid test nexio_1870_0100",
1414
rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0e 65 11 09 30 35 00 46 1e 19 81 02 26 ff 3f 09 31 35 00 46 be 0f 81 02 26 ff 3f c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0e 65 11 09 30 35 00 46 1e 19 81 02 26 ff 3f 09 31 35 00 46 be 0f 81 02 26 ff 3f c0 05 0d 09 54 95 01 75 08 25 02 81 02 85 02 09 55 25 02 b1 02 c0 09 0e a1 01 85 03 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 05 01 09 02 a1 01 09 01 a1 00 85 04 05 09 95 03 75 01 19 01 29 03 15 00 25 01 81 02 95 01 75 05 81 01 05 01 75 10 95 02 09 30 09 31 15 00 26 ff 7f 81 02 c0 c0 05 0d 09 02 a1 01 85 05 09 20 a1 00 09 42 09 32 15 00 25 01 75 01 95 02 81 02 95 0e 81 03 05 01 26 ff 3f 75 10 95 01 55 0e 65 11 09 30 35 00 46 1e 19 81 02 26 ff 3f 09 31 35 00 46 be 0f 81 02 26 ff 3f c0 c0 06 00 ff 09 01 a1 01 85 06 19 01 29 40 15 00 26 ff 00 75 08 95 40 81 00 19 01 29 40 91 00 c0",
1415
input_info=(BusType.USB, 0x1870, 0x0100),
1416
)
1417
1418
1419
class TestNexio_1870_010d(BaseTest.TestTablet):
1420
def create_device(self):
1421
return PenDigitizer(
1422
"uhid test nexio_1870_010d",
1423
rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 05 0d 09 54 95 01 75 08 25 02 81 02 85 02 09 55 25 06 b1 02 c0 09 0e a1 01 85 03 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 05 01 09 02 a1 01 09 01 a1 00 85 04 05 09 95 03 75 01 19 01 29 03 15 00 25 01 81 02 95 01 75 05 81 01 05 01 75 10 95 02 09 30 09 31 15 00 26 ff 7f 81 02 c0 c0 05 0d 09 02 a1 01 85 05 09 20 a1 00 09 42 09 32 15 00 25 01 75 01 95 02 81 02 95 0e 81 03 05 01 26 ff 3f 75 10 95 01 55 0e 65 11 09 30 35 00 46 1e 19 81 02 26 ff 3f 09 31 35 00 46 be 0f 81 02 26 ff 3f c0 c0 06 00 ff 09 01 a1 01 85 06 19 01 29 40 15 00 26 ff 00 75 08 95 3e 81 00 19 01 29 40 91 00 c0",
1424
input_info=(BusType.USB, 0x1870, 0x010D),
1425
)
1426
1427
1428
class TestNexio_1870_0119(BaseTest.TestTablet):
1429
def create_device(self):
1430
return PenDigitizer(
1431
"uhid test nexio_1870_0119",
1432
rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 95 06 81 03 75 08 09 51 95 01 81 02 05 01 26 ff 3f 75 10 55 0d 65 00 09 30 35 00 46 00 00 81 02 26 ff 3f 09 31 35 00 46 00 00 81 02 26 ff 3f 05 0d 09 48 35 00 26 ff 3f 81 02 09 49 35 00 26 ff 3f 81 02 c0 05 0d 09 54 95 01 75 08 25 02 81 02 85 02 09 55 25 06 b1 02 c0 09 0e a1 01 85 03 09 23 a1 02 09 52 09 53 15 00 25 0a 75 08 95 02 b1 02 c0 c0 05 01 09 02 a1 01 09 01 a1 00 85 04 05 09 95 03 75 01 19 01 29 03 15 00 25 01 81 02 95 01 75 05 81 01 05 01 75 10 95 02 09 30 09 31 15 00 26 ff 7f 81 02 c0 c0 05 0d 09 02 a1 01 85 05 09 20 a1 00 09 42 09 32 15 00 25 01 75 01 95 02 81 02 95 0e 81 03 05 01 26 ff 3f 75 10 95 01 55 0e 65 11 09 30 35 00 46 1e 19 81 02 26 ff 3f 09 31 35 00 46 be 0f 81 02 26 ff 3f c0 c0 06 00 ff 09 01 a1 01 85 06 19 01 29 40 15 00 26 ff 00 75 08 95 3e 81 00 19 01 29 40 91 00 c0",
1433
input_info=(BusType.USB, 0x1870, 0x0119),
1434
)
1435
1436
1437
################################################################################
1438
#
1439
# Windows 8 compatible devices
1440
#
1441
################################################################################
1442
1443
# bogus: application is 'undefined'
1444
# class Testatmel_03eb_8409(BaseTest.TestTablet):
1445
# def create_device(self):
1446
# return PenDigitizer('uhid test atmel_03eb_8409', rdesc='05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 02 46 c8 0a 26 6f 08 09 30 81 02 35 00 35 00 46 18 06 26 77 0f 09 31 81 02 35 00 35 00 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 48 81 02 09 49 81 02 c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 02 46 c8 0a 26 6f 08 09 30 81 02 35 00 35 00 46 18 06 26 77 0f 09 31 81 02 35 00 35 00 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 48 81 02 09 49 81 02 c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 02 46 c8 0a 26 6f 08 09 30 81 02 35 00 35 00 46 18 06 26 77 0f 09 31 81 02 35 00 35 00 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 48 81 02 09 49 81 02 c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 02 46 c8 0a 26 6f 08 09 30 81 02 35 00 35 00 46 18 06 26 77 0f 09 31 81 02 35 00 35 00 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 48 81 02 09 49 81 02 c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 02 46 c8 0a 26 6f 08 09 30 81 02 35 00 35 00 46 18 06 26 77 0f 09 31 81 02 35 00 35 00 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 48 81 02 09 49 81 02 c0 05 0d 27 ff ff 00 00 75 10 95 01 09 56 81 02 15 00 25 1f 75 05 09 54 95 01 81 02 75 03 25 01 95 01 81 03 75 08 85 02 09 55 25 10 b1 02 06 00 ff 85 05 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 0d 09 00 a1 01 85 03 09 20 a1 00 15 00 25 01 75 01 95 01 09 42 81 02 09 44 81 02 09 45 81 02 81 03 09 32 81 02 95 03 81 03 05 01 55 0e 65 11 35 00 75 10 95 02 46 c8 0a 26 6f 08 09 30 81 02 46 18 06 26 77 0f 09 31 81 02 05 0d 09 30 15 01 26 ff 00 75 08 95 01 81 02 c0 c0')
1447
1448
1449
class Testatmel_03eb_840b(BaseTest.TestTablet):
1450
def create_device(self):
1451
return PenDigitizer(
1452
"uhid test atmel_03eb_840b",
1453
rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 01 46 00 0a 26 ff 0f 09 30 81 02 09 00 81 03 46 a0 05 26 ff 0f 09 31 81 02 09 00 81 03 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 00 81 03 09 00 81 03 c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 01 46 00 0a 26 ff 0f 09 30 81 02 09 00 81 03 46 a0 05 26 ff 0f 09 31 81 02 09 00 81 03 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 00 81 03 09 00 81 03 c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 01 46 00 0a 26 ff 0f 09 30 81 02 09 00 81 03 46 a0 05 26 ff 0f 09 31 81 02 09 00 81 03 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 00 81 03 09 00 81 03 c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 01 46 00 0a 26 ff 0f 09 30 81 02 09 00 81 03 46 a0 05 26 ff 0f 09 31 81 02 09 00 81 03 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 00 81 03 09 00 81 03 c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 95 01 81 03 25 1f 75 05 09 51 81 02 05 01 55 0e 65 11 35 00 75 10 95 01 46 00 0a 26 ff 0f 09 30 81 02 09 00 81 03 46 a0 05 26 ff 0f 09 31 81 02 09 00 81 03 05 0d 95 01 75 08 15 00 26 ff 00 46 ff 00 09 00 81 03 09 00 81 03 c0 05 0d 27 ff ff 00 00 75 10 95 01 09 56 81 02 15 00 25 1f 75 05 09 54 95 01 81 02 75 03 25 01 95 01 81 03 75 08 85 02 09 55 25 10 b1 02 06 00 ff 85 05 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 0d 09 02 a1 01 85 03 09 20 a1 00 15 00 25 01 75 01 95 01 09 42 81 02 09 44 81 02 09 45 81 02 81 03 09 32 81 02 95 03 81 03 05 01 55 0e 65 11 35 00 75 10 95 02 46 00 0a 26 ff 0f 09 30 81 02 46 a0 05 26 ff 0f 09 31 81 02 05 0d 09 30 15 01 26 ff 00 75 08 95 01 81 02 c0 c0",
1454
)
1455
1456
1457
class Testn_trig_1b96_0c01(BaseTest.TestTablet):
1458
def create_device(self):
1459
return PenDigitizer(
1460
"uhid test n_trig_1b96_0c01",
1461
rdesc="75 08 15 00 26 ff 00 06 0b ff 09 0b a1 01 95 0f 09 29 85 29 b1 02 95 1f 09 2a 85 2a b1 02 95 3e 09 2b 85 2b b1 02 95 fe 09 2c 85 2c b1 02 96 fe 01 09 2d 85 2d b1 02 95 02 09 48 85 48 b1 02 95 0f 09 2e 85 2e 81 02 95 1f 09 2f 85 2f 81 02 95 3e 09 30 85 30 81 02 95 fe 09 31 85 31 81 02 96 fe 01 09 32 85 32 81 02 75 08 96 fe 0f 09 35 85 35 81 02 c0 05 0d 09 02 a1 01 85 01 09 20 35 00 a1 00 09 32 09 42 09 44 09 3c 09 45 15 00 25 01 75 01 95 05 81 02 95 03 81 03 05 01 09 30 75 10 95 01 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 b4 05 0d 09 30 26 00 01 81 02 06 00 ff 09 01 81 02 c0 85 0c 06 00 ff 09 0c 75 08 95 06 26 ff 00 b1 02 85 0b 09 0b 95 02 b1 02 85 11 09 11 b1 02 85 15 09 15 95 05 b1 02 85 18 09 18 95 0c b1 02 c0 05 0d 09 04 a1 01 85 03 06 00 ff 09 01 75 10 95 01 15 00 27 ff ff 00 00 81 02 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 09 32 81 02 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 54 95 01 75 08 81 02 09 56 75 20 95 01 27 ff ff ff 0f 81 02 85 04 09 55 75 08 95 01 25 0b b1 02 85 0a 06 00 ff 09 03 15 00 b1 02 85 1b 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 01 09 02 a1 01 85 02 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 03 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 c0 c0",
1462
)
1463
1464
1465
class Testn_trig_1b96_0c03(BaseTest.TestTablet):
1466
def create_device(self):
1467
return PenDigitizer(
1468
"uhid test n_trig_1b96_0c03",
1469
rdesc="75 08 15 00 26 ff 00 06 0b ff 09 0b a1 01 95 0f 09 29 85 29 b1 02 95 1f 09 2a 85 2a b1 02 95 3e 09 2b 85 2b b1 02 95 fe 09 2c 85 2c b1 02 96 fe 01 09 2d 85 2d b1 02 95 02 09 48 85 48 b1 02 95 0f 09 2e 85 2e 81 02 95 1f 09 2f 85 2f 81 02 95 3e 09 30 85 30 81 02 95 fe 09 31 85 31 81 02 96 fe 01 09 32 85 32 81 02 75 08 96 fe 0f 09 35 85 35 81 02 c0 05 0d 09 02 a1 01 85 01 09 20 35 00 a1 00 09 32 09 42 09 44 09 3c 09 45 15 00 25 01 75 01 95 05 81 02 95 03 81 03 05 01 09 30 75 10 95 01 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 b4 05 0d 09 30 26 00 01 81 02 06 00 ff 09 01 81 02 c0 85 0c 06 00 ff 09 0c 75 08 95 06 26 ff 00 b1 02 85 0b 09 0b 95 02 b1 02 85 11 09 11 b1 02 85 15 09 15 95 05 b1 02 85 18 09 18 95 0c b1 02 c0 05 0d 09 04 a1 01 85 03 06 00 ff 09 01 75 10 95 01 15 00 27 ff ff 00 00 81 02 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 15 0a 26 80 25 81 02 09 31 46 b4 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 54 95 01 75 08 81 02 09 56 75 20 95 01 27 ff ff ff 0f 81 02 85 04 09 55 75 08 95 01 25 0b b1 02 85 0a 06 00 ff 09 03 15 00 b1 02 85 1b 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 01 09 02 a1 01 85 02 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 03 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 c0 c0",
1470
)
1471
1472
1473
class Testn_trig_1b96_0f00(BaseTest.TestTablet):
1474
def create_device(self):
1475
return PenDigitizer(
1476
"uhid test n_trig_1b96_0f00",
1477
rdesc="75 08 15 00 26 ff 00 06 0b ff 09 0b a1 01 95 0f 09 29 85 29 b1 02 95 1f 09 2a 85 2a b1 02 95 3e 09 2b 85 2b b1 02 95 fe 09 2c 85 2c b1 02 96 fe 01 09 2d 85 2d b1 02 95 02 09 48 85 48 b1 02 95 0f 09 2e 85 2e 81 02 95 1f 09 2f 85 2f 81 02 95 3e 09 30 85 30 81 02 95 fe 09 31 85 31 81 02 96 fe 01 09 32 85 32 81 02 75 08 96 fe 0f 09 35 85 35 81 02 c0 05 0d 09 02 a1 01 85 01 09 20 35 00 a1 00 09 32 09 42 09 44 09 3c 09 45 15 00 25 01 75 01 95 05 81 02 95 03 81 03 05 01 09 30 75 10 95 01 a4 55 0e 65 11 46 03 0a 26 80 25 81 02 09 31 46 a1 05 26 20 1c 81 02 b4 05 0d 09 30 26 00 01 81 02 06 00 ff 09 01 81 02 c0 85 0c 06 00 ff 09 0c 75 08 95 06 26 ff 00 b1 02 85 0b 09 0b 95 02 b1 02 85 11 09 11 b1 02 85 15 09 15 95 05 b1 02 85 18 09 18 95 0c b1 02 c0 05 0d 09 04 a1 01 85 03 06 00 ff 09 01 75 10 95 01 15 00 27 ff ff 00 00 81 02 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 03 0a 26 80 25 81 02 09 31 46 a1 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 03 0a 26 80 25 81 02 09 31 46 a1 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 54 95 01 75 08 81 02 09 56 75 20 95 01 27 ff ff ff 0f 81 02 85 04 09 55 75 08 95 01 25 0b b1 02 85 0a 06 00 ff 09 03 15 00 b1 02 85 1b 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 01 09 02 a1 01 85 02 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 03 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 c0 c0",
1478
)
1479
1480
1481
class Testn_trig_1b96_0f04(BaseTest.TestTablet):
1482
def create_device(self):
1483
return PenDigitizer(
1484
"uhid test n_trig_1b96_0f04",
1485
rdesc="75 08 15 00 26 ff 00 06 0b ff 09 0b a1 01 95 0f 09 29 85 29 b1 02 95 1f 09 2a 85 2a b1 02 95 3e 09 2b 85 2b b1 02 95 fe 09 2c 85 2c b1 02 96 fe 01 09 2d 85 2d b1 02 95 02 09 48 85 48 b1 02 95 0f 09 2e 85 2e 81 02 95 1f 09 2f 85 2f 81 02 95 3e 09 30 85 30 81 02 95 fe 09 31 85 31 81 02 96 fe 01 09 32 85 32 81 02 75 08 96 fe 0f 09 35 85 35 81 02 c0 05 0d 09 02 a1 01 85 01 09 20 35 00 a1 00 09 32 09 42 09 44 09 3c 09 45 15 00 25 01 75 01 95 05 81 02 95 03 81 03 05 01 09 30 75 10 95 01 a4 55 0e 65 11 46 7f 0b 26 80 25 81 02 09 31 46 78 06 26 20 1c 81 02 b4 05 0d 09 30 26 00 01 81 02 06 00 ff 09 01 81 02 c0 85 0c 06 00 ff 09 0c 75 08 95 06 26 ff 00 b1 02 85 0b 09 0b 95 02 b1 02 85 11 09 11 b1 02 85 15 09 15 95 05 b1 02 85 18 09 18 95 0c b1 02 c0 05 0d 09 04 a1 01 85 03 06 00 ff 09 01 75 10 95 01 15 00 27 ff ff 00 00 81 02 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 7f 0b 26 80 25 81 02 09 31 46 78 06 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 7f 0b 26 80 25 81 02 09 31 46 78 06 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 54 95 01 75 08 81 02 09 56 75 20 95 01 27 ff ff ff 0f 81 02 85 04 09 55 75 08 95 01 25 0b b1 02 85 0a 06 00 ff 09 03 15 00 b1 02 85 1b 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 01 09 02 a1 01 85 02 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 03 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 c0 c0",
1486
)
1487
1488
1489
class Testn_trig_1b96_1000(BaseTest.TestTablet):
1490
def create_device(self):
1491
return PenDigitizer(
1492
"uhid test n_trig_1b96_1000",
1493
rdesc="75 08 15 00 26 ff 00 06 0b ff 09 0b a1 01 95 0f 09 29 85 29 b1 02 95 1f 09 2a 85 2a b1 02 95 3e 09 2b 85 2b b1 02 95 fe 09 2c 85 2c b1 02 96 fe 01 09 2d 85 2d b1 02 95 02 09 48 85 48 b1 02 95 0f 09 2e 85 2e 81 02 95 1f 09 2f 85 2f 81 02 95 3e 09 30 85 30 81 02 95 fe 09 31 85 31 81 02 96 fe 01 09 32 85 32 81 02 75 08 96 fe 0f 09 35 85 35 81 02 c0 05 0d 09 02 a1 01 85 01 09 20 35 00 a1 00 09 32 09 42 09 44 09 3c 09 45 15 00 25 01 75 01 95 05 81 02 95 03 81 03 05 01 09 30 75 10 95 01 a4 55 0e 65 11 46 03 0a 26 80 25 81 02 09 31 46 a1 05 26 20 1c 81 02 b4 05 0d 09 30 26 00 01 81 02 06 00 ff 09 01 81 02 c0 85 0c 06 00 ff 09 0c 75 08 95 06 26 ff 00 b1 02 85 0b 09 0b 95 02 b1 02 85 11 09 11 b1 02 85 15 09 15 95 05 b1 02 85 18 09 18 95 0c b1 02 c0 05 0d 09 04 a1 01 85 03 06 00 ff 09 01 75 10 95 01 15 00 27 ff ff 00 00 81 02 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 03 0a 26 80 25 81 02 09 31 46 a1 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 01 81 03 09 47 81 02 95 05 81 03 75 10 09 51 27 ff ff 00 00 95 01 81 02 05 01 09 30 75 10 95 02 a4 55 0e 65 11 46 03 0a 26 80 25 81 02 09 31 46 a1 05 26 20 1c 81 02 05 0d 09 48 95 01 26 80 25 81 02 09 49 26 20 1c 81 02 b4 06 00 ff 09 02 75 08 95 04 15 00 26 ff 00 81 02 c0 05 0d 09 54 95 01 75 08 81 02 09 56 75 20 95 01 27 ff ff ff 0f 81 02 85 04 09 55 75 08 95 01 25 0b b1 02 85 0a 06 00 ff 09 03 15 00 b1 02 85 1b 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 01 09 02 a1 01 85 02 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 95 06 81 03 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 c0 c0",
1494
)
1495
1496
1497
class TestGXTP_27c6_0113(BaseTest.TestTablet):
1498
def create_device(self):
1499
return GXTP_pen(
1500
"uhid test GXTP_27c6_0113",
1501
rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 55 0e 65 11 35 00 15 00 09 42 25 01 75 01 95 01 81 02 95 07 81 01 95 01 75 08 09 51 81 02 75 10 05 01 26 00 14 46 1f 07 09 30 81 02 26 80 0c 46 77 04 09 31 81 02 05 0d c0 09 22 a1 02 09 42 25 01 75 01 95 01 81 02 95 07 81 01 95 01 75 08 09 51 81 02 75 10 05 01 26 00 14 46 1f 07 09 30 81 02 26 80 0c 46 77 04 09 31 81 02 05 0d c0 09 22 a1 02 09 42 25 01 75 01 95 01 81 02 95 07 81 01 95 01 75 08 09 51 81 02 75 10 05 01 26 00 14 46 1f 07 09 30 81 02 26 80 0c 46 77 04 09 31 81 02 05 0d c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 07 81 01 75 08 09 51 95 01 81 02 05 01 26 00 14 75 10 55 0e 65 11 09 30 35 00 46 1f 07 81 02 26 80 0c 46 77 04 09 31 81 02 05 0d c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 95 07 81 01 75 08 09 51 95 01 81 02 05 01 26 00 14 75 10 55 0e 65 11 09 30 35 00 46 1f 07 81 02 26 80 0c 46 77 04 09 31 81 02 05 0d c0 09 54 15 00 25 7f 75 08 95 01 81 02 85 02 09 55 95 01 25 0a b1 02 85 03 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 0d 09 02 a1 01 85 08 09 20 a1 00 09 42 09 44 09 3c 09 45 15 00 25 01 75 01 95 04 81 02 95 01 81 03 09 32 81 02 95 02 81 03 95 01 75 08 09 51 81 02 05 01 09 30 75 10 95 01 a4 55 0e 65 11 35 00 26 00 14 46 1f 07 81 42 09 31 26 80 0c 46 77 04 81 42 b4 05 0d 09 30 26 ff 0f 81 02 09 3d 65 14 55 0e 36 d8 dc 46 28 23 16 d8 dc 26 28 23 81 02 09 3e 81 02 c0 c0 06 f0 ff 09 01 a1 01 85 0e 09 01 15 00 25 ff 75 08 95 40 91 02 09 01 15 00 25 ff 75 08 95 40 81 02 c0 05 01 09 06 a1 01 85 04 05 07 09 e3 15 00 25 01 75 01 95 01 81 02 95 07 81 03 c0",
1502
)
1503
1504
1505
################################################################################
1506
#
1507
# Windows 8 compatible devices with USI Pen
1508
#
1509
################################################################################
1510
1511
1512
class TestElan_04f3_2A49(BaseTest.TestTablet):
1513
def create_device(self):
1514
return USIPen(
1515
"uhid test Elan_04f3_2A49",
1516
rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 55 0f 65 11 35 00 45 ff 09 48 81 02 09 49 81 02 09 30 81 02 95 01 05 01 a4 26 cf 0f 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 01 81 02 26 77 0a 46 a6 00 09 31 81 02 b4 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 55 0f 65 11 35 00 45 ff 09 48 81 02 09 49 81 02 09 30 81 02 95 01 05 01 a4 26 cf 0f 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 01 81 02 26 77 0a 46 a6 00 09 31 81 02 b4 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 55 0f 65 11 35 00 45 ff 09 48 81 02 09 49 81 02 09 30 81 02 95 01 05 01 a4 26 cf 0f 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 01 81 02 26 77 0a 46 a6 00 09 31 81 02 b4 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 55 0f 65 11 35 00 45 ff 09 48 81 02 09 49 81 02 09 30 81 02 95 01 05 01 a4 26 cf 0f 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 01 81 02 26 77 0a 46 a6 00 09 31 81 02 b4 c0 05 0d 09 22 a1 02 05 0d 09 42 15 00 25 01 75 01 95 01 81 02 75 01 81 03 75 06 09 51 25 3f 81 02 26 ff 00 75 08 55 0f 65 11 35 00 45 ff 09 48 81 02 09 49 81 02 09 30 81 02 95 01 05 01 a4 26 cf 0f 75 10 55 0f 65 11 09 30 35 00 46 26 01 95 01 81 02 26 77 0a 46 a6 00 09 31 81 02 b4 c0 05 0d 09 54 25 7f 96 01 00 75 08 81 02 85 0a 09 55 25 0a b1 02 85 44 06 00 ff 09 c5 16 00 00 26 ff 00 75 08 96 00 01 b1 02 c0 06 ff 01 09 01 a1 01 85 02 16 00 00 26 ff 00 75 08 95 40 09 00 81 02 c0 06 00 ff 09 01 a1 01 85 03 75 08 95 20 09 01 91 02 c0 06 00 ff 09 01 a1 01 85 06 09 03 75 08 95 12 91 02 09 04 75 08 95 03 b1 02 c0 06 01 ff 09 01 a1 01 85 04 15 00 26 ff 00 75 08 95 13 09 00 81 02 c0 05 0d 09 02 a1 01 85 07 35 00 09 20 a1 00 09 32 09 42 09 44 09 3c 09 45 15 00 25 01 75 01 95 05 81 02 95 03 81 03 05 01 09 30 75 10 95 01 a4 55 0f 65 11 46 26 01 26 1c 48 81 42 09 31 46 a6 00 26 bc 2f 81 42 b4 05 0d 09 30 26 00 10 81 02 75 08 95 01 09 3b 25 64 81 42 09 38 15 00 25 02 81 02 09 5c 26 ff 00 81 02 09 5e 81 02 09 70 a1 02 15 01 25 06 09 72 09 73 09 74 09 75 09 76 09 77 81 20 09 5b 25 ff 75 40 81 02 c0 06 00 ff 75 08 95 02 09 01 81 02 c0 05 0d 85 60 09 81 a1 02 09 38 75 08 95 01 15 00 25 02 81 02 09 81 15 01 25 04 09 82 09 83 09 84 09 85 81 20 c0 85 61 09 5c a1 02 15 00 26 ff 00 75 08 95 01 09 38 b1 02 09 5c 26 ff 00 b1 02 09 5d 75 01 95 01 25 01 b1 02 95 07 b1 03 c0 85 62 09 5e a1 02 09 38 15 00 25 02 75 08 95 01 b1 02 09 5e 26 ff 00 b1 02 09 5f 75 01 25 01 b1 02 75 07 b1 03 c0 85 63 09 70 a1 02 75 08 95 01 15 00 25 02 09 38 b1 02 09 70 a1 02 25 06 09 72 09 73 09 74 09 75 09 76 09 77 b1 20 c0 09 71 75 01 25 01 b1 02 75 07 b1 03 c0 85 64 09 80 15 00 25 ff 75 40 95 01 b1 02 85 65 09 44 a1 02 09 38 75 08 95 01 25 02 b1 02 15 01 25 03 09 44 a1 02 09 a4 09 44 09 5a 09 45 09 a3 b1 20 c0 09 5a a1 02 09 a4 09 44 09 5a 09 45 09 a3 b1 20 c0 09 45 a1 02 09 a4 09 44 09 5a 09 45 09 a3 b1 20 c0 c0 85 66 75 08 95 01 05 0d 09 90 a1 02 09 38 25 02 b1 02 09 91 75 10 26 ff 0f b1 02 09 92 75 40 25 ff b1 02 05 06 09 2a 75 08 26 ff 00 a1 02 09 2d b1 02 09 2e b1 02 c0 c0 85 67 05 06 09 2b a1 02 05 0d 25 02 09 38 b1 02 05 06 09 2b a1 02 09 2d 26 ff 00 b1 02 09 2e b1 02 c0 c0 85 68 06 00 ff 09 01 a1 02 05 0d 09 38 75 08 95 01 25 02 b1 02 06 00 ff 09 01 75 10 27 ff ff 00 00 b1 02 c0 85 69 05 0d 09 38 75 08 95 01 15 00 25 02 b1 02 c0 06 00 ff 09 81 a1 01 85 17 75 08 95 1f 09 05 81 02 c0",
1517
input_info=(BusType.I2C, 0x04F3, 0x2A49),
1518
)
1519
1520
1521
class TestGoodix_27c6_0e00(BaseTest.TestTablet):
1522
def create_device(self):
1523
return USIPen(
1524
"uhid test Elan_04f3_2A49",
1525
rdesc="05 0d 09 04 a1 01 85 01 09 22 a1 02 55 0e 65 11 35 00 15 00 09 42 25 01 75 01 95 01 81 02 25 7f 09 30 75 07 81 42 95 01 75 08 09 51 81 02 75 10 05 01 26 04 20 46 e6 09 09 30 81 02 26 60 15 46 9a 06 09 31 81 02 05 0d 55 0f 75 08 25 ff 45 ff 09 48 81 42 09 49 81 42 55 0e c0 09 22 a1 02 09 42 25 01 75 01 95 01 81 02 25 7f 09 30 75 07 81 42 95 01 75 08 09 51 81 02 75 10 05 01 26 04 20 46 e6 09 09 30 81 02 26 60 15 46 9a 06 09 31 81 02 05 0d 55 0f 75 08 25 ff 45 ff 09 48 81 42 09 49 81 42 55 0e c0 09 22 a1 02 09 42 25 01 75 01 95 01 81 02 25 7f 09 30 75 07 81 42 95 01 75 08 09 51 81 02 75 10 05 01 26 04 20 46 e6 09 09 30 81 02 26 60 15 46 9a 06 09 31 81 02 05 0d 55 0f 75 08 25 ff 45 ff 09 48 81 42 09 49 81 42 55 0e c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 25 7f 09 30 75 07 81 42 75 08 09 51 95 01 81 02 05 01 26 04 20 75 10 55 0e 65 11 09 30 35 00 46 e6 09 81 02 26 60 15 46 9a 06 09 31 81 02 05 0d 55 0f 75 08 25 ff 45 ff 09 48 81 42 09 49 81 42 55 0e c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 25 7f 09 30 75 07 81 42 75 08 09 51 95 01 81 02 05 01 26 04 20 75 10 55 0e 65 11 09 30 35 00 46 e6 09 81 02 26 60 15 46 9a 06 09 31 81 02 05 0d 55 0f 75 08 25 ff 45 ff 09 48 81 42 09 49 81 42 55 0e c0 09 54 15 00 25 7f 75 08 95 01 81 02 85 02 09 55 95 01 25 0a b1 02 85 03 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 0d 09 02 a1 01 09 20 a1 00 85 08 05 01 a4 09 30 35 00 46 e6 09 15 00 26 04 20 55 0d 65 13 75 10 95 01 81 02 09 31 46 9a 06 26 60 15 81 02 b4 05 0d 09 38 95 01 75 08 15 00 25 01 81 02 09 30 75 10 26 ff 0f 81 02 09 31 81 02 09 42 09 44 09 5a 09 3c 09 45 09 32 75 01 95 06 25 01 81 02 95 02 81 03 09 3d 55 0e 65 14 36 d8 dc 46 28 23 16 d8 dc 26 28 23 95 01 75 10 81 02 09 3e 81 02 09 41 15 00 27 a0 8c 00 00 35 00 47 a0 8c 00 00 81 02 05 20 0a 53 04 65 00 16 01 f8 26 ff 07 75 10 95 01 81 02 0a 54 04 81 02 0a 55 04 81 02 0a 57 04 81 02 0a 58 04 81 02 0a 59 04 81 02 0a 72 04 81 02 0a 73 04 81 02 0a 74 04 81 02 05 0d 09 3b 15 00 25 64 75 08 81 02 09 5b 25 ff 75 40 81 02 06 00 ff 09 5b 75 20 81 02 05 0d 09 5c 26 ff 00 75 08 81 02 09 5e 81 02 09 70 a1 02 15 01 25 06 09 72 09 73 09 74 09 75 09 76 09 77 81 20 c0 06 00 ff 09 01 15 00 27 ff ff 00 00 75 10 95 01 81 02 85 09 09 81 a1 02 09 81 15 01 25 04 09 82 09 83 09 84 09 85 81 20 c0 85 10 09 5c a1 02 15 00 25 01 75 08 95 01 09 38 b1 02 09 5c 26 ff 00 b1 02 09 5d 75 01 95 01 25 01 b1 02 95 07 b1 03 c0 85 11 09 5e a1 02 09 38 15 00 25 01 75 08 95 01 b1 02 09 5e 26 ff 00 b1 02 09 5f 75 01 25 01 b1 02 75 07 b1 03 c0 85 12 09 70 a1 02 75 08 95 01 15 00 25 01 09 38 b1 02 09 70 a1 02 25 06 09 72 09 73 09 74 09 75 09 76 09 77 b1 20 c0 09 71 75 01 25 01 b1 02 75 07 b1 03 c0 85 13 09 80 15 00 25 ff 75 40 95 01 b1 02 85 14 09 44 a1 02 09 38 75 08 95 01 25 01 b1 02 15 01 25 03 09 44 a1 02 09 a4 09 44 09 5a 09 45 09 a3 b1 20 c0 09 5a a1 02 09 a4 09 44 09 5a 09 45 09 a3 b1 20 c0 09 45 a1 02 09 a4 09 44 09 5a 09 45 09 a3 b1 20 c0 c0 85 15 75 08 95 01 05 0d 09 90 a1 02 09 38 25 01 b1 02 09 91 75 10 26 ff 0f b1 02 09 92 75 40 25 ff b1 02 05 06 09 2a 75 08 26 ff 00 a1 02 09 2d b1 02 09 2e b1 02 c0 c0 85 16 05 06 09 2b a1 02 05 0d 25 01 09 38 b1 02 05 06 09 2b a1 02 09 2d 26 ff 00 b1 02 09 2e b1 02 c0 c0 85 17 06 00 ff 09 01 a1 02 05 0d 09 38 75 08 95 01 25 01 b1 02 06 00 ff 09 01 75 10 27 ff ff 00 00 b1 02 c0 85 18 05 0d 09 38 75 08 95 01 15 00 25 01 b1 02 c0 c0 06 f0 ff 09 01 a1 01 85 0e 09 01 15 00 25 ff 75 08 95 40 91 02 09 01 15 00 25 ff 75 08 95 40 81 02 c0",
1526
input_info=(BusType.I2C, 0x27C6, 0x0E00),
1527
)
1528
1529
1530
class TestXPPen_ArtistPro16Gen2_28bd_095b(BaseTest.TestTablet):
1531
hid_bpfs = [HidBpf("XPPen__ArtistPro16Gen2.bpf.o", True)]
1532
1533
def create_device(self):
1534
dev = XPPen_ArtistPro16Gen2_28bd_095b(
1535
"uhid test XPPen Artist Pro 16 Gen2 28bd 095b",
1536
rdesc="05 0d 09 02 a1 01 85 07 09 20 a1 00 09 42 09 44 09 45 09 3c 15 00 25 01 75 01 95 04 81 02 95 01 81 03 09 32 15 00 25 01 95 01 81 02 95 02 81 03 75 10 95 01 35 00 a4 05 01 09 30 65 13 55 0d 46 ff 34 26 ff 7f 81 02 09 31 46 20 21 26 ff 7f 81 02 b4 09 30 45 00 26 ff 3f 81 42 09 3d 15 81 25 7f 75 08 95 01 81 02 09 3e 15 81 25 7f 81 02 c0 c0",
1537
input_info=(BusType.USB, 0x28BD, 0x095B),
1538
)
1539
return dev
1540
1541
1542
class TestXPPen_Artist24_28bd_093a(BaseTest.TestTablet):
1543
hid_bpfs = [HidBpf("XPPen__Artist24.bpf.o", True)]
1544
1545
def create_device(self):
1546
return XPPen_Artist24_28bd_093a(
1547
"uhid test XPPen Artist 24 28bd 093a",
1548
rdesc="05 0d 09 02 a1 01 85 07 09 20 a1 00 09 42 09 44 09 45 15 00 25 01 75 01 95 03 81 02 95 02 81 03 09 32 95 01 81 02 95 02 81 03 75 10 95 01 35 00 a4 05 01 09 30 65 13 55 0d 46 f0 50 26 ff 7f 81 02 09 31 46 91 2d 26 ff 7f 81 02 b4 09 30 45 00 26 ff 1f 81 42 09 3d 15 81 25 7f 75 08 95 01 81 02 09 3e 15 81 25 7f 81 02 c0 c0",
1549
input_info=(BusType.USB, 0x28BD, 0x093A),
1550
)
1551
1552
1553
class TestHuion_Kamvas_Pro_19_256c_006b(BaseTest.TestTablet):
1554
hid_bpfs = [HidBpf("Huion__Kamvas-Pro-19.bpf.o", True)]
1555
1556
def create_device(self):
1557
return Huion_Kamvas_Pro_19_256c_006b(
1558
"uhid test HUION Huion Tablet_GT1902",
1559
rdesc="05 0d 09 02 a1 01 85 0a 09 20 a1 01 09 42 09 44 09 43 09 3c 09 45 15 00 25 01 75 01 95 06 81 02 09 32 75 01 95 01 81 02 81 03 05 01 09 30 09 31 55 0d 65 33 26 ff 7f 35 00 46 00 08 75 10 95 02 81 02 05 0d 09 30 26 ff 3f 75 10 95 01 81 02 09 3d 09 3e 15 a6 25 5a 75 08 95 02 81 02 c0 c0 05 0d 09 04 a1 01 85 04 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 ff 7f 35 00 46 15 0c 81 42 09 31 26 ff 7f 46 cb 06 81 42 05 0d 09 30 26 ff 1f 75 10 95 01 81 02 c0 05 0d 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 ff 7f 35 00 46 15 0c 81 42 09 31 26 ff 7f 46 cb 06 81 42 05 0d 09 30 26 ff 1f 75 10 95 01 81 02 c0 05 0d 09 56 55 00 65 00 27 ff ff ff 7f 95 01 75 20 81 02 09 54 25 7f 95 01 75 08 81 02 75 08 95 08 81 03 85 05 09 55 25 0a 75 08 95 01 b1 02 06 00 ff 09 c5 85 06 15 00 26 ff 00 75 08 96 00 01 b1 02 c0",
1560
input_info=(BusType.USB, 0x256C, 0x006B),
1561
)
1562
1563
1564
################################################################################
1565
#
1566
# Devices Reporting Distance
1567
#
1568
################################################################################
1569
1570
1571
class TestWacom_2d1f_014b(BaseTest.TestTablet):
1572
def create_device(self):
1573
return Wacom_2d1f_014b(
1574
"uhid test Wacom 2d1f_014b",
1575
rdesc="05 0d 09 02 a1 01 85 02 09 20 a1 00 09 42 09 44 09 45 09 3c 09 5a 09 32 15 00 25 01 75 01 95 06 81 02 95 02 81 03 05 01 09 30 26 88 3e 46 88 3e 65 11 55 0d 75 10 95 01 81 02 09 31 26 60 53 46 60 53 81 02 05 0d 09 30 26 ff 0f 45 00 65 00 55 00 81 02 06 00 ff 09 04 75 08 26 ff 00 46 ff 00 65 11 55 0e 81 02 05 0d 09 3d 75 10 16 d8 dc 26 28 23 36 d8 dc 46 28 23 65 14 81 02 09 3e 81 02 05 01 09 32 16 01 ff 25 00 36 01 ff 45 00 65 11 81 02 05 0d 09 56 15 00 27 ff ff 00 00 35 00 47 ff ff 00 00 66 01 10 55 0c 81 02 45 00 65 00 55 00 c0 09 00 75 08 26 ff 00 b1 12 85 03 09 00 95 12 b1 12 85 05 09 00 95 04 b1 02 85 06 09 00 95 24 b1 02 85 16 09 00 15 00 26 ff 00 95 06 b1 02 85 17 09 00 95 0c b1 02 85 19 09 00 95 01 b1 02 85 0a 09 00 75 08 95 01 15 10 26 ff 00 b1 02 85 1e 09 00 95 10 b1 02 c0 06 00 ff 09 00 a1 01 85 09 05 0d 09 20 a1 00 09 00 15 00 26 ff 00 75 08 95 10 81 02 c0 09 00 95 03 b1 12 c0 06 00 ff 09 02 a1 01 85 07 09 00 96 09 01 b1 02 85 08 09 00 95 03 81 02 09 00 b1 02 85 0e 09 00 96 0a 01 b1 02 c0 05 0d 09 02 a1 01 85 1a 09 20 a1 00 09 42 09 44 09 45 09 3c 09 5a 09 32 15 00 25 01 75 01 95 06 81 02 09 38 25 03 75 02 95 01 81 02 05 01 09 30 26 88 3e 46 88 3e 65 11 55 0d 75 10 95 01 81 02 09 31 26 60 53 46 60 53 81 02 05 0d 09 30 26 ff 0f 46 b0 0f 66 11 e1 55 02 81 02 06 00 ff 09 04 75 08 26 ff 00 46 ff 00 65 11 55 0e 81 02 05 0d 09 3d 75 10 16 d8 dc 26 28 23 36 d8 dc 46 28 23 65 14 81 02 09 3e 81 02 05 01 09 32 16 01 ff 25 00 36 01 ff 45 00 65 11 81 02 05 0d 09 56 15 00 27 ff ff 00 00 35 00 47 ff ff 00 00 66 01 10 55 0c 81 02 45 00 65 00 55 00 c0 c0 06 00 ff 09 00 a1 01 85 1b 05 0d 09 20 a1 00 09 00 26 ff 00 75 08 95 10 81 02 c0 c0",
1576
input_info=(BusType.USB, 0x2D1F, 0x014B),
1577
)
1578
1579