Path: blob/master/tools/testing/selftests/hid/tests/test_mouse.py
26308 views
#!/bin/env python31# SPDX-License-Identifier: GPL-2.02# -*- coding: utf-8 -*-3#4# Copyright (c) 2017 Benjamin Tissoires <[email protected]>5# Copyright (c) 2017 Red Hat, Inc.6#78from . import base9import hidtools.hid10from hidtools.util import BusType11import libevdev12import logging13import pytest1415logger = logging.getLogger("hidtools.test.mouse")1617# workaround https://gitlab.freedesktop.org/libevdev/python-libevdev/issues/618try:19libevdev.EV_REL.REL_WHEEL_HI_RES20except AttributeError:21libevdev.EV_REL.REL_WHEEL_HI_RES = libevdev.EV_REL.REL_0B22libevdev.EV_REL.REL_HWHEEL_HI_RES = libevdev.EV_REL.REL_0C232425class InvalidHIDCommunication(Exception):26pass272829class MouseData(object):30pass313233class BaseMouse(base.UHIDTestDevice):34def __init__(self, rdesc, name=None, input_info=None):35assert rdesc is not None36super().__init__(name, "Mouse", input_info=input_info, rdesc=rdesc)37self.left = False38self.right = False39self.middle = False4041def create_report(self, x, y, buttons=None, wheels=None, reportID=None):42"""43Return an input report for this device.4445:param x: relative x46:param y: relative y47:param buttons: a (l, r, m) tuple of bools for the button states,48where ``None`` is "leave unchanged"49:param wheels: a single value for the vertical wheel or a (vertical, horizontal) tuple for50the two wheels51:param reportID: the numeric report ID for this report, if needed52"""53if buttons is not None:54left, right, middle = buttons55if left is not None:56self.left = left57if right is not None:58self.right = right59if middle is not None:60self.middle = middle61left = self.left62right = self.right63middle = self.middle64# Note: the BaseMouse doesn't actually have a wheel but the65# create_report magic only fills in those fields exist, so let's66# make this generic here.67wheel, acpan = 0, 068if wheels is not None:69if isinstance(wheels, tuple):70wheel = wheels[0]71acpan = wheels[1]72else:73wheel = wheels7475reportID = reportID or self.default_reportID7677mouse = MouseData()78mouse.b1 = int(left)79mouse.b2 = int(right)80mouse.b3 = int(middle)81mouse.x = x82mouse.y = y83mouse.wheel = wheel84mouse.acpan = acpan85return super().create_report(mouse, reportID=reportID)8687def event(self, x, y, buttons=None, wheels=None):88"""89Send an input event on the default report ID.9091:param x: relative x92:param y: relative y93:param buttons: a (l, r, m) tuple of bools for the button states,94where ``None`` is "leave unchanged"95:param wheels: a single value for the vertical wheel or a (vertical, horizontal) tuple for96the two wheels97"""98r = self.create_report(x, y, buttons, wheels)99self.call_input_event(r)100return [r]101102103class ButtonMouse(BaseMouse):104# fmt: off105report_descriptor = [1060x05, 0x01, # .Usage Page (Generic Desktop) 01070x09, 0x02, # .Usage (Mouse) 21080xa1, 0x01, # .Collection (Application) 41090x09, 0x02, # ..Usage (Mouse) 61100xa1, 0x02, # ..Collection (Logical) 81110x09, 0x01, # ...Usage (Pointer) 101120xa1, 0x00, # ...Collection (Physical) 121130x05, 0x09, # ....Usage Page (Button) 141140x19, 0x01, # ....Usage Minimum (1) 161150x29, 0x03, # ....Usage Maximum (3) 181160x15, 0x00, # ....Logical Minimum (0) 201170x25, 0x01, # ....Logical Maximum (1) 221180x75, 0x01, # ....Report Size (1) 241190x95, 0x03, # ....Report Count (3) 261200x81, 0x02, # ....Input (Data,Var,Abs) 281210x75, 0x05, # ....Report Size (5) 301220x95, 0x01, # ....Report Count (1) 321230x81, 0x03, # ....Input (Cnst,Var,Abs) 341240x05, 0x01, # ....Usage Page (Generic Desktop) 361250x09, 0x30, # ....Usage (X) 381260x09, 0x31, # ....Usage (Y) 401270x15, 0x81, # ....Logical Minimum (-127) 421280x25, 0x7f, # ....Logical Maximum (127) 441290x75, 0x08, # ....Report Size (8) 461300x95, 0x02, # ....Report Count (2) 481310x81, 0x06, # ....Input (Data,Var,Rel) 501320xc0, # ...End Collection 521330xc0, # ..End Collection 531340xc0, # .End Collection 54135]136# fmt: on137138def __init__(self, rdesc=report_descriptor, name=None, input_info=None):139super().__init__(rdesc, name, input_info)140141def fake_report(self, x, y, buttons):142if buttons is not None:143left, right, middle = buttons144if left is None:145left = self.left146if right is None:147right = self.right148if middle is None:149middle = self.middle150else:151left = self.left152right = self.right153middle = self.middle154155button_mask = sum(1 << i for i, b in enumerate([left, right, middle]) if b)156x = max(-127, min(127, x))157y = max(-127, min(127, y))158x = hidtools.util.to_twos_comp(x, 8)159y = hidtools.util.to_twos_comp(y, 8)160return [button_mask, x, y]161162163class WheelMouse(ButtonMouse):164# fmt: off165report_descriptor = [1660x05, 0x01, # Usage Page (Generic Desktop) 01670x09, 0x02, # Usage (Mouse) 21680xa1, 0x01, # Collection (Application) 41690x05, 0x09, # .Usage Page (Button) 61700x19, 0x01, # .Usage Minimum (1) 81710x29, 0x03, # .Usage Maximum (3) 101720x15, 0x00, # .Logical Minimum (0) 121730x25, 0x01, # .Logical Maximum (1) 141740x95, 0x03, # .Report Count (3) 161750x75, 0x01, # .Report Size (1) 181760x81, 0x02, # .Input (Data,Var,Abs) 201770x95, 0x01, # .Report Count (1) 221780x75, 0x05, # .Report Size (5) 241790x81, 0x03, # .Input (Cnst,Var,Abs) 261800x05, 0x01, # .Usage Page (Generic Desktop) 281810x09, 0x01, # .Usage (Pointer) 301820xa1, 0x00, # .Collection (Physical) 321830x09, 0x30, # ..Usage (X) 341840x09, 0x31, # ..Usage (Y) 361850x15, 0x81, # ..Logical Minimum (-127) 381860x25, 0x7f, # ..Logical Maximum (127) 401870x75, 0x08, # ..Report Size (8) 421880x95, 0x02, # ..Report Count (2) 441890x81, 0x06, # ..Input (Data,Var,Rel) 461900xc0, # .End Collection 481910x09, 0x38, # .Usage (Wheel) 491920x15, 0x81, # .Logical Minimum (-127) 511930x25, 0x7f, # .Logical Maximum (127) 531940x75, 0x08, # .Report Size (8) 551950x95, 0x01, # .Report Count (1) 571960x81, 0x06, # .Input (Data,Var,Rel) 591970xc0, # End Collection 61198]199# fmt: on200201def __init__(self, rdesc=report_descriptor, name=None, input_info=None):202super().__init__(rdesc, name, input_info)203self.wheel_multiplier = 1204205206class TwoWheelMouse(WheelMouse):207# fmt: off208report_descriptor = [2090x05, 0x01, # Usage Page (Generic Desktop) 02100x09, 0x02, # Usage (Mouse) 22110xa1, 0x01, # Collection (Application) 42120x09, 0x01, # .Usage (Pointer) 62130xa1, 0x00, # .Collection (Physical) 82140x05, 0x09, # ..Usage Page (Button) 102150x19, 0x01, # ..Usage Minimum (1) 122160x29, 0x10, # ..Usage Maximum (16) 142170x15, 0x00, # ..Logical Minimum (0) 162180x25, 0x01, # ..Logical Maximum (1) 182190x95, 0x10, # ..Report Count (16) 202200x75, 0x01, # ..Report Size (1) 222210x81, 0x02, # ..Input (Data,Var,Abs) 242220x05, 0x01, # ..Usage Page (Generic Desktop) 262230x16, 0x01, 0x80, # ..Logical Minimum (-32767) 282240x26, 0xff, 0x7f, # ..Logical Maximum (32767) 312250x75, 0x10, # ..Report Size (16) 342260x95, 0x02, # ..Report Count (2) 362270x09, 0x30, # ..Usage (X) 382280x09, 0x31, # ..Usage (Y) 402290x81, 0x06, # ..Input (Data,Var,Rel) 422300x15, 0x81, # ..Logical Minimum (-127) 442310x25, 0x7f, # ..Logical Maximum (127) 462320x75, 0x08, # ..Report Size (8) 482330x95, 0x01, # ..Report Count (1) 502340x09, 0x38, # ..Usage (Wheel) 522350x81, 0x06, # ..Input (Data,Var,Rel) 542360x05, 0x0c, # ..Usage Page (Consumer Devices) 562370x0a, 0x38, 0x02, # ..Usage (AC Pan) 582380x95, 0x01, # ..Report Count (1) 612390x81, 0x06, # ..Input (Data,Var,Rel) 632400xc0, # .End Collection 652410xc0, # End Collection 66242]243# fmt: on244245def __init__(self, rdesc=report_descriptor, name=None, input_info=None):246super().__init__(rdesc, name, input_info)247self.hwheel_multiplier = 1248249250class MIDongleMIWirelessMouse(TwoWheelMouse):251# fmt: off252report_descriptor = [2530x05, 0x01, # Usage Page (Generic Desktop)2540x09, 0x02, # Usage (Mouse)2550xa1, 0x01, # Collection (Application)2560x85, 0x01, # .Report ID (1)2570x09, 0x01, # .Usage (Pointer)2580xa1, 0x00, # .Collection (Physical)2590x95, 0x05, # ..Report Count (5)2600x75, 0x01, # ..Report Size (1)2610x05, 0x09, # ..Usage Page (Button)2620x19, 0x01, # ..Usage Minimum (1)2630x29, 0x05, # ..Usage Maximum (5)2640x15, 0x00, # ..Logical Minimum (0)2650x25, 0x01, # ..Logical Maximum (1)2660x81, 0x02, # ..Input (Data,Var,Abs)2670x95, 0x01, # ..Report Count (1)2680x75, 0x03, # ..Report Size (3)2690x81, 0x01, # ..Input (Cnst,Arr,Abs)2700x75, 0x08, # ..Report Size (8)2710x95, 0x01, # ..Report Count (1)2720x05, 0x01, # ..Usage Page (Generic Desktop)2730x09, 0x38, # ..Usage (Wheel)2740x15, 0x81, # ..Logical Minimum (-127)2750x25, 0x7f, # ..Logical Maximum (127)2760x81, 0x06, # ..Input (Data,Var,Rel)2770x05, 0x0c, # ..Usage Page (Consumer Devices)2780x0a, 0x38, 0x02, # ..Usage (AC Pan)2790x95, 0x01, # ..Report Count (1)2800x81, 0x06, # ..Input (Data,Var,Rel)2810xc0, # .End Collection2820x85, 0x02, # .Report ID (2)2830x09, 0x01, # .Usage (Consumer Control)2840xa1, 0x00, # .Collection (Physical)2850x75, 0x0c, # ..Report Size (12)2860x95, 0x02, # ..Report Count (2)2870x05, 0x01, # ..Usage Page (Generic Desktop)2880x09, 0x30, # ..Usage (X)2890x09, 0x31, # ..Usage (Y)2900x16, 0x01, 0xf8, # ..Logical Minimum (-2047)2910x26, 0xff, 0x07, # ..Logical Maximum (2047)2920x81, 0x06, # ..Input (Data,Var,Rel)2930xc0, # .End Collection2940xc0, # End Collection2950x05, 0x0c, # Usage Page (Consumer Devices)2960x09, 0x01, # Usage (Consumer Control)2970xa1, 0x01, # Collection (Application)2980x85, 0x03, # .Report ID (3)2990x15, 0x00, # .Logical Minimum (0)3000x25, 0x01, # .Logical Maximum (1)3010x75, 0x01, # .Report Size (1)3020x95, 0x01, # .Report Count (1)3030x09, 0xcd, # .Usage (Play/Pause)3040x81, 0x06, # .Input (Data,Var,Rel)3050x0a, 0x83, 0x01, # .Usage (AL Consumer Control Config)3060x81, 0x06, # .Input (Data,Var,Rel)3070x09, 0xb5, # .Usage (Scan Next Track)3080x81, 0x06, # .Input (Data,Var,Rel)3090x09, 0xb6, # .Usage (Scan Previous Track)3100x81, 0x06, # .Input (Data,Var,Rel)3110x09, 0xea, # .Usage (Volume Down)3120x81, 0x06, # .Input (Data,Var,Rel)3130x09, 0xe9, # .Usage (Volume Up)3140x81, 0x06, # .Input (Data,Var,Rel)3150x0a, 0x25, 0x02, # .Usage (AC Forward)3160x81, 0x06, # .Input (Data,Var,Rel)3170x0a, 0x24, 0x02, # .Usage (AC Back)3180x81, 0x06, # .Input (Data,Var,Rel)3190xc0, # End Collection320]321# fmt: on322device_input_info = (BusType.USB, 0x2717, 0x003B)323device_name = "uhid test MI Dongle MI Wireless Mouse"324325def __init__(326self, rdesc=report_descriptor, name=device_name, input_info=device_input_info327):328super().__init__(rdesc, name, input_info)329330def event(self, x, y, buttons=None, wheels=None):331# this mouse spreads the relative pointer and the mouse buttons332# onto 2 distinct reports333rs = []334r = self.create_report(x, y, buttons, wheels, reportID=1)335self.call_input_event(r)336rs.append(r)337r = self.create_report(x, y, buttons, reportID=2)338self.call_input_event(r)339rs.append(r)340return rs341342343class ResolutionMultiplierMouse(TwoWheelMouse):344# fmt: off345report_descriptor = [3460x05, 0x01, # Usage Page (Generic Desktop) 833470x09, 0x02, # Usage (Mouse) 853480xa1, 0x01, # Collection (Application) 873490x05, 0x01, # .Usage Page (Generic Desktop) 893500x09, 0x02, # .Usage (Mouse) 913510xa1, 0x02, # .Collection (Logical) 933520x85, 0x11, # ..Report ID (17) 953530x09, 0x01, # ..Usage (Pointer) 973540xa1, 0x00, # ..Collection (Physical) 993550x05, 0x09, # ...Usage Page (Button) 1013560x19, 0x01, # ...Usage Minimum (1) 1033570x29, 0x03, # ...Usage Maximum (3) 1053580x95, 0x03, # ...Report Count (3) 1073590x75, 0x01, # ...Report Size (1) 1093600x25, 0x01, # ...Logical Maximum (1) 1113610x81, 0x02, # ...Input (Data,Var,Abs) 1133620x95, 0x01, # ...Report Count (1) 1153630x81, 0x01, # ...Input (Cnst,Arr,Abs) 1173640x09, 0x05, # ...Usage (Vendor Usage 0x05) 1193650x81, 0x02, # ...Input (Data,Var,Abs) 1213660x95, 0x03, # ...Report Count (3) 1233670x81, 0x01, # ...Input (Cnst,Arr,Abs) 1253680x05, 0x01, # ...Usage Page (Generic Desktop) 1273690x09, 0x30, # ...Usage (X) 1293700x09, 0x31, # ...Usage (Y) 1313710x95, 0x02, # ...Report Count (2) 1333720x75, 0x08, # ...Report Size (8) 1353730x15, 0x81, # ...Logical Minimum (-127) 1373740x25, 0x7f, # ...Logical Maximum (127) 1393750x81, 0x06, # ...Input (Data,Var,Rel) 1413760xa1, 0x02, # ...Collection (Logical) 1433770x85, 0x12, # ....Report ID (18) 1453780x09, 0x48, # ....Usage (Resolution Multiplier) 1473790x95, 0x01, # ....Report Count (1) 1493800x75, 0x02, # ....Report Size (2) 1513810x15, 0x00, # ....Logical Minimum (0) 1533820x25, 0x01, # ....Logical Maximum (1) 1553830x35, 0x01, # ....Physical Minimum (1) 1573840x45, 0x04, # ....Physical Maximum (4) 1593850xb1, 0x02, # ....Feature (Data,Var,Abs) 1613860x35, 0x00, # ....Physical Minimum (0) 1633870x45, 0x00, # ....Physical Maximum (0) 1653880x75, 0x06, # ....Report Size (6) 1673890xb1, 0x01, # ....Feature (Cnst,Arr,Abs) 1693900x85, 0x11, # ....Report ID (17) 1713910x09, 0x38, # ....Usage (Wheel) 1733920x15, 0x81, # ....Logical Minimum (-127) 1753930x25, 0x7f, # ....Logical Maximum (127) 1773940x75, 0x08, # ....Report Size (8) 1793950x81, 0x06, # ....Input (Data,Var,Rel) 1813960xc0, # ...End Collection 1833970x05, 0x0c, # ...Usage Page (Consumer Devices) 1843980x75, 0x08, # ...Report Size (8) 1863990x0a, 0x38, 0x02, # ...Usage (AC Pan) 1884000x81, 0x06, # ...Input (Data,Var,Rel) 1914010xc0, # ..End Collection 1934020xc0, # .End Collection 1944030xc0, # End Collection 195404]405# fmt: on406407def __init__(self, rdesc=report_descriptor, name=None, input_info=None):408super().__init__(rdesc, name, input_info)409self.default_reportID = 0x11410411# Feature Report 12, multiplier Feature value must be set to 0b01,412# i.e. 1. We should extract that from the descriptor instead413# of hardcoding it here, but meanwhile this will do.414self.set_feature_report = [0x12, 0x1]415416def set_report(self, req, rnum, rtype, data):417if rtype != self.UHID_FEATURE_REPORT:418raise InvalidHIDCommunication(f"Unexpected report type: {rtype}")419if rnum != 0x12:420raise InvalidHIDCommunication(f"Unexpected report number: {rnum}")421422if data != self.set_feature_report:423raise InvalidHIDCommunication(424f"Unexpected data: {data}, expected {self.set_feature_report}"425)426427self.wheel_multiplier = 4428429return 0430431432class BadResolutionMultiplierMouse(ResolutionMultiplierMouse):433def set_report(self, req, rnum, rtype, data):434super().set_report(req, rnum, rtype, data)435436self.wheel_multiplier = 1437self.hwheel_multiplier = 1438return 32 # EPIPE439440441class BadReportDescriptorMouse(BaseMouse):442"""443This "device" was one autogenerated by syzbot. There are a lot of issues in444it, and the most problematic is that it declares features that have no445size.446447This leads to report->size being set to 0 and can mess up with usbhid448internals. Fortunately, uhid merely passes the incoming buffer, without449touching it so a buffer of size 0 will be translated to [] without450triggering a kernel oops.451452Because the report descriptor is wrong, no input are created, and we need453to tweak a little bit the parameters to make it look correct.454"""455456# fmt: off457report_descriptor = [4580x96, 0x01, 0x00, # Report Count (1) 04590x06, 0x01, 0x00, # Usage Page (Generic Desktop) 3460# 0x03, 0x00, 0x00, 0x00, 0x00, # Ignored by the kernel somehow4610x2a, 0x90, 0xa0, # Usage Maximum (41104) 64620x27, 0x00, 0x00, 0x00, 0x00, # Logical Maximum (0) 94630xb3, 0x81, 0x3e, 0x25, 0x03, # Feature (Cnst,Arr,Abs,Vol) 144640x1b, 0xdd, 0xe8, 0x40, 0x50, # Usage Minimum (1346431197) 194650x3b, 0x5d, 0x8c, 0x3d, 0xda, # Designator Index 24466]467# fmt: on468469def __init__(470self, rdesc=report_descriptor, name=None, input_info=(3, 0x045E, 0x07DA)471):472super().__init__(rdesc, name, input_info)473self.high_resolution_report_called = False474475def get_evdev(self, application=None):476assert self._input_nodes is None477return (478"Ok" # should be a list or None, but both would fail, so abusing the system479)480481def next_sync_events(self, application=None):482# there are no evdev nodes, so no events483return []484485def is_ready(self):486# we wait for the SET_REPORT command to come487return self.high_resolution_report_called488489def set_report(self, req, rnum, rtype, data):490if rtype != self.UHID_FEATURE_REPORT:491raise InvalidHIDCommunication(f"Unexpected report type: {rtype}")492if rnum != 0x0:493raise InvalidHIDCommunication(f"Unexpected report number: {rnum}")494495if len(data) != 1:496raise InvalidHIDCommunication(f"Unexpected data: {data}, expected '[0]'")497498self.high_resolution_report_called = True499500return 0501502503class ResolutionMultiplierHWheelMouse(TwoWheelMouse):504# fmt: off505report_descriptor = [5060x05, 0x01, # Usage Page (Generic Desktop) 05070x09, 0x02, # Usage (Mouse) 25080xa1, 0x01, # Collection (Application) 45090x05, 0x01, # .Usage Page (Generic Desktop) 65100x09, 0x02, # .Usage (Mouse) 85110xa1, 0x02, # .Collection (Logical) 105120x85, 0x1a, # ..Report ID (26) 125130x09, 0x01, # ..Usage (Pointer) 145140xa1, 0x00, # ..Collection (Physical) 165150x05, 0x09, # ...Usage Page (Button) 185160x19, 0x01, # ...Usage Minimum (1) 205170x29, 0x05, # ...Usage Maximum (5) 225180x95, 0x05, # ...Report Count (5) 245190x75, 0x01, # ...Report Size (1) 265200x15, 0x00, # ...Logical Minimum (0) 285210x25, 0x01, # ...Logical Maximum (1) 305220x81, 0x02, # ...Input (Data,Var,Abs) 325230x75, 0x03, # ...Report Size (3) 345240x95, 0x01, # ...Report Count (1) 365250x81, 0x01, # ...Input (Cnst,Arr,Abs) 385260x05, 0x01, # ...Usage Page (Generic Desktop) 405270x09, 0x30, # ...Usage (X) 425280x09, 0x31, # ...Usage (Y) 445290x95, 0x02, # ...Report Count (2) 465300x75, 0x10, # ...Report Size (16) 485310x16, 0x01, 0x80, # ...Logical Minimum (-32767) 505320x26, 0xff, 0x7f, # ...Logical Maximum (32767) 535330x81, 0x06, # ...Input (Data,Var,Rel) 565340xa1, 0x02, # ...Collection (Logical) 585350x85, 0x12, # ....Report ID (18) 605360x09, 0x48, # ....Usage (Resolution Multiplier) 625370x95, 0x01, # ....Report Count (1) 645380x75, 0x02, # ....Report Size (2) 665390x15, 0x00, # ....Logical Minimum (0) 685400x25, 0x01, # ....Logical Maximum (1) 705410x35, 0x01, # ....Physical Minimum (1) 725420x45, 0x0c, # ....Physical Maximum (12) 745430xb1, 0x02, # ....Feature (Data,Var,Abs) 765440x85, 0x1a, # ....Report ID (26) 785450x09, 0x38, # ....Usage (Wheel) 805460x35, 0x00, # ....Physical Minimum (0) 825470x45, 0x00, # ....Physical Maximum (0) 845480x95, 0x01, # ....Report Count (1) 865490x75, 0x10, # ....Report Size (16) 885500x16, 0x01, 0x80, # ....Logical Minimum (-32767) 905510x26, 0xff, 0x7f, # ....Logical Maximum (32767) 935520x81, 0x06, # ....Input (Data,Var,Rel) 965530xc0, # ...End Collection 985540xa1, 0x02, # ...Collection (Logical) 995550x85, 0x12, # ....Report ID (18) 1015560x09, 0x48, # ....Usage (Resolution Multiplier) 1035570x75, 0x02, # ....Report Size (2) 1055580x15, 0x00, # ....Logical Minimum (0) 1075590x25, 0x01, # ....Logical Maximum (1) 1095600x35, 0x01, # ....Physical Minimum (1) 1115610x45, 0x0c, # ....Physical Maximum (12) 1135620xb1, 0x02, # ....Feature (Data,Var,Abs) 1155630x35, 0x00, # ....Physical Minimum (0) 1175640x45, 0x00, # ....Physical Maximum (0) 1195650x75, 0x04, # ....Report Size (4) 1215660xb1, 0x01, # ....Feature (Cnst,Arr,Abs) 1235670x85, 0x1a, # ....Report ID (26) 1255680x05, 0x0c, # ....Usage Page (Consumer Devices) 1275690x95, 0x01, # ....Report Count (1) 1295700x75, 0x10, # ....Report Size (16) 1315710x16, 0x01, 0x80, # ....Logical Minimum (-32767) 1335720x26, 0xff, 0x7f, # ....Logical Maximum (32767) 1365730x0a, 0x38, 0x02, # ....Usage (AC Pan) 1395740x81, 0x06, # ....Input (Data,Var,Rel) 1425750xc0, # ...End Collection 1445760xc0, # ..End Collection 1455770xc0, # .End Collection 1465780xc0, # End Collection 147579]580# fmt: on581582def __init__(self, rdesc=report_descriptor, name=None, input_info=None):583super().__init__(rdesc, name, input_info)584self.default_reportID = 0x1A585586# Feature Report 12, multiplier Feature value must be set to 0b0101,587# i.e. 5. We should extract that from the descriptor instead588# of hardcoding it here, but meanwhile this will do.589self.set_feature_report = [0x12, 0x5]590591def set_report(self, req, rnum, rtype, data):592super().set_report(req, rnum, rtype, data)593594self.wheel_multiplier = 12595self.hwheel_multiplier = 12596597return 0598599600class BaseTest:601class TestMouse(base.BaseTestCase.TestUhid):602def test_buttons(self):603"""check for button reliability."""604uhdev = self.uhdev605evdev = uhdev.get_evdev()606syn_event = self.syn_event607608r = uhdev.event(0, 0, (None, True, None))609expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 1)610events = uhdev.next_sync_events()611self.debug_reports(r, uhdev, events)612self.assertInputEventsIn((syn_event, expected_event), events)613assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 1614615r = uhdev.event(0, 0, (None, False, None))616expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 0)617events = uhdev.next_sync_events()618self.debug_reports(r, uhdev, events)619self.assertInputEventsIn((syn_event, expected_event), events)620assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 0621622r = uhdev.event(0, 0, (None, None, True))623expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_MIDDLE, 1)624events = uhdev.next_sync_events()625self.debug_reports(r, uhdev, events)626self.assertInputEventsIn((syn_event, expected_event), events)627assert evdev.value[libevdev.EV_KEY.BTN_MIDDLE] == 1628629r = uhdev.event(0, 0, (None, None, False))630expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_MIDDLE, 0)631events = uhdev.next_sync_events()632self.debug_reports(r, uhdev, events)633self.assertInputEventsIn((syn_event, expected_event), events)634assert evdev.value[libevdev.EV_KEY.BTN_MIDDLE] == 0635636r = uhdev.event(0, 0, (True, None, None))637expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 1)638events = uhdev.next_sync_events()639self.debug_reports(r, uhdev, events)640self.assertInputEventsIn((syn_event, expected_event), events)641assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 1642643r = uhdev.event(0, 0, (False, None, None))644expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 0)645events = uhdev.next_sync_events()646self.debug_reports(r, uhdev, events)647self.assertInputEventsIn((syn_event, expected_event), events)648assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 0649650r = uhdev.event(0, 0, (True, True, None))651expected_event0 = libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 1)652expected_event1 = libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 1)653events = uhdev.next_sync_events()654self.debug_reports(r, uhdev, events)655self.assertInputEventsIn(656(syn_event, expected_event0, expected_event1), events657)658assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 1659assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 1660661r = uhdev.event(0, 0, (False, None, None))662expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 0)663events = uhdev.next_sync_events()664self.debug_reports(r, uhdev, events)665self.assertInputEventsIn((syn_event, expected_event), events)666assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 1667assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 0668669r = uhdev.event(0, 0, (None, False, None))670expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 0)671events = uhdev.next_sync_events()672self.debug_reports(r, uhdev, events)673self.assertInputEventsIn((syn_event, expected_event), events)674assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 0675assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 0676677def test_relative(self):678"""Check for relative events."""679uhdev = self.uhdev680681syn_event = self.syn_event682683r = uhdev.event(0, -1)684expected_event = libevdev.InputEvent(libevdev.EV_REL.REL_Y, -1)685events = uhdev.next_sync_events()686self.debug_reports(r, uhdev, events)687self.assertInputEvents((syn_event, expected_event), events)688689r = uhdev.event(1, 0)690expected_event = libevdev.InputEvent(libevdev.EV_REL.REL_X, 1)691events = uhdev.next_sync_events()692self.debug_reports(r, uhdev, events)693self.assertInputEvents((syn_event, expected_event), events)694695r = uhdev.event(-1, 2)696expected_event0 = libevdev.InputEvent(libevdev.EV_REL.REL_X, -1)697expected_event1 = libevdev.InputEvent(libevdev.EV_REL.REL_Y, 2)698events = uhdev.next_sync_events()699self.debug_reports(r, uhdev, events)700self.assertInputEvents(701(syn_event, expected_event0, expected_event1), events702)703704705class TestSimpleMouse(BaseTest.TestMouse):706def create_device(self):707return ButtonMouse()708709def test_rdesc(self):710"""Check that the testsuite actually manages to format the711reports according to the report descriptors.712No kernel device is used here"""713uhdev = self.uhdev714715event = (0, 0, (None, None, None))716assert uhdev.fake_report(*event) == uhdev.create_report(*event)717718event = (0, 0, (None, True, None))719assert uhdev.fake_report(*event) == uhdev.create_report(*event)720721event = (0, 0, (True, True, None))722assert uhdev.fake_report(*event) == uhdev.create_report(*event)723724event = (0, 0, (False, False, False))725assert uhdev.fake_report(*event) == uhdev.create_report(*event)726727event = (1, 0, (True, False, True))728assert uhdev.fake_report(*event) == uhdev.create_report(*event)729730event = (-1, 0, (True, False, True))731assert uhdev.fake_report(*event) == uhdev.create_report(*event)732733event = (-5, 5, (True, False, True))734assert uhdev.fake_report(*event) == uhdev.create_report(*event)735736event = (-127, 127, (True, False, True))737assert uhdev.fake_report(*event) == uhdev.create_report(*event)738739event = (0, -128, (True, False, True))740with pytest.raises(hidtools.hid.RangeError):741uhdev.create_report(*event)742743744class TestWheelMouse(BaseTest.TestMouse):745def create_device(self):746return WheelMouse()747748def is_wheel_highres(self, uhdev):749evdev = uhdev.get_evdev()750assert evdev.has(libevdev.EV_REL.REL_WHEEL)751return evdev.has(libevdev.EV_REL.REL_WHEEL_HI_RES)752753def test_wheel(self):754uhdev = self.uhdev755756# check if the kernel is high res wheel compatible757high_res_wheel = self.is_wheel_highres(uhdev)758759syn_event = self.syn_event760# The Resolution Multiplier is applied to the HID reports, so we761# need to pre-multiply too.762mult = uhdev.wheel_multiplier763764r = uhdev.event(0, 0, wheels=1 * mult)765expected = [syn_event]766expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, 1))767if high_res_wheel:768expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, 120))769events = uhdev.next_sync_events()770self.debug_reports(r, uhdev, events)771self.assertInputEvents(expected, events)772773r = uhdev.event(0, 0, wheels=-1 * mult)774expected = [syn_event]775expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, -1))776if high_res_wheel:777expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, -120))778events = uhdev.next_sync_events()779self.debug_reports(r, uhdev, events)780self.assertInputEvents(expected, events)781782r = uhdev.event(-1, 2, wheels=3 * mult)783expected = [syn_event]784expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, -1))785expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, 2))786expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, 3))787if high_res_wheel:788expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, 360))789events = uhdev.next_sync_events()790self.debug_reports(r, uhdev, events)791self.assertInputEvents(expected, events)792793794class TestTwoWheelMouse(TestWheelMouse):795def create_device(self):796return TwoWheelMouse()797798def is_hwheel_highres(self, uhdev):799evdev = uhdev.get_evdev()800assert evdev.has(libevdev.EV_REL.REL_HWHEEL)801return evdev.has(libevdev.EV_REL.REL_HWHEEL_HI_RES)802803def test_ac_pan(self):804uhdev = self.uhdev805806# check if the kernel is high res wheel compatible807high_res_wheel = self.is_wheel_highres(uhdev)808high_res_hwheel = self.is_hwheel_highres(uhdev)809assert high_res_wheel == high_res_hwheel810811syn_event = self.syn_event812# The Resolution Multiplier is applied to the HID reports, so we813# need to pre-multiply too.814hmult = uhdev.hwheel_multiplier815vmult = uhdev.wheel_multiplier816817r = uhdev.event(0, 0, wheels=(0, 1 * hmult))818expected = [syn_event]819expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, 1))820if high_res_hwheel:821expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 120))822events = uhdev.next_sync_events()823self.debug_reports(r, uhdev, events)824self.assertInputEvents(expected, events)825826r = uhdev.event(0, 0, wheels=(0, -1 * hmult))827expected = [syn_event]828expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, -1))829if high_res_hwheel:830expected.append(831libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, -120)832)833events = uhdev.next_sync_events()834self.debug_reports(r, uhdev, events)835self.assertInputEvents(expected, events)836837r = uhdev.event(-1, 2, wheels=(0, 3 * hmult))838expected = [syn_event]839expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, -1))840expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, 2))841expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, 3))842if high_res_hwheel:843expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 360))844events = uhdev.next_sync_events()845self.debug_reports(r, uhdev, events)846self.assertInputEvents(expected, events)847848r = uhdev.event(-1, 2, wheels=(-3 * vmult, 4 * hmult))849expected = [syn_event]850expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, -1))851expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, 2))852expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, -3))853if high_res_wheel:854expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, -360))855expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, 4))856if high_res_wheel:857expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 480))858events = uhdev.next_sync_events()859self.debug_reports(r, uhdev, events)860self.assertInputEvents(expected, events)861862863class TestResolutionMultiplierMouse(TestTwoWheelMouse):864def create_device(self):865return ResolutionMultiplierMouse()866867def is_wheel_highres(self, uhdev):868high_res = super().is_wheel_highres(uhdev)869870if not high_res:871# the kernel doesn't seem to support the high res wheel mice,872# make sure we haven't triggered the feature873assert uhdev.wheel_multiplier == 1874875return high_res876877def test_resolution_multiplier_wheel(self):878uhdev = self.uhdev879880if not self.is_wheel_highres(uhdev):881pytest.skip("Kernel not compatible, we can not trigger the conditions")882883assert uhdev.wheel_multiplier > 1884assert 120 % uhdev.wheel_multiplier == 0885886def test_wheel_with_multiplier(self):887uhdev = self.uhdev888889if not self.is_wheel_highres(uhdev):890pytest.skip("Kernel not compatible, we can not trigger the conditions")891892assert uhdev.wheel_multiplier > 1893894syn_event = self.syn_event895mult = uhdev.wheel_multiplier896897r = uhdev.event(0, 0, wheels=1)898expected = [syn_event]899expected.append(900libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, 120 / mult)901)902events = uhdev.next_sync_events()903self.debug_reports(r, uhdev, events)904self.assertInputEvents(expected, events)905906r = uhdev.event(0, 0, wheels=-1)907expected = [syn_event]908expected.append(909libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, -120 / mult)910)911events = uhdev.next_sync_events()912self.debug_reports(r, uhdev, events)913self.assertInputEvents(expected, events)914915expected = [syn_event]916expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, 1))917expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, -2))918expected.append(919libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, 120 / mult)920)921922for _ in range(mult - 1):923r = uhdev.event(1, -2, wheels=1)924events = uhdev.next_sync_events()925self.debug_reports(r, uhdev, events)926self.assertInputEvents(expected, events)927928r = uhdev.event(1, -2, wheels=1)929expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, 1))930events = uhdev.next_sync_events()931self.debug_reports(r, uhdev, events)932self.assertInputEvents(expected, events)933934935class TestBadResolutionMultiplierMouse(TestTwoWheelMouse):936def create_device(self):937return BadResolutionMultiplierMouse()938939def is_wheel_highres(self, uhdev):940high_res = super().is_wheel_highres(uhdev)941942assert uhdev.wheel_multiplier == 1943944return high_res945946def test_resolution_multiplier_wheel(self):947uhdev = self.uhdev948949assert uhdev.wheel_multiplier == 1950951952class TestResolutionMultiplierHWheelMouse(TestResolutionMultiplierMouse):953def create_device(self):954return ResolutionMultiplierHWheelMouse()955956def is_hwheel_highres(self, uhdev):957high_res = super().is_hwheel_highres(uhdev)958959if not high_res:960# the kernel doesn't seem to support the high res wheel mice,961# make sure we haven't triggered the feature962assert uhdev.hwheel_multiplier == 1963964return high_res965966def test_resolution_multiplier_ac_pan(self):967uhdev = self.uhdev968969if not self.is_hwheel_highres(uhdev):970pytest.skip("Kernel not compatible, we can not trigger the conditions")971972assert uhdev.hwheel_multiplier > 1973assert 120 % uhdev.hwheel_multiplier == 0974975def test_ac_pan_with_multiplier(self):976uhdev = self.uhdev977978if not self.is_hwheel_highres(uhdev):979pytest.skip("Kernel not compatible, we can not trigger the conditions")980981assert uhdev.hwheel_multiplier > 1982983syn_event = self.syn_event984hmult = uhdev.hwheel_multiplier985986r = uhdev.event(0, 0, wheels=(0, 1))987expected = [syn_event]988expected.append(989libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 120 / hmult)990)991events = uhdev.next_sync_events()992self.debug_reports(r, uhdev, events)993self.assertInputEvents(expected, events)994995r = uhdev.event(0, 0, wheels=(0, -1))996expected = [syn_event]997expected.append(998libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, -120 / hmult)999)1000events = uhdev.next_sync_events()1001self.debug_reports(r, uhdev, events)1002self.assertInputEvents(expected, events)10031004expected = [syn_event]1005expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, 1))1006expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, -2))1007expected.append(1008libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 120 / hmult)1009)10101011for _ in range(hmult - 1):1012r = uhdev.event(1, -2, wheels=(0, 1))1013events = uhdev.next_sync_events()1014self.debug_reports(r, uhdev, events)1015self.assertInputEvents(expected, events)10161017r = uhdev.event(1, -2, wheels=(0, 1))1018expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, 1))1019events = uhdev.next_sync_events()1020self.debug_reports(r, uhdev, events)1021self.assertInputEvents(expected, events)102210231024class TestMiMouse(TestWheelMouse):1025def create_device(self):1026return MIDongleMIWirelessMouse()10271028def assertInputEvents(self, expected_events, effective_events):1029# Buttons and x/y are spread over two HID reports, so we can get two1030# event frames for this device.1031remaining = self.assertInputEventsIn(expected_events, effective_events)1032try:1033remaining.remove(libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT, 0))1034except ValueError:1035# If there's no SYN_REPORT in the list, continue and let the1036# assert below print out the real error1037pass1038assert remaining == []103910401041class TestBadReportDescriptorMouse(base.BaseTestCase.TestUhid):1042def create_device(self):1043return BadReportDescriptorMouse()10441045def assertName(self, uhdev):1046pass104710481049