test_tablet.py 91 KB

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