File Index
Files and directories
Sign in
FILE

test_tablet.py

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/hid/tests/test_tablet.py PYTHON
   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        )
~/ Read-only file preview.