2292 lines · 233.8 KB · Python
RawDownload
#!/bin/env python3# SPDX-License-Identifier: GPL-2.0# -*- coding: utf-8 -*-## Copyright (c) 2017 Benjamin Tissoires <benjamin.tissoires@gmail.com># Copyright (c) 2017 Red Hat, Inc.# from . import basefrom hidtools.hut import HUTfrom hidtools.util import BusTypeimport enumimport libevdevimport loggingimport pytestimport sysimport time logger = logging.getLogger("hidtools.test.multitouch") KERNEL_MODULE = base.KernelModule("hid-multitouch", "hid_multitouch")  def BIT(x):    return 1 << x  mt_quirks = {    "NOT_SEEN_MEANS_UP": BIT(0),    "SLOT_IS_CONTACTID": BIT(1),    "CYPRESS": BIT(2),    "SLOT_IS_CONTACTNUMBER": BIT(3),    "ALWAYS_VALID": BIT(4),    "VALID_IS_INRANGE": BIT(5),    "VALID_IS_CONFIDENCE": BIT(6),    "CONFIDENCE": BIT(7),    "SLOT_IS_CONTACTID_MINUS_ONE": BIT(8),    "NO_AREA": BIT(9),    "IGNORE_DUPLICATES": BIT(10),    "HOVERING": BIT(11),    "CONTACT_CNT_ACCURATE": BIT(12),    "FORCE_GET_FEATURE": BIT(13),    "FIX_CONST_CONTACT_ID": BIT(14),    "TOUCH_SIZE_SCALING": BIT(15),    "STICKY_FINGERS": BIT(16),    "ASUS_CUSTOM_UP": BIT(17),    "WIN8_PTP_BUTTONS": BIT(18),    "SEPARATE_APP_REPORT": BIT(19),    "MT_QUIRK_FORCE_MULTI_INPUT": BIT(20),}  class Data(object):    pass  class Touch(object):    def __init__(self, id, x, y):        self.contactid = id        self.x = x        self.y = y        self.cx = x        self.cy = y        self.tipswitch = True        self.confidence = True        self.tippressure = 15        self.azimuth = 0        self.inrange = True        self.width = 10        self.height = 10  class Pen(Touch):    def __init__(self, x, y):        super().__init__(0, x, y)        self.barrel = False        self.invert = False        self.eraser = False        self.x_tilt = False        self.y_tilt = False        self.twist = 0  class Digitizer(base.UHIDTestDevice):    @classmethod    def msCertificationBlob(cls, reportID):        return f"""        Usage Page (Digitizers)        Usage (Touch Screen)        Collection (Application)         Report ID ({reportID})         Usage Page (0xff00)         Usage (0xc5)         Logical Minimum (0)         Logical Maximum (255)         Report Size (8)         Report Count (256)         Feature (Data,Var,Abs)        End Collection    """     def __init__(        self,        name,        rdesc_str=None,        rdesc=None,        application="Touch Screen",        physical="Finger",        max_contacts=None,        input_info=(BusType.USB, 1, 2),        quirks=None,    ):        super().__init__(name, application, rdesc_str, rdesc, input_info)        self.scantime = 0        self.quirks = quirks        if max_contacts is None:            self.max_contacts = sys.maxsize            for features in self.parsed_rdesc.feature_reports.values():                for feature in features:                    if feature.usage_name in ["Contact Max"]:                        self.max_contacts = feature.logical_max            for inputs in self.parsed_rdesc.input_reports.values():                for i in inputs:                    if (                        i.usage_name in ["Contact Count"]                        and i.logical_max > 0                        and self.max_contacts > i.logical_max                    ):                        self.max_contacts = i.logical_max            if self.max_contacts == sys.maxsize:                self.max_contacts = 1        else:            self.max_contacts = max_contacts        self.physical = physical        self.cur_application = application         for features in self.parsed_rdesc.feature_reports.values():            for feature in features:                if feature.usage_name == "Inputmode":                    self.cur_application = "Mouse"         self.fields = []        for r in self.parsed_rdesc.input_reports.values():            if r.application_name == self.application:                physicals = [f.physical_name for f in r]                if self.physical not in physicals and None not in physicals:                    continue                self.fields = [f.usage_name for f in r]     @property    def touches_in_a_report(self):        return self.fields.count("Contact Id")     def event(self, slots, global_data=None, contact_count=None, incr_scantime=True):        if incr_scantime:            self.scantime += 1        rs = []        # make sure we have only the required number of available slots        slots = slots[: self.max_contacts]         if global_data is None:            global_data = Data()        if contact_count is None:            global_data.contactcount = len(slots)        else:            global_data.contactcount = contact_count        global_data.scantime = self.scantime         while len(slots):            r = self.create_report(                application=self.cur_application, data=slots, global_data=global_data            )            self.call_input_event(r)            rs.append(r)            global_data.contactcount = 0        return rs     def get_report(self, req, rnum, rtype):        if rtype != self.UHID_FEATURE_REPORT:            return (1, [])         rdesc = None        for v in self.parsed_rdesc.feature_reports.values():            if v.report_ID == rnum:                rdesc = v         if rdesc is None:            return (1, [])         if "Contact Max" not in [f.usage_name for f in rdesc]:            return (1, [])         self.contactmax = self.max_contacts        r = rdesc.create_report([self], None)        return (0, r)     def set_report(self, req, rnum, rtype, data):        if rtype != self.UHID_FEATURE_REPORT:            return 1         rdesc = None        for v in self.parsed_rdesc.feature_reports.values():            if v.report_ID == rnum:                rdesc = v         if rdesc is None:            return 1         if "Inputmode" not in [f.usage_name for f in rdesc]:            return 0         Inputmode_seen = False        for f in rdesc:            if "Inputmode" == f.usage_name:                values = f.get_values(data)                assert len(values) == 1                value = values[0]                 if not Inputmode_seen:                    Inputmode_seen = True                    if value == 0:                        self.cur_application = "Mouse"                    elif value == 2:                        self.cur_application = "Touch Screen"                    elif value == 3:                        self.cur_application = "Touch Pad"                else:                    if value != 0:                        # Elan bug where the device doesn't work properly                        # if we set twice an Input Mode in the same Feature                        self.cur_application = "Mouse"         return 0  class HIDButtonType(enum.IntEnum):    CLICKPAD = 0    PRESSUREPAD = 1    DISCRETE_BUTTONS = 2  class PTP(Digitizer):    def __init__(        self,        name,        buttontype=HIDButtonType.CLICKPAD,        rdesc_str=None,        rdesc=None,        application="Touch Pad",        physical="Pointer",        max_contacts=None,        input_info=None,    ):        self.buttontype = buttontype         self.clickpad_state = False        self.left_state = False        self.right_state = False        super().__init__(            name, rdesc_str, rdesc, application, physical, max_contacts, input_info        )     def event(        self,        slots=None,        click=None,        left=None,        right=None,        contact_count=None,        incr_scantime=True,    ):        # update our internal state        if click is not None:            self.clickpad_state = click        if left is not None:            self.left_state = left        if right is not None:            self.right_state = right         # now create the global data        global_data = Data()        global_data.b1 = 1 if self.clickpad_state else 0        global_data.b2 = 1 if self.left_state else 0        global_data.b3 = 1 if self.right_state else 0         if slots is None:            slots = [Data()]         return super().event(slots, global_data, contact_count, incr_scantime)  class MinWin8TSParallel(Digitizer):    def __init__(self, max_slots):        self.max_slots = max_slots        self.phys_max = 120, 90        rdesc_finger_str = f"""            Usage Page (Digitizers)            Usage (Finger)            Collection (Logical)             Report Size (1)             Report Count (1)             Logical Minimum (0)             Logical Maximum (1)             Usage (Tip Switch)             Input (Data,Var,Abs)             Report Size (7)             Logical Maximum (127)             Input (Cnst,Var,Abs)             Report Size (8)             Logical Maximum (255)             Usage (Contact Id)             Input (Data,Var,Abs)             Report Size (16)             Unit Exponent (-1)             Unit (SILinear: cm)             Logical Maximum (4095)             Physical Minimum (0)             Physical Maximum ({self.phys_max[0]})             Usage Page (Generic Desktop)             Usage (X)             Input (Data,Var,Abs)             Physical Maximum ({self.phys_max[1]})             Usage (Y)             Input (Data,Var,Abs)             Usage Page (Digitizers)             Usage (Azimuth)             Logical Maximum (360)             Unit (SILinear: deg)             Report Size (16)             Input (Data,Var,Abs)            End Collection"""        rdesc_str = f"""           Usage Page (Digitizers)           Usage (Touch Screen)           Collection (Application)            Report ID (1)            {rdesc_finger_str * self.max_slots}            Unit Exponent (-4)            Unit (SILinear: s)            Logical Maximum (65535)            Physical Maximum (65535)            Usage Page (Digitizers)            Usage (Scan Time)            Input (Data,Var,Abs)            Report Size (8)            Logical Maximum (255)            Usage (Contact Count)            Input (Data,Var,Abs)            Report ID (2)            Logical Maximum ({self.max_slots})            Usage (Contact Max)            Feature (Data,Var,Abs)          End Collection          {Digitizer.msCertificationBlob(68)}"""        super().__init__(f"uhid test parallel {self.max_slots}", rdesc_str)  class MinWin8TSHybrid(Digitizer):    def __init__(self):        self.max_slots = 10        self.phys_max = 120, 90        rdesc_finger_str = f"""            Usage Page (Digitizers)            Usage (Finger)            Collection (Logical)             Report Size (1)             Report Count (1)             Logical Minimum (0)             Logical Maximum (1)             Usage (Tip Switch)             Input (Data,Var,Abs)             Report Size (7)             Logical Maximum (127)             Input (Cnst,Var,Abs)             Report Size (8)             Logical Maximum (255)             Usage (Contact Id)             Input (Data,Var,Abs)             Report Size (16)             Unit Exponent (-1)             Unit (SILinear: cm)             Logical Maximum (4095)             Physical Minimum (0)             Physical Maximum ({self.phys_max[0]})             Usage Page (Generic Desktop)             Usage (X)             Input (Data,Var,Abs)             Physical Maximum ({self.phys_max[1]})             Usage (Y)             Input (Data,Var,Abs)            End Collection"""        rdesc_str = f"""           Usage Page (Digitizers)           Usage (Touch Screen)           Collection (Application)            Report ID (1)            {rdesc_finger_str * 2}            Unit Exponent (-4)            Unit (SILinear: s)            Logical Maximum (65535)            Physical Maximum (65535)            Usage Page (Digitizers)            Usage (Scan Time)            Input (Data,Var,Abs)            Report Size (8)            Logical Maximum (255)            Usage (Contact Count)            Input (Data,Var,Abs)            Report ID (2)            Logical Maximum ({self.max_slots})            Usage (Contact Max)            Feature (Data,Var,Abs)          End Collection          {Digitizer.msCertificationBlob(68)}"""        super().__init__("uhid test hybrid", rdesc_str)  class Win8TSConfidence(Digitizer):    def __init__(self, max_slots):        self.max_slots = max_slots        self.phys_max = 120, 90        rdesc_finger_str = f"""            Usage Page (Digitizers)            Usage (Finger)            Collection (Logical)             Report Size (1)             Report Count (1)             Logical Minimum (0)             Logical Maximum (1)             Usage (Tip Switch)             Input (Data,Var,Abs)             Usage (Confidence)             Input (Data,Var,Abs)             Report Size (6)             Logical Maximum (127)             Input (Cnst,Var,Abs)             Report Size (8)             Logical Maximum (255)             Usage (Contact Id)             Input (Data,Var,Abs)             Report Size (16)             Unit Exponent (-1)             Unit (SILinear: cm)             Logical Maximum (4095)             Physical Minimum (0)             Physical Maximum ({self.phys_max[0]})             Usage Page (Generic Desktop)             Usage (X)             Input (Data,Var,Abs)             Physical Maximum ({self.phys_max[1]})             Usage (Y)             Input (Data,Var,Abs)             Usage Page (Digitizers)             Usage (Azimuth)             Logical Maximum (360)             Unit (SILinear: deg)             Report Size (16)             Input (Data,Var,Abs)            End Collection"""        rdesc_str = f"""           Usage Page (Digitizers)           Usage (Touch Screen)           Collection (Application)            Report ID (1)            {rdesc_finger_str * self.max_slots}            Unit Exponent (-4)            Unit (SILinear: s)            Logical Maximum (65535)            Physical Maximum (65535)            Usage Page (Digitizers)            Usage (Scan Time)            Input (Data,Var,Abs)            Report Size (8)            Logical Maximum (255)            Usage (Contact Count)            Input (Data,Var,Abs)            Report ID (2)            Logical Maximum ({self.max_slots})            Usage (Contact Max)            Feature (Data,Var,Abs)          End Collection          {Digitizer.msCertificationBlob(68)}"""        super().__init__(f"uhid test confidence {self.max_slots}", rdesc_str)  class SmartTechDigitizer(Digitizer):    def __init__(self, name, input_info):        super().__init__(            name,            rdesc="05 01 09 02 a1 01 85 01 09 01 a1 00 05 09 19 01 29 03 15 00 25 01 95 03 75 01 81 02 95 05 81 03 05 01 15 00 26 ff 0f 55 0e 65 11 75 10 95 01 35 00 46 c8 37 09 30 81 02 46 68 1f 09 31 81 02 45 00 c0 c0 05 0d 09 06 15 00 26 ff 00 a1 01 85 02 75 08 95 3f 09 00 82 02 01 95 3f 09 00 92 02 01 c0 05 0d 09 04 a1 01 85 05 05 0d 09 20 a1 00 25 01 75 01 95 02 09 42 09 45 81 02 75 06 95 01 09 30 81 02 26 ff 00 75 08 09 51 81 02 75 10 09 38 81 02 95 02 26 ff 0f 09 48 09 49 81 02 05 01 09 30 09 31 81 02 c0 05 0d 09 20 a1 00 25 01 75 01 95 02 09 42 09 45 81 02 75 06 95 01 09 30 81 02 26 ff 00 75 08 09 51 81 02 75 10 09 38 81 02 95 02 26 ff 0f 09 48 09 49 81 02 05 01 09 30 09 31 81 02 c0 05 0d 09 20 a1 00 25 01 75 01 95 02 09 42 09 45 81 02 75 06 95 01 09 30 81 02 26 ff 00 75 08 09 51 81 02 75 10 09 38 81 02 95 02 26 ff 0f 09 48 09 49 81 02 05 01 09 30 09 31 81 02 c0 05 0d 09 20 a1 00 25 01 75 01 95 02 09 42 09 45 81 02 75 06 95 01 09 30 81 02 26 ff 00 75 08 09 51 81 02 75 10 09 38 81 02 95 02 26 ff 0f 09 48 09 49 81 02 05 01 09 30 09 31 81 02 c0 05 0d 75 08 95 01 15 00 25 0a 09 54 81 02 09 55 b1 02 c0 05 0d 09 0e a1 01 85 04 09 23 a1 02 15 00 25 02 75 08 95 02 09 52 09 53 b1 02 c0 c0 05 0d 09 04 a1 01 85 03 05 0d 09 22 a1 02 15 00 25 01 75 01 95 02 09 42 09 47 81 02 95 02 81 03 75 04 95 01 25 0f 09 30 81 02 26 ff 00 75 08 95 01 09 51 81 02 75 10 27 a0 8c 00 00 55 0e 65 14 47 a0 8c 00 00 09 3f 81 02 65 11 26 ff 0f 46 c8 37 09 48 81 02 46 68 1f 09 49 81 02 05 01 46 c8 37 09 30 81 02 46 68 1f 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 15 00 25 01 75 01 95 02 09 42 09 47 81 02 95 02 81 03 75 04 95 01 25 0f 09 30 81 02 26 ff 00 75 08 95 01 09 51 81 02 75 10 27 a0 8c 00 00 55 0e 65 14 47 a0 8c 00 00 09 3f 81 02 65 11 26 ff 0f 46 c8 37 09 48 81 02 46 68 1f 09 49 81 02 05 01 46 c8 37 09 30 81 02 46 68 1f 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 15 00 25 01 75 01 95 02 09 42 09 47 81 02 95 02 81 03 75 04 95 01 25 0f 09 30 81 02 26 ff 00 75 08 95 01 09 51 81 02 75 10 27 a0 8c 00 00 55 0e 65 14 47 a0 8c 00 00 09 3f 81 02 65 11 26 ff 0f 46 c8 37 09 48 81 02 46 68 1f 09 49 81 02 05 01 46 c8 37 09 30 81 02 46 68 1f 09 31 81 02 45 00 c0 05 0d 09 22 a1 02 15 00 25 01 75 01 95 02 09 42 09 47 81 02 95 02 81 03 75 04 95 01 25 0f 09 30 81 02 26 ff 00 75 08 95 01 09 51 81 02 75 10 27 a0 8c 00 00 55 0e 65 14 47 a0 8c 00 00 09 3f 81 02 65 11 26 ff 0f 46 c8 37 09 48 81 02 46 68 1f 09 49 81 02 05 01 46 c8 37 09 30 81 02 46 68 1f 09 31 81 02 45 00 c0 05 0d 75 08 95 01 15 00 25 0a 09 54 81 02 09 55 b1 02 c0 05 0d 09 04 a1 01 85 06 09 22 a1 02 15 00 25 01 75 01 95 02 09 42 09 47 81 02 95 06 81 03 95 01 75 10 65 11 55 0e 26 ff 0f 46 c8 37 09 48 81 02 46 68 1f 09 49 81 02 05 01 46 c8 37 09 30 81 02 46 68 1f 09 31 81 02 45 00 c0 c0 05 0d 09 02 a1 01 85 07 09 20 a1 02 25 01 75 01 95 04 09 42 09 44 09 3c 09 45 81 02 75 04 95 01 25 0f 09 30 81 02 26 ff 00 75 08 09 38 81 02 75 10 27 a0 8c 00 00 55 0e 65 14 47 a0 8c 00 00 09 3f 81 02 65 11 26 ff 0f 46 c8 37 09 48 81 02 46 68 1f 09 49 81 02 05 01 46 c8 37 09 30 81 02 46 68 1f 09 31 81 02 45 00 c0 c0 05 0d 09 02 a1 01 85 08 09 20 a1 02 25 01 75 01 95 04 09 42 09 44 09 3c 09 45 81 02 75 04 95 01 25 0f 09 30 81 02 26 ff 00 75 08 09 38 81 02 75 10 27 a0 8c 00 00 55 0e 65 14 47 a0 8c 00 00 09 3f 81 02 65 11 26 ff 0f 46 c8 37 09 48 81 02 46 68 1f 09 49 81 02 05 01 46 c8 37 09 30 81 02 46 68 1f 09 31 81 02 45 00 c0 c0 05 0d 09 02 a1 01 85 09 09 20 a1 02 25 01 75 01 95 04 09 42 09 44 09 3c 09 45 81 02 75 04 95 01 25 0f 09 30 81 02 26 ff 00 75 08 09 38 81 02 75 10 27 a0 8c 00 00 55 0e 65 14 47 a0 8c 00 00 09 3f 81 02 65 11 26 ff 0f 46 c8 37 09 48 81 02 46 68 1f 09 49 81 02 05 01 46 c8 37 09 30 81 02 46 68 1f 09 31 81 02 45 00 c0 c0 05 0d 09 02 a1 01 85 0a 09 20 a1 02 25 01 75 01 95 04 09 42 09 44 09 3c 09 45 81 02 75 04 95 01 25 0f 09 30 81 02 26 ff 00 75 08 09 38 81 02 75 10 27 a0 8c 00 00 55 0e 65 14 47 a0 8c 00 00 09 3f 81 02 65 11 26 ff 0f 46 c8 37 09 48 81 02 46 68 1f 09 49 81 02 05 01 46 c8 37 09 30 81 02 46 68 1f 09 31 81 02 45 00 c0 c0",            input_info=input_info,        )     def create_report(self, data, global_data=None, reportID=None, application=None):

Lines 1–500 of 2292.