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