xref: /linux/tools/testing/selftests/hid/tests/test_tablet.py (revision b80a75cf6999fb79971b41eaec7af2bb4b514714)
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
13from .base import HidBpf
14import libevdev
15import logging
16import pytest
17from typing import Dict, List, Optional, Tuple
18
19logger = logging.getLogger("hidtools.test.tablet")
20
21
22class BtnTouch(Enum):
23    """Represents whether the BTN_TOUCH event is set to True or False"""
24
25    DOWN = True
26    UP = False
27
28
29class ToolType(Enum):
30    PEN = libevdev.EV_KEY.BTN_TOOL_PEN
31    RUBBER = libevdev.EV_KEY.BTN_TOOL_RUBBER
32
33
34class 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
42class 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
451class Pen(object):
452    def __init__(self, x, y):
453        self.x = x
454        self.y = y
455        self.tipswitch = False
456        self.tippressure = 15
457        self.azimuth = 0
458        self.inrange = False
459        self.width = 10
460        self.height = 10
461        self.barrelswitch = False
462        self.secondarybarrelswitch = False
463        self.invert = False
464        self.eraser = False
465        self.xtilt = 1
466        self.ytilt = 1
467        self.twist = 1
468        self._old_values = None
469        self.current_state = None
470
471    def restore(self):
472        if self._old_values is not None:
473            for i in [
474                "x",
475                "y",
476                "tippressure",
477                "azimuth",
478                "width",
479                "height",
480                "twist",
481                "xtilt",
482                "ytilt",
483            ]:
484                setattr(self, i, getattr(self._old_values, i))
485
486    def backup(self):
487        self._old_values = copy.copy(self)
488
489    def __assert_axis(self, evdev, axis, value):
490        if (
491            axis == libevdev.EV_KEY.BTN_TOOL_RUBBER
492            and evdev.value[libevdev.EV_KEY.BTN_TOOL_RUBBER] is None
493        ):
494            return
495
496        assert (
497            evdev.value[axis] == value
498        ), f"assert evdev.value[{axis}] ({evdev.value[axis]}) != {value}"
499
500    def assert_expected_input_events(self, evdev, button):
501        assert evdev.value[libevdev.EV_ABS.ABS_X] == self.x
502        assert evdev.value[libevdev.EV_ABS.ABS_Y] == self.y
503
504        # assert no other buttons than the tested ones are set
505        buttons = [
506            BtnPressed.PRIMARY_PRESSED,
507            BtnPressed.SECONDARY_PRESSED,
508            BtnPressed.THIRD_PRESSED,
509        ]
510        if button is not None:
511            buttons.remove(button)
512        for b in buttons:
513            assert evdev.value[b.value] is None or evdev.value[b.value] == False
514
515        assert self.current_state == PenState.from_evdev(evdev, button)
516
517
518class PenDigitizer(base.UHIDTestDevice):
519    def __init__(
520        self,
521        name,
522        rdesc_str=None,
523        rdesc=None,
524        application="Pen",
525        physical="Stylus",
526        input_info=(BusType.USB, 1, 2),
527        evdev_name_suffix=None,
528    ):
529        super().__init__(name, application, rdesc_str, rdesc, input_info)
530        self.physical = physical
531        self.cur_application = application
532        if evdev_name_suffix is not None:
533            self.name += evdev_name_suffix
534
535        self.fields = []
536        for r in self.parsed_rdesc.input_reports.values():
537            if r.application_name == self.application:
538                physicals = [f.physical_name for f in r]
539                if self.physical not in physicals and None not in physicals:
540                    continue
541                self.fields = [f.usage_name for f in r]
542
543    def move_to(self, pen, state, button):
544        # fill in the previous values
545        if pen.current_state == PenState.PEN_IS_OUT_OF_RANGE:
546            pen.restore()
547
548        print(f"\n  *** pen is moving to {state} ***")
549
550        if state == PenState.PEN_IS_OUT_OF_RANGE:
551            pen.backup()
552            pen.x = 0
553            pen.y = 0
554            pen.tipswitch = False
555            pen.tippressure = 0
556            pen.azimuth = 0
557            pen.inrange = False
558            pen.width = 0
559            pen.height = 0
560            pen.invert = False
561            pen.eraser = False
562            pen.xtilt = 0
563            pen.ytilt = 0
564            pen.twist = 0
565            pen.barrelswitch = False
566            pen.secondarybarrelswitch = False
567        elif state == PenState.PEN_IS_IN_RANGE:
568            pen.tipswitch = False
569            pen.inrange = True
570            pen.invert = False
571            pen.eraser = False
572            pen.barrelswitch = False
573            pen.secondarybarrelswitch = False
574        elif state == PenState.PEN_IS_IN_CONTACT:
575            pen.tipswitch = True
576            pen.inrange = True
577            pen.invert = False
578            pen.eraser = False
579            pen.barrelswitch = False
580            pen.secondarybarrelswitch = False
581        elif state == PenState.PEN_IS_IN_RANGE_WITH_BUTTON:
582            pen.tipswitch = False
583            pen.inrange = True
584            pen.invert = False
585            pen.eraser = False
586            assert button is not None
587            pen.barrelswitch = button == BtnPressed.PRIMARY_PRESSED
588            pen.secondarybarrelswitch = button == BtnPressed.SECONDARY_PRESSED
589        elif state == PenState.PEN_IS_IN_CONTACT_WITH_BUTTON:
590            pen.tipswitch = True
591            pen.inrange = True
592            pen.invert = False
593            pen.eraser = False
594            assert button is not None
595            pen.barrelswitch = button == BtnPressed.PRIMARY_PRESSED
596            pen.secondarybarrelswitch = button == BtnPressed.SECONDARY_PRESSED
597        elif state == PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT:
598            pen.tipswitch = False
599            pen.inrange = True
600            pen.invert = True
601            pen.eraser = False
602            pen.barrelswitch = False
603            pen.secondarybarrelswitch = False
604        elif state == PenState.PEN_IS_ERASING:
605            pen.tipswitch = False
606            pen.inrange = True
607            pen.invert = False
608            pen.eraser = True
609            pen.barrelswitch = False
610            pen.secondarybarrelswitch = False
611
612        pen.current_state = state
613
614    def event(self, pen, button):
615        rs = []
616        r = self.create_report(application=self.cur_application, data=pen)
617        self.call_input_event(r)
618        rs.append(r)
619        return rs
620
621    def get_report(self, req, rnum, rtype):
622        if rtype != self.UHID_FEATURE_REPORT:
623            return (1, [])
624
625        rdesc = None
626        for v in self.parsed_rdesc.feature_reports.values():
627            if v.report_ID == rnum:
628                rdesc = v
629
630        if rdesc is None:
631            return (1, [])
632
633        return (1, [])
634
635    def set_report(self, req, rnum, rtype, data):
636        if rtype != self.UHID_FEATURE_REPORT:
637            return 1
638
639        rdesc = None
640        for v in self.parsed_rdesc.feature_reports.values():
641            if v.report_ID == rnum:
642                rdesc = v
643
644        if rdesc is None:
645            return 1
646
647        return 1
648
649
650class BaseTest:
651    class TestTablet(base.BaseTestCase.TestUhid):
652        def create_device(self):
653            raise Exception("please reimplement me in subclasses")
654
655        def post(self, uhdev, pen, test_button):
656            r = uhdev.event(pen, test_button)
657            events = uhdev.next_sync_events()
658            self.debug_reports(r, uhdev, events)
659            return events
660
661        def validate_transitions(
662            self, from_state, pen, evdev, events, allow_intermediate_states, button
663        ):
664            # check that the final state is correct
665            pen.assert_expected_input_events(evdev, button)
666
667            state = from_state
668
669            # check that the transitions are valid
670            sync_events = []
671            while libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT) in events:
672                # split the first EV_SYN from the list
673                idx = events.index(libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT))
674                sync_events = events[:idx]
675                events = events[idx + 1 :]
676
677                # now check for a valid transition
678                state = state.apply(sync_events, not allow_intermediate_states, button)
679
680            if events:
681                state = state.apply(sync_events, not allow_intermediate_states, button)
682
683        def _test_states(
684            self, state_list, scribble, allow_intermediate_states, button=None
685        ):
686            """Internal method to test against a list of
687            transition between states.
688            state_list is a list of PenState objects
689            scribble is a boolean which tells if we need
690            to wobble a little the X,Y coordinates of the pen
691            between each state transition."""
692            uhdev = self.uhdev
693            evdev = uhdev.get_evdev()
694
695            cur_state = PenState.PEN_IS_OUT_OF_RANGE
696
697            p = Pen(50, 60)
698            uhdev.move_to(p, PenState.PEN_IS_OUT_OF_RANGE, button)
699            events = self.post(uhdev, p, button)
700            self.validate_transitions(
701                cur_state, p, evdev, events, allow_intermediate_states, button
702            )
703
704            cur_state = p.current_state
705
706            for state in state_list:
707                if scribble and cur_state != PenState.PEN_IS_OUT_OF_RANGE:
708                    p.x += 1
709                    p.y -= 1
710                    events = self.post(uhdev, p, button)
711                    self.validate_transitions(
712                        cur_state, p, evdev, events, allow_intermediate_states, button
713                    )
714                    assert len(events) >= 3  # X, Y, SYN
715                uhdev.move_to(p, state, button)
716                if scribble and state != PenState.PEN_IS_OUT_OF_RANGE:
717                    p.x += 1
718                    p.y -= 1
719                events = self.post(uhdev, p, button)
720                self.validate_transitions(
721                    cur_state, p, evdev, events, allow_intermediate_states, button
722                )
723                cur_state = p.current_state
724
725        @pytest.mark.parametrize("scribble", [True, False], ids=["scribble", "static"])
726        @pytest.mark.parametrize(
727            "state_list",
728            [pytest.param(v, id=k) for k, v in PenState.legal_transitions().items()],
729        )
730        def test_valid_pen_states(self, state_list, scribble):
731            """This is the first half of the Windows Pen Implementation state machine:
732            we don't have Invert nor Erase bits, so just move in/out-of-range or proximity.
733            https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states
734            """
735            self._test_states(state_list, scribble, allow_intermediate_states=False)
736
737        @pytest.mark.parametrize("scribble", [True, False], ids=["scribble", "static"])
738        @pytest.mark.parametrize(
739            "state_list",
740            [
741                pytest.param(v, id=k)
742                for k, v in PenState.tolerated_transitions().items()
743            ],
744        )
745        def test_tolerated_pen_states(self, state_list, scribble):
746            """This is not adhering to the Windows Pen Implementation state machine
747            but we should expect the kernel to behave properly, mostly for historical
748            reasons."""
749            self._test_states(state_list, scribble, allow_intermediate_states=True)
750
751        @pytest.mark.skip_if_uhdev(
752            lambda uhdev: "Barrel Switch" not in uhdev.fields,
753            "Device not compatible, missing Barrel Switch usage",
754        )
755        @pytest.mark.parametrize("scribble", [True, False], ids=["scribble", "static"])
756        @pytest.mark.parametrize(
757            "state_list",
758            [
759                pytest.param(v, id=k)
760                for k, v in PenState.legal_transitions_with_button().items()
761            ],
762        )
763        def test_valid_primary_button_pen_states(self, state_list, scribble):
764            """Rework the transition state machine by adding the primary button."""
765            self._test_states(
766                state_list,
767                scribble,
768                allow_intermediate_states=False,
769                button=BtnPressed.PRIMARY_PRESSED,
770            )
771
772        @pytest.mark.skip_if_uhdev(
773            lambda uhdev: "Secondary Barrel Switch" not in uhdev.fields,
774            "Device not compatible, missing Secondary Barrel Switch usage",
775        )
776        @pytest.mark.parametrize("scribble", [True, False], ids=["scribble", "static"])
777        @pytest.mark.parametrize(
778            "state_list",
779            [
780                pytest.param(v, id=k)
781                for k, v in PenState.legal_transitions_with_button().items()
782            ],
783        )
784        def test_valid_secondary_button_pen_states(self, state_list, scribble):
785            """Rework the transition state machine by adding the secondary button."""
786            self._test_states(
787                state_list,
788                scribble,
789                allow_intermediate_states=False,
790                button=BtnPressed.SECONDARY_PRESSED,
791            )
792
793        @pytest.mark.skip_if_uhdev(
794            lambda uhdev: "Third Barrel Switch" not in uhdev.fields,
795            "Device not compatible, missing Third Barrel Switch usage",
796        )
797        @pytest.mark.parametrize("scribble", [True, False], ids=["scribble", "static"])
798        @pytest.mark.parametrize(
799            "state_list",
800            [
801                pytest.param(v, id=k)
802                for k, v in PenState.legal_transitions_with_button().items()
803            ],
804        )
805        def test_valid_third_button_pen_states(self, state_list, scribble):
806            """Rework the transition state machine by adding the secondary button."""
807            self._test_states(
808                state_list,
809                scribble,
810                allow_intermediate_states=False,
811                button=BtnPressed.THIRD_PRESSED,
812            )
813
814        @pytest.mark.skip_if_uhdev(
815            lambda uhdev: "Invert" not in uhdev.fields,
816            "Device not compatible, missing Invert usage",
817        )
818        @pytest.mark.parametrize("scribble", [True, False], ids=["scribble", "static"])
819        @pytest.mark.parametrize(
820            "state_list",
821            [
822                pytest.param(v, id=k)
823                for k, v in PenState.legal_transitions_with_invert().items()
824            ],
825        )
826        def test_valid_invert_pen_states(self, state_list, scribble):
827            """This is the second half of the Windows Pen Implementation state machine:
828            we now have Invert and Erase bits, so move in/out or proximity with the intend
829            to erase.
830            https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states
831            """
832            self._test_states(state_list, scribble, allow_intermediate_states=False)
833
834        @pytest.mark.skip_if_uhdev(
835            lambda uhdev: "Invert" not in uhdev.fields,
836            "Device not compatible, missing Invert usage",
837        )
838        @pytest.mark.parametrize("scribble", [True, False], ids=["scribble", "static"])
839        @pytest.mark.parametrize(
840            "state_list",
841            [
842                pytest.param(v, id=k)
843                for k, v in PenState.tolerated_transitions_with_invert().items()
844            ],
845        )
846        def test_tolerated_invert_pen_states(self, state_list, scribble):
847            """This is the second half of the Windows Pen Implementation state machine:
848            we now have Invert and Erase bits, so move in/out or proximity with the intend
849            to erase.
850            https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states
851            """
852            self._test_states(state_list, scribble, allow_intermediate_states=True)
853
854        @pytest.mark.skip_if_uhdev(
855            lambda uhdev: "Invert" not in uhdev.fields,
856            "Device not compatible, missing Invert usage",
857        )
858        @pytest.mark.parametrize("scribble", [True, False], ids=["scribble", "static"])
859        @pytest.mark.parametrize(
860            "state_list",
861            [pytest.param(v, id=k) for k, v in PenState.broken_transitions().items()],
862        )
863        def test_tolerated_broken_pen_states(self, state_list, scribble):
864            """Those tests are definitely not part of the Windows specification.
865            However, a half broken device might export those transitions.
866            For example, a pen that has the eraser button might wobble between
867            touching and erasing if the tablet doesn't enforce the Windows
868            state machine."""
869            self._test_states(state_list, scribble, allow_intermediate_states=True)
870
871
872class GXTP_pen(PenDigitizer):
873    def event(self, pen, test_button):
874        if not hasattr(self, "prev_tip_state"):
875            self.prev_tip_state = False
876
877        internal_pen = copy.copy(pen)
878
879        # bug in the controller: when the pen touches the
880        # surface, in-range stays to 1, but when
881        # the pen moves in-range gets reverted to 0
882        if pen.tipswitch and self.prev_tip_state:
883            internal_pen.inrange = False
884
885        self.prev_tip_state = pen.tipswitch
886
887        # another bug in the controller: when the pen is
888        # inverted, invert is set to 1, but as soon as
889        # the pen touches the surface, eraser is correctly
890        # set to 1 but invert is released
891        if pen.eraser:
892            internal_pen.invert = False
893
894        return super().event(internal_pen, test_button)
895
896
897class USIPen(PenDigitizer):
898    pass
899
900
901class XPPen_ArtistPro16Gen2_28bd_095b(PenDigitizer):
902    """
903    Pen with two buttons and a rubber end, but which reports
904    the second button as an eraser
905    """
906
907    def __init__(
908        self,
909        name,
910        rdesc_str=None,
911        rdesc=None,
912        application="Pen",
913        physical="Stylus",
914        input_info=(BusType.USB, 0x28BD, 0x095B),
915        evdev_name_suffix=None,
916    ):
917        super().__init__(
918            name, rdesc_str, rdesc, application, physical, input_info, evdev_name_suffix
919        )
920        self.fields.append("Secondary Barrel Switch")
921
922    def move_to(self, pen, state, button):
923        # fill in the previous values
924        if pen.current_state == PenState.PEN_IS_OUT_OF_RANGE:
925            pen.restore()
926
927        print(f"\n  *** pen is moving to {state} ***")
928
929        if state == PenState.PEN_IS_OUT_OF_RANGE:
930            pen.backup()
931            pen.x = 0
932            pen.y = 0
933            pen.tipswitch = False
934            pen.tippressure = 0
935            pen.azimuth = 0
936            pen.inrange = False
937            pen.width = 0
938            pen.height = 0
939            pen.invert = False
940            pen.eraser = False
941            pen.xtilt = 0
942            pen.ytilt = 0
943            pen.twist = 0
944            pen.barrelswitch = False
945        elif state == PenState.PEN_IS_IN_RANGE:
946            pen.tipswitch = False
947            pen.inrange = True
948            pen.invert = False
949            pen.eraser = False
950            pen.barrelswitch = False
951        elif state == PenState.PEN_IS_IN_CONTACT:
952            pen.tipswitch = True
953            pen.inrange = True
954            pen.invert = False
955            pen.eraser = False
956            pen.barrelswitch = False
957        elif state == PenState.PEN_IS_IN_RANGE_WITH_BUTTON:
958            pen.tipswitch = False
959            pen.inrange = True
960            pen.invert = False
961            assert button is not None
962            pen.barrelswitch = button == BtnPressed.PRIMARY_PRESSED
963            pen.eraser = button == BtnPressed.SECONDARY_PRESSED
964        elif state == PenState.PEN_IS_IN_CONTACT_WITH_BUTTON:
965            pen.tipswitch = True
966            pen.inrange = True
967            pen.invert = False
968            assert button is not None
969            pen.barrelswitch = button == BtnPressed.PRIMARY_PRESSED
970            pen.eraser = button == BtnPressed.SECONDARY_PRESSED
971        elif state == PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT:
972            pen.tipswitch = False
973            pen.inrange = True
974            pen.invert = True
975            pen.eraser = False
976            pen.barrelswitch = False
977        elif state == PenState.PEN_IS_ERASING:
978            pen.tipswitch = True
979            pen.inrange = True
980            pen.invert = True
981            pen.eraser = False
982            pen.barrelswitch = False
983
984        pen.current_state = state
985
986    def event(self, pen, test_button):
987        import math
988
989        pen_copy = copy.copy(pen)
990        width = 13.567
991        height = 8.480
992        tip_height = 0.055677699
993        hx = tip_height * (32767 / width)
994        hy = tip_height * (32767 / height)
995        if pen_copy.xtilt != 0:
996            pen_copy.x += round(hx * math.sin(math.radians(pen_copy.xtilt)))
997        if pen_copy.ytilt != 0:
998            pen_copy.y += round(hy * math.sin(math.radians(pen_copy.ytilt)))
999
1000        return super().event(pen_copy, test_button)
1001
1002
1003class XPPen_Artist24_28bd_093a(PenDigitizer):
1004    """
1005    Pen that reports secondary barrel switch through eraser
1006    """
1007
1008    def __init__(
1009        self,
1010        name,
1011        rdesc_str=None,
1012        rdesc=None,
1013        application="Pen",
1014        physical="Stylus",
1015        input_info=(BusType.USB, 0x28BD, 0x093A),
1016        evdev_name_suffix=None,
1017    ):
1018        super().__init__(
1019            name, rdesc_str, rdesc, application, physical, input_info, evdev_name_suffix
1020        )
1021        self.fields.append("Secondary Barrel Switch")
1022        self.previous_state = PenState.PEN_IS_OUT_OF_RANGE
1023
1024    def move_to(self, pen, state, button, debug=True):
1025        # fill in the previous values
1026        if pen.current_state == PenState.PEN_IS_OUT_OF_RANGE:
1027            pen.restore()
1028
1029        if debug:
1030            print(f"\n  *** pen is moving to {state} ***")
1031
1032        if state == PenState.PEN_IS_OUT_OF_RANGE:
1033            pen.backup()
1034            pen.tipswitch = False
1035            pen.tippressure = 0
1036            pen.azimuth = 0
1037            pen.inrange = False
1038            pen.width = 0
1039            pen.height = 0
1040            pen.invert = False
1041            pen.eraser = False
1042            pen.xtilt = 0
1043            pen.ytilt = 0
1044            pen.twist = 0
1045            pen.barrelswitch = False
1046        elif state == PenState.PEN_IS_IN_RANGE:
1047            pen.tipswitch = False
1048            pen.inrange = True
1049            pen.invert = False
1050            pen.eraser = False
1051            pen.barrelswitch = False
1052        elif state == PenState.PEN_IS_IN_CONTACT:
1053            pen.tipswitch = True
1054            pen.inrange = True
1055            pen.invert = False
1056            pen.eraser = False
1057            pen.barrelswitch = False
1058        elif state == PenState.PEN_IS_IN_RANGE_WITH_BUTTON:
1059            pen.tipswitch = False
1060            pen.inrange = True
1061            pen.invert = False
1062            assert button is not None
1063            pen.barrelswitch = button == BtnPressed.PRIMARY_PRESSED
1064            pen.eraser = button == BtnPressed.SECONDARY_PRESSED
1065        elif state == PenState.PEN_IS_IN_CONTACT_WITH_BUTTON:
1066            pen.tipswitch = True
1067            pen.inrange = True
1068            pen.invert = False
1069            assert button is not None
1070            pen.barrelswitch = button == BtnPressed.PRIMARY_PRESSED
1071            pen.eraser = button == BtnPressed.SECONDARY_PRESSED
1072
1073        pen.current_state = state
1074
1075    def send_intermediate_state(self, pen, state, button):
1076        intermediate_pen = copy.copy(pen)
1077        self.move_to(intermediate_pen, state, button, debug=False)
1078        return super().event(intermediate_pen, button)
1079
1080    def event(self, pen, button):
1081        rs = []
1082
1083        # the pen reliably sends in-range events in a normal case (non emulation of eraser mode)
1084        if self.previous_state == PenState.PEN_IS_IN_CONTACT:
1085            if pen.current_state == PenState.PEN_IS_OUT_OF_RANGE:
1086                rs.extend(
1087                    self.send_intermediate_state(pen, PenState.PEN_IS_IN_RANGE, button)
1088                )
1089
1090        if button == BtnPressed.SECONDARY_PRESSED:
1091            if self.previous_state == PenState.PEN_IS_IN_RANGE:
1092                if pen.current_state == PenState.PEN_IS_IN_RANGE_WITH_BUTTON:
1093                    rs.extend(
1094                        self.send_intermediate_state(
1095                            pen, PenState.PEN_IS_OUT_OF_RANGE, button
1096                        )
1097                    )
1098
1099            if self.previous_state == PenState.PEN_IS_IN_RANGE_WITH_BUTTON:
1100                if pen.current_state == PenState.PEN_IS_IN_RANGE:
1101                    rs.extend(
1102                        self.send_intermediate_state(
1103                            pen, PenState.PEN_IS_OUT_OF_RANGE, button
1104                        )
1105                    )
1106
1107            if self.previous_state == PenState.PEN_IS_IN_CONTACT:
1108                if pen.current_state == PenState.PEN_IS_IN_CONTACT_WITH_BUTTON:
1109                    rs.extend(
1110                        self.send_intermediate_state(
1111                            pen, PenState.PEN_IS_OUT_OF_RANGE, button
1112                        )
1113                    )
1114                    rs.extend(
1115                        self.send_intermediate_state(
1116                            pen, PenState.PEN_IS_IN_RANGE_WITH_BUTTON, button
1117                        )
1118                    )
1119
1120            if self.previous_state == PenState.PEN_IS_IN_CONTACT_WITH_BUTTON:
1121                if pen.current_state == PenState.PEN_IS_IN_CONTACT:
1122                    rs.extend(
1123                        self.send_intermediate_state(
1124                            pen, PenState.PEN_IS_OUT_OF_RANGE, button
1125                        )
1126                    )
1127                    rs.extend(
1128                        self.send_intermediate_state(
1129                            pen, PenState.PEN_IS_IN_RANGE, button
1130                        )
1131                    )
1132
1133        rs.extend(super().event(pen, button))
1134        self.previous_state = pen.current_state
1135        return rs
1136
1137
1138class Huion_Kamvas_Pro_19_256c_006b(PenDigitizer):
1139    """
1140    Pen that reports secondary barrel switch through secondary TipSwtich
1141    and 3rd button through Invert
1142    """
1143
1144    def __init__(
1145        self,
1146        name,
1147        rdesc_str=None,
1148        rdesc=None,
1149        application="Stylus",
1150        physical=None,
1151        input_info=(BusType.USB, 0x256C, 0x006B),
1152        evdev_name_suffix=None,
1153    ):
1154        super().__init__(
1155            name, rdesc_str, rdesc, application, physical, input_info, evdev_name_suffix
1156        )
1157        self.fields.append("Secondary Barrel Switch")
1158        self.fields.append("Third Barrel Switch")
1159        self.previous_state = PenState.PEN_IS_OUT_OF_RANGE
1160
1161    def move_to(self, pen, state, button, debug=True):
1162        # fill in the previous values
1163        if pen.current_state == PenState.PEN_IS_OUT_OF_RANGE:
1164            pen.restore()
1165
1166        if debug:
1167            print(f"\n  *** pen is moving to {state} ***")
1168
1169        if state == PenState.PEN_IS_OUT_OF_RANGE:
1170            pen.backup()
1171            pen.tipswitch = False
1172            pen.tippressure = 0
1173            pen.azimuth = 0
1174            pen.inrange = False
1175            pen.width = 0
1176            pen.height = 0
1177            pen.invert = False
1178            pen.eraser = False
1179            pen.xtilt = 0
1180            pen.ytilt = 0
1181            pen.twist = 0
1182            pen.barrelswitch = False
1183            pen.secondarytipswitch = False
1184        elif state == PenState.PEN_IS_IN_RANGE:
1185            pen.tipswitch = False
1186            pen.inrange = True
1187            pen.invert = False
1188            pen.eraser = False
1189            pen.barrelswitch = False
1190            pen.secondarytipswitch = False
1191        elif state == PenState.PEN_IS_IN_CONTACT:
1192            pen.tipswitch = True
1193            pen.inrange = True
1194            pen.invert = False
1195            pen.eraser = False
1196            pen.barrelswitch = False
1197            pen.secondarytipswitch = False
1198        elif state == PenState.PEN_IS_IN_RANGE_WITH_BUTTON:
1199            pen.tipswitch = False
1200            pen.inrange = True
1201            pen.eraser = False
1202            assert button is not None
1203            pen.barrelswitch = button == BtnPressed.PRIMARY_PRESSED
1204            pen.secondarytipswitch = button == BtnPressed.SECONDARY_PRESSED
1205            pen.invert = button == BtnPressed.THIRD_PRESSED
1206        elif state == PenState.PEN_IS_IN_CONTACT_WITH_BUTTON:
1207            pen.tipswitch = True
1208            pen.inrange = True
1209            pen.eraser = False
1210            assert button is not None
1211            pen.barrelswitch = button == BtnPressed.PRIMARY_PRESSED
1212            pen.secondarytipswitch = button == BtnPressed.SECONDARY_PRESSED
1213            pen.invert = button == BtnPressed.THIRD_PRESSED
1214        elif state == PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT:
1215            pen.tipswitch = False
1216            pen.inrange = True
1217            pen.invert = True
1218            pen.eraser = False
1219            pen.barrelswitch = False
1220            pen.secondarytipswitch = False
1221        elif state == PenState.PEN_IS_ERASING:
1222            pen.tipswitch = False
1223            pen.inrange = True
1224            pen.invert = False
1225            pen.eraser = True
1226            pen.barrelswitch = False
1227            pen.secondarytipswitch = False
1228
1229        pen.current_state = state
1230
1231    def call_input_event(self, report):
1232        if report[0] == 0x0A:
1233            # ensures the original second Eraser usage is null
1234            report[1] &= 0xDF
1235
1236            # ensures the original last bit is equal to bit 6 (In Range)
1237            if report[1] & 0x40:
1238                report[1] |= 0x80
1239
1240        super().call_input_event(report)
1241
1242    def send_intermediate_state(self, pen, state, test_button):
1243        intermediate_pen = copy.copy(pen)
1244        self.move_to(intermediate_pen, state, test_button, debug=False)
1245        return super().event(intermediate_pen, test_button)
1246
1247    def event(self, pen, button):
1248        rs = []
1249
1250        # it's not possible to go between eraser mode or not without
1251        # going out-of-prox: the eraser mode is activated by presenting
1252        # the tail of the pen
1253        if self.previous_state in (
1254            PenState.PEN_IS_IN_RANGE,
1255            PenState.PEN_IS_IN_RANGE_WITH_BUTTON,
1256            PenState.PEN_IS_IN_CONTACT,
1257            PenState.PEN_IS_IN_CONTACT_WITH_BUTTON,
1258        ) and pen.current_state in (
1259            PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
1260            PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT_WITH_BUTTON,
1261            PenState.PEN_IS_ERASING,
1262            PenState.PEN_IS_ERASING_WITH_BUTTON,
1263        ):
1264            rs.extend(
1265                self.send_intermediate_state(pen, PenState.PEN_IS_OUT_OF_RANGE, button)
1266            )
1267
1268        # same than above except from eraser to normal
1269        if self.previous_state in (
1270            PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
1271            PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT_WITH_BUTTON,
1272            PenState.PEN_IS_ERASING,
1273            PenState.PEN_IS_ERASING_WITH_BUTTON,
1274        ) and pen.current_state in (
1275            PenState.PEN_IS_IN_RANGE,
1276            PenState.PEN_IS_IN_RANGE_WITH_BUTTON,
1277            PenState.PEN_IS_IN_CONTACT,
1278            PenState.PEN_IS_IN_CONTACT_WITH_BUTTON,
1279        ):
1280            rs.extend(
1281                self.send_intermediate_state(pen, PenState.PEN_IS_OUT_OF_RANGE, button)
1282            )
1283
1284        if self.previous_state == PenState.PEN_IS_OUT_OF_RANGE:
1285            if pen.current_state == PenState.PEN_IS_IN_RANGE_WITH_BUTTON:
1286                rs.extend(
1287                    self.send_intermediate_state(pen, PenState.PEN_IS_IN_RANGE, button)
1288                )
1289
1290        rs.extend(super().event(pen, button))
1291        self.previous_state = pen.current_state
1292        return rs
1293
1294
1295################################################################################
1296#
1297# Windows 7 compatible devices
1298#
1299################################################################################
1300# class TestEgalax_capacitive_0eef_7224(BaseTest.TestTablet):
1301#     def create_device(self):
1302#         return PenDigitizer('uhid test egalax-capacitive_0eef_7224',
1303#                             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',
1304#                             input_info=(BusType.USB, 0x0eef, 0x7224),
1305#                             evdev_name_suffix=' Touchscreen')
1306#
1307#
1308# class TestEgalax_capacitive_0eef_72fa(BaseTest.TestTablet):
1309#     def create_device(self):
1310#         return PenDigitizer('uhid test egalax-capacitive_0eef_72fa',
1311#                             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',
1312#                             input_info=(BusType.USB, 0x0eef, 0x72fa),
1313#                             evdev_name_suffix=' Touchscreen')
1314#
1315#
1316# class TestEgalax_capacitive_0eef_7336(BaseTest.TestTablet):
1317#     def create_device(self):
1318#         return PenDigitizer('uhid test egalax-capacitive_0eef_7336',
1319#                             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',
1320#                             input_info=(BusType.USB, 0x0eef, 0x7336),
1321#                             evdev_name_suffix=' Touchscreen')
1322#
1323#
1324# class TestEgalax_capacitive_0eef_7337(BaseTest.TestTablet):
1325#     def create_device(self):
1326#         return PenDigitizer('uhid test egalax-capacitive_0eef_7337',
1327#                             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',
1328#                             input_info=(BusType.USB, 0x0eef, 0x7337),
1329#                             evdev_name_suffix=' Touchscreen')
1330#
1331#
1332# class TestEgalax_capacitive_0eef_7349(BaseTest.TestTablet):
1333#     def create_device(self):
1334#         return PenDigitizer('uhid test egalax-capacitive_0eef_7349',
1335#                             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',
1336#                             input_info=(BusType.USB, 0x0eef, 0x7349),
1337#                             evdev_name_suffix=' Touchscreen')
1338#
1339#
1340# class TestEgalax_capacitive_0eef_73f4(BaseTest.TestTablet):
1341#     def create_device(self):
1342#         return PenDigitizer('uhid test egalax-capacitive_0eef_73f4',
1343#                             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',
1344#                             input_info=(BusType.USB, 0x0eef, 0x73f4),
1345#                             evdev_name_suffix=' Touchscreen')
1346#
1347#  bogus: BTN_TOOL_PEN is not emitted
1348# class TestIrtouch_6615_0070(BaseTest.TestTablet):
1349#     def create_device(self):
1350#         return PenDigitizer('uhid test irtouch_6615_0070',
1351#                             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',
1352#                             input_info=(BusType.USB, 0x6615, 0x0070))
1353
1354
1355class TestNexio_1870_0100(BaseTest.TestTablet):
1356    def create_device(self):
1357        return PenDigitizer(
1358            "uhid test nexio_1870_0100",
1359            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",
1360            input_info=(BusType.USB, 0x1870, 0x0100),
1361        )
1362
1363
1364class TestNexio_1870_010d(BaseTest.TestTablet):
1365    def create_device(self):
1366        return PenDigitizer(
1367            "uhid test nexio_1870_010d",
1368            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",
1369            input_info=(BusType.USB, 0x1870, 0x010D),
1370        )
1371
1372
1373class TestNexio_1870_0119(BaseTest.TestTablet):
1374    def create_device(self):
1375        return PenDigitizer(
1376            "uhid test nexio_1870_0119",
1377            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",
1378            input_info=(BusType.USB, 0x1870, 0x0119),
1379        )
1380
1381
1382################################################################################
1383#
1384# Windows 8 compatible devices
1385#
1386################################################################################
1387
1388# bogus: application is 'undefined'
1389# class Testatmel_03eb_8409(BaseTest.TestTablet):
1390#     def create_device(self):
1391#         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')
1392
1393
1394class Testatmel_03eb_840b(BaseTest.TestTablet):
1395    def create_device(self):
1396        return PenDigitizer(
1397            "uhid test atmel_03eb_840b",
1398            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",
1399        )
1400
1401
1402class Testn_trig_1b96_0c01(BaseTest.TestTablet):
1403    def create_device(self):
1404        return PenDigitizer(
1405            "uhid test n_trig_1b96_0c01",
1406            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",
1407        )
1408
1409
1410class Testn_trig_1b96_0c03(BaseTest.TestTablet):
1411    def create_device(self):
1412        return PenDigitizer(
1413            "uhid test n_trig_1b96_0c03",
1414            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",
1415        )
1416
1417
1418class Testn_trig_1b96_0f00(BaseTest.TestTablet):
1419    def create_device(self):
1420        return PenDigitizer(
1421            "uhid test n_trig_1b96_0f00",
1422            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",
1423        )
1424
1425
1426class Testn_trig_1b96_0f04(BaseTest.TestTablet):
1427    def create_device(self):
1428        return PenDigitizer(
1429            "uhid test n_trig_1b96_0f04",
1430            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",
1431        )
1432
1433
1434class Testn_trig_1b96_1000(BaseTest.TestTablet):
1435    def create_device(self):
1436        return PenDigitizer(
1437            "uhid test n_trig_1b96_1000",
1438            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",
1439        )
1440
1441
1442class TestGXTP_27c6_0113(BaseTest.TestTablet):
1443    def create_device(self):
1444        return GXTP_pen(
1445            "uhid test GXTP_27c6_0113",
1446            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",
1447        )
1448
1449
1450################################################################################
1451#
1452# Windows 8 compatible devices with USI Pen
1453#
1454################################################################################
1455
1456
1457class TestElan_04f3_2A49(BaseTest.TestTablet):
1458    def create_device(self):
1459        return USIPen(
1460            "uhid test Elan_04f3_2A49",
1461            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",
1462            input_info=(BusType.I2C, 0x04F3, 0x2A49),
1463        )
1464
1465
1466class TestGoodix_27c6_0e00(BaseTest.TestTablet):
1467    def create_device(self):
1468        return USIPen(
1469            "uhid test Elan_04f3_2A49",
1470            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",
1471            input_info=(BusType.I2C, 0x27C6, 0x0E00),
1472        )
1473
1474
1475class TestXPPen_ArtistPro16Gen2_28bd_095b(BaseTest.TestTablet):
1476    hid_bpfs = [HidBpf("XPPen__ArtistPro16Gen2.bpf.o", True)]
1477
1478    def create_device(self):
1479        dev = XPPen_ArtistPro16Gen2_28bd_095b(
1480            "uhid test XPPen Artist Pro 16 Gen2 28bd 095b",
1481            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",
1482            input_info=(BusType.USB, 0x28BD, 0x095B),
1483        )
1484        return dev
1485
1486
1487class TestXPPen_Artist24_28bd_093a(BaseTest.TestTablet):
1488    hid_bpfs = [HidBpf("XPPen__Artist24.bpf.o", True)]
1489
1490    def create_device(self):
1491        return XPPen_Artist24_28bd_093a(
1492            "uhid test XPPen Artist 24 28bd 093a",
1493            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",
1494            input_info=(BusType.USB, 0x28BD, 0x093A),
1495        )
1496
1497
1498class TestHuion_Kamvas_Pro_19_256c_006b(BaseTest.TestTablet):
1499    hid_bpfs = [HidBpf("Huion__Kamvas-Pro-19.bpf.o", True)]
1500
1501    def create_device(self):
1502        return Huion_Kamvas_Pro_19_256c_006b(
1503            "uhid test HUION Huion Tablet_GT1902",
1504            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",
1505            input_info=(BusType.USB, 0x256C, 0x006B),
1506        )
1507