| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047 |
- #!/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 base
- import hidtools.hid
- from hidtools.util import BusType
- import libevdev
- import logging
- import pytest
- logger = logging.getLogger("hidtools.test.mouse")
- # workaround https://gitlab.freedesktop.org/libevdev/python-libevdev/issues/6
- try:
- libevdev.EV_REL.REL_WHEEL_HI_RES
- except AttributeError:
- libevdev.EV_REL.REL_WHEEL_HI_RES = libevdev.EV_REL.REL_0B
- libevdev.EV_REL.REL_HWHEEL_HI_RES = libevdev.EV_REL.REL_0C
- class InvalidHIDCommunication(Exception):
- pass
- class MouseData(object):
- pass
- class BaseMouse(base.UHIDTestDevice):
- def __init__(self, rdesc, name=None, input_info=None):
- assert rdesc is not None
- super().__init__(name, "Mouse", input_info=input_info, rdesc=rdesc)
- self.left = False
- self.right = False
- self.middle = False
- def create_report(self, x, y, buttons=None, wheels=None, reportID=None):
- """
- Return an input report for this device.
- :param x: relative x
- :param y: relative y
- :param buttons: a (l, r, m) tuple of bools for the button states,
- where ``None`` is "leave unchanged"
- :param wheels: a single value for the vertical wheel or a (vertical, horizontal) tuple for
- the two wheels
- :param reportID: the numeric report ID for this report, if needed
- """
- if buttons is not None:
- left, right, middle = buttons
- if left is not None:
- self.left = left
- if right is not None:
- self.right = right
- if middle is not None:
- self.middle = middle
- left = self.left
- right = self.right
- middle = self.middle
- # Note: the BaseMouse doesn't actually have a wheel but the
- # create_report magic only fills in those fields exist, so let's
- # make this generic here.
- wheel, acpan = 0, 0
- if wheels is not None:
- if isinstance(wheels, tuple):
- wheel = wheels[0]
- acpan = wheels[1]
- else:
- wheel = wheels
- reportID = reportID or self.default_reportID
- mouse = MouseData()
- mouse.b1 = int(left)
- mouse.b2 = int(right)
- mouse.b3 = int(middle)
- mouse.x = x
- mouse.y = y
- mouse.wheel = wheel
- mouse.acpan = acpan
- return super().create_report(mouse, reportID=reportID)
- def event(self, x, y, buttons=None, wheels=None):
- """
- Send an input event on the default report ID.
- :param x: relative x
- :param y: relative y
- :param buttons: a (l, r, m) tuple of bools for the button states,
- where ``None`` is "leave unchanged"
- :param wheels: a single value for the vertical wheel or a (vertical, horizontal) tuple for
- the two wheels
- """
- r = self.create_report(x, y, buttons, wheels)
- self.call_input_event(r)
- return [r]
- class ButtonMouse(BaseMouse):
- # fmt: off
- report_descriptor = [
- 0x05, 0x01, # .Usage Page (Generic Desktop) 0
- 0x09, 0x02, # .Usage (Mouse) 2
- 0xa1, 0x01, # .Collection (Application) 4
- 0x09, 0x02, # ..Usage (Mouse) 6
- 0xa1, 0x02, # ..Collection (Logical) 8
- 0x09, 0x01, # ...Usage (Pointer) 10
- 0xa1, 0x00, # ...Collection (Physical) 12
- 0x05, 0x09, # ....Usage Page (Button) 14
- 0x19, 0x01, # ....Usage Minimum (1) 16
- 0x29, 0x03, # ....Usage Maximum (3) 18
- 0x15, 0x00, # ....Logical Minimum (0) 20
- 0x25, 0x01, # ....Logical Maximum (1) 22
- 0x75, 0x01, # ....Report Size (1) 24
- 0x95, 0x03, # ....Report Count (3) 26
- 0x81, 0x02, # ....Input (Data,Var,Abs) 28
- 0x75, 0x05, # ....Report Size (5) 30
- 0x95, 0x01, # ....Report Count (1) 32
- 0x81, 0x03, # ....Input (Cnst,Var,Abs) 34
- 0x05, 0x01, # ....Usage Page (Generic Desktop) 36
- 0x09, 0x30, # ....Usage (X) 38
- 0x09, 0x31, # ....Usage (Y) 40
- 0x15, 0x81, # ....Logical Minimum (-127) 42
- 0x25, 0x7f, # ....Logical Maximum (127) 44
- 0x75, 0x08, # ....Report Size (8) 46
- 0x95, 0x02, # ....Report Count (2) 48
- 0x81, 0x06, # ....Input (Data,Var,Rel) 50
- 0xc0, # ...End Collection 52
- 0xc0, # ..End Collection 53
- 0xc0, # .End Collection 54
- ]
- # fmt: on
- def __init__(self, rdesc=report_descriptor, name=None, input_info=None):
- super().__init__(rdesc, name, input_info)
- def fake_report(self, x, y, buttons):
- if buttons is not None:
- left, right, middle = buttons
- if left is None:
- left = self.left
- if right is None:
- right = self.right
- if middle is None:
- middle = self.middle
- else:
- left = self.left
- right = self.right
- middle = self.middle
- button_mask = sum(1 << i for i, b in enumerate([left, right, middle]) if b)
- x = max(-127, min(127, x))
- y = max(-127, min(127, y))
- x = hidtools.util.to_twos_comp(x, 8)
- y = hidtools.util.to_twos_comp(y, 8)
- return [button_mask, x, y]
- class WheelMouse(ButtonMouse):
- # fmt: off
- report_descriptor = [
- 0x05, 0x01, # Usage Page (Generic Desktop) 0
- 0x09, 0x02, # Usage (Mouse) 2
- 0xa1, 0x01, # Collection (Application) 4
- 0x05, 0x09, # .Usage Page (Button) 6
- 0x19, 0x01, # .Usage Minimum (1) 8
- 0x29, 0x03, # .Usage Maximum (3) 10
- 0x15, 0x00, # .Logical Minimum (0) 12
- 0x25, 0x01, # .Logical Maximum (1) 14
- 0x95, 0x03, # .Report Count (3) 16
- 0x75, 0x01, # .Report Size (1) 18
- 0x81, 0x02, # .Input (Data,Var,Abs) 20
- 0x95, 0x01, # .Report Count (1) 22
- 0x75, 0x05, # .Report Size (5) 24
- 0x81, 0x03, # .Input (Cnst,Var,Abs) 26
- 0x05, 0x01, # .Usage Page (Generic Desktop) 28
- 0x09, 0x01, # .Usage (Pointer) 30
- 0xa1, 0x00, # .Collection (Physical) 32
- 0x09, 0x30, # ..Usage (X) 34
- 0x09, 0x31, # ..Usage (Y) 36
- 0x15, 0x81, # ..Logical Minimum (-127) 38
- 0x25, 0x7f, # ..Logical Maximum (127) 40
- 0x75, 0x08, # ..Report Size (8) 42
- 0x95, 0x02, # ..Report Count (2) 44
- 0x81, 0x06, # ..Input (Data,Var,Rel) 46
- 0xc0, # .End Collection 48
- 0x09, 0x38, # .Usage (Wheel) 49
- 0x15, 0x81, # .Logical Minimum (-127) 51
- 0x25, 0x7f, # .Logical Maximum (127) 53
- 0x75, 0x08, # .Report Size (8) 55
- 0x95, 0x01, # .Report Count (1) 57
- 0x81, 0x06, # .Input (Data,Var,Rel) 59
- 0xc0, # End Collection 61
- ]
- # fmt: on
- def __init__(self, rdesc=report_descriptor, name=None, input_info=None):
- super().__init__(rdesc, name, input_info)
- self.wheel_multiplier = 1
- class TwoWheelMouse(WheelMouse):
- # fmt: off
- report_descriptor = [
- 0x05, 0x01, # Usage Page (Generic Desktop) 0
- 0x09, 0x02, # Usage (Mouse) 2
- 0xa1, 0x01, # Collection (Application) 4
- 0x09, 0x01, # .Usage (Pointer) 6
- 0xa1, 0x00, # .Collection (Physical) 8
- 0x05, 0x09, # ..Usage Page (Button) 10
- 0x19, 0x01, # ..Usage Minimum (1) 12
- 0x29, 0x10, # ..Usage Maximum (16) 14
- 0x15, 0x00, # ..Logical Minimum (0) 16
- 0x25, 0x01, # ..Logical Maximum (1) 18
- 0x95, 0x10, # ..Report Count (16) 20
- 0x75, 0x01, # ..Report Size (1) 22
- 0x81, 0x02, # ..Input (Data,Var,Abs) 24
- 0x05, 0x01, # ..Usage Page (Generic Desktop) 26
- 0x16, 0x01, 0x80, # ..Logical Minimum (-32767) 28
- 0x26, 0xff, 0x7f, # ..Logical Maximum (32767) 31
- 0x75, 0x10, # ..Report Size (16) 34
- 0x95, 0x02, # ..Report Count (2) 36
- 0x09, 0x30, # ..Usage (X) 38
- 0x09, 0x31, # ..Usage (Y) 40
- 0x81, 0x06, # ..Input (Data,Var,Rel) 42
- 0x15, 0x81, # ..Logical Minimum (-127) 44
- 0x25, 0x7f, # ..Logical Maximum (127) 46
- 0x75, 0x08, # ..Report Size (8) 48
- 0x95, 0x01, # ..Report Count (1) 50
- 0x09, 0x38, # ..Usage (Wheel) 52
- 0x81, 0x06, # ..Input (Data,Var,Rel) 54
- 0x05, 0x0c, # ..Usage Page (Consumer Devices) 56
- 0x0a, 0x38, 0x02, # ..Usage (AC Pan) 58
- 0x95, 0x01, # ..Report Count (1) 61
- 0x81, 0x06, # ..Input (Data,Var,Rel) 63
- 0xc0, # .End Collection 65
- 0xc0, # End Collection 66
- ]
- # fmt: on
- def __init__(self, rdesc=report_descriptor, name=None, input_info=None):
- super().__init__(rdesc, name, input_info)
- self.hwheel_multiplier = 1
- class MIDongleMIWirelessMouse(TwoWheelMouse):
- # fmt: off
- report_descriptor = [
- 0x05, 0x01, # Usage Page (Generic Desktop)
- 0x09, 0x02, # Usage (Mouse)
- 0xa1, 0x01, # Collection (Application)
- 0x85, 0x01, # .Report ID (1)
- 0x09, 0x01, # .Usage (Pointer)
- 0xa1, 0x00, # .Collection (Physical)
- 0x95, 0x05, # ..Report Count (5)
- 0x75, 0x01, # ..Report Size (1)
- 0x05, 0x09, # ..Usage Page (Button)
- 0x19, 0x01, # ..Usage Minimum (1)
- 0x29, 0x05, # ..Usage Maximum (5)
- 0x15, 0x00, # ..Logical Minimum (0)
- 0x25, 0x01, # ..Logical Maximum (1)
- 0x81, 0x02, # ..Input (Data,Var,Abs)
- 0x95, 0x01, # ..Report Count (1)
- 0x75, 0x03, # ..Report Size (3)
- 0x81, 0x01, # ..Input (Cnst,Arr,Abs)
- 0x75, 0x08, # ..Report Size (8)
- 0x95, 0x01, # ..Report Count (1)
- 0x05, 0x01, # ..Usage Page (Generic Desktop)
- 0x09, 0x38, # ..Usage (Wheel)
- 0x15, 0x81, # ..Logical Minimum (-127)
- 0x25, 0x7f, # ..Logical Maximum (127)
- 0x81, 0x06, # ..Input (Data,Var,Rel)
- 0x05, 0x0c, # ..Usage Page (Consumer Devices)
- 0x0a, 0x38, 0x02, # ..Usage (AC Pan)
- 0x95, 0x01, # ..Report Count (1)
- 0x81, 0x06, # ..Input (Data,Var,Rel)
- 0xc0, # .End Collection
- 0x85, 0x02, # .Report ID (2)
- 0x09, 0x01, # .Usage (Consumer Control)
- 0xa1, 0x00, # .Collection (Physical)
- 0x75, 0x0c, # ..Report Size (12)
- 0x95, 0x02, # ..Report Count (2)
- 0x05, 0x01, # ..Usage Page (Generic Desktop)
- 0x09, 0x30, # ..Usage (X)
- 0x09, 0x31, # ..Usage (Y)
- 0x16, 0x01, 0xf8, # ..Logical Minimum (-2047)
- 0x26, 0xff, 0x07, # ..Logical Maximum (2047)
- 0x81, 0x06, # ..Input (Data,Var,Rel)
- 0xc0, # .End Collection
- 0xc0, # End Collection
- 0x05, 0x0c, # Usage Page (Consumer Devices)
- 0x09, 0x01, # Usage (Consumer Control)
- 0xa1, 0x01, # Collection (Application)
- 0x85, 0x03, # .Report ID (3)
- 0x15, 0x00, # .Logical Minimum (0)
- 0x25, 0x01, # .Logical Maximum (1)
- 0x75, 0x01, # .Report Size (1)
- 0x95, 0x01, # .Report Count (1)
- 0x09, 0xcd, # .Usage (Play/Pause)
- 0x81, 0x06, # .Input (Data,Var,Rel)
- 0x0a, 0x83, 0x01, # .Usage (AL Consumer Control Config)
- 0x81, 0x06, # .Input (Data,Var,Rel)
- 0x09, 0xb5, # .Usage (Scan Next Track)
- 0x81, 0x06, # .Input (Data,Var,Rel)
- 0x09, 0xb6, # .Usage (Scan Previous Track)
- 0x81, 0x06, # .Input (Data,Var,Rel)
- 0x09, 0xea, # .Usage (Volume Down)
- 0x81, 0x06, # .Input (Data,Var,Rel)
- 0x09, 0xe9, # .Usage (Volume Up)
- 0x81, 0x06, # .Input (Data,Var,Rel)
- 0x0a, 0x25, 0x02, # .Usage (AC Forward)
- 0x81, 0x06, # .Input (Data,Var,Rel)
- 0x0a, 0x24, 0x02, # .Usage (AC Back)
- 0x81, 0x06, # .Input (Data,Var,Rel)
- 0xc0, # End Collection
- ]
- # fmt: on
- device_input_info = (BusType.USB, 0x2717, 0x003B)
- device_name = "uhid test MI Dongle MI Wireless Mouse"
- def __init__(
- self, rdesc=report_descriptor, name=device_name, input_info=device_input_info
- ):
- super().__init__(rdesc, name, input_info)
- def event(self, x, y, buttons=None, wheels=None):
- # this mouse spreads the relative pointer and the mouse buttons
- # onto 2 distinct reports
- rs = []
- r = self.create_report(x, y, buttons, wheels, reportID=1)
- self.call_input_event(r)
- rs.append(r)
- r = self.create_report(x, y, buttons, reportID=2)
- self.call_input_event(r)
- rs.append(r)
- return rs
- class ResolutionMultiplierMouse(TwoWheelMouse):
- # fmt: off
- report_descriptor = [
- 0x05, 0x01, # Usage Page (Generic Desktop) 83
- 0x09, 0x02, # Usage (Mouse) 85
- 0xa1, 0x01, # Collection (Application) 87
- 0x05, 0x01, # .Usage Page (Generic Desktop) 89
- 0x09, 0x02, # .Usage (Mouse) 91
- 0xa1, 0x02, # .Collection (Logical) 93
- 0x85, 0x11, # ..Report ID (17) 95
- 0x09, 0x01, # ..Usage (Pointer) 97
- 0xa1, 0x00, # ..Collection (Physical) 99
- 0x05, 0x09, # ...Usage Page (Button) 101
- 0x19, 0x01, # ...Usage Minimum (1) 103
- 0x29, 0x03, # ...Usage Maximum (3) 105
- 0x95, 0x03, # ...Report Count (3) 107
- 0x75, 0x01, # ...Report Size (1) 109
- 0x25, 0x01, # ...Logical Maximum (1) 111
- 0x81, 0x02, # ...Input (Data,Var,Abs) 113
- 0x95, 0x01, # ...Report Count (1) 115
- 0x81, 0x01, # ...Input (Cnst,Arr,Abs) 117
- 0x09, 0x05, # ...Usage (Vendor Usage 0x05) 119
- 0x81, 0x02, # ...Input (Data,Var,Abs) 121
- 0x95, 0x03, # ...Report Count (3) 123
- 0x81, 0x01, # ...Input (Cnst,Arr,Abs) 125
- 0x05, 0x01, # ...Usage Page (Generic Desktop) 127
- 0x09, 0x30, # ...Usage (X) 129
- 0x09, 0x31, # ...Usage (Y) 131
- 0x95, 0x02, # ...Report Count (2) 133
- 0x75, 0x08, # ...Report Size (8) 135
- 0x15, 0x81, # ...Logical Minimum (-127) 137
- 0x25, 0x7f, # ...Logical Maximum (127) 139
- 0x81, 0x06, # ...Input (Data,Var,Rel) 141
- 0xa1, 0x02, # ...Collection (Logical) 143
- 0x85, 0x12, # ....Report ID (18) 145
- 0x09, 0x48, # ....Usage (Resolution Multiplier) 147
- 0x95, 0x01, # ....Report Count (1) 149
- 0x75, 0x02, # ....Report Size (2) 151
- 0x15, 0x00, # ....Logical Minimum (0) 153
- 0x25, 0x01, # ....Logical Maximum (1) 155
- 0x35, 0x01, # ....Physical Minimum (1) 157
- 0x45, 0x04, # ....Physical Maximum (4) 159
- 0xb1, 0x02, # ....Feature (Data,Var,Abs) 161
- 0x35, 0x00, # ....Physical Minimum (0) 163
- 0x45, 0x00, # ....Physical Maximum (0) 165
- 0x75, 0x06, # ....Report Size (6) 167
- 0xb1, 0x01, # ....Feature (Cnst,Arr,Abs) 169
- 0x85, 0x11, # ....Report ID (17) 171
- 0x09, 0x38, # ....Usage (Wheel) 173
- 0x15, 0x81, # ....Logical Minimum (-127) 175
- 0x25, 0x7f, # ....Logical Maximum (127) 177
- 0x75, 0x08, # ....Report Size (8) 179
- 0x81, 0x06, # ....Input (Data,Var,Rel) 181
- 0xc0, # ...End Collection 183
- 0x05, 0x0c, # ...Usage Page (Consumer Devices) 184
- 0x75, 0x08, # ...Report Size (8) 186
- 0x0a, 0x38, 0x02, # ...Usage (AC Pan) 188
- 0x81, 0x06, # ...Input (Data,Var,Rel) 191
- 0xc0, # ..End Collection 193
- 0xc0, # .End Collection 194
- 0xc0, # End Collection 195
- ]
- # fmt: on
- def __init__(self, rdesc=report_descriptor, name=None, input_info=None):
- super().__init__(rdesc, name, input_info)
- self.default_reportID = 0x11
- # Feature Report 12, multiplier Feature value must be set to 0b01,
- # i.e. 1. We should extract that from the descriptor instead
- # of hardcoding it here, but meanwhile this will do.
- self.set_feature_report = [0x12, 0x1]
- def set_report(self, req, rnum, rtype, data):
- if rtype != self.UHID_FEATURE_REPORT:
- raise InvalidHIDCommunication(f"Unexpected report type: {rtype}")
- if rnum != 0x12:
- raise InvalidHIDCommunication(f"Unexpected report number: {rnum}")
- if data != self.set_feature_report:
- raise InvalidHIDCommunication(
- f"Unexpected data: {data}, expected {self.set_feature_report}"
- )
- self.wheel_multiplier = 4
- return 0
- class BadResolutionMultiplierMouse(ResolutionMultiplierMouse):
- def set_report(self, req, rnum, rtype, data):
- super().set_report(req, rnum, rtype, data)
- self.wheel_multiplier = 1
- self.hwheel_multiplier = 1
- return 32 # EPIPE
- class BadReportDescriptorMouse(BaseMouse):
- """
- This "device" was one autogenerated by syzbot. There are a lot of issues in
- it, and the most problematic is that it declares features that have no
- size.
- This leads to report->size being set to 0 and can mess up with usbhid
- internals. Fortunately, uhid merely passes the incoming buffer, without
- touching it so a buffer of size 0 will be translated to [] without
- triggering a kernel oops.
- Because the report descriptor is wrong, no input are created, and we need
- to tweak a little bit the parameters to make it look correct.
- """
- # fmt: off
- report_descriptor = [
- 0x96, 0x01, 0x00, # Report Count (1) 0
- 0x06, 0x01, 0x00, # Usage Page (Generic Desktop) 3
- # 0x03, 0x00, 0x00, 0x00, 0x00, # Ignored by the kernel somehow
- 0x2a, 0x90, 0xa0, # Usage Maximum (41104) 6
- 0x27, 0x00, 0x00, 0x00, 0x00, # Logical Maximum (0) 9
- 0xb3, 0x81, 0x3e, 0x25, 0x03, # Feature (Cnst,Arr,Abs,Vol) 14
- 0x1b, 0xdd, 0xe8, 0x40, 0x50, # Usage Minimum (1346431197) 19
- 0x3b, 0x5d, 0x8c, 0x3d, 0xda, # Designator Index 24
- ]
- # fmt: on
- def __init__(
- self, rdesc=report_descriptor, name=None, input_info=(3, 0x045E, 0x07DA)
- ):
- super().__init__(rdesc, name, input_info)
- self.high_resolution_report_called = False
- def get_evdev(self, application=None):
- assert self._input_nodes is None
- return (
- "Ok" # should be a list or None, but both would fail, so abusing the system
- )
- def next_sync_events(self, application=None):
- # there are no evdev nodes, so no events
- return []
- def is_ready(self):
- # we wait for the SET_REPORT command to come
- return self.high_resolution_report_called
- def set_report(self, req, rnum, rtype, data):
- if rtype != self.UHID_FEATURE_REPORT:
- raise InvalidHIDCommunication(f"Unexpected report type: {rtype}")
- if rnum != 0x0:
- raise InvalidHIDCommunication(f"Unexpected report number: {rnum}")
- if len(data) != 1:
- raise InvalidHIDCommunication(f"Unexpected data: {data}, expected '[0]'")
- self.high_resolution_report_called = True
- return 0
- class ResolutionMultiplierHWheelMouse(TwoWheelMouse):
- # fmt: off
- report_descriptor = [
- 0x05, 0x01, # Usage Page (Generic Desktop) 0
- 0x09, 0x02, # Usage (Mouse) 2
- 0xa1, 0x01, # Collection (Application) 4
- 0x05, 0x01, # .Usage Page (Generic Desktop) 6
- 0x09, 0x02, # .Usage (Mouse) 8
- 0xa1, 0x02, # .Collection (Logical) 10
- 0x85, 0x1a, # ..Report ID (26) 12
- 0x09, 0x01, # ..Usage (Pointer) 14
- 0xa1, 0x00, # ..Collection (Physical) 16
- 0x05, 0x09, # ...Usage Page (Button) 18
- 0x19, 0x01, # ...Usage Minimum (1) 20
- 0x29, 0x05, # ...Usage Maximum (5) 22
- 0x95, 0x05, # ...Report Count (5) 24
- 0x75, 0x01, # ...Report Size (1) 26
- 0x15, 0x00, # ...Logical Minimum (0) 28
- 0x25, 0x01, # ...Logical Maximum (1) 30
- 0x81, 0x02, # ...Input (Data,Var,Abs) 32
- 0x75, 0x03, # ...Report Size (3) 34
- 0x95, 0x01, # ...Report Count (1) 36
- 0x81, 0x01, # ...Input (Cnst,Arr,Abs) 38
- 0x05, 0x01, # ...Usage Page (Generic Desktop) 40
- 0x09, 0x30, # ...Usage (X) 42
- 0x09, 0x31, # ...Usage (Y) 44
- 0x95, 0x02, # ...Report Count (2) 46
- 0x75, 0x10, # ...Report Size (16) 48
- 0x16, 0x01, 0x80, # ...Logical Minimum (-32767) 50
- 0x26, 0xff, 0x7f, # ...Logical Maximum (32767) 53
- 0x81, 0x06, # ...Input (Data,Var,Rel) 56
- 0xa1, 0x02, # ...Collection (Logical) 58
- 0x85, 0x12, # ....Report ID (18) 60
- 0x09, 0x48, # ....Usage (Resolution Multiplier) 62
- 0x95, 0x01, # ....Report Count (1) 64
- 0x75, 0x02, # ....Report Size (2) 66
- 0x15, 0x00, # ....Logical Minimum (0) 68
- 0x25, 0x01, # ....Logical Maximum (1) 70
- 0x35, 0x01, # ....Physical Minimum (1) 72
- 0x45, 0x0c, # ....Physical Maximum (12) 74
- 0xb1, 0x02, # ....Feature (Data,Var,Abs) 76
- 0x85, 0x1a, # ....Report ID (26) 78
- 0x09, 0x38, # ....Usage (Wheel) 80
- 0x35, 0x00, # ....Physical Minimum (0) 82
- 0x45, 0x00, # ....Physical Maximum (0) 84
- 0x95, 0x01, # ....Report Count (1) 86
- 0x75, 0x10, # ....Report Size (16) 88
- 0x16, 0x01, 0x80, # ....Logical Minimum (-32767) 90
- 0x26, 0xff, 0x7f, # ....Logical Maximum (32767) 93
- 0x81, 0x06, # ....Input (Data,Var,Rel) 96
- 0xc0, # ...End Collection 98
- 0xa1, 0x02, # ...Collection (Logical) 99
- 0x85, 0x12, # ....Report ID (18) 101
- 0x09, 0x48, # ....Usage (Resolution Multiplier) 103
- 0x75, 0x02, # ....Report Size (2) 105
- 0x15, 0x00, # ....Logical Minimum (0) 107
- 0x25, 0x01, # ....Logical Maximum (1) 109
- 0x35, 0x01, # ....Physical Minimum (1) 111
- 0x45, 0x0c, # ....Physical Maximum (12) 113
- 0xb1, 0x02, # ....Feature (Data,Var,Abs) 115
- 0x35, 0x00, # ....Physical Minimum (0) 117
- 0x45, 0x00, # ....Physical Maximum (0) 119
- 0x75, 0x04, # ....Report Size (4) 121
- 0xb1, 0x01, # ....Feature (Cnst,Arr,Abs) 123
- 0x85, 0x1a, # ....Report ID (26) 125
- 0x05, 0x0c, # ....Usage Page (Consumer Devices) 127
- 0x95, 0x01, # ....Report Count (1) 129
- 0x75, 0x10, # ....Report Size (16) 131
- 0x16, 0x01, 0x80, # ....Logical Minimum (-32767) 133
- 0x26, 0xff, 0x7f, # ....Logical Maximum (32767) 136
- 0x0a, 0x38, 0x02, # ....Usage (AC Pan) 139
- 0x81, 0x06, # ....Input (Data,Var,Rel) 142
- 0xc0, # ...End Collection 144
- 0xc0, # ..End Collection 145
- 0xc0, # .End Collection 146
- 0xc0, # End Collection 147
- ]
- # fmt: on
- def __init__(self, rdesc=report_descriptor, name=None, input_info=None):
- super().__init__(rdesc, name, input_info)
- self.default_reportID = 0x1A
- # Feature Report 12, multiplier Feature value must be set to 0b0101,
- # i.e. 5. We should extract that from the descriptor instead
- # of hardcoding it here, but meanwhile this will do.
- self.set_feature_report = [0x12, 0x5]
- def set_report(self, req, rnum, rtype, data):
- super().set_report(req, rnum, rtype, data)
- self.wheel_multiplier = 12
- self.hwheel_multiplier = 12
- return 0
- class BaseTest:
- class TestMouse(base.BaseTestCase.TestUhid):
- def test_buttons(self):
- """check for button reliability."""
- uhdev = self.uhdev
- evdev = uhdev.get_evdev()
- syn_event = self.syn_event
- r = uhdev.event(0, 0, (None, True, None))
- expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 1)
- events = uhdev.next_sync_events()
- self.debug_reports(r, uhdev, events)
- self.assertInputEventsIn((syn_event, expected_event), events)
- assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 1
- r = uhdev.event(0, 0, (None, False, None))
- expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 0)
- events = uhdev.next_sync_events()
- self.debug_reports(r, uhdev, events)
- self.assertInputEventsIn((syn_event, expected_event), events)
- assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 0
- r = uhdev.event(0, 0, (None, None, True))
- expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_MIDDLE, 1)
- events = uhdev.next_sync_events()
- self.debug_reports(r, uhdev, events)
- self.assertInputEventsIn((syn_event, expected_event), events)
- assert evdev.value[libevdev.EV_KEY.BTN_MIDDLE] == 1
- r = uhdev.event(0, 0, (None, None, False))
- expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_MIDDLE, 0)
- events = uhdev.next_sync_events()
- self.debug_reports(r, uhdev, events)
- self.assertInputEventsIn((syn_event, expected_event), events)
- assert evdev.value[libevdev.EV_KEY.BTN_MIDDLE] == 0
- r = uhdev.event(0, 0, (True, None, None))
- expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 1)
- events = uhdev.next_sync_events()
- self.debug_reports(r, uhdev, events)
- self.assertInputEventsIn((syn_event, expected_event), events)
- assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 1
- r = uhdev.event(0, 0, (False, None, None))
- expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 0)
- events = uhdev.next_sync_events()
- self.debug_reports(r, uhdev, events)
- self.assertInputEventsIn((syn_event, expected_event), events)
- assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 0
- r = uhdev.event(0, 0, (True, True, None))
- expected_event0 = libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 1)
- expected_event1 = libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 1)
- events = uhdev.next_sync_events()
- self.debug_reports(r, uhdev, events)
- self.assertInputEventsIn(
- (syn_event, expected_event0, expected_event1), events
- )
- assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 1
- assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 1
- r = uhdev.event(0, 0, (False, None, None))
- expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 0)
- events = uhdev.next_sync_events()
- self.debug_reports(r, uhdev, events)
- self.assertInputEventsIn((syn_event, expected_event), events)
- assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 1
- assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 0
- r = uhdev.event(0, 0, (None, False, None))
- expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 0)
- events = uhdev.next_sync_events()
- self.debug_reports(r, uhdev, events)
- self.assertInputEventsIn((syn_event, expected_event), events)
- assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 0
- assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 0
- def test_relative(self):
- """Check for relative events."""
- uhdev = self.uhdev
- syn_event = self.syn_event
- r = uhdev.event(0, -1)
- expected_event = libevdev.InputEvent(libevdev.EV_REL.REL_Y, -1)
- events = uhdev.next_sync_events()
- self.debug_reports(r, uhdev, events)
- self.assertInputEvents((syn_event, expected_event), events)
- r = uhdev.event(1, 0)
- expected_event = libevdev.InputEvent(libevdev.EV_REL.REL_X, 1)
- events = uhdev.next_sync_events()
- self.debug_reports(r, uhdev, events)
- self.assertInputEvents((syn_event, expected_event), events)
- r = uhdev.event(-1, 2)
- expected_event0 = libevdev.InputEvent(libevdev.EV_REL.REL_X, -1)
- expected_event1 = libevdev.InputEvent(libevdev.EV_REL.REL_Y, 2)
- events = uhdev.next_sync_events()
- self.debug_reports(r, uhdev, events)
- self.assertInputEvents(
- (syn_event, expected_event0, expected_event1), events
- )
- class TestSimpleMouse(BaseTest.TestMouse):
- def create_device(self):
- return ButtonMouse()
- def test_rdesc(self):
- """Check that the testsuite actually manages to format the
- reports according to the report descriptors.
- No kernel device is used here"""
- uhdev = self.uhdev
- event = (0, 0, (None, None, None))
- assert uhdev.fake_report(*event) == uhdev.create_report(*event)
- event = (0, 0, (None, True, None))
- assert uhdev.fake_report(*event) == uhdev.create_report(*event)
- event = (0, 0, (True, True, None))
- assert uhdev.fake_report(*event) == uhdev.create_report(*event)
- event = (0, 0, (False, False, False))
- assert uhdev.fake_report(*event) == uhdev.create_report(*event)
- event = (1, 0, (True, False, True))
- assert uhdev.fake_report(*event) == uhdev.create_report(*event)
- event = (-1, 0, (True, False, True))
- assert uhdev.fake_report(*event) == uhdev.create_report(*event)
- event = (-5, 5, (True, False, True))
- assert uhdev.fake_report(*event) == uhdev.create_report(*event)
- event = (-127, 127, (True, False, True))
- assert uhdev.fake_report(*event) == uhdev.create_report(*event)
- event = (0, -128, (True, False, True))
- with pytest.raises(hidtools.hid.RangeError):
- uhdev.create_report(*event)
- class TestWheelMouse(BaseTest.TestMouse):
- def create_device(self):
- return WheelMouse()
- def is_wheel_highres(self, uhdev):
- evdev = uhdev.get_evdev()
- assert evdev.has(libevdev.EV_REL.REL_WHEEL)
- return evdev.has(libevdev.EV_REL.REL_WHEEL_HI_RES)
- def test_wheel(self):
- uhdev = self.uhdev
- # check if the kernel is high res wheel compatible
- high_res_wheel = self.is_wheel_highres(uhdev)
- syn_event = self.syn_event
- # The Resolution Multiplier is applied to the HID reports, so we
- # need to pre-multiply too.
- mult = uhdev.wheel_multiplier
- r = uhdev.event(0, 0, wheels=1 * mult)
- expected = [syn_event]
- expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, 1))
- if high_res_wheel:
- expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, 120))
- events = uhdev.next_sync_events()
- self.debug_reports(r, uhdev, events)
- self.assertInputEvents(expected, events)
- r = uhdev.event(0, 0, wheels=-1 * mult)
- expected = [syn_event]
- expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, -1))
- if high_res_wheel:
- expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, -120))
- events = uhdev.next_sync_events()
- self.debug_reports(r, uhdev, events)
- self.assertInputEvents(expected, events)
- r = uhdev.event(-1, 2, wheels=3 * mult)
- expected = [syn_event]
- expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, -1))
- expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, 2))
- expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, 3))
- if high_res_wheel:
- expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, 360))
- events = uhdev.next_sync_events()
- self.debug_reports(r, uhdev, events)
- self.assertInputEvents(expected, events)
- class TestTwoWheelMouse(TestWheelMouse):
- def create_device(self):
- return TwoWheelMouse()
- def is_hwheel_highres(self, uhdev):
- evdev = uhdev.get_evdev()
- assert evdev.has(libevdev.EV_REL.REL_HWHEEL)
- return evdev.has(libevdev.EV_REL.REL_HWHEEL_HI_RES)
- def test_ac_pan(self):
- uhdev = self.uhdev
- # check if the kernel is high res wheel compatible
- high_res_wheel = self.is_wheel_highres(uhdev)
- high_res_hwheel = self.is_hwheel_highres(uhdev)
- assert high_res_wheel == high_res_hwheel
- syn_event = self.syn_event
- # The Resolution Multiplier is applied to the HID reports, so we
- # need to pre-multiply too.
- hmult = uhdev.hwheel_multiplier
- vmult = uhdev.wheel_multiplier
- r = uhdev.event(0, 0, wheels=(0, 1 * hmult))
- expected = [syn_event]
- expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, 1))
- if high_res_hwheel:
- expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 120))
- events = uhdev.next_sync_events()
- self.debug_reports(r, uhdev, events)
- self.assertInputEvents(expected, events)
- r = uhdev.event(0, 0, wheels=(0, -1 * hmult))
- expected = [syn_event]
- expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, -1))
- if high_res_hwheel:
- expected.append(
- libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, -120)
- )
- events = uhdev.next_sync_events()
- self.debug_reports(r, uhdev, events)
- self.assertInputEvents(expected, events)
- r = uhdev.event(-1, 2, wheels=(0, 3 * hmult))
- expected = [syn_event]
- expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, -1))
- expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, 2))
- expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, 3))
- if high_res_hwheel:
- expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 360))
- events = uhdev.next_sync_events()
- self.debug_reports(r, uhdev, events)
- self.assertInputEvents(expected, events)
- r = uhdev.event(-1, 2, wheels=(-3 * vmult, 4 * hmult))
- expected = [syn_event]
- expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, -1))
- expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, 2))
- expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, -3))
- if high_res_wheel:
- expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, -360))
- expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, 4))
- if high_res_wheel:
- expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 480))
- events = uhdev.next_sync_events()
- self.debug_reports(r, uhdev, events)
- self.assertInputEvents(expected, events)
- class TestResolutionMultiplierMouse(TestTwoWheelMouse):
- def create_device(self):
- return ResolutionMultiplierMouse()
- def is_wheel_highres(self, uhdev):
- high_res = super().is_wheel_highres(uhdev)
- if not high_res:
- # the kernel doesn't seem to support the high res wheel mice,
- # make sure we haven't triggered the feature
- assert uhdev.wheel_multiplier == 1
- return high_res
- def test_resolution_multiplier_wheel(self):
- uhdev = self.uhdev
- if not self.is_wheel_highres(uhdev):
- pytest.skip("Kernel not compatible, we can not trigger the conditions")
- assert uhdev.wheel_multiplier > 1
- assert 120 % uhdev.wheel_multiplier == 0
- def test_wheel_with_multiplier(self):
- uhdev = self.uhdev
- if not self.is_wheel_highres(uhdev):
- pytest.skip("Kernel not compatible, we can not trigger the conditions")
- assert uhdev.wheel_multiplier > 1
- syn_event = self.syn_event
- mult = uhdev.wheel_multiplier
- r = uhdev.event(0, 0, wheels=1)
- expected = [syn_event]
- expected.append(
- libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, 120 / mult)
- )
- events = uhdev.next_sync_events()
- self.debug_reports(r, uhdev, events)
- self.assertInputEvents(expected, events)
- r = uhdev.event(0, 0, wheels=-1)
- expected = [syn_event]
- expected.append(
- libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, -120 / mult)
- )
- events = uhdev.next_sync_events()
- self.debug_reports(r, uhdev, events)
- self.assertInputEvents(expected, events)
- expected = [syn_event]
- expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, 1))
- expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, -2))
- expected.append(
- libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL_HI_RES, 120 / mult)
- )
- for _ in range(mult - 1):
- r = uhdev.event(1, -2, wheels=1)
- events = uhdev.next_sync_events()
- self.debug_reports(r, uhdev, events)
- self.assertInputEvents(expected, events)
- r = uhdev.event(1, -2, wheels=1)
- expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, 1))
- events = uhdev.next_sync_events()
- self.debug_reports(r, uhdev, events)
- self.assertInputEvents(expected, events)
- class TestBadResolutionMultiplierMouse(TestTwoWheelMouse):
- def create_device(self):
- return BadResolutionMultiplierMouse()
- def is_wheel_highres(self, uhdev):
- high_res = super().is_wheel_highres(uhdev)
- assert uhdev.wheel_multiplier == 1
- return high_res
- def test_resolution_multiplier_wheel(self):
- uhdev = self.uhdev
- assert uhdev.wheel_multiplier == 1
- class TestResolutionMultiplierHWheelMouse(TestResolutionMultiplierMouse):
- def create_device(self):
- return ResolutionMultiplierHWheelMouse()
- def is_hwheel_highres(self, uhdev):
- high_res = super().is_hwheel_highres(uhdev)
- if not high_res:
- # the kernel doesn't seem to support the high res wheel mice,
- # make sure we haven't triggered the feature
- assert uhdev.hwheel_multiplier == 1
- return high_res
- def test_resolution_multiplier_ac_pan(self):
- uhdev = self.uhdev
- if not self.is_hwheel_highres(uhdev):
- pytest.skip("Kernel not compatible, we can not trigger the conditions")
- assert uhdev.hwheel_multiplier > 1
- assert 120 % uhdev.hwheel_multiplier == 0
- def test_ac_pan_with_multiplier(self):
- uhdev = self.uhdev
- if not self.is_hwheel_highres(uhdev):
- pytest.skip("Kernel not compatible, we can not trigger the conditions")
- assert uhdev.hwheel_multiplier > 1
- syn_event = self.syn_event
- hmult = uhdev.hwheel_multiplier
- r = uhdev.event(0, 0, wheels=(0, 1))
- expected = [syn_event]
- expected.append(
- libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 120 / hmult)
- )
- events = uhdev.next_sync_events()
- self.debug_reports(r, uhdev, events)
- self.assertInputEvents(expected, events)
- r = uhdev.event(0, 0, wheels=(0, -1))
- expected = [syn_event]
- expected.append(
- libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, -120 / hmult)
- )
- events = uhdev.next_sync_events()
- self.debug_reports(r, uhdev, events)
- self.assertInputEvents(expected, events)
- expected = [syn_event]
- expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, 1))
- expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, -2))
- expected.append(
- libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL_HI_RES, 120 / hmult)
- )
- for _ in range(hmult - 1):
- r = uhdev.event(1, -2, wheels=(0, 1))
- events = uhdev.next_sync_events()
- self.debug_reports(r, uhdev, events)
- self.assertInputEvents(expected, events)
- r = uhdev.event(1, -2, wheels=(0, 1))
- expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, 1))
- events = uhdev.next_sync_events()
- self.debug_reports(r, uhdev, events)
- self.assertInputEvents(expected, events)
- class TestMiMouse(TestWheelMouse):
- def create_device(self):
- return MIDongleMIWirelessMouse()
- def assertInputEvents(self, expected_events, effective_events):
- # Buttons and x/y are spread over two HID reports, so we can get two
- # event frames for this device.
- remaining = self.assertInputEventsIn(expected_events, effective_events)
- try:
- remaining.remove(libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT, 0))
- except ValueError:
- # If there's no SYN_REPORT in the list, continue and let the
- # assert below print out the real error
- pass
- assert remaining == []
- class TestBadReportDescriptorMouse(base.BaseTestCase.TestUhid):
- def create_device(self):
- return BadReportDescriptorMouse()
- def assertName(self, uhdev):
- pass
|