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