brintos

brintos / linux-shallow public Read only

0
0
Text · 42.4 KiB · 66daf7e Raw
978 lines · python
1#!/bin/env python32# SPDX-License-Identifier: GPL-2.03# -*- coding: utf-8 -*-4#5# Copyright (c) 2017 Benjamin Tissoires <benjamin.tissoires@gmail.com>6# Copyright (c) 2017 Red Hat, Inc.7#8 9from . import base10import hidtools.hid11from hidtools.util import BusType12import libevdev13import logging14import pytest15 16logger = logging.getLogger("hidtools.test.mouse")17 18# workaround https://gitlab.freedesktop.org/libevdev/python-libevdev/issues/619try:20    libevdev.EV_REL.REL_WHEEL_HI_RES21except AttributeError:22    libevdev.EV_REL.REL_WHEEL_HI_RES = libevdev.EV_REL.REL_0B23    libevdev.EV_REL.REL_HWHEEL_HI_RES = libevdev.EV_REL.REL_0C24 25 26class InvalidHIDCommunication(Exception):27    pass28 29 30class MouseData(object):31    pass32 33 34class BaseMouse(base.UHIDTestDevice):35    def __init__(self, rdesc, name=None, input_info=None):36        assert rdesc is not None37        super().__init__(name, "Mouse", input_info=input_info, rdesc=rdesc)38        self.left = False39        self.right = False40        self.middle = False41 42    def create_report(self, x, y, buttons=None, wheels=None, reportID=None):43        """44        Return an input report for this device.45 46        :param x: relative x47        :param y: relative y48        :param buttons: a (l, r, m) tuple of bools for the button states,49            where ``None`` is "leave unchanged"50        :param wheels: a single value for the vertical wheel or a (vertical, horizontal) tuple for51            the two wheels52        :param reportID: the numeric report ID for this report, if needed53        """54        if buttons is not None:55            left, right, middle = buttons56            if left is not None:57                self.left = left58            if right is not None:59                self.right = right60            if middle is not None:61                self.middle = middle62        left = self.left63        right = self.right64        middle = self.middle65        # Note: the BaseMouse doesn't actually have a wheel but the66        # create_report magic only fills in those fields exist, so let's67        # make this generic here.68        wheel, acpan = 0, 069        if wheels is not None:70            if isinstance(wheels, tuple):71                wheel = wheels[0]72                acpan = wheels[1]73            else:74                wheel = wheels75 76        reportID = reportID or self.default_reportID77 78        mouse = MouseData()79        mouse.b1 = int(left)80        mouse.b2 = int(right)81        mouse.b3 = int(middle)82        mouse.x = x83        mouse.y = y84        mouse.wheel = wheel85        mouse.acpan = acpan86        return super().create_report(mouse, reportID=reportID)87 88    def event(self, x, y, buttons=None, wheels=None):89        """90        Send an input event on the default report ID.91 92        :param x: relative x93        :param y: relative y94        :param buttons: a (l, r, m) tuple of bools for the button states,95            where ``None`` is "leave unchanged"96        :param wheels: a single value for the vertical wheel or a (vertical, horizontal) tuple for97            the two wheels98        """99        r = self.create_report(x, y, buttons, wheels)100        self.call_input_event(r)101        return [r]102 103 104class ButtonMouse(BaseMouse):105    # fmt: off106    report_descriptor = [107        0x05, 0x01,  # .Usage Page (Generic Desktop)        0108        0x09, 0x02,  # .Usage (Mouse)                       2109        0xa1, 0x01,  # .Collection (Application)            4110        0x09, 0x02,  # ..Usage (Mouse)                      6111        0xa1, 0x02,  # ..Collection (Logical)               8112        0x09, 0x01,  # ...Usage (Pointer)                   10113        0xa1, 0x00,  # ...Collection (Physical)             12114        0x05, 0x09,  # ....Usage Page (Button)              14115        0x19, 0x01,  # ....Usage Minimum (1)                16116        0x29, 0x03,  # ....Usage Maximum (3)                18117        0x15, 0x00,  # ....Logical Minimum (0)              20118        0x25, 0x01,  # ....Logical Maximum (1)              22119        0x75, 0x01,  # ....Report Size (1)                  24120        0x95, 0x03,  # ....Report Count (3)                 26121        0x81, 0x02,  # ....Input (Data,Var,Abs)             28122        0x75, 0x05,  # ....Report Size (5)                  30123        0x95, 0x01,  # ....Report Count (1)                 32124        0x81, 0x03,  # ....Input (Cnst,Var,Abs)             34125        0x05, 0x01,  # ....Usage Page (Generic Desktop)     36126        0x09, 0x30,  # ....Usage (X)                        38127        0x09, 0x31,  # ....Usage (Y)                        40128        0x15, 0x81,  # ....Logical Minimum (-127)           42129        0x25, 0x7f,  # ....Logical Maximum (127)            44130        0x75, 0x08,  # ....Report Size (8)                  46131        0x95, 0x02,  # ....Report Count (2)                 48132        0x81, 0x06,  # ....Input (Data,Var,Rel)             50133        0xc0,        # ...End Collection                    52134        0xc0,        # ..End Collection                     53135        0xc0,        # .End Collection                      54136    ]137    # fmt: on138 139    def __init__(self, rdesc=report_descriptor, name=None, input_info=None):140        super().__init__(rdesc, name, input_info)141 142    def fake_report(self, x, y, buttons):143        if buttons is not None:144            left, right, middle = buttons145            if left is None:146                left = self.left147            if right is None:148                right = self.right149            if middle is None:150                middle = self.middle151        else:152            left = self.left153            right = self.right154            middle = self.middle155 156        button_mask = sum(1 << i for i, b in enumerate([left, right, middle]) if b)157        x = max(-127, min(127, x))158        y = max(-127, min(127, y))159        x = hidtools.util.to_twos_comp(x, 8)160        y = hidtools.util.to_twos_comp(y, 8)161        return [button_mask, x, y]162 163 164class WheelMouse(ButtonMouse):165    # fmt: off166    report_descriptor = [167        0x05, 0x01,  # Usage Page (Generic Desktop)        0168        0x09, 0x02,  # Usage (Mouse)                       2169        0xa1, 0x01,  # Collection (Application)            4170        0x05, 0x09,  # .Usage Page (Button)                6171        0x19, 0x01,  # .Usage Minimum (1)                  8172        0x29, 0x03,  # .Usage Maximum (3)                  10173        0x15, 0x00,  # .Logical Minimum (0)                12174        0x25, 0x01,  # .Logical Maximum (1)                14175        0x95, 0x03,  # .Report Count (3)                   16176        0x75, 0x01,  # .Report Size (1)                    18177        0x81, 0x02,  # .Input (Data,Var,Abs)               20178        0x95, 0x01,  # .Report Count (1)                   22179        0x75, 0x05,  # .Report Size (5)                    24180        0x81, 0x03,  # .Input (Cnst,Var,Abs)               26181        0x05, 0x01,  # .Usage Page (Generic Desktop)       28182        0x09, 0x01,  # .Usage (Pointer)                    30183        0xa1, 0x00,  # .Collection (Physical)              32184        0x09, 0x30,  # ..Usage (X)                         34185        0x09, 0x31,  # ..Usage (Y)                         36186        0x15, 0x81,  # ..Logical Minimum (-127)            38187        0x25, 0x7f,  # ..Logical Maximum (127)             40188        0x75, 0x08,  # ..Report Size (8)                   42189        0x95, 0x02,  # ..Report Count (2)                  44190        0x81, 0x06,  # ..Input (Data,Var,Rel)              46191        0xc0,        # .End Collection                     48192        0x09, 0x38,  # .Usage (Wheel)                      49193        0x15, 0x81,  # .Logical Minimum (-127)             51194        0x25, 0x7f,  # .Logical Maximum (127)              53195        0x75, 0x08,  # .Report Size (8)                    55196        0x95, 0x01,  # .Report Count (1)                   57197        0x81, 0x06,  # .Input (Data,Var,Rel)               59198        0xc0,        # End Collection                      61199    ]200    # fmt: on201 202    def __init__(self, rdesc=report_descriptor, name=None, input_info=None):203        super().__init__(rdesc, name, input_info)204        self.wheel_multiplier = 1205 206 207class TwoWheelMouse(WheelMouse):208    # fmt: off209    report_descriptor = [210        0x05, 0x01,        # Usage Page (Generic Desktop)        0211        0x09, 0x02,        # Usage (Mouse)                       2212        0xa1, 0x01,        # Collection (Application)            4213        0x09, 0x01,        # .Usage (Pointer)                    6214        0xa1, 0x00,        # .Collection (Physical)              8215        0x05, 0x09,        # ..Usage Page (Button)               10216        0x19, 0x01,        # ..Usage Minimum (1)                 12217        0x29, 0x10,        # ..Usage Maximum (16)                14218        0x15, 0x00,        # ..Logical Minimum (0)               16219        0x25, 0x01,        # ..Logical Maximum (1)               18220        0x95, 0x10,        # ..Report Count (16)                 20221        0x75, 0x01,        # ..Report Size (1)                   22222        0x81, 0x02,        # ..Input (Data,Var,Abs)              24223        0x05, 0x01,        # ..Usage Page (Generic Desktop)      26224        0x16, 0x01, 0x80,  # ..Logical Minimum (-32767)          28225        0x26, 0xff, 0x7f,  # ..Logical Maximum (32767)           31226        0x75, 0x10,        # ..Report Size (16)                  34227        0x95, 0x02,        # ..Report Count (2)                  36228        0x09, 0x30,        # ..Usage (X)                         38229        0x09, 0x31,        # ..Usage (Y)                         40230        0x81, 0x06,        # ..Input (Data,Var,Rel)              42231        0x15, 0x81,        # ..Logical Minimum (-127)            44232        0x25, 0x7f,        # ..Logical Maximum (127)             46233        0x75, 0x08,        # ..Report Size (8)                   48234        0x95, 0x01,        # ..Report Count (1)                  50235        0x09, 0x38,        # ..Usage (Wheel)                     52236        0x81, 0x06,        # ..Input (Data,Var,Rel)              54237        0x05, 0x0c,        # ..Usage Page (Consumer Devices)     56238        0x0a, 0x38, 0x02,  # ..Usage (AC Pan)                    58239        0x95, 0x01,        # ..Report Count (1)                  61240        0x81, 0x06,        # ..Input (Data,Var,Rel)              63241        0xc0,              # .End Collection                     65242        0xc0,              # End Collection                      66243    ]244    # fmt: on245 246    def __init__(self, rdesc=report_descriptor, name=None, input_info=None):247        super().__init__(rdesc, name, input_info)248        self.hwheel_multiplier = 1249 250 251class MIDongleMIWirelessMouse(TwoWheelMouse):252    # fmt: off253    report_descriptor = [254        0x05, 0x01,         # Usage Page (Generic Desktop)255        0x09, 0x02,         # Usage (Mouse)256        0xa1, 0x01,         # Collection (Application)257        0x85, 0x01,         # .Report ID (1)258        0x09, 0x01,         # .Usage (Pointer)259        0xa1, 0x00,         # .Collection (Physical)260        0x95, 0x05,         # ..Report Count (5)261        0x75, 0x01,         # ..Report Size (1)262        0x05, 0x09,         # ..Usage Page (Button)263        0x19, 0x01,         # ..Usage Minimum (1)264        0x29, 0x05,         # ..Usage Maximum (5)265        0x15, 0x00,         # ..Logical Minimum (0)266        0x25, 0x01,         # ..Logical Maximum (1)267        0x81, 0x02,         # ..Input (Data,Var,Abs)268        0x95, 0x01,         # ..Report Count (1)269        0x75, 0x03,         # ..Report Size (3)270        0x81, 0x01,         # ..Input (Cnst,Arr,Abs)271        0x75, 0x08,         # ..Report Size (8)272        0x95, 0x01,         # ..Report Count (1)273        0x05, 0x01,         # ..Usage Page (Generic Desktop)274        0x09, 0x38,         # ..Usage (Wheel)275        0x15, 0x81,         # ..Logical Minimum (-127)276        0x25, 0x7f,         # ..Logical Maximum (127)277        0x81, 0x06,         # ..Input (Data,Var,Rel)278        0x05, 0x0c,         # ..Usage Page (Consumer Devices)279        0x0a, 0x38, 0x02,   # ..Usage (AC Pan)280        0x95, 0x01,         # ..Report Count (1)281        0x81, 0x06,         # ..Input (Data,Var,Rel)282        0xc0,               # .End Collection283        0x85, 0x02,         # .Report ID (2)284        0x09, 0x01,         # .Usage (Consumer Control)285        0xa1, 0x00,         # .Collection (Physical)286        0x75, 0x0c,         # ..Report Size (12)287        0x95, 0x02,         # ..Report Count (2)288        0x05, 0x01,         # ..Usage Page (Generic Desktop)289        0x09, 0x30,         # ..Usage (X)290        0x09, 0x31,         # ..Usage (Y)291        0x16, 0x01, 0xf8,   # ..Logical Minimum (-2047)292        0x26, 0xff, 0x07,   # ..Logical Maximum (2047)293        0x81, 0x06,         # ..Input (Data,Var,Rel)294        0xc0,               # .End Collection295        0xc0,               # End Collection296        0x05, 0x0c,         # Usage Page (Consumer Devices)297        0x09, 0x01,         # Usage (Consumer Control)298        0xa1, 0x01,         # Collection (Application)299        0x85, 0x03,         # .Report ID (3)300        0x15, 0x00,         # .Logical Minimum (0)301        0x25, 0x01,         # .Logical Maximum (1)302        0x75, 0x01,         # .Report Size (1)303        0x95, 0x01,         # .Report Count (1)304        0x09, 0xcd,         # .Usage (Play/Pause)305        0x81, 0x06,         # .Input (Data,Var,Rel)306        0x0a, 0x83, 0x01,   # .Usage (AL Consumer Control Config)307        0x81, 0x06,         # .Input (Data,Var,Rel)308        0x09, 0xb5,         # .Usage (Scan Next Track)309        0x81, 0x06,         # .Input (Data,Var,Rel)310        0x09, 0xb6,         # .Usage (Scan Previous Track)311        0x81, 0x06,         # .Input (Data,Var,Rel)312        0x09, 0xea,         # .Usage (Volume Down)313        0x81, 0x06,         # .Input (Data,Var,Rel)314        0x09, 0xe9,         # .Usage (Volume Up)315        0x81, 0x06,         # .Input (Data,Var,Rel)316        0x0a, 0x25, 0x02,   # .Usage (AC Forward)317        0x81, 0x06,         # .Input (Data,Var,Rel)318        0x0a, 0x24, 0x02,   # .Usage (AC Back)319        0x81, 0x06,         # .Input (Data,Var,Rel)320        0xc0,               # End Collection321    ]322    # fmt: on323    device_input_info = (BusType.USB, 0x2717, 0x003B)324    device_name = "uhid test MI Dongle MI Wireless Mouse"325 326    def __init__(327        self, rdesc=report_descriptor, name=device_name, input_info=device_input_info328    ):329        super().__init__(rdesc, name, input_info)330 331    def event(self, x, y, buttons=None, wheels=None):332        # this mouse spreads the relative pointer and the mouse buttons333        # onto 2 distinct reports334        rs = []335        r = self.create_report(x, y, buttons, wheels, reportID=1)336        self.call_input_event(r)337        rs.append(r)338        r = self.create_report(x, y, buttons, reportID=2)339        self.call_input_event(r)340        rs.append(r)341        return rs342 343 344class ResolutionMultiplierMouse(TwoWheelMouse):345    # fmt: off346    report_descriptor = [347        0x05, 0x01,        # Usage Page (Generic Desktop)        83348        0x09, 0x02,        # Usage (Mouse)                       85349        0xa1, 0x01,        # Collection (Application)            87350        0x05, 0x01,        # .Usage Page (Generic Desktop)       89351        0x09, 0x02,        # .Usage (Mouse)                      91352        0xa1, 0x02,        # .Collection (Logical)               93353        0x85, 0x11,        # ..Report ID (17)                    95354        0x09, 0x01,        # ..Usage (Pointer)                   97355        0xa1, 0x00,        # ..Collection (Physical)             99356        0x05, 0x09,        # ...Usage Page (Button)              101357        0x19, 0x01,        # ...Usage Minimum (1)                103358        0x29, 0x03,        # ...Usage Maximum (3)                105359        0x95, 0x03,        # ...Report Count (3)                 107360        0x75, 0x01,        # ...Report Size (1)                  109361        0x25, 0x01,        # ...Logical Maximum (1)              111362        0x81, 0x02,        # ...Input (Data,Var,Abs)             113363        0x95, 0x01,        # ...Report Count (1)                 115364        0x81, 0x01,        # ...Input (Cnst,Arr,Abs)             117365        0x09, 0x05,        # ...Usage (Vendor Usage 0x05)        119366        0x81, 0x02,        # ...Input (Data,Var,Abs)             121367        0x95, 0x03,        # ...Report Count (3)                 123368        0x81, 0x01,        # ...Input (Cnst,Arr,Abs)             125369        0x05, 0x01,        # ...Usage Page (Generic Desktop)     127370        0x09, 0x30,        # ...Usage (X)                        129371        0x09, 0x31,        # ...Usage (Y)                        131372        0x95, 0x02,        # ...Report Count (2)                 133373        0x75, 0x08,        # ...Report Size (8)                  135374        0x15, 0x81,        # ...Logical Minimum (-127)           137375        0x25, 0x7f,        # ...Logical Maximum (127)            139376        0x81, 0x06,        # ...Input (Data,Var,Rel)             141377        0xa1, 0x02,        # ...Collection (Logical)             143378        0x85, 0x12,        # ....Report ID (18)                  145379        0x09, 0x48,        # ....Usage (Resolution Multiplier)   147380        0x95, 0x01,        # ....Report Count (1)                149381        0x75, 0x02,        # ....Report Size (2)                 151382        0x15, 0x00,        # ....Logical Minimum (0)             153383        0x25, 0x01,        # ....Logical Maximum (1)             155384        0x35, 0x01,        # ....Physical Minimum (1)            157385        0x45, 0x04,        # ....Physical Maximum (4)            159386        0xb1, 0x02,        # ....Feature (Data,Var,Abs)          161387        0x35, 0x00,        # ....Physical Minimum (0)            163388        0x45, 0x00,        # ....Physical Maximum (0)            165389        0x75, 0x06,        # ....Report Size (6)                 167390        0xb1, 0x01,        # ....Feature (Cnst,Arr,Abs)          169391        0x85, 0x11,        # ....Report ID (17)                  171392        0x09, 0x38,        # ....Usage (Wheel)                   173393        0x15, 0x81,        # ....Logical Minimum (-127)          175394        0x25, 0x7f,        # ....Logical Maximum (127)           177395        0x75, 0x08,        # ....Report Size (8)                 179396        0x81, 0x06,        # ....Input (Data,Var,Rel)            181397        0xc0,              # ...End Collection                   183398        0x05, 0x0c,        # ...Usage Page (Consumer Devices)    184399        0x75, 0x08,        # ...Report Size (8)                  186400        0x0a, 0x38, 0x02,  # ...Usage (AC Pan)                   188401        0x81, 0x06,        # ...Input (Data,Var,Rel)             191402        0xc0,              # ..End Collection                    193403        0xc0,              # .End Collection                     194404        0xc0,              # End Collection                      195405    ]406    # fmt: on407 408    def __init__(self, rdesc=report_descriptor, name=None, input_info=None):409        super().__init__(rdesc, name, input_info)410        self.default_reportID = 0x11411 412        # Feature Report 12, multiplier Feature value must be set to 0b01,413        # i.e. 1. We should extract that from the descriptor instead414        # of hardcoding it here, but meanwhile this will do.415        self.set_feature_report = [0x12, 0x1]416 417    def set_report(self, req, rnum, rtype, data):418        if rtype != self.UHID_FEATURE_REPORT:419            raise InvalidHIDCommunication(f"Unexpected report type: {rtype}")420        if rnum != 0x12:421            raise InvalidHIDCommunication(f"Unexpected report number: {rnum}")422 423        if data != self.set_feature_report:424            raise InvalidHIDCommunication(425                f"Unexpected data: {data}, expected {self.set_feature_report}"426            )427 428        self.wheel_multiplier = 4429 430        return 0431 432 433class BadResolutionMultiplierMouse(ResolutionMultiplierMouse):434    def set_report(self, req, rnum, rtype, data):435        super().set_report(req, rnum, rtype, data)436 437        self.wheel_multiplier = 1438        self.hwheel_multiplier = 1439        return 32  # EPIPE440 441 442class ResolutionMultiplierHWheelMouse(TwoWheelMouse):443    # fmt: off444    report_descriptor = [445        0x05, 0x01,         # Usage Page (Generic Desktop)        0446        0x09, 0x02,         # Usage (Mouse)                       2447        0xa1, 0x01,         # Collection (Application)            4448        0x05, 0x01,         # .Usage Page (Generic Desktop)       6449        0x09, 0x02,         # .Usage (Mouse)                      8450        0xa1, 0x02,         # .Collection (Logical)               10451        0x85, 0x1a,         # ..Report ID (26)                    12452        0x09, 0x01,         # ..Usage (Pointer)                   14453        0xa1, 0x00,         # ..Collection (Physical)             16454        0x05, 0x09,         # ...Usage Page (Button)              18455        0x19, 0x01,         # ...Usage Minimum (1)                20456        0x29, 0x05,         # ...Usage Maximum (5)                22457        0x95, 0x05,         # ...Report Count (5)                 24458        0x75, 0x01,         # ...Report Size (1)                  26459        0x15, 0x00,         # ...Logical Minimum (0)              28460        0x25, 0x01,         # ...Logical Maximum (1)              30461        0x81, 0x02,         # ...Input (Data,Var,Abs)             32462        0x75, 0x03,         # ...Report Size (3)                  34463        0x95, 0x01,         # ...Report Count (1)                 36464        0x81, 0x01,         # ...Input (Cnst,Arr,Abs)             38465        0x05, 0x01,         # ...Usage Page (Generic Desktop)     40466        0x09, 0x30,         # ...Usage (X)                        42467        0x09, 0x31,         # ...Usage (Y)                        44468        0x95, 0x02,         # ...Report Count (2)                 46469        0x75, 0x10,         # ...Report Size (16)                 48470        0x16, 0x01, 0x80,   # ...Logical Minimum (-32767)         50471        0x26, 0xff, 0x7f,   # ...Logical Maximum (32767)          53472        0x81, 0x06,         # ...Input (Data,Var,Rel)             56473        0xa1, 0x02,         # ...Collection (Logical)             58474        0x85, 0x12,         # ....Report ID (18)                  60475        0x09, 0x48,         # ....Usage (Resolution Multiplier)   62476        0x95, 0x01,         # ....Report Count (1)                64477        0x75, 0x02,         # ....Report Size (2)                 66478        0x15, 0x00,         # ....Logical Minimum (0)             68479        0x25, 0x01,         # ....Logical Maximum (1)             70480        0x35, 0x01,         # ....Physical Minimum (1)            72481        0x45, 0x0c,         # ....Physical Maximum (12)           74482        0xb1, 0x02,         # ....Feature (Data,Var,Abs)          76483        0x85, 0x1a,         # ....Report ID (26)                  78484        0x09, 0x38,         # ....Usage (Wheel)                   80485        0x35, 0x00,         # ....Physical Minimum (0)            82486        0x45, 0x00,         # ....Physical Maximum (0)            84487        0x95, 0x01,         # ....Report Count (1)                86488        0x75, 0x10,         # ....Report Size (16)                88489        0x16, 0x01, 0x80,   # ....Logical Minimum (-32767)        90490        0x26, 0xff, 0x7f,   # ....Logical Maximum (32767)         93491        0x81, 0x06,         # ....Input (Data,Var,Rel)            96492        0xc0,               # ...End Collection                   98493        0xa1, 0x02,         # ...Collection (Logical)             99494        0x85, 0x12,         # ....Report ID (18)                  101495        0x09, 0x48,         # ....Usage (Resolution Multiplier)   103496        0x75, 0x02,         # ....Report Size (2)                 105497        0x15, 0x00,         # ....Logical Minimum (0)             107498        0x25, 0x01,         # ....Logical Maximum (1)             109499        0x35, 0x01,         # ....Physical Minimum (1)            111500        0x45, 0x0c,         # ....Physical Maximum (12)           113501        0xb1, 0x02,         # ....Feature (Data,Var,Abs)          115502        0x35, 0x00,         # ....Physical Minimum (0)            117503        0x45, 0x00,         # ....Physical Maximum (0)            119504        0x75, 0x04,         # ....Report Size (4)                 121505        0xb1, 0x01,         # ....Feature (Cnst,Arr,Abs)          123506        0x85, 0x1a,         # ....Report ID (26)                  125507        0x05, 0x0c,         # ....Usage Page (Consumer Devices)   127508        0x95, 0x01,         # ....Report Count (1)                129509        0x75, 0x10,         # ....Report Size (16)                131510        0x16, 0x01, 0x80,   # ....Logical Minimum (-32767)        133511        0x26, 0xff, 0x7f,   # ....Logical Maximum (32767)         136512        0x0a, 0x38, 0x02,   # ....Usage (AC Pan)                  139513        0x81, 0x06,         # ....Input (Data,Var,Rel)            142514        0xc0,               # ...End Collection                   144515        0xc0,               # ..End Collection                    145516        0xc0,               # .End Collection                     146517        0xc0,               # End Collection                      147518    ]519    # fmt: on520 521    def __init__(self, rdesc=report_descriptor, name=None, input_info=None):522        super().__init__(rdesc, name, input_info)523        self.default_reportID = 0x1A524 525        # Feature Report 12, multiplier Feature value must be set to 0b0101,526        # i.e. 5. We should extract that from the descriptor instead527        # of hardcoding it here, but meanwhile this will do.528        self.set_feature_report = [0x12, 0x5]529 530    def set_report(self, req, rnum, rtype, data):531        super().set_report(req, rnum, rtype, data)532 533        self.wheel_multiplier = 12534        self.hwheel_multiplier = 12535 536        return 0537 538 539class BaseTest:540    class TestMouse(base.BaseTestCase.TestUhid):541        def test_buttons(self):542            """check for button reliability."""543            uhdev = self.uhdev544            evdev = uhdev.get_evdev()545            syn_event = self.syn_event546 547            r = uhdev.event(0, 0, (None, True, None))548            expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 1)549            events = uhdev.next_sync_events()550            self.debug_reports(r, uhdev, events)551            self.assertInputEventsIn((syn_event, expected_event), events)552            assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 1553 554            r = uhdev.event(0, 0, (None, False, None))555            expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 0)556            events = uhdev.next_sync_events()557            self.debug_reports(r, uhdev, events)558            self.assertInputEventsIn((syn_event, expected_event), events)559            assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 0560 561            r = uhdev.event(0, 0, (None, None, True))562            expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_MIDDLE, 1)563            events = uhdev.next_sync_events()564            self.debug_reports(r, uhdev, events)565            self.assertInputEventsIn((syn_event, expected_event), events)566            assert evdev.value[libevdev.EV_KEY.BTN_MIDDLE] == 1567 568            r = uhdev.event(0, 0, (None, None, False))569            expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_MIDDLE, 0)570            events = uhdev.next_sync_events()571            self.debug_reports(r, uhdev, events)572            self.assertInputEventsIn((syn_event, expected_event), events)573            assert evdev.value[libevdev.EV_KEY.BTN_MIDDLE] == 0574 575            r = uhdev.event(0, 0, (True, None, None))576            expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 1)577            events = uhdev.next_sync_events()578            self.debug_reports(r, uhdev, events)579            self.assertInputEventsIn((syn_event, expected_event), events)580            assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 1581 582            r = uhdev.event(0, 0, (False, None, None))583            expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 0)584            events = uhdev.next_sync_events()585            self.debug_reports(r, uhdev, events)586            self.assertInputEventsIn((syn_event, expected_event), events)587            assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 0588 589            r = uhdev.event(0, 0, (True, True, None))590            expected_event0 = libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 1)591            expected_event1 = libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 1)592            events = uhdev.next_sync_events()593            self.debug_reports(r, uhdev, events)594            self.assertInputEventsIn(595                (syn_event, expected_event0, expected_event1), events596            )597            assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 1598            assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 1599 600            r = uhdev.event(0, 0, (False, None, None))601            expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 0)602            events = uhdev.next_sync_events()603            self.debug_reports(r, uhdev, events)604            self.assertInputEventsIn((syn_event, expected_event), events)605            assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 1606            assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 0607 608            r = uhdev.event(0, 0, (None, False, None))609            expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 0)610            events = uhdev.next_sync_events()611            self.debug_reports(r, uhdev, events)612            self.assertInputEventsIn((syn_event, expected_event), events)613            assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 0614            assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 0615 616        def test_relative(self):617            """Check for relative events."""618            uhdev = self.uhdev619 620            syn_event = self.syn_event621 622            r = uhdev.event(0, -1)623            expected_event = libevdev.InputEvent(libevdev.EV_REL.REL_Y, -1)624            events = uhdev.next_sync_events()625            self.debug_reports(r, uhdev, events)626            self.assertInputEvents((syn_event, expected_event), events)627 628            r = uhdev.event(1, 0)629            expected_event = libevdev.InputEvent(libevdev.EV_REL.REL_X, 1)630            events = uhdev.next_sync_events()631            self.debug_reports(r, uhdev, events)632            self.assertInputEvents((syn_event, expected_event), events)633 634            r = uhdev.event(-1, 2)635            expected_event0 = libevdev.InputEvent(libevdev.EV_REL.REL_X, -1)636            expected_event1 = libevdev.InputEvent(libevdev.EV_REL.REL_Y, 2)637            events = uhdev.next_sync_events()638            self.debug_reports(r, uhdev, events)639            self.assertInputEvents(640                (syn_event, expected_event0, expected_event1), events641            )642 643 644class TestSimpleMouse(BaseTest.TestMouse):645    def create_device(self):646        return ButtonMouse()647 648    def test_rdesc(self):649        """Check that the testsuite actually manages to format the650        reports according to the report descriptors.651        No kernel device is used here"""652        uhdev = self.uhdev653 654        event = (0, 0, (None, None, None))655        assert uhdev.fake_report(*event) == uhdev.create_report(*event)656 657        event = (0, 0, (None, True, None))658        assert uhdev.fake_report(*event) == uhdev.create_report(*event)659 660        event = (0, 0, (True, True, None))661        assert uhdev.fake_report(*event) == uhdev.create_report(*event)662 663        event = (0, 0, (False, False, False))664        assert uhdev.fake_report(*event) == uhdev.create_report(*event)665 666        event = (1, 0, (True, False, True))667        assert uhdev.fake_report(*event) == uhdev.create_report(*event)668 669        event = (-1, 0, (True, False, True))670        assert uhdev.fake_report(*event) == uhdev.create_report(*event)671 672        event = (-5, 5, (True, False, True))673        assert uhdev.fake_report(*event) == uhdev.create_report(*event)674 675        event = (-127, 127, (True, False, True))676        assert uhdev.fake_report(*event) == uhdev.create_report(*event)677 678        event = (0, -128, (True, False, True))679        with pytest.raises(hidtools.hid.RangeError):680            uhdev.create_report(*event)681 682 683class TestWheelMouse(BaseTest.TestMouse):684    def create_device(self):685        return WheelMouse()686 687    def is_wheel_highres(self, uhdev):688        evdev = uhdev.get_evdev()689        assert evdev.has(libevdev.EV_REL.REL_WHEEL)690        return evdev.has(libevdev.EV_REL.REL_WHEEL_HI_RES)691 692    def test_wheel(self):693        uhdev = self.uhdev694 695        # check if the kernel is high res wheel compatible696        high_res_wheel = self.is_wheel_highres(uhdev)697 698        syn_event = self.syn_event699        # The Resolution Multiplier is applied to the HID reports, so we700        # need to pre-multiply too.701        mult = uhdev.wheel_multiplier702 703        r = uhdev.event(0, 0, wheels=1 * mult)704        expected = [syn_event]705        expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, 1))706        if high_res_wheel:707            expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, 120))708        events = uhdev.next_sync_events()709        self.debug_reports(r, uhdev, events)710        self.assertInputEvents(expected, events)711 712        r = uhdev.event(0, 0, wheels=-1 * mult)713        expected = [syn_event]714        expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, -1))715        if high_res_wheel:716            expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, -120))717        events = uhdev.next_sync_events()718        self.debug_reports(r, uhdev, events)719        self.assertInputEvents(expected, events)720 721        r = uhdev.event(-1, 2, wheels=3 * mult)722        expected = [syn_event]723        expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, -1))724        expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, 2))725        expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, 3))726        if high_res_wheel:727            expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, 360))728        events = uhdev.next_sync_events()729        self.debug_reports(r, uhdev, events)730        self.assertInputEvents(expected, events)731 732 733class TestTwoWheelMouse(TestWheelMouse):734    def create_device(self):735        return TwoWheelMouse()736 737    def is_hwheel_highres(self, uhdev):738        evdev = uhdev.get_evdev()739        assert evdev.has(libevdev.EV_REL.REL_HWHEEL)740        return evdev.has(libevdev.EV_REL.REL_HWHEEL_HI_RES)741 742    def test_ac_pan(self):743        uhdev = self.uhdev744 745        # check if the kernel is high res wheel compatible746        high_res_wheel = self.is_wheel_highres(uhdev)747        high_res_hwheel = self.is_hwheel_highres(uhdev)748        assert high_res_wheel == high_res_hwheel749 750        syn_event = self.syn_event751        # The Resolution Multiplier is applied to the HID reports, so we752        # need to pre-multiply too.753        hmult = uhdev.hwheel_multiplier754        vmult = uhdev.wheel_multiplier755 756        r = uhdev.event(0, 0, wheels=(0, 1 * hmult))757        expected = [syn_event]758        expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, 1))759        if high_res_hwheel:760            expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 120))761        events = uhdev.next_sync_events()762        self.debug_reports(r, uhdev, events)763        self.assertInputEvents(expected, events)764 765        r = uhdev.event(0, 0, wheels=(0, -1 * hmult))766        expected = [syn_event]767        expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, -1))768        if high_res_hwheel:769            expected.append(770                libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, -120)771            )772        events = uhdev.next_sync_events()773        self.debug_reports(r, uhdev, events)774        self.assertInputEvents(expected, events)775 776        r = uhdev.event(-1, 2, wheels=(0, 3 * hmult))777        expected = [syn_event]778        expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, -1))779        expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, 2))780        expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, 3))781        if high_res_hwheel:782            expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 360))783        events = uhdev.next_sync_events()784        self.debug_reports(r, uhdev, events)785        self.assertInputEvents(expected, events)786 787        r = uhdev.event(-1, 2, wheels=(-3 * vmult, 4 * hmult))788        expected = [syn_event]789        expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, -1))790        expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, 2))791        expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, -3))792        if high_res_wheel:793            expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, -360))794        expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, 4))795        if high_res_wheel:796            expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 480))797        events = uhdev.next_sync_events()798        self.debug_reports(r, uhdev, events)799        self.assertInputEvents(expected, events)800 801 802class TestResolutionMultiplierMouse(TestTwoWheelMouse):803    def create_device(self):804        return ResolutionMultiplierMouse()805 806    def is_wheel_highres(self, uhdev):807        high_res = super().is_wheel_highres(uhdev)808 809        if not high_res:810            # the kernel doesn't seem to support the high res wheel mice,811            # make sure we haven't triggered the feature812            assert uhdev.wheel_multiplier == 1813 814        return high_res815 816    def test_resolution_multiplier_wheel(self):817        uhdev = self.uhdev818 819        if not self.is_wheel_highres(uhdev):820            pytest.skip("Kernel not compatible, we can not trigger the conditions")821 822        assert uhdev.wheel_multiplier > 1823        assert 120 % uhdev.wheel_multiplier == 0824 825    def test_wheel_with_multiplier(self):826        uhdev = self.uhdev827 828        if not self.is_wheel_highres(uhdev):829            pytest.skip("Kernel not compatible, we can not trigger the conditions")830 831        assert uhdev.wheel_multiplier > 1832 833        syn_event = self.syn_event834        mult = uhdev.wheel_multiplier835 836        r = uhdev.event(0, 0, wheels=1)837        expected = [syn_event]838        expected.append(839            libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, 120 / mult)840        )841        events = uhdev.next_sync_events()842        self.debug_reports(r, uhdev, events)843        self.assertInputEvents(expected, events)844 845        r = uhdev.event(0, 0, wheels=-1)846        expected = [syn_event]847        expected.append(848            libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, -120 / mult)849        )850        events = uhdev.next_sync_events()851        self.debug_reports(r, uhdev, events)852        self.assertInputEvents(expected, events)853 854        expected = [syn_event]855        expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, 1))856        expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, -2))857        expected.append(858            libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, 120 / mult)859        )860 861        for _ in range(mult - 1):862            r = uhdev.event(1, -2, wheels=1)863            events = uhdev.next_sync_events()864            self.debug_reports(r, uhdev, events)865            self.assertInputEvents(expected, events)866 867        r = uhdev.event(1, -2, wheels=1)868        expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, 1))869        events = uhdev.next_sync_events()870        self.debug_reports(r, uhdev, events)871        self.assertInputEvents(expected, events)872 873 874class TestBadResolutionMultiplierMouse(TestTwoWheelMouse):875    def create_device(self):876        return BadResolutionMultiplierMouse()877 878    def is_wheel_highres(self, uhdev):879        high_res = super().is_wheel_highres(uhdev)880 881        assert uhdev.wheel_multiplier == 1882 883        return high_res884 885    def test_resolution_multiplier_wheel(self):886        uhdev = self.uhdev887 888        assert uhdev.wheel_multiplier == 1889 890 891class TestResolutionMultiplierHWheelMouse(TestResolutionMultiplierMouse):892    def create_device(self):893        return ResolutionMultiplierHWheelMouse()894 895    def is_hwheel_highres(self, uhdev):896        high_res = super().is_hwheel_highres(uhdev)897 898        if not high_res:899            # the kernel doesn't seem to support the high res wheel mice,900            # make sure we haven't triggered the feature901            assert uhdev.hwheel_multiplier == 1902 903        return high_res904 905    def test_resolution_multiplier_ac_pan(self):906        uhdev = self.uhdev907 908        if not self.is_hwheel_highres(uhdev):909            pytest.skip("Kernel not compatible, we can not trigger the conditions")910 911        assert uhdev.hwheel_multiplier > 1912        assert 120 % uhdev.hwheel_multiplier == 0913 914    def test_ac_pan_with_multiplier(self):915        uhdev = self.uhdev916 917        if not self.is_hwheel_highres(uhdev):918            pytest.skip("Kernel not compatible, we can not trigger the conditions")919 920        assert uhdev.hwheel_multiplier > 1921 922        syn_event = self.syn_event923        hmult = uhdev.hwheel_multiplier924 925        r = uhdev.event(0, 0, wheels=(0, 1))926        expected = [syn_event]927        expected.append(928            libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 120 / hmult)929        )930        events = uhdev.next_sync_events()931        self.debug_reports(r, uhdev, events)932        self.assertInputEvents(expected, events)933 934        r = uhdev.event(0, 0, wheels=(0, -1))935        expected = [syn_event]936        expected.append(937            libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, -120 / hmult)938        )939        events = uhdev.next_sync_events()940        self.debug_reports(r, uhdev, events)941        self.assertInputEvents(expected, events)942 943        expected = [syn_event]944        expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, 1))945        expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, -2))946        expected.append(947            libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 120 / hmult)948        )949 950        for _ in range(hmult - 1):951            r = uhdev.event(1, -2, wheels=(0, 1))952            events = uhdev.next_sync_events()953            self.debug_reports(r, uhdev, events)954            self.assertInputEvents(expected, events)955 956        r = uhdev.event(1, -2, wheels=(0, 1))957        expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, 1))958        events = uhdev.next_sync_events()959        self.debug_reports(r, uhdev, events)960        self.assertInputEvents(expected, events)961 962 963class TestMiMouse(TestWheelMouse):964    def create_device(self):965        return MIDongleMIWirelessMouse()966 967    def assertInputEvents(self, expected_events, effective_events):968        # Buttons and x/y are spread over two HID reports, so we can get two969        # event frames for this device.970        remaining = self.assertInputEventsIn(expected_events, effective_events)971        try:972            remaining.remove(libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT, 0))973        except ValueError:974            # If there's no SYN_REPORT in the list, continue and let the975            # assert below print out the real error976            pass977        assert remaining == []978