~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/testing/selftests/hid/tests/test_wacom_generic.py

Version: ~ [ linux-6.12-rc7 ] ~ [ linux-6.11.7 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.60 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.116 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.171 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.229 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.285 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.323 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.12 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 #!/bin/env python3
  2 # SPDX-License-Identifier: GPL-2.0
  3 # -*- coding: utf-8 -*-
  4 #
  5 # Copyright (c) 2017 Benjamin Tissoires <benjamin.tissoires@gmail.com>
  6 # Copyright (c) 2017 Red Hat, Inc.
  7 # Copyright (c) 2020 Wacom Technology Corp.
  8 #
  9 # Authors:
 10 #     Jason Gerecke <jason.gerecke@wacom.com>
 11 
 12 """
 13 Tests for the Wacom driver generic codepath.
 14 
 15 This module tests the function of the Wacom driver's generic codepath.
 16 The generic codepath is used by devices which are not explicitly listed
 17 in the driver's device table. It uses the device's HID descriptor to
 18 decode reports sent by the device.
 19 """
 20 
 21 from .descriptors_wacom import (
 22     wacom_pth660_v145,
 23     wacom_pth660_v150,
 24     wacom_pth860_v145,
 25     wacom_pth860_v150,
 26     wacom_pth460_v105,
 27 )
 28 
 29 import attr
 30 from collections import namedtuple
 31 from enum import Enum
 32 from hidtools.hut import HUT
 33 from hidtools.hid import HidUnit
 34 from . import base
 35 from . import test_multitouch
 36 import libevdev
 37 import pytest
 38 
 39 import logging
 40 
 41 logger = logging.getLogger("hidtools.test.wacom")
 42 
 43 KERNEL_MODULE = ("wacom", "wacom")
 44 
 45 
 46 class ProximityState(Enum):
 47     """
 48     Enumeration of allowed proximity states.
 49     """
 50 
 51     # Tool is not able to be sensed by the device
 52     OUT = 0
 53 
 54     # Tool is close enough to be sensed, but some data may be invalid
 55     # or inaccurate
 56     IN_PROXIMITY = 1
 57 
 58     # Tool is close enough to be sensed with high accuracy. All data
 59     # valid.
 60     IN_RANGE = 2
 61 
 62     def fill(self, reportdata):
 63         """Fill a report with approrpiate HID properties/values."""
 64         reportdata.inrange = self in [ProximityState.IN_RANGE]
 65         reportdata.wacomsense = self in [
 66             ProximityState.IN_PROXIMITY,
 67             ProximityState.IN_RANGE,
 68         ]
 69 
 70 
 71 class ReportData:
 72     """
 73     Placeholder for HID report values.
 74     """
 75 
 76     pass
 77 
 78 
 79 @attr.s
 80 class Buttons:
 81     """
 82     Stylus button state.
 83 
 84     Describes the state of each of the buttons / "side switches" that
 85     may be present on a stylus. Buttons set to 'None' indicate the
 86     state is "unchanged" since the previous event.
 87     """
 88 
 89     primary = attr.ib(default=None)
 90     secondary = attr.ib(default=None)
 91     tertiary = attr.ib(default=None)
 92 
 93     @staticmethod
 94     def clear():
 95         """Button object with all states cleared."""
 96         return Buttons(False, False, False)
 97 
 98     def fill(self, reportdata):
 99         """Fill a report with approrpiate HID properties/values."""
100         reportdata.barrelswitch = int(self.primary or 0)
101         reportdata.secondarybarrelswitch = int(self.secondary or 0)
102         reportdata.b3 = int(self.tertiary or 0)
103 
104 
105 @attr.s
106 class ToolID:
107     """
108     Stylus tool identifiers.
109 
110     Contains values used to identify a specific stylus, e.g. its serial
111     number and tool-type identifier. Values of ``0`` may sometimes be
112     used for the out-of-range condition.
113     """
114 
115     serial = attr.ib()
116     tooltype = attr.ib()
117 
118     @staticmethod
119     def clear():
120         """ToolID object with all fields cleared."""
121         return ToolID(0, 0)
122 
123     def fill(self, reportdata):
124         """Fill a report with approrpiate HID properties/values."""
125         reportdata.transducerserialnumber = self.serial & 0xFFFFFFFF
126         reportdata.serialhi = (self.serial >> 32) & 0xFFFFFFFF
127         reportdata.tooltype = self.tooltype
128 
129 
130 @attr.s
131 class PhysRange:
132     """
133     Range of HID physical values, with units.
134     """
135 
136     unit = attr.ib()
137     min_size = attr.ib()
138     max_size = attr.ib()
139 
140     CENTIMETER = HidUnit.from_string("SILinear: cm")
141     DEGREE = HidUnit.from_string("EnglishRotation: deg")
142 
143     def contains(self, field):
144         """
145         Check if the physical size of the provided field is in range.
146 
147         Compare the physical size described by the provided HID field
148         against the range of sizes described by this object. This is
149         an exclusive range comparison (e.g. 0 cm is not within the
150         range 0 cm - 5 cm) and exact unit comparison (e.g. 1 inch is
151         not within the range 0 cm - 5 cm).
152         """
153         phys_size = (field.physical_max - field.physical_min) * 10 ** (field.unit_exp)
154         return (
155             field.unit == self.unit.value
156             and phys_size > self.min_size
157             and phys_size < self.max_size
158         )
159 
160 
161 class BaseTablet(base.UHIDTestDevice):
162     """
163     Skeleton object for all kinds of tablet devices.
164     """
165 
166     def __init__(self, rdesc, name=None, info=None):
167         assert rdesc is not None
168         super().__init__(name, "Pen", input_info=info, rdesc=rdesc)
169         self.buttons = Buttons.clear()
170         self.toolid = ToolID.clear()
171         self.proximity = ProximityState.OUT
172         self.offset = 0
173         self.ring = -1
174         self.ek0 = False
175 
176     def match_evdev_rule(self, application, evdev):
177         """
178         Filter out evdev nodes based on the requested application.
179 
180         The Wacom driver may create several device nodes for each USB
181         interface device. It is crucial that we run tests with the
182         expected device node or things will obviously go off the rails.
183         Use the Wacom driver's usual naming conventions to apply a
184         sensible default filter.
185         """
186         if application in ["Pen", "Pad"]:
187             return evdev.name.endswith(application)
188         else:
189             return True
190 
191     def create_report(
192         self, x, y, pressure, buttons=None, toolid=None, proximity=None, reportID=None
193     ):
194         """
195         Return an input report for this device.
196 
197         :param x: absolute x
198         :param y: absolute y
199         :param pressure: pressure
200         :param buttons: stylus button state. Use ``None`` for unchanged.
201         :param toolid: tool identifiers. Use ``None`` for unchanged.
202         :param proximity: a ProximityState indicating the sensor's ability
203              to detect and report attributes of this tool. Use ``None``
204              for unchanged.
205         :param reportID: the numeric report ID for this report, if needed
206         """
207         if buttons is not None:
208             self.buttons = buttons
209         buttons = self.buttons
210 
211         if toolid is not None:
212             self.toolid = toolid
213         toolid = self.toolid
214 
215         if proximity is not None:
216             self.proximity = proximity
217         proximity = self.proximity
218 
219         reportID = reportID or self.default_reportID
220 
221         report = ReportData()
222         report.x = x
223         report.y = y
224         report.tippressure = pressure
225         report.tipswitch = pressure > 0
226         buttons.fill(report)
227         proximity.fill(report)
228         toolid.fill(report)
229 
230         return super().create_report(report, reportID=reportID)
231 
232     def create_report_heartbeat(self, reportID):
233         """
234         Return a heartbeat input report for this device.
235 
236         Heartbeat reports generally contain battery status information,
237         among other things.
238         """
239         report = ReportData()
240         report.wacombatterycharging = 1
241         return super().create_report(report, reportID=reportID)
242 
243     def create_report_pad(self, reportID, ring, ek0):
244         report = ReportData()
245 
246         if ring is not None:
247             self.ring = ring
248         ring = self.ring
249 
250         if ek0 is not None:
251             self.ek0 = ek0
252         ek0 = self.ek0
253 
254         if ring >= 0:
255             report.wacomtouchring = ring
256             report.wacomtouchringstatus = 1
257         else:
258             report.wacomtouchring = 0x7F
259             report.wacomtouchringstatus = 0
260 
261         report.wacomexpresskey00 = ek0
262         return super().create_report(report, reportID=reportID)
263 
264     def event(self, x, y, pressure, buttons=None, toolid=None, proximity=None):
265         """
266         Send an input event on the default report ID.
267 
268         :param x: absolute x
269         :param y: absolute y
270         :param buttons: stylus button state. Use ``None`` for unchanged.
271         :param toolid: tool identifiers. Use ``None`` for unchanged.
272         :param proximity: a ProximityState indicating the sensor's ability
273              to detect and report attributes of this tool. Use ``None``
274              for unchanged.
275         """
276         r = self.create_report(x, y, pressure, buttons, toolid, proximity)
277         self.call_input_event(r)
278         return [r]
279 
280     def event_heartbeat(self, reportID):
281         """
282         Send a heartbeat event on the requested report ID.
283         """
284         r = self.create_report_heartbeat(reportID)
285         self.call_input_event(r)
286         return [r]
287 
288     def event_pad(self, reportID, ring=None, ek0=None):
289         """
290         Send a pad event on the requested report ID.
291         """
292         r = self.create_report_pad(reportID, ring, ek0)
293         self.call_input_event(r)
294         return [r]
295 
296     def get_report(self, req, rnum, rtype):
297         if rtype != self.UHID_FEATURE_REPORT:
298             return (1, [])
299 
300         rdesc = None
301         for v in self.parsed_rdesc.feature_reports.values():
302             if v.report_ID == rnum:
303                 rdesc = v
304 
305         if rdesc is None:
306             return (1, [])
307 
308         result = (1, [])
309         result = self.create_report_offset(rdesc) or result
310         return result
311 
312     def create_report_offset(self, rdesc):
313         require = [
314             "Wacom Offset Left",
315             "Wacom Offset Top",
316             "Wacom Offset Right",
317             "Wacom Offset Bottom",
318         ]
319         if not set(require).issubset(set([f.usage_name for f in rdesc])):
320             return None
321 
322         report = ReportData()
323         report.wacomoffsetleft = self.offset
324         report.wacomoffsettop = self.offset
325         report.wacomoffsetright = self.offset
326         report.wacomoffsetbottom = self.offset
327         r = rdesc.create_report([report], None)
328         return (0, r)
329 
330 
331 class OpaqueTablet(BaseTablet):
332     """
333     Bare-bones opaque tablet with a minimum of features.
334 
335     A tablet stripped down to its absolute core. It is capable of
336     reporting X/Y position and if the pen is in contact. No pressure,
337     no barrel switches, no eraser. Notably it *does* report an "In
338     Range" flag, but this is only because the Wacom driver expects
339     one to function properly. The device uses only standard HID usages,
340     not any of Wacom's vendor-defined pages.
341     """
342 
343     # fmt: off
344     report_descriptor = [
345         0x05, 0x0D,                     # . Usage Page (Digitizer),
346         0x09, 0x01,                     # . Usage (Digitizer),
347         0xA1, 0x01,                     # . Collection (Application),
348         0x85, 0x01,                     # .     Report ID (1),
349         0x09, 0x20,                     # .     Usage (Stylus),
350         0xA1, 0x00,                     # .     Collection (Physical),
351         0x09, 0x42,                     # .         Usage (Tip Switch),
352         0x09, 0x32,                     # .         Usage (In Range),
353         0x15, 0x00,                     # .         Logical Minimum (0),
354         0x25, 0x01,                     # .         Logical Maximum (1),
355         0x75, 0x01,                     # .         Report Size (1),
356         0x95, 0x02,                     # .         Report Count (2),
357         0x81, 0x02,                     # .         Input (Variable),
358         0x95, 0x06,                     # .         Report Count (6),
359         0x81, 0x03,                     # .         Input (Constant, Variable),
360         0x05, 0x01,                     # .         Usage Page (Desktop),
361         0x09, 0x30,                     # .         Usage (X),
362         0x27, 0x80, 0x3E, 0x00, 0x00,   # .         Logical Maximum (16000),
363         0x47, 0x80, 0x3E, 0x00, 0x00,   # .         Physical Maximum (16000),
364         0x65, 0x11,                     # .         Unit (Centimeter),
365         0x55, 0x0D,                     # .         Unit Exponent (13),
366         0x75, 0x10,                     # .         Report Size (16),
367         0x95, 0x01,                     # .         Report Count (1),
368         0x81, 0x02,                     # .         Input (Variable),
369         0x09, 0x31,                     # .         Usage (Y),
370         0x27, 0x28, 0x23, 0x00, 0x00,   # .         Logical Maximum (9000),
371         0x47, 0x28, 0x23, 0x00, 0x00,   # .         Physical Maximum (9000),
372         0x81, 0x02,                     # .         Input (Variable),
373         0xC0,                           # .     End Collection,
374         0xC0,                           # . End Collection,
375     ]
376     # fmt: on
377 
378     def __init__(self, rdesc=report_descriptor, name=None, info=(0x3, 0x056A, 0x9999)):
379         super().__init__(rdesc, name, info)
380         self.default_reportID = 1
381 
382 
383 class OpaqueCTLTablet(BaseTablet):
384     """
385     Opaque tablet similar to something in the CTL product line.
386 
387     A pen-only tablet with most basic features you would expect from
388     an actual device. Position, eraser, pressure, barrel buttons.
389     Uses the Wacom vendor-defined usage page.
390     """
391 
392     # fmt: off
393     report_descriptor = [
394         0x06, 0x0D, 0xFF,               # . Usage Page (Vnd Wacom Emr),
395         0x09, 0x01,                     # . Usage (Digitizer),
396         0xA1, 0x01,                     # . Collection (Application),
397         0x85, 0x10,                     # .     Report ID (16),
398         0x09, 0x20,                     # .     Usage (Stylus),
399         0x35, 0x00,                     # .     Physical Minimum (0),
400         0x45, 0x00,                     # .     Physical Maximum (0),
401         0x15, 0x00,                     # .     Logical Minimum (0),
402         0x25, 0x01,                     # .     Logical Maximum (1),
403         0xA1, 0x00,                     # .     Collection (Physical),
404         0x09, 0x42,                     # .         Usage (Tip Switch),
405         0x09, 0x44,                     # .         Usage (Barrel Switch),
406         0x09, 0x5A,                     # .         Usage (Secondary Barrel Switch),
407         0x09, 0x45,                     # .         Usage (Eraser),
408         0x09, 0x3C,                     # .         Usage (Invert),
409         0x09, 0x32,                     # .         Usage (In Range),
410         0x09, 0x36,                     # .         Usage (In Proximity),
411         0x25, 0x01,                     # .         Logical Maximum (1),
412         0x75, 0x01,                     # .         Report Size (1),
413         0x95, 0x07,                     # .         Report Count (7),
414         0x81, 0x02,                     # .         Input (Variable),
415         0x95, 0x01,                     # .         Report Count (1),
416         0x81, 0x03,                     # .         Input (Constant, Variable),
417         0x0A, 0x30, 0x01,               # .         Usage (X),
418         0x65, 0x11,                     # .         Unit (Centimeter),
419         0x55, 0x0D,                     # .         Unit Exponent (13),
420         0x47, 0x80, 0x3E, 0x00, 0x00,   # .         Physical Maximum (16000),
421         0x27, 0x80, 0x3E, 0x00, 0x00,   # .         Logical Maximum (16000),
422         0x75, 0x18,                     # .         Report Size (24),
423         0x95, 0x01,                     # .         Report Count (1),
424         0x81, 0x02,                     # .         Input (Variable),
425         0x0A, 0x31, 0x01,               # .         Usage (Y),
426         0x47, 0x28, 0x23, 0x00, 0x00,   # .         Physical Maximum (9000),
427         0x27, 0x28, 0x23, 0x00, 0x00,   # .         Logical Maximum (9000),
428         0x81, 0x02,                     # .         Input (Variable),
429         0x09, 0x30,                     # .         Usage (Tip Pressure),
430         0x55, 0x00,                     # .         Unit Exponent (0),
431         0x65, 0x00,                     # .         Unit,
432         0x47, 0x00, 0x00, 0x00, 0x00,   # .         Physical Maximum (0),
433         0x26, 0xFF, 0x0F,               # .         Logical Maximum (4095),
434         0x75, 0x10,                     # .         Report Size (16),
435         0x81, 0x02,                     # .         Input (Variable),
436         0x75, 0x08,                     # .         Report Size (8),
437         0x95, 0x06,                     # .         Report Count (6),
438         0x81, 0x03,                     # .         Input (Constant, Variable),
439         0x0A, 0x32, 0x01,               # .         Usage (Z),
440         0x25, 0x3F,                     # .         Logical Maximum (63),
441         0x75, 0x08,                     # .         Report Size (8),
442         0x95, 0x01,                     # .         Report Count (1),
443         0x81, 0x02,                     # .         Input (Variable),
444         0x09, 0x5B,                     # .         Usage (Transducer Serial Number),
445         0x09, 0x5C,                     # .         Usage (Transducer Serial Number Hi),
446         0x17, 0x00, 0x00, 0x00, 0x80,   # .         Logical Minimum (-2147483648),
447         0x27, 0xFF, 0xFF, 0xFF, 0x7F,   # .         Logical Maximum (2147483647),
448         0x75, 0x20,                     # .         Report Size (32),
449         0x95, 0x02,                     # .         Report Count (2),
450         0x81, 0x02,                     # .         Input (Variable),
451         0x09, 0x77,                     # .         Usage (Tool Type),
452         0x15, 0x00,                     # .         Logical Minimum (0),
453         0x26, 0xFF, 0x0F,               # .         Logical Maximum (4095),
454         0x75, 0x10,                     # .         Report Size (16),
455         0x95, 0x01,                     # .         Report Count (1),
456         0x81, 0x02,                     # .         Input (Variable),
457         0xC0,                           # .     End Collection,
458         0xC0                            # . End Collection
459     ]
460     # fmt: on
461 
462     def __init__(self, rdesc=report_descriptor, name=None, info=(0x3, 0x056A, 0x9999)):
463         super().__init__(rdesc, name, info)
464         self.default_reportID = 16
465 
466 
467 class PTHX60_Pen(BaseTablet):
468     """
469     Pen interface of a PTH-660 / PTH-860 / PTH-460 tablet.
470 
471     This generation of devices are nearly identical to each other, though
472     the PTH-460 uses a slightly different descriptor construction (splits
473     the pad among several physical collections)
474     """
475 
476     def __init__(self, rdesc=None, name=None, info=None):
477         super().__init__(rdesc, name, info)
478         self.default_reportID = 16
479 
480 
481 class BaseTest:
482     class TestTablet(base.BaseTestCase.TestUhid):
483         kernel_modules = [KERNEL_MODULE]
484 
485         def sync_and_assert_events(
486             self, report, expected_events, auto_syn=True, strict=False
487         ):
488             """
489             Assert we see the expected events in response to a report.
490             """
491             uhdev = self.uhdev
492             syn_event = self.syn_event
493             if auto_syn:
494                 expected_events.append(syn_event)
495             actual_events = uhdev.next_sync_events()
496             self.debug_reports(report, uhdev, actual_events)
497             if strict:
498                 self.assertInputEvents(expected_events, actual_events)
499             else:
500                 self.assertInputEventsIn(expected_events, actual_events)
501 
502         def get_usages(self, uhdev):
503             def get_report_usages(report):
504                 application = report.application
505                 for field in report.fields:
506                     if field.usages is not None:
507                         for usage in field.usages:
508                             yield (field, usage, application)
509                     else:
510                         yield (field, field.usage, application)
511 
512             desc = uhdev.parsed_rdesc
513             reports = [
514                 *desc.input_reports.values(),
515                 *desc.feature_reports.values(),
516                 *desc.output_reports.values(),
517             ]
518             for report in reports:
519                 for usage in get_report_usages(report):
520                     yield usage
521 
522         def assertName(self, uhdev, type):
523             """
524             Assert that the name is as we expect.
525 
526             The Wacom driver applies a number of decorations to the name
527             provided by the hardware. We cannot rely on the definition of
528             this assertion from the base class to work properly.
529             """
530             evdev = uhdev.get_evdev()
531             expected_name = uhdev.name + type
532             if "wacom" not in expected_name.lower():
533                 expected_name = "Wacom " + expected_name
534             assert evdev.name == expected_name
535 
536         def test_descriptor_physicals(self):
537             """
538             Verify that all HID usages which should have a physical range
539             actually do, and those which shouldn't don't. Also verify that
540             the associated unit is correct and within a sensible range.
541             """
542 
543             def usage_id(page_name, usage_name):
544                 page = HUT.usage_page_from_name(page_name)
545                 return (page.page_id << 16) | page[usage_name].usage
546 
547             required = {
548                 usage_id("Generic Desktop", "X"): PhysRange(
549                     PhysRange.CENTIMETER, 5, 150
550                 ),
551                 usage_id("Generic Desktop", "Y"): PhysRange(
552                     PhysRange.CENTIMETER, 5, 150
553                 ),
554                 usage_id("Digitizers", "Width"): PhysRange(
555                     PhysRange.CENTIMETER, 5, 150
556                 ),
557                 usage_id("Digitizers", "Height"): PhysRange(
558                     PhysRange.CENTIMETER, 5, 150
559                 ),
560                 usage_id("Digitizers", "X Tilt"): PhysRange(PhysRange.DEGREE, 90, 180),
561                 usage_id("Digitizers", "Y Tilt"): PhysRange(PhysRange.DEGREE, 90, 180),
562                 usage_id("Digitizers", "Twist"): PhysRange(PhysRange.DEGREE, 358, 360),
563                 usage_id("Wacom", "X Tilt"): PhysRange(PhysRange.DEGREE, 90, 180),
564                 usage_id("Wacom", "Y Tilt"): PhysRange(PhysRange.DEGREE, 90, 180),
565                 usage_id("Wacom", "Twist"): PhysRange(PhysRange.DEGREE, 358, 360),
566                 usage_id("Wacom", "X"): PhysRange(PhysRange.CENTIMETER, 5, 150),
567                 usage_id("Wacom", "Y"): PhysRange(PhysRange.CENTIMETER, 5, 150),
568                 usage_id("Wacom", "Wacom TouchRing"): PhysRange(
569                     PhysRange.DEGREE, 358, 360
570                 ),
571                 usage_id("Wacom", "Wacom Offset Left"): PhysRange(
572                     PhysRange.CENTIMETER, 0, 0.5
573                 ),
574                 usage_id("Wacom", "Wacom Offset Top"): PhysRange(
575                     PhysRange.CENTIMETER, 0, 0.5
576                 ),
577                 usage_id("Wacom", "Wacom Offset Right"): PhysRange(
578                     PhysRange.CENTIMETER, 0, 0.5
579                 ),
580                 usage_id("Wacom", "Wacom Offset Bottom"): PhysRange(
581                     PhysRange.CENTIMETER, 0, 0.5
582                 ),
583             }
584             for field, usage, application in self.get_usages(self.uhdev):
585                 if application == usage_id("Generic Desktop", "Mouse"):
586                     # Ignore the vestigial Mouse collection which exists
587                     # on Wacom tablets only for backwards compatibility.
588                     continue
589 
590                 expect_physical = usage in required
591 
592                 phys_set = field.physical_min != 0 or field.physical_max != 0
593                 assert phys_set == expect_physical
594 
595                 unit_set = field.unit != 0
596                 assert unit_set == expect_physical
597 
598                 if unit_set:
599                     assert required[usage].contains(field)
600 
601         def test_prop_direct(self):
602             """
603             Todo: Verify that INPUT_PROP_DIRECT is set on display devices.
604             """
605             pass
606 
607         def test_prop_pointer(self):
608             """
609             Todo: Verify that INPUT_PROP_POINTER is set on opaque devices.
610             """
611             pass
612 
613 
614 class PenTabletTest(BaseTest.TestTablet):
615     def assertName(self, uhdev):
616         super().assertName(uhdev, " Pen")
617 
618 
619 class TouchTabletTest(BaseTest.TestTablet):
620     def assertName(self, uhdev):
621         super().assertName(uhdev, " Finger")
622 
623 
624 class TestOpaqueTablet(PenTabletTest):
625     def create_device(self):
626         return OpaqueTablet()
627 
628     def test_sanity(self):
629         """
630         Bring a pen into contact with the tablet, then remove it.
631 
632         Ensure that we get the basic tool/touch/motion events that should
633         be sent by the driver.
634         """
635         uhdev = self.uhdev
636 
637         self.sync_and_assert_events(
638             uhdev.event(
639                 100,
640                 200,
641                 pressure=300,
642                 buttons=Buttons.clear(),
643                 toolid=ToolID(serial=1, tooltype=1),
644                 proximity=ProximityState.IN_RANGE,
645             ),
646             [
647                 libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN, 1),
648                 libevdev.InputEvent(libevdev.EV_ABS.ABS_X, 100),
649                 libevdev.InputEvent(libevdev.EV_ABS.ABS_Y, 200),
650                 libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1),
651             ],
652         )
653 
654         self.sync_and_assert_events(
655             uhdev.event(110, 220, pressure=0),
656             [
657                 libevdev.InputEvent(libevdev.EV_ABS.ABS_X, 110),
658                 libevdev.InputEvent(libevdev.EV_ABS.ABS_Y, 220),
659                 libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 0),
660             ],
661         )
662 
663         self.sync_and_assert_events(
664             uhdev.event(
665                 120,
666                 230,
667                 pressure=0,
668                 toolid=ToolID.clear(),
669                 proximity=ProximityState.OUT,
670             ),
671             [
672                 libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN, 0),
673             ],
674         )
675 
676         self.sync_and_assert_events(
677             uhdev.event(130, 240, pressure=0), [], auto_syn=False, strict=True
678         )
679 
680 
681 class TestOpaqueCTLTablet(TestOpaqueTablet):
682     def create_device(self):
683         return OpaqueCTLTablet()
684 
685     def test_buttons(self):
686         """
687         Test that the barrel buttons (side switches) work as expected.
688 
689         Press and release each button individually to verify that we get
690         the expected events.
691         """
692         uhdev = self.uhdev
693 
694         self.sync_and_assert_events(
695             uhdev.event(
696                 100,
697                 200,
698                 pressure=0,
699                 buttons=Buttons.clear(),
700                 toolid=ToolID(serial=1, tooltype=1),
701                 proximity=ProximityState.IN_RANGE,
702             ),
703             [
704                 libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN, 1),
705                 libevdev.InputEvent(libevdev.EV_ABS.ABS_X, 100),
706                 libevdev.InputEvent(libevdev.EV_ABS.ABS_Y, 200),
707                 libevdev.InputEvent(libevdev.EV_MSC.MSC_SERIAL, 1),
708             ],
709         )
710 
711         self.sync_and_assert_events(
712             uhdev.event(100, 200, pressure=0, buttons=Buttons(primary=True)),
713             [
714                 libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS, 1),
715                 libevdev.InputEvent(libevdev.EV_MSC.MSC_SERIAL, 1),
716             ],
717         )
718 
719         self.sync_and_assert_events(
720             uhdev.event(100, 200, pressure=0, buttons=Buttons(primary=False)),
721             [
722                 libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS, 0),
723                 libevdev.InputEvent(libevdev.EV_MSC.MSC_SERIAL, 1),
724             ],
725         )
726 
727         self.sync_and_assert_events(
728             uhdev.event(100, 200, pressure=0, buttons=Buttons(secondary=True)),
729             [
730                 libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS2, 1),
731                 libevdev.InputEvent(libevdev.EV_MSC.MSC_SERIAL, 1),
732             ],
733         )
734 
735         self.sync_and_assert_events(
736             uhdev.event(100, 200, pressure=0, buttons=Buttons(secondary=False)),
737             [
738                 libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS2, 0),
739                 libevdev.InputEvent(libevdev.EV_MSC.MSC_SERIAL, 1),
740             ],
741         )
742 
743 
744 PTHX60_Devices = [
745     {"rdesc": wacom_pth660_v145, "info": (0x3, 0x056A, 0x0357)},
746     {"rdesc": wacom_pth660_v150, "info": (0x3, 0x056A, 0x0357)},
747     {"rdesc": wacom_pth860_v145, "info": (0x3, 0x056A, 0x0358)},
748     {"rdesc": wacom_pth860_v150, "info": (0x3, 0x056A, 0x0358)},
749     {"rdesc": wacom_pth460_v105, "info": (0x3, 0x056A, 0x0392)},
750 ]
751 
752 PTHX60_Names = [
753     "PTH-660/v145",
754     "PTH-660/v150",
755     "PTH-860/v145",
756     "PTH-860/v150",
757     "PTH-460/v105",
758 ]
759 
760 
761 class TestPTHX60_Pen(TestOpaqueCTLTablet):
762     @pytest.fixture(
763         autouse=True, scope="class", params=PTHX60_Devices, ids=PTHX60_Names
764     )
765     def set_device_params(self, request):
766         request.cls.device_params = request.param
767 
768     def create_device(self):
769         return PTHX60_Pen(**self.device_params)
770 
771     @pytest.mark.xfail
772     def test_descriptor_physicals(self):
773         # XFAIL: Various documented errata
774         super().test_descriptor_physicals()
775 
776     def test_heartbeat_spurious(self):
777         """
778         Test that the heartbeat report does not send spurious events.
779         """
780         uhdev = self.uhdev
781 
782         self.sync_and_assert_events(
783             uhdev.event(
784                 100,
785                 200,
786                 pressure=300,
787                 buttons=Buttons.clear(),
788                 toolid=ToolID(serial=1, tooltype=0x822),
789                 proximity=ProximityState.IN_RANGE,
790             ),
791             [
792                 libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN, 1),
793                 libevdev.InputEvent(libevdev.EV_ABS.ABS_X, 100),
794                 libevdev.InputEvent(libevdev.EV_ABS.ABS_Y, 200),
795                 libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1),
796             ],
797         )
798 
799         # Exactly zero events: not even a SYN
800         self.sync_and_assert_events(
801             uhdev.event_heartbeat(19), [], auto_syn=False, strict=True
802         )
803 
804         self.sync_and_assert_events(
805             uhdev.event(110, 200, pressure=300),
806             [
807                 libevdev.InputEvent(libevdev.EV_ABS.ABS_X, 110),
808             ],
809         )
810 
811     def test_empty_pad_sync(self):
812         self.empty_pad_sync(num=3, denom=16, reverse=True)
813 
814     def empty_pad_sync(self, num, denom, reverse):
815         """
816         Test that multiple pad collections do not trigger empty syncs.
817         """
818 
819         def offset_rotation(value):
820             """
821             Offset touchring rotation values by the same factor as the
822             Linux kernel. Tablets historically don't use the same origin
823             as HID, and it sometimes changes from tablet to tablet...
824             """
825             evdev = self.uhdev.get_evdev()
826             info = evdev.absinfo[libevdev.EV_ABS.ABS_WHEEL]
827             delta = info.maximum - info.minimum + 1
828             if reverse:
829                 value = info.maximum - value
830             value += num * delta // denom
831             if value > info.maximum:
832                 value -= delta
833             elif value < info.minimum:
834                 value += delta
835             return value
836 
837         uhdev = self.uhdev
838         uhdev.application = "Pad"
839         evdev = uhdev.get_evdev()
840 
841         print(evdev.name)
842         self.sync_and_assert_events(
843             uhdev.event_pad(reportID=17, ring=0, ek0=1),
844             [
845                 libevdev.InputEvent(libevdev.EV_KEY.BTN_0, 1),
846                 libevdev.InputEvent(libevdev.EV_ABS.ABS_WHEEL, offset_rotation(0)),
847                 libevdev.InputEvent(libevdev.EV_ABS.ABS_MISC, 15),
848             ],
849         )
850 
851         self.sync_and_assert_events(
852             uhdev.event_pad(reportID=17, ring=1, ek0=1),
853             [libevdev.InputEvent(libevdev.EV_ABS.ABS_WHEEL, offset_rotation(1))],
854         )
855 
856         self.sync_and_assert_events(
857             uhdev.event_pad(reportID=17, ring=2, ek0=0),
858             [
859                 libevdev.InputEvent(libevdev.EV_ABS.ABS_WHEEL, offset_rotation(2)),
860                 libevdev.InputEvent(libevdev.EV_KEY.BTN_0, 0),
861             ],
862         )
863 
864 
865 class TestDTH2452Tablet(test_multitouch.BaseTest.TestMultitouch, TouchTabletTest):
866     ContactIds = namedtuple("ContactIds", "contact_id, tracking_id, slot_num")
867 
868     def create_device(self):
869         return test_multitouch.Digitizer(
870             "DTH 2452",
871             rdesc="05 0d 09 04 a1 01 85 0c 95 01 75 08 15 00 26 ff 00 81 03 09 54 81 02 09 22 a1 02 05 0d 95 01 75 01 25 01 09 42 81 02 81 03 09 47 81 02 95 05 81 03 09 51 26 ff 00 75 10 95 01 81 02 35 00 65 11 55 0e 05 01 09 30 26 a0 44 46 96 14 81 42 09 31 26 9a 26 46 95 0b 81 42 05 0d 75 08 95 01 15 00 09 48 26 5f 00 46 7c 14 81 02 09 49 25 35 46 7d 0b 81 02 45 00 65 00 55 00 c0 05 0d 09 22 a1 02 05 0d 95 01 75 01 25 01 09 42 81 02 81 03 09 47 81 02 95 05 81 03 09 51 26 ff 00 75 10 95 01 81 02 35 00 65 11 55 0e 05 01 09 30 26 a0 44 46 96 14 81 42 09 31 26 9a 26 46 95 0b 81 42 05 0d 75 08 95 01 15 00 09 48 26 5f 00 46 7c 14 81 02 09 49 25 35 46 7d 0b 81 02 45 00 65 00 55 00 c0 05 0d 09 22 a1 02 05 0d 95 01 75 01 25 01 09 42 81 02 81 03 09 47 81 02 95 05 81 03 09 51 26 ff 00 75 10 95 01 81 02 35 00 65 11 55 0e 05 01 09 30 26 a0 44 46 96 14 81 42 09 31 26 9a 26 46 95 0b 81 42 05 0d 75 08 95 01 15 00 09 48 26 5f 00 46 7c 14 81 02 09 49 25 35 46 7d 0b 81 02 45 00 65 00 55 00 c0 05 0d 09 22 a1 02 05 0d 95 01 75 01 25 01 09 42 81 02 81 03 09 47 81 02 95 05 81 03 09 51 26 ff 00 75 10 95 01 81 02 35 00 65 11 55 0e 05 01 09 30 26 a0 44 46 96 14 81 42 09 31 26 9a 26 46 95 0b 81 42 05 0d 75 08 95 01 15 00 09 48 26 5f 00 46 7c 14 81 02 09 49 25 35 46 7d 0b 81 02 45 00 65 00 55 00 c0 05 0d 09 22 a1 02 05 0d 95 01 75 01 25 01 09 42 81 02 81 03 09 47 81 02 95 05 81 03 09 51 26 ff 00 75 10 95 01 81 02 35 00 65 11 55 0e 05 01 09 30 26 a0 44 46 96 14 81 42 09 31 26 9a 26 46 95 0b 81 42 05 0d 75 08 95 01 15 00 09 48 26 5f 00 46 7c 14 81 02 09 49 25 35 46 7d 0b 81 02 45 00 65 00 55 00 c0 05 0d 27 ff ff 00 00 75 10 95 01 09 56 81 02 75 08 95 0e 81 03 09 55 26 ff 00 75 08 b1 02 85 0a 06 00 ff 09 c5 96 00 01 b1 02 c0 06 00 ff 09 01 a1 01 09 01 85 13 15 00 26 ff 00 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0",
872             input_info=(0x3, 0x056A, 0x0383),
873         )
874 
875     def make_contact(self, contact_id=0, t=0):
876         """
877         Make a single touch contact that can move over time.
878 
879         Creates a touch object that has a well-known position in space that
880         does not overlap with other contacts. The value of `t` may be
881         incremented over time to move the point along a linear path.
882         """
883         x = 50 + 10 * contact_id + t * 11
884         y = 100 + 100 * contact_id + t * 11
885         return test_multitouch.Touch(contact_id, x, y)
886 
887     def make_contacts(self, n, t=0):
888         """
889         Make multiple touch contacts that can move over time.
890 
891         Returns a list of `n` touch objects that are positioned at well-known
892         locations. The value of `t` may be incremented over time to move the
893         points along a linear path.
894         """
895         return [ self.make_contact(id, t) for id in range(0, n) ]
896 
897     def assert_contact(self, uhdev, evdev, contact_ids, t=0):
898         """
899         Assert properties of a contact generated by make_contact.
900         """
901         contact_id = contact_ids.contact_id
902         tracking_id = contact_ids.tracking_id
903         slot_num = contact_ids.slot_num
904 
905         x = 50 + 10 * contact_id + t * 11
906         y = 100 + 100 * contact_id + t * 11
907 
908         # If the data isn't supposed to be stored in any slots, there is
909         # nothing we can check for in the evdev stream.
910         if slot_num is None:
911             assert tracking_id == -1
912             return
913 
914         assert evdev.slots[slot_num][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == tracking_id
915         if tracking_id != -1:
916             assert evdev.slots[slot_num][libevdev.EV_ABS.ABS_MT_POSITION_X] == x
917             assert evdev.slots[slot_num][libevdev.EV_ABS.ABS_MT_POSITION_Y] == y
918 
919     def assert_contacts(self, uhdev, evdev, data, t=0):
920         """
921         Assert properties of a list of contacts generated by make_contacts.
922         """
923         for contact_ids in data:
924             self.assert_contact(uhdev, evdev, contact_ids, t)
925 
926     def test_contact_id_0(self):
927         """
928         Bring a finger in contact with the tablet, then hold it down and remove it.
929 
930         Ensure that even with contact ID = 0 which is usually given as an invalid
931         touch event by most tablets with the exception of a few, that given the
932         confidence bit is set to 1 it should process it as a valid touch to cover
933         the few tablets using contact ID = 0 as a valid touch value.
934         """
935         uhdev = self.uhdev
936         evdev = uhdev.get_evdev()
937 
938         t0 = test_multitouch.Touch(0, 50, 100)
939         r = uhdev.event([t0])
940         events = uhdev.next_sync_events()
941         self.debug_reports(r, uhdev, events)
942 
943         slot = self.get_slot(uhdev, t0, 0)
944 
945         assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1) in events
946         assert evdev.slots[slot][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0
947         assert evdev.slots[slot][libevdev.EV_ABS.ABS_MT_POSITION_X] == 50
948         assert evdev.slots[slot][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 100
949 
950         t0.tipswitch = False
951         if uhdev.quirks is None or "VALID_IS_INRANGE" not in uhdev.quirks:
952             t0.inrange = False
953         r = uhdev.event([t0])
954         events = uhdev.next_sync_events()
955         self.debug_reports(r, uhdev, events)
956         assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 0) in events
957         assert evdev.slots[slot][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1
958 
959     def test_confidence_false(self):
960         """
961         Bring a finger in contact with the tablet with confidence set to false.
962 
963         Ensure that the confidence bit being set to false should not result in a touch event.
964         """
965         uhdev = self.uhdev
966         _evdev = uhdev.get_evdev()
967 
968         t0 = test_multitouch.Touch(1, 50, 100)
969         t0.confidence = False
970         r = uhdev.event([t0])
971         events = uhdev.next_sync_events()
972         self.debug_reports(r, uhdev, events)
973 
974         _slot = self.get_slot(uhdev, t0, 0)
975 
976         assert not events
977 
978     def test_confidence_multitouch(self):
979         """
980         Bring multiple fingers in contact with the tablet, some with the
981         confidence bit set, and some without.
982 
983         Ensure that all confident touches are reported and that all non-
984         confident touches are ignored.
985         """
986         uhdev = self.uhdev
987         evdev = uhdev.get_evdev()
988 
989         touches = self.make_contacts(5)
990         touches[0].confidence = False
991         touches[2].confidence = False
992         touches[4].confidence = False
993 
994         r = uhdev.event(touches)
995         events = uhdev.next_sync_events()
996         self.debug_reports(r, uhdev, events)
997 
998         assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1) in events
999 
1000         self.assert_contacts(uhdev, evdev,
1001             [ self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = None),
1002               self.ContactIds(contact_id = 1, tracking_id = 0, slot_num = 0),
1003               self.ContactIds(contact_id = 2, tracking_id = -1, slot_num = None),
1004               self.ContactIds(contact_id = 3, tracking_id = 1, slot_num = 1),
1005               self.ContactIds(contact_id = 4, tracking_id = -1, slot_num = None) ])
1006 
1007     def confidence_change_assert_playback(self, uhdev, evdev, timeline):
1008         """
1009         Assert proper behavior of contacts that move and change tipswitch /
1010         confidence status over time.
1011 
1012         Given a `timeline` list of touch states to iterate over, verify
1013         that the contacts move and are reported as up/down as expected
1014         by the state of the tipswitch and confidence bits.
1015         """
1016         t = 0
1017 
1018         for state in timeline:
1019             touches = self.make_contacts(len(state), t)
1020 
1021             for item in zip(touches, state):
1022                 item[0].tipswitch = item[1][1]
1023                 item[0].confidence = item[1][2]
1024 
1025             r = uhdev.event(touches)
1026             events = uhdev.next_sync_events()
1027             self.debug_reports(r, uhdev, events)
1028 
1029             ids = [ x[0] for x in state ]
1030             self.assert_contacts(uhdev, evdev, ids, t)
1031 
1032             t += 1
1033 
1034     def test_confidence_loss_a(self):
1035         """
1036         Transition a confident contact to a non-confident contact by
1037         first clearing the tipswitch.
1038 
1039         Ensure that the driver reports the transitioned contact as
1040         being removed and that other contacts continue to report
1041         normally. This mode of confidence loss is used by the
1042         DTH-2452.
1043         """
1044         uhdev = self.uhdev
1045         evdev = uhdev.get_evdev()
1046 
1047         self.confidence_change_assert_playback(uhdev, evdev, [
1048             # t=0: Contact 0 == Down + confident; Contact 1 == Down + confident
1049             # Both fingers confidently in contact
1050             [(self.ContactIds(contact_id = 0, tracking_id = 0, slot_num = 0), True, True),
1051              (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)],
1052 
1053             # t=1: Contact 0 == !Down + confident; Contact 1 == Down + confident
1054             # First finger looses confidence and clears only the tipswitch flag
1055             [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), False, True),
1056              (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)],
1057 
1058             # t=2: Contact 0 == !Down + !confident; Contact 1 == Down + confident
1059             # First finger has lost confidence and has both flags cleared
1060             [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), False, False),
1061              (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)],
1062 
1063             # t=3: Contact 0 == !Down + !confident; Contact 1 == Down + confident
1064             # First finger has lost confidence and has both flags cleared
1065             [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), False, False),
1066              (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)]
1067         ])
1068 
1069     def test_confidence_loss_b(self):
1070         """
1071         Transition a confident contact to a non-confident contact by
1072         cleraing both tipswitch and confidence bits simultaneously.
1073 
1074         Ensure that the driver reports the transitioned contact as
1075         being removed and that other contacts continue to report
1076         normally. This mode of confidence loss is used by some
1077         AES devices.
1078         """
1079         uhdev = self.uhdev
1080         evdev = uhdev.get_evdev()
1081 
1082         self.confidence_change_assert_playback(uhdev, evdev, [
1083             # t=0: Contact 0 == Down + confident; Contact 1 == Down + confident
1084             # Both fingers confidently in contact
1085             [(self.ContactIds(contact_id = 0, tracking_id = 0, slot_num = 0), True, True),
1086              (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)],
1087 
1088             # t=1: Contact 0 == !Down + !confident; Contact 1 == Down + confident
1089             # First finger looses confidence and has both flags cleared simultaneously
1090             [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), False, False),
1091              (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)],
1092 
1093             # t=2: Contact 0 == !Down + !confident; Contact 1 == Down + confident
1094             # First finger has lost confidence and has both flags cleared
1095             [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), False, False),
1096              (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)],
1097 
1098             # t=3: Contact 0 == !Down + !confident; Contact 1 == Down + confident
1099             # First finger has lost confidence and has both flags cleared
1100             [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), False, False),
1101              (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)]
1102         ])
1103 
1104     def test_confidence_loss_c(self):
1105         """
1106         Transition a confident contact to a non-confident contact by
1107         clearing only the confidence bit.
1108 
1109         Ensure that the driver reports the transitioned contact as
1110         being removed and that other contacts continue to report
1111         normally.
1112         """
1113         uhdev = self.uhdev
1114         evdev = uhdev.get_evdev()
1115 
1116         self.confidence_change_assert_playback(uhdev, evdev, [
1117             # t=0: Contact 0 == Down + confident; Contact 1 == Down + confident
1118             # Both fingers confidently in contact
1119             [(self.ContactIds(contact_id = 0, tracking_id = 0, slot_num = 0), True, True),
1120              (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)],
1121 
1122             # t=1: Contact 0 == Down + !confident; Contact 1 == Down + confident
1123             # First finger looses confidence and clears only the confidence flag
1124             [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), True, False),
1125              (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)],
1126 
1127             # t=2: Contact 0 == !Down + !confident; Contact 1 == Down + confident
1128             # First finger has lost confidence and has both flags cleared
1129             [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), False, False),
1130              (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)],
1131 
1132             # t=3: Contact 0 == !Down + !confident; Contact 1 == Down + confident
1133             # First finger has lost confidence and has both flags cleared
1134             [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), False, False),
1135              (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)]
1136         ])
1137 
1138     def test_confidence_gain_a(self):
1139         """
1140         Transition a contact that was always non-confident to confident.
1141 
1142         Ensure that the confident contact is reported normally.
1143         """
1144         uhdev = self.uhdev
1145         evdev = uhdev.get_evdev()
1146 
1147         self.confidence_change_assert_playback(uhdev, evdev, [
1148             # t=0: Contact 0 == Down + !confident; Contact 1 == Down + confident
1149             # Only second finger is confidently in contact
1150             [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = None), True, False),
1151              (self.ContactIds(contact_id = 1, tracking_id = 0, slot_num = 0), True, True)],
1152 
1153             # t=1: Contact 0 == Down + !confident; Contact 1 == Down + confident
1154             # First finger gains confidence
1155             [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = None), True, False),
1156              (self.ContactIds(contact_id = 1, tracking_id = 0, slot_num = 0), True, True)],
1157 
1158             # t=2: Contact 0 == Down + confident; Contact 1 == Down + confident
1159             # First finger remains confident
1160             [(self.ContactIds(contact_id = 0, tracking_id = 1, slot_num = 1), True, True),
1161              (self.ContactIds(contact_id = 1, tracking_id = 0, slot_num = 0), True, True)],
1162 
1163             # t=3: Contact 0 == Down + confident; Contact 1 == Down + confident
1164             # First finger remains confident
1165             [(self.ContactIds(contact_id = 0, tracking_id = 1, slot_num = 1), True, True),
1166              (self.ContactIds(contact_id = 1, tracking_id = 0, slot_num = 0), True, True)]
1167         ])
1168 
1169     def test_confidence_gain_b(self):
1170         """
1171         Transition a contact from non-confident to confident.
1172 
1173         Ensure that the confident contact is reported normally.
1174         """
1175         uhdev = self.uhdev
1176         evdev = uhdev.get_evdev()
1177 
1178         self.confidence_change_assert_playback(uhdev, evdev, [
1179             # t=0: Contact 0 == Down + confident; Contact 1 == Down + confident
1180             # First and second finger confidently in contact
1181             [(self.ContactIds(contact_id = 0, tracking_id = 0, slot_num = 0), True, True),
1182              (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)],
1183 
1184             # t=1: Contact 0 == Down + !confident; Contact 1 == Down + confident
1185             # Firtst finger looses confidence
1186             [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), True, False),
1187              (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)],
1188 
1189             # t=2: Contact 0 == Down + confident; Contact 1 == Down + confident
1190             # First finger gains confidence
1191             [(self.ContactIds(contact_id = 0, tracking_id = 2, slot_num = 0), True, True),
1192              (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)],
1193 
1194             # t=3: Contact 0 == !Down + confident; Contact 1 == Down + confident
1195             # First finger goes up
1196             [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), False, True),
1197              (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)]
1198         ])

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php