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