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