xref: /linux/tools/testing/selftests/hid/tests/test_tablet.py (revision be3382ecdf317f005e7d47356d0a9256cc36dd88)
1#!/bin/env python3
2# SPDX-License-Identifier: GPL-2.0
3# -*- coding: utf-8 -*-
4#
5# Copyright (c) 2021 Benjamin Tissoires <benjamin.tissoires@gmail.com>
6# Copyright (c) 2021 Red Hat, Inc.
7#
8
9from . import base
10import copy
11from enum import Enum
12from hidtools.util import BusType
13import libevdev
14import logging
15import pytest
16from typing import Dict, List, Optional, Tuple
17
18logger = logging.getLogger("hidtools.test.tablet")
19
20
21class BtnTouch(Enum):
22    """Represents whether the BTN_TOUCH event is set to True or False"""
23
24    DOWN = True
25    UP = False
26
27
28class ToolType(Enum):
29    PEN = libevdev.EV_KEY.BTN_TOOL_PEN
30    RUBBER = libevdev.EV_KEY.BTN_TOOL_RUBBER
31
32
33class BtnPressed(Enum):
34    """Represents whether a button is pressed on the stylus"""
35
36    PRIMARY_PRESSED = libevdev.EV_KEY.BTN_STYLUS
37    SECONDARY_PRESSED = libevdev.EV_KEY.BTN_STYLUS2
38
39
40class PenState(Enum):
41    """Pen states according to Microsoft reference:
42    https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states
43
44    We extend it with the various buttons when we need to check them.
45    """
46
47    PEN_IS_OUT_OF_RANGE = BtnTouch.UP, None, None
48    PEN_IS_IN_RANGE = BtnTouch.UP, ToolType.PEN, None
49    PEN_IS_IN_RANGE_WITH_BUTTON = BtnTouch.UP, ToolType.PEN, BtnPressed.PRIMARY_PRESSED
50    PEN_IS_IN_RANGE_WITH_SECOND_BUTTON = (
51        BtnTouch.UP,
52        ToolType.PEN,
53        BtnPressed.SECONDARY_PRESSED,
54    )
55    PEN_IS_IN_CONTACT = BtnTouch.DOWN, ToolType.PEN, None
56    PEN_IS_IN_CONTACT_WITH_BUTTON = (
57        BtnTouch.DOWN,
58        ToolType.PEN,
59        BtnPressed.PRIMARY_PRESSED,
60    )
61    PEN_IS_IN_CONTACT_WITH_SECOND_BUTTON = (
62        BtnTouch.DOWN,
63        ToolType.PEN,
64        BtnPressed.SECONDARY_PRESSED,
65    )
66    PEN_IS_IN_RANGE_WITH_ERASING_INTENT = BtnTouch.UP, ToolType.RUBBER, None
67    PEN_IS_IN_RANGE_WITH_ERASING_INTENT_WITH_BUTTON = (
68        BtnTouch.UP,
69        ToolType.RUBBER,
70        BtnPressed.PRIMARY_PRESSED,
71    )
72    PEN_IS_IN_RANGE_WITH_ERASING_INTENT_WITH_SECOND_BUTTON = (
73        BtnTouch.UP,
74        ToolType.RUBBER,
75        BtnPressed.SECONDARY_PRESSED,
76    )
77    PEN_IS_ERASING = BtnTouch.DOWN, ToolType.RUBBER, None
78    PEN_IS_ERASING_WITH_BUTTON = (
79        BtnTouch.DOWN,
80        ToolType.RUBBER,
81        BtnPressed.PRIMARY_PRESSED,
82    )
83    PEN_IS_ERASING_WITH_SECOND_BUTTON = (
84        BtnTouch.DOWN,
85        ToolType.RUBBER,
86        BtnPressed.SECONDARY_PRESSED,
87    )
88
89    def __init__(self, touch: BtnTouch, tool: Optional[ToolType], button: Optional[BtnPressed]):
90        self.touch = touch  # type: ignore
91        self.tool = tool  # type: ignore
92        self.button = button  # type: ignore
93
94    @classmethod
95    def from_evdev(cls, evdev) -> "PenState":
96        touch = BtnTouch(evdev.value[libevdev.EV_KEY.BTN_TOUCH])
97        tool = None
98        button = None
99        if (
100            evdev.value[libevdev.EV_KEY.BTN_TOOL_RUBBER]
101            and not evdev.value[libevdev.EV_KEY.BTN_TOOL_PEN]
102        ):
103            tool = ToolType(libevdev.EV_KEY.BTN_TOOL_RUBBER)
104        elif (
105            evdev.value[libevdev.EV_KEY.BTN_TOOL_PEN]
106            and not evdev.value[libevdev.EV_KEY.BTN_TOOL_RUBBER]
107        ):
108            tool = ToolType(libevdev.EV_KEY.BTN_TOOL_PEN)
109        elif (
110            evdev.value[libevdev.EV_KEY.BTN_TOOL_PEN]
111            or evdev.value[libevdev.EV_KEY.BTN_TOOL_RUBBER]
112        ):
113            raise ValueError("2 tools are not allowed")
114
115        # we take only the highest button in account
116        for b in [libevdev.EV_KEY.BTN_STYLUS, libevdev.EV_KEY.BTN_STYLUS2]:
117            if bool(evdev.value[b]):
118                button = BtnPressed(b)
119
120        # the kernel tends to insert an EV_SYN once removing the tool, so
121        # the button will be released after
122        if tool is None:
123            button = None
124
125        return cls((touch, tool, button))  # type: ignore
126
127    def apply(self, events: List[libevdev.InputEvent], strict: bool) -> "PenState":
128        if libevdev.EV_SYN.SYN_REPORT in events:
129            raise ValueError("EV_SYN is in the event sequence")
130        touch = self.touch
131        touch_found = False
132        tool = self.tool
133        tool_found = False
134        button = self.button
135        button_found = False
136
137        for ev in events:
138            if ev == libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH):
139                if touch_found:
140                    raise ValueError(f"duplicated BTN_TOUCH in {events}")
141                touch_found = True
142                touch = BtnTouch(ev.value)
143            elif ev in (
144                libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN),
145                libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_RUBBER),
146            ):
147                if tool_found:
148                    raise ValueError(f"duplicated BTN_TOOL_* in {events}")
149                tool_found = True
150                tool = ToolType(ev.code) if ev.value else None
151            elif ev in (
152                libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS),
153                libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS2),
154            ):
155                if button_found:
156                    raise ValueError(f"duplicated BTN_STYLUS* in {events}")
157                button_found = True
158                button = BtnPressed(ev.code) if ev.value else None
159
160        # the kernel tends to insert an EV_SYN once removing the tool, so
161        # the button will be released after
162        if tool is None:
163            button = None
164
165        new_state = PenState((touch, tool, button))  # type: ignore
166        if strict:
167            assert (
168                new_state in self.valid_transitions()
169            ), f"moving from {self} to {new_state} is forbidden"
170        else:
171            assert (
172                new_state in self.historically_tolerated_transitions()
173            ), f"moving from {self} to {new_state} is forbidden"
174
175        return new_state
176
177    def valid_transitions(self) -> Tuple["PenState", ...]:
178        """Following the state machine in the URL above.
179
180        Note that those transitions are from the evdev point of view, not HID"""
181        if self == PenState.PEN_IS_OUT_OF_RANGE:
182            return (
183                PenState.PEN_IS_OUT_OF_RANGE,
184                PenState.PEN_IS_IN_RANGE,
185                PenState.PEN_IS_IN_RANGE_WITH_BUTTON,
186                PenState.PEN_IS_IN_RANGE_WITH_SECOND_BUTTON,
187                PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
188                PenState.PEN_IS_IN_CONTACT,
189                PenState.PEN_IS_IN_CONTACT_WITH_BUTTON,
190                PenState.PEN_IS_IN_CONTACT_WITH_SECOND_BUTTON,
191                PenState.PEN_IS_ERASING,
192            )
193
194        if self == PenState.PEN_IS_IN_RANGE:
195            return (
196                PenState.PEN_IS_IN_RANGE,
197                PenState.PEN_IS_IN_RANGE_WITH_BUTTON,
198                PenState.PEN_IS_IN_RANGE_WITH_SECOND_BUTTON,
199                PenState.PEN_IS_OUT_OF_RANGE,
200                PenState.PEN_IS_IN_CONTACT,
201            )
202
203        if self == PenState.PEN_IS_IN_CONTACT:
204            return (
205                PenState.PEN_IS_IN_CONTACT,
206                PenState.PEN_IS_IN_CONTACT_WITH_BUTTON,
207                PenState.PEN_IS_IN_CONTACT_WITH_SECOND_BUTTON,
208                PenState.PEN_IS_IN_RANGE,
209            )
210
211        if self == PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT:
212            return (
213                PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
214                PenState.PEN_IS_OUT_OF_RANGE,
215                PenState.PEN_IS_ERASING,
216            )
217
218        if self == PenState.PEN_IS_ERASING:
219            return (
220                PenState.PEN_IS_ERASING,
221                PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
222            )
223
224        if self == PenState.PEN_IS_IN_RANGE_WITH_BUTTON:
225            return (
226                PenState.PEN_IS_IN_RANGE_WITH_BUTTON,
227                PenState.PEN_IS_IN_RANGE,
228                PenState.PEN_IS_OUT_OF_RANGE,
229                PenState.PEN_IS_IN_CONTACT_WITH_BUTTON,
230            )
231
232        if self == PenState.PEN_IS_IN_CONTACT_WITH_BUTTON:
233            return (
234                PenState.PEN_IS_IN_CONTACT_WITH_BUTTON,
235                PenState.PEN_IS_IN_CONTACT,
236                PenState.PEN_IS_IN_RANGE_WITH_BUTTON,
237            )
238
239        if self == PenState.PEN_IS_IN_RANGE_WITH_SECOND_BUTTON:
240            return (
241                PenState.PEN_IS_IN_RANGE_WITH_SECOND_BUTTON,
242                PenState.PEN_IS_IN_RANGE,
243                PenState.PEN_IS_OUT_OF_RANGE,
244                PenState.PEN_IS_IN_CONTACT_WITH_SECOND_BUTTON,
245            )
246
247        if self == PenState.PEN_IS_IN_CONTACT_WITH_SECOND_BUTTON:
248            return (
249                PenState.PEN_IS_IN_CONTACT_WITH_SECOND_BUTTON,
250                PenState.PEN_IS_IN_CONTACT,
251                PenState.PEN_IS_IN_RANGE_WITH_SECOND_BUTTON,
252            )
253
254        return tuple()
255
256    def historically_tolerated_transitions(self) -> Tuple["PenState", ...]:
257        """Following the state machine in the URL above, with a couple of addition
258        for skipping the in-range state, due to historical reasons.
259
260        Note that those transitions are from the evdev point of view, not HID"""
261        if self == PenState.PEN_IS_OUT_OF_RANGE:
262            return (
263                PenState.PEN_IS_OUT_OF_RANGE,
264                PenState.PEN_IS_IN_RANGE,
265                PenState.PEN_IS_IN_RANGE_WITH_BUTTON,
266                PenState.PEN_IS_IN_RANGE_WITH_SECOND_BUTTON,
267                PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
268                PenState.PEN_IS_IN_CONTACT,
269                PenState.PEN_IS_IN_CONTACT_WITH_BUTTON,
270                PenState.PEN_IS_IN_CONTACT_WITH_SECOND_BUTTON,
271                PenState.PEN_IS_ERASING,
272            )
273
274        if self == PenState.PEN_IS_IN_RANGE:
275            return (
276                PenState.PEN_IS_IN_RANGE,
277                PenState.PEN_IS_IN_RANGE_WITH_BUTTON,
278                PenState.PEN_IS_IN_RANGE_WITH_SECOND_BUTTON,
279                PenState.PEN_IS_OUT_OF_RANGE,
280                PenState.PEN_IS_IN_CONTACT,
281            )
282
283        if self == PenState.PEN_IS_IN_CONTACT:
284            return (
285                PenState.PEN_IS_IN_CONTACT,
286                PenState.PEN_IS_IN_CONTACT_WITH_BUTTON,
287                PenState.PEN_IS_IN_CONTACT_WITH_SECOND_BUTTON,
288                PenState.PEN_IS_IN_RANGE,
289                PenState.PEN_IS_OUT_OF_RANGE,
290            )
291
292        if self == PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT:
293            return (
294                PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
295                PenState.PEN_IS_OUT_OF_RANGE,
296                PenState.PEN_IS_ERASING,
297            )
298
299        if self == PenState.PEN_IS_ERASING:
300            return (
301                PenState.PEN_IS_ERASING,
302                PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
303                PenState.PEN_IS_OUT_OF_RANGE,
304            )
305
306        if self == PenState.PEN_IS_IN_RANGE_WITH_BUTTON:
307            return (
308                PenState.PEN_IS_IN_RANGE_WITH_BUTTON,
309                PenState.PEN_IS_IN_RANGE,
310                PenState.PEN_IS_OUT_OF_RANGE,
311                PenState.PEN_IS_IN_CONTACT_WITH_BUTTON,
312            )
313
314        if self == PenState.PEN_IS_IN_CONTACT_WITH_BUTTON:
315            return (
316                PenState.PEN_IS_IN_CONTACT_WITH_BUTTON,
317                PenState.PEN_IS_IN_CONTACT,
318                PenState.PEN_IS_IN_RANGE_WITH_BUTTON,
319                PenState.PEN_IS_OUT_OF_RANGE,
320            )
321
322        if self == PenState.PEN_IS_IN_RANGE_WITH_SECOND_BUTTON:
323            return (
324                PenState.PEN_IS_IN_RANGE_WITH_SECOND_BUTTON,
325                PenState.PEN_IS_IN_RANGE,
326                PenState.PEN_IS_OUT_OF_RANGE,
327                PenState.PEN_IS_IN_CONTACT_WITH_SECOND_BUTTON,
328            )
329
330        if self == PenState.PEN_IS_IN_CONTACT_WITH_SECOND_BUTTON:
331            return (
332                PenState.PEN_IS_IN_CONTACT_WITH_SECOND_BUTTON,
333                PenState.PEN_IS_IN_CONTACT,
334                PenState.PEN_IS_IN_RANGE_WITH_SECOND_BUTTON,
335                PenState.PEN_IS_OUT_OF_RANGE,
336            )
337
338        return tuple()
339
340    @staticmethod
341    def legal_transitions() -> Dict[str, Tuple["PenState", ...]]:
342        """This is the first half of the Windows Pen Implementation state machine:
343        we don't have Invert nor Erase bits, so just move in/out-of-range or proximity.
344        https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states
345        """
346        return {
347            "in-range": (PenState.PEN_IS_IN_RANGE,),
348            "in-range -> out-of-range": (
349                PenState.PEN_IS_IN_RANGE,
350                PenState.PEN_IS_OUT_OF_RANGE,
351            ),
352            "in-range -> touch": (PenState.PEN_IS_IN_RANGE, PenState.PEN_IS_IN_CONTACT),
353            "in-range -> touch -> release": (
354                PenState.PEN_IS_IN_RANGE,
355                PenState.PEN_IS_IN_CONTACT,
356                PenState.PEN_IS_IN_RANGE,
357            ),
358            "in-range -> touch -> release -> out-of-range": (
359                PenState.PEN_IS_IN_RANGE,
360                PenState.PEN_IS_IN_CONTACT,
361                PenState.PEN_IS_IN_RANGE,
362                PenState.PEN_IS_OUT_OF_RANGE,
363            ),
364        }
365
366    @staticmethod
367    def legal_transitions_with_invert() -> Dict[str, Tuple["PenState", ...]]:
368        """This is the second half of the Windows Pen Implementation state machine:
369        we now have Invert and Erase bits, so move in/out or proximity with the intend
370        to erase.
371        https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states
372        """
373        return {
374            "hover-erasing": (PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,),
375            "hover-erasing -> out-of-range": (
376                PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
377                PenState.PEN_IS_OUT_OF_RANGE,
378            ),
379            "hover-erasing -> erase": (
380                PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
381                PenState.PEN_IS_ERASING,
382            ),
383            "hover-erasing -> erase -> release": (
384                PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
385                PenState.PEN_IS_ERASING,
386                PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
387            ),
388            "hover-erasing -> erase -> release -> out-of-range": (
389                PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
390                PenState.PEN_IS_ERASING,
391                PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
392                PenState.PEN_IS_OUT_OF_RANGE,
393            ),
394            "hover-erasing -> in-range": (
395                PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
396                PenState.PEN_IS_IN_RANGE,
397            ),
398            "in-range -> hover-erasing": (
399                PenState.PEN_IS_IN_RANGE,
400                PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
401            ),
402        }
403
404    @staticmethod
405    def legal_transitions_with_primary_button() -> Dict[str, Tuple["PenState", ...]]:
406        """We revisit the Windows Pen Implementation state machine:
407        we now have a primary button.
408        """
409        return {
410            "hover-button": (PenState.PEN_IS_IN_RANGE_WITH_BUTTON,),
411            "hover-button -> out-of-range": (
412                PenState.PEN_IS_IN_RANGE_WITH_BUTTON,
413                PenState.PEN_IS_OUT_OF_RANGE,
414            ),
415            "in-range -> button-press": (
416                PenState.PEN_IS_IN_RANGE,
417                PenState.PEN_IS_IN_RANGE_WITH_BUTTON,
418            ),
419            "in-range -> button-press -> button-release": (
420                PenState.PEN_IS_IN_RANGE,
421                PenState.PEN_IS_IN_RANGE_WITH_BUTTON,
422                PenState.PEN_IS_IN_RANGE,
423            ),
424            "in-range -> touch -> button-press -> button-release": (
425                PenState.PEN_IS_IN_RANGE,
426                PenState.PEN_IS_IN_CONTACT,
427                PenState.PEN_IS_IN_CONTACT_WITH_BUTTON,
428                PenState.PEN_IS_IN_CONTACT,
429            ),
430            "in-range -> touch -> button-press -> release -> button-release": (
431                PenState.PEN_IS_IN_RANGE,
432                PenState.PEN_IS_IN_CONTACT,
433                PenState.PEN_IS_IN_CONTACT_WITH_BUTTON,
434                PenState.PEN_IS_IN_RANGE_WITH_BUTTON,
435                PenState.PEN_IS_IN_RANGE,
436            ),
437            "in-range -> button-press -> touch -> release -> button-release": (
438                PenState.PEN_IS_IN_RANGE,
439                PenState.PEN_IS_IN_RANGE_WITH_BUTTON,
440                PenState.PEN_IS_IN_CONTACT_WITH_BUTTON,
441                PenState.PEN_IS_IN_RANGE_WITH_BUTTON,
442                PenState.PEN_IS_IN_RANGE,
443            ),
444            "in-range -> button-press -> touch -> button-release -> release": (
445                PenState.PEN_IS_IN_RANGE,
446                PenState.PEN_IS_IN_RANGE_WITH_BUTTON,
447                PenState.PEN_IS_IN_CONTACT_WITH_BUTTON,
448                PenState.PEN_IS_IN_CONTACT,
449                PenState.PEN_IS_IN_RANGE,
450            ),
451        }
452
453    @staticmethod
454    def legal_transitions_with_secondary_button() -> Dict[str, Tuple["PenState", ...]]:
455        """We revisit the Windows Pen Implementation state machine:
456        we now have a secondary button.
457        Note: we don't looks for 2 buttons interactions.
458        """
459        return {
460            "hover-button": (PenState.PEN_IS_IN_RANGE_WITH_SECOND_BUTTON,),
461            "hover-button -> out-of-range": (
462                PenState.PEN_IS_IN_RANGE_WITH_SECOND_BUTTON,
463                PenState.PEN_IS_OUT_OF_RANGE,
464            ),
465            "in-range -> button-press": (
466                PenState.PEN_IS_IN_RANGE,
467                PenState.PEN_IS_IN_RANGE_WITH_SECOND_BUTTON,
468            ),
469            "in-range -> button-press -> button-release": (
470                PenState.PEN_IS_IN_RANGE,
471                PenState.PEN_IS_IN_RANGE_WITH_SECOND_BUTTON,
472                PenState.PEN_IS_IN_RANGE,
473            ),
474            "in-range -> touch -> button-press -> button-release": (
475                PenState.PEN_IS_IN_RANGE,
476                PenState.PEN_IS_IN_CONTACT,
477                PenState.PEN_IS_IN_CONTACT_WITH_SECOND_BUTTON,
478                PenState.PEN_IS_IN_CONTACT,
479            ),
480            "in-range -> touch -> button-press -> release -> button-release": (
481                PenState.PEN_IS_IN_RANGE,
482                PenState.PEN_IS_IN_CONTACT,
483                PenState.PEN_IS_IN_CONTACT_WITH_SECOND_BUTTON,
484                PenState.PEN_IS_IN_RANGE_WITH_SECOND_BUTTON,
485                PenState.PEN_IS_IN_RANGE,
486            ),
487            "in-range -> button-press -> touch -> release -> button-release": (
488                PenState.PEN_IS_IN_RANGE,
489                PenState.PEN_IS_IN_RANGE_WITH_SECOND_BUTTON,
490                PenState.PEN_IS_IN_CONTACT_WITH_SECOND_BUTTON,
491                PenState.PEN_IS_IN_RANGE_WITH_SECOND_BUTTON,
492                PenState.PEN_IS_IN_RANGE,
493            ),
494            "in-range -> button-press -> touch -> button-release -> release": (
495                PenState.PEN_IS_IN_RANGE,
496                PenState.PEN_IS_IN_RANGE_WITH_SECOND_BUTTON,
497                PenState.PEN_IS_IN_CONTACT_WITH_SECOND_BUTTON,
498                PenState.PEN_IS_IN_CONTACT,
499                PenState.PEN_IS_IN_RANGE,
500            ),
501        }
502
503    @staticmethod
504    def tolerated_transitions() -> Dict[str, Tuple["PenState", ...]]:
505        """This is not adhering to the Windows Pen Implementation state machine
506        but we should expect the kernel to behave properly, mostly for historical
507        reasons."""
508        return {
509            "direct-in-contact": (PenState.PEN_IS_IN_CONTACT,),
510            "direct-in-contact -> out-of-range": (
511                PenState.PEN_IS_IN_CONTACT,
512                PenState.PEN_IS_OUT_OF_RANGE,
513            ),
514        }
515
516    @staticmethod
517    def tolerated_transitions_with_invert() -> Dict[str, Tuple["PenState", ...]]:
518        """This is the second half of the Windows Pen Implementation state machine:
519        we now have Invert and Erase bits, so move in/out or proximity with the intend
520        to erase.
521        https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states
522        """
523        return {
524            "direct-erase": (PenState.PEN_IS_ERASING,),
525            "direct-erase -> out-of-range": (
526                PenState.PEN_IS_ERASING,
527                PenState.PEN_IS_OUT_OF_RANGE,
528            ),
529        }
530
531    @staticmethod
532    def broken_transitions() -> Dict[str, Tuple["PenState", ...]]:
533        """Those tests are definitely not part of the Windows specification.
534        However, a half broken device might export those transitions.
535        For example, a pen that has the eraser button might wobble between
536        touching and erasing if the tablet doesn't enforce the Windows
537        state machine."""
538        return {
539            "in-range -> touch -> erase -> hover-erase": (
540                PenState.PEN_IS_IN_RANGE,
541                PenState.PEN_IS_IN_CONTACT,
542                PenState.PEN_IS_ERASING,
543                PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
544            ),
545            "in-range -> erase -> hover-erase": (
546                PenState.PEN_IS_IN_RANGE,
547                PenState.PEN_IS_ERASING,
548                PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
549            ),
550            "hover-erase -> erase -> touch -> in-range": (
551                PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
552                PenState.PEN_IS_ERASING,
553                PenState.PEN_IS_IN_CONTACT,
554                PenState.PEN_IS_IN_RANGE,
555            ),
556            "hover-erase -> touch -> in-range": (
557                PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT,
558                PenState.PEN_IS_IN_CONTACT,
559                PenState.PEN_IS_IN_RANGE,
560            ),
561            "touch -> erase -> touch -> erase": (
562                PenState.PEN_IS_IN_CONTACT,
563                PenState.PEN_IS_ERASING,
564                PenState.PEN_IS_IN_CONTACT,
565                PenState.PEN_IS_ERASING,
566            ),
567        }
568
569
570class Pen(object):
571    def __init__(self, x, y):
572        self.x = x
573        self.y = y
574        self.tipswitch = False
575        self.tippressure = 15
576        self.azimuth = 0
577        self.inrange = False
578        self.width = 10
579        self.height = 10
580        self.barrelswitch = False
581        self.secondarybarrelswitch = False
582        self.invert = False
583        self.eraser = False
584        self.xtilt = 1
585        self.ytilt = 1
586        self.twist = 1
587        self._old_values = None
588        self.current_state = None
589
590    def restore(self):
591        if self._old_values is not None:
592            for i in [
593                "x",
594                "y",
595                "tippressure",
596                "azimuth",
597                "width",
598                "height",
599                "twist",
600                "xtilt",
601                "ytilt",
602            ]:
603                setattr(self, i, getattr(self._old_values, i))
604
605    def backup(self):
606        self._old_values = copy.copy(self)
607
608    def __assert_axis(self, evdev, axis, value):
609        if (
610            axis == libevdev.EV_KEY.BTN_TOOL_RUBBER
611            and evdev.value[libevdev.EV_KEY.BTN_TOOL_RUBBER] is None
612        ):
613            return
614
615        assert (
616            evdev.value[axis] == value
617        ), f"assert evdev.value[{axis}] ({evdev.value[axis]}) != {value}"
618
619    def assert_expected_input_events(self, evdev):
620        assert evdev.value[libevdev.EV_ABS.ABS_X] == self.x
621        assert evdev.value[libevdev.EV_ABS.ABS_Y] == self.y
622        assert self.current_state == PenState.from_evdev(evdev)
623
624
625class PenDigitizer(base.UHIDTestDevice):
626    def __init__(
627        self,
628        name,
629        rdesc_str=None,
630        rdesc=None,
631        application="Pen",
632        physical="Stylus",
633        input_info=(BusType.USB, 1, 2),
634        evdev_name_suffix=None,
635    ):
636        super().__init__(name, application, rdesc_str, rdesc, input_info)
637        self.physical = physical
638        self.cur_application = application
639        if evdev_name_suffix is not None:
640            self.name += evdev_name_suffix
641
642        self.fields = []
643        for r in self.parsed_rdesc.input_reports.values():
644            if r.application_name == self.application:
645                physicals = [f.physical_name for f in r]
646                if self.physical not in physicals and None not in physicals:
647                    continue
648                self.fields = [f.usage_name for f in r]
649
650    def move_to(self, pen, state):
651        # fill in the previous values
652        if pen.current_state == PenState.PEN_IS_OUT_OF_RANGE:
653            pen.restore()
654
655        print(f"\n  *** pen is moving to {state} ***")
656
657        if state == PenState.PEN_IS_OUT_OF_RANGE:
658            pen.backup()
659            pen.x = 0
660            pen.y = 0
661            pen.tipswitch = False
662            pen.tippressure = 0
663            pen.azimuth = 0
664            pen.inrange = False
665            pen.width = 0
666            pen.height = 0
667            pen.invert = False
668            pen.eraser = False
669            pen.xtilt = 0
670            pen.ytilt = 0
671            pen.twist = 0
672            pen.barrelswitch = False
673            pen.secondarybarrelswitch = False
674        elif state == PenState.PEN_IS_IN_RANGE:
675            pen.tipswitch = False
676            pen.inrange = True
677            pen.invert = False
678            pen.eraser = False
679            pen.barrelswitch = False
680            pen.secondarybarrelswitch = False
681        elif state == PenState.PEN_IS_IN_CONTACT:
682            pen.tipswitch = True
683            pen.inrange = True
684            pen.invert = False
685            pen.eraser = False
686            pen.barrelswitch = False
687            pen.secondarybarrelswitch = False
688        elif state == PenState.PEN_IS_IN_RANGE_WITH_BUTTON:
689            pen.tipswitch = False
690            pen.inrange = True
691            pen.invert = False
692            pen.eraser = False
693            pen.barrelswitch = True
694            pen.secondarybarrelswitch = False
695        elif state == PenState.PEN_IS_IN_CONTACT_WITH_BUTTON:
696            pen.tipswitch = True
697            pen.inrange = True
698            pen.invert = False
699            pen.eraser = False
700            pen.barrelswitch = True
701            pen.secondarybarrelswitch = False
702        elif state == PenState.PEN_IS_IN_RANGE_WITH_SECOND_BUTTON:
703            pen.tipswitch = False
704            pen.inrange = True
705            pen.invert = False
706            pen.eraser = False
707            pen.barrelswitch = False
708            pen.secondarybarrelswitch = True
709        elif state == PenState.PEN_IS_IN_CONTACT_WITH_SECOND_BUTTON:
710            pen.tipswitch = True
711            pen.inrange = True
712            pen.invert = False
713            pen.eraser = False
714            pen.barrelswitch = False
715            pen.secondarybarrelswitch = True
716        elif state == PenState.PEN_IS_IN_RANGE_WITH_ERASING_INTENT:
717            pen.tipswitch = False
718            pen.inrange = True
719            pen.invert = True
720            pen.eraser = False
721            pen.barrelswitch = False
722            pen.secondarybarrelswitch = False
723        elif state == PenState.PEN_IS_ERASING:
724            pen.tipswitch = False
725            pen.inrange = True
726            pen.invert = False
727            pen.eraser = True
728            pen.barrelswitch = False
729            pen.secondarybarrelswitch = False
730
731        pen.current_state = state
732
733    def event(self, pen):
734        rs = []
735        r = self.create_report(application=self.cur_application, data=pen)
736        self.call_input_event(r)
737        rs.append(r)
738        return rs
739
740    def get_report(self, req, rnum, rtype):
741        if rtype != self.UHID_FEATURE_REPORT:
742            return (1, [])
743
744        rdesc = None
745        for v in self.parsed_rdesc.feature_reports.values():
746            if v.report_ID == rnum:
747                rdesc = v
748
749        if rdesc is None:
750            return (1, [])
751
752        return (1, [])
753
754    def set_report(self, req, rnum, rtype, data):
755        if rtype != self.UHID_FEATURE_REPORT:
756            return 1
757
758        rdesc = None
759        for v in self.parsed_rdesc.feature_reports.values():
760            if v.report_ID == rnum:
761                rdesc = v
762
763        if rdesc is None:
764            return 1
765
766        return 1
767
768
769class BaseTest:
770    class TestTablet(base.BaseTestCase.TestUhid):
771        def create_device(self):
772            raise Exception("please reimplement me in subclasses")
773
774        def post(self, uhdev, pen):
775            r = uhdev.event(pen)
776            events = uhdev.next_sync_events()
777            self.debug_reports(r, uhdev, events)
778            return events
779
780        def validate_transitions(
781            self, from_state, pen, evdev, events, allow_intermediate_states
782        ):
783            # check that the final state is correct
784            pen.assert_expected_input_events(evdev)
785
786            state = from_state
787
788            # check that the transitions are valid
789            sync_events = []
790            while libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT) in events:
791                # split the first EV_SYN from the list
792                idx = events.index(libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT))
793                sync_events = events[:idx]
794                events = events[idx + 1 :]
795
796                # now check for a valid transition
797                state = state.apply(sync_events, not allow_intermediate_states)
798
799            if events:
800                state = state.apply(sync_events, not allow_intermediate_states)
801
802        def _test_states(self, state_list, scribble, allow_intermediate_states):
803            """Internal method to test against a list of
804            transition between states.
805            state_list is a list of PenState objects
806            scribble is a boolean which tells if we need
807            to wobble a little the X,Y coordinates of the pen
808            between each state transition."""
809            uhdev = self.uhdev
810            evdev = uhdev.get_evdev()
811
812            cur_state = PenState.PEN_IS_OUT_OF_RANGE
813
814            p = Pen(50, 60)
815            uhdev.move_to(p, PenState.PEN_IS_OUT_OF_RANGE)
816            events = self.post(uhdev, p)
817            self.validate_transitions(
818                cur_state, p, evdev, events, allow_intermediate_states
819            )
820
821            cur_state = p.current_state
822
823            for state in state_list:
824                if scribble and cur_state != PenState.PEN_IS_OUT_OF_RANGE:
825                    p.x += 1
826                    p.y -= 1
827                    events = self.post(uhdev, p)
828                    self.validate_transitions(
829                        cur_state, p, evdev, events, allow_intermediate_states
830                    )
831                    assert len(events) >= 3  # X, Y, SYN
832                uhdev.move_to(p, state)
833                if scribble and state != PenState.PEN_IS_OUT_OF_RANGE:
834                    p.x += 1
835                    p.y -= 1
836                events = self.post(uhdev, p)
837                self.validate_transitions(
838                    cur_state, p, evdev, events, allow_intermediate_states
839                )
840                cur_state = p.current_state
841
842        @pytest.mark.parametrize("scribble", [True, False], ids=["scribble", "static"])
843        @pytest.mark.parametrize(
844            "state_list",
845            [pytest.param(v, id=k) for k, v in PenState.legal_transitions().items()],
846        )
847        def test_valid_pen_states(self, state_list, scribble):
848            """This is the first half of the Windows Pen Implementation state machine:
849            we don't have Invert nor Erase bits, so just move in/out-of-range or proximity.
850            https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states
851            """
852            self._test_states(state_list, scribble, allow_intermediate_states=False)
853
854        @pytest.mark.parametrize("scribble", [True, False], ids=["scribble", "static"])
855        @pytest.mark.parametrize(
856            "state_list",
857            [
858                pytest.param(v, id=k)
859                for k, v in PenState.tolerated_transitions().items()
860            ],
861        )
862        def test_tolerated_pen_states(self, state_list, scribble):
863            """This is not adhering to the Windows Pen Implementation state machine
864            but we should expect the kernel to behave properly, mostly for historical
865            reasons."""
866            self._test_states(state_list, scribble, allow_intermediate_states=True)
867
868        @pytest.mark.skip_if_uhdev(
869            lambda uhdev: "Barrel Switch" not in uhdev.fields,
870            "Device not compatible, missing Barrel Switch usage",
871        )
872        @pytest.mark.parametrize("scribble", [True, False], ids=["scribble", "static"])
873        @pytest.mark.parametrize(
874            "state_list",
875            [
876                pytest.param(v, id=k)
877                for k, v in PenState.legal_transitions_with_primary_button().items()
878            ],
879        )
880        def test_valid_primary_button_pen_states(self, state_list, scribble):
881            """Rework the transition state machine by adding the primary button."""
882            self._test_states(state_list, scribble, allow_intermediate_states=False)
883
884        @pytest.mark.skip_if_uhdev(
885            lambda uhdev: "Secondary Barrel Switch" not in uhdev.fields,
886            "Device not compatible, missing Secondary Barrel Switch usage",
887        )
888        @pytest.mark.parametrize("scribble", [True, False], ids=["scribble", "static"])
889        @pytest.mark.parametrize(
890            "state_list",
891            [
892                pytest.param(v, id=k)
893                for k, v in PenState.legal_transitions_with_secondary_button().items()
894            ],
895        )
896        def test_valid_secondary_button_pen_states(self, state_list, scribble):
897            """Rework the transition state machine by adding the secondary button."""
898            self._test_states(state_list, scribble, allow_intermediate_states=False)
899
900        @pytest.mark.skip_if_uhdev(
901            lambda uhdev: "Invert" not in uhdev.fields,
902            "Device not compatible, missing Invert usage",
903        )
904        @pytest.mark.parametrize("scribble", [True, False], ids=["scribble", "static"])
905        @pytest.mark.parametrize(
906            "state_list",
907            [
908                pytest.param(v, id=k)
909                for k, v in PenState.legal_transitions_with_invert().items()
910            ],
911        )
912        def test_valid_invert_pen_states(self, state_list, scribble):
913            """This is the second half of the Windows Pen Implementation state machine:
914            we now have Invert and Erase bits, so move in/out or proximity with the intend
915            to erase.
916            https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states
917            """
918            self._test_states(state_list, scribble, allow_intermediate_states=False)
919
920        @pytest.mark.skip_if_uhdev(
921            lambda uhdev: "Invert" not in uhdev.fields,
922            "Device not compatible, missing Invert usage",
923        )
924        @pytest.mark.parametrize("scribble", [True, False], ids=["scribble", "static"])
925        @pytest.mark.parametrize(
926            "state_list",
927            [
928                pytest.param(v, id=k)
929                for k, v in PenState.tolerated_transitions_with_invert().items()
930            ],
931        )
932        def test_tolerated_invert_pen_states(self, state_list, scribble):
933            """This is the second half of the Windows Pen Implementation state machine:
934            we now have Invert and Erase bits, so move in/out or proximity with the intend
935            to erase.
936            https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states
937            """
938            self._test_states(state_list, scribble, allow_intermediate_states=True)
939
940        @pytest.mark.skip_if_uhdev(
941            lambda uhdev: "Invert" not in uhdev.fields,
942            "Device not compatible, missing Invert usage",
943        )
944        @pytest.mark.parametrize("scribble", [True, False], ids=["scribble", "static"])
945        @pytest.mark.parametrize(
946            "state_list",
947            [pytest.param(v, id=k) for k, v in PenState.broken_transitions().items()],
948        )
949        def test_tolerated_broken_pen_states(self, state_list, scribble):
950            """Those tests are definitely not part of the Windows specification.
951            However, a half broken device might export those transitions.
952            For example, a pen that has the eraser button might wobble between
953            touching and erasing if the tablet doesn't enforce the Windows
954            state machine."""
955            self._test_states(state_list, scribble, allow_intermediate_states=True)
956
957
958class GXTP_pen(PenDigitizer):
959    def event(self, pen):
960        if not hasattr(self, "prev_tip_state"):
961            self.prev_tip_state = False
962
963        internal_pen = copy.copy(pen)
964
965        # bug in the controller: when the pen touches the
966        # surface, in-range stays to 1, but when
967        # the pen moves in-range gets reverted to 0
968        if pen.tipswitch and self.prev_tip_state:
969            internal_pen.inrange = False
970
971        self.prev_tip_state = pen.tipswitch
972
973        # another bug in the controller: when the pen is
974        # inverted, invert is set to 1, but as soon as
975        # the pen touches the surface, eraser is correctly
976        # set to 1 but invert is released
977        if pen.eraser:
978            internal_pen.invert = False
979
980        return super().event(internal_pen)
981
982
983class USIPen(PenDigitizer):
984    pass
985
986
987################################################################################
988#
989# Windows 7 compatible devices
990#
991################################################################################
992# class TestEgalax_capacitive_0eef_7224(BaseTest.TestTablet):
993#     def create_device(self):
994#         return PenDigitizer('uhid test egalax-capacitive_0eef_7224',
995#                             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',
996#                             input_info=(BusType.USB, 0x0eef, 0x7224),
997#                             evdev_name_suffix=' Touchscreen')
998#
999#
1000# class TestEgalax_capacitive_0eef_72fa(BaseTest.TestTablet):
1001#     def create_device(self):
1002#         return PenDigitizer('uhid test egalax-capacitive_0eef_72fa',
1003#                             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',
1004#                             input_info=(BusType.USB, 0x0eef, 0x72fa),
1005#                             evdev_name_suffix=' Touchscreen')
1006#
1007#
1008# class TestEgalax_capacitive_0eef_7336(BaseTest.TestTablet):
1009#     def create_device(self):
1010#         return PenDigitizer('uhid test egalax-capacitive_0eef_7336',
1011#                             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',
1012#                             input_info=(BusType.USB, 0x0eef, 0x7336),
1013#                             evdev_name_suffix=' Touchscreen')
1014#
1015#
1016# class TestEgalax_capacitive_0eef_7337(BaseTest.TestTablet):
1017#     def create_device(self):
1018#         return PenDigitizer('uhid test egalax-capacitive_0eef_7337',
1019#                             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',
1020#                             input_info=(BusType.USB, 0x0eef, 0x7337),
1021#                             evdev_name_suffix=' Touchscreen')
1022#
1023#
1024# class TestEgalax_capacitive_0eef_7349(BaseTest.TestTablet):
1025#     def create_device(self):
1026#         return PenDigitizer('uhid test egalax-capacitive_0eef_7349',
1027#                             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',
1028#                             input_info=(BusType.USB, 0x0eef, 0x7349),
1029#                             evdev_name_suffix=' Touchscreen')
1030#
1031#
1032# class TestEgalax_capacitive_0eef_73f4(BaseTest.TestTablet):
1033#     def create_device(self):
1034#         return PenDigitizer('uhid test egalax-capacitive_0eef_73f4',
1035#                             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',
1036#                             input_info=(BusType.USB, 0x0eef, 0x73f4),
1037#                             evdev_name_suffix=' Touchscreen')
1038#
1039#  bogus: BTN_TOOL_PEN is not emitted
1040# class TestIrtouch_6615_0070(BaseTest.TestTablet):
1041#     def create_device(self):
1042#         return PenDigitizer('uhid test irtouch_6615_0070',
1043#                             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',
1044#                             input_info=(BusType.USB, 0x6615, 0x0070))
1045
1046
1047class TestNexio_1870_0100(BaseTest.TestTablet):
1048    def create_device(self):
1049        return PenDigitizer(
1050            "uhid test nexio_1870_0100",
1051            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",
1052            input_info=(BusType.USB, 0x1870, 0x0100),
1053        )
1054
1055
1056class TestNexio_1870_010d(BaseTest.TestTablet):
1057    def create_device(self):
1058        return PenDigitizer(
1059            "uhid test nexio_1870_010d",
1060            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",
1061            input_info=(BusType.USB, 0x1870, 0x010D),
1062        )
1063
1064
1065class TestNexio_1870_0119(BaseTest.TestTablet):
1066    def create_device(self):
1067        return PenDigitizer(
1068            "uhid test nexio_1870_0119",
1069            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",
1070            input_info=(BusType.USB, 0x1870, 0x0119),
1071        )
1072
1073
1074################################################################################
1075#
1076# Windows 8 compatible devices
1077#
1078################################################################################
1079
1080# bogus: application is 'undefined'
1081# class Testatmel_03eb_8409(BaseTest.TestTablet):
1082#     def create_device(self):
1083#         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')
1084
1085
1086class Testatmel_03eb_840b(BaseTest.TestTablet):
1087    def create_device(self):
1088        return PenDigitizer(
1089            "uhid test atmel_03eb_840b",
1090            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",
1091        )
1092
1093
1094class Testn_trig_1b96_0c01(BaseTest.TestTablet):
1095    def create_device(self):
1096        return PenDigitizer(
1097            "uhid test n_trig_1b96_0c01",
1098            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",
1099        )
1100
1101
1102class Testn_trig_1b96_0c03(BaseTest.TestTablet):
1103    def create_device(self):
1104        return PenDigitizer(
1105            "uhid test n_trig_1b96_0c03",
1106            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",
1107        )
1108
1109
1110class Testn_trig_1b96_0f00(BaseTest.TestTablet):
1111    def create_device(self):
1112        return PenDigitizer(
1113            "uhid test n_trig_1b96_0f00",
1114            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",
1115        )
1116
1117
1118class Testn_trig_1b96_0f04(BaseTest.TestTablet):
1119    def create_device(self):
1120        return PenDigitizer(
1121            "uhid test n_trig_1b96_0f04",
1122            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",
1123        )
1124
1125
1126class Testn_trig_1b96_1000(BaseTest.TestTablet):
1127    def create_device(self):
1128        return PenDigitizer(
1129            "uhid test n_trig_1b96_1000",
1130            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",
1131        )
1132
1133
1134class TestGXTP_27c6_0113(BaseTest.TestTablet):
1135    def create_device(self):
1136        return GXTP_pen(
1137            "uhid test GXTP_27c6_0113",
1138            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",
1139        )
1140
1141
1142################################################################################
1143#
1144# Windows 8 compatible devices with USI Pen
1145#
1146################################################################################
1147
1148
1149class TestElan_04f3_2A49(BaseTest.TestTablet):
1150    def create_device(self):
1151        return USIPen(
1152            "uhid test Elan_04f3_2A49",
1153            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",
1154            input_info=(BusType.I2C, 0x04F3, 0x2A49),
1155        )
1156
1157
1158class TestGoodix_27c6_0e00(BaseTest.TestTablet):
1159    def create_device(self):
1160        return USIPen(
1161            "uhid test Elan_04f3_2A49",
1162            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",
1163            input_info=(BusType.I2C, 0x27C6, 0x0E00),
1164        )
1165