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