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