~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/testing/selftests/hid/tests/test_tablet.py

Version: ~ [ linux-6.12-rc7 ] ~ [ linux-6.11.7 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.60 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.116 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.171 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.229 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.285 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.323 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.12 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php