1#!/bin/env python3 2# SPDX-License-Identifier: GPL-2.0 3# -*- coding: utf-8 -*- 4# 5# Copyright (c) 2021 Benjamin Tissoires <benjamin.tissoires@gmail.com> 6# Copyright (c) 2021 Red Hat, Inc. 7# 8 9from . import base 10import copy 11from enum import Enum 12from hidtools.util import BusType 13import libevdev 14import logging 15import pytest 16from typing import Dict, List, Optional, Tuple 17 18logger = logging.getLogger("hidtools.test.tablet") 19 20 21class BtnTouch(Enum): 22 """Represents whether the BTN_TOUCH event is set to True or False""" 23 24 DOWN = True 25 UP = False 26 27 28class ToolType(Enum): 29 PEN = libevdev.EV_KEY.BTN_TOOL_PEN 30 RUBBER = libevdev.EV_KEY.BTN_TOOL_RUBBER 31 32 33class BtnPressed(Enum): 34 """Represents whether a button is pressed on the stylus""" 35 36 PRIMARY_PRESSED = libevdev.EV_KEY.BTN_STYLUS 37 SECONDARY_PRESSED = libevdev.EV_KEY.BTN_STYLUS2 38 THIRD_PRESSED = libevdev.EV_KEY.BTN_STYLUS3 39 40 41class 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 450class 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 517class 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 649class 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 871class 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 896class USIPen(PenDigitizer): 897 pass 898 899 900class 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 1002class 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 1137class 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 1354class 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 1363class 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 1372class 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 1393class 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 1401class 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 1409class 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 1417class 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 1425class 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 1433class 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 1441class 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 1456class 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 1465class 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 1474class 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 1486class 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 1497class 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 ) 1506