Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

selftests: hid: import hid-tools hid-mouse tests

These tests have been developed in the hid-tools[0] tree for a while.
Now that we have a proper selftests/hid kernel entry and that the tests
are more reliable, it is time to directly include those in the kernel
tree.

[0] https://gitlab.freedesktop.org/libevdev/hid-tools

Cc: Peter Hutterer <peter.hutterer@who-t.net>
Cc: Roderick Colenbrander <roderick.colenbrander@sony.com>
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Roderick Colenbrander <roderick.colenbrander@sony.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

+985
+1
tools/testing/selftests/hid/Makefile
··· 8 8 TEST_PROGS := hid-core.sh 9 9 TEST_PROGS += hid-gamepad.sh 10 10 TEST_PROGS += hid-keyboard.sh 11 + TEST_PROGS += hid-mouse.sh 11 12 12 13 CXX ?= $(CROSS_COMPILE)g++ 13 14
+7
tools/testing/selftests/hid/hid-mouse.sh
··· 1 + #!/bin/sh 2 + # SPDX-License-Identifier: GPL-2.0 3 + # Runs tests for the HID subsystem 4 + 5 + export TARGET=test_mouse.py 6 + 7 + bash ./run-hid-tools-tests.sh
+977
tools/testing/selftests/hid/tests/test_mouse.py
··· 1 + #!/bin/env python3 2 + # SPDX-License-Identifier: GPL-2.0 3 + # -*- coding: utf-8 -*- 4 + # 5 + # Copyright (c) 2017 Benjamin Tissoires <benjamin.tissoires@gmail.com> 6 + # Copyright (c) 2017 Red Hat, Inc. 7 + # 8 + 9 + from . import base 10 + import hidtools.hid 11 + from hidtools.util import BusType 12 + import libevdev 13 + import logging 14 + import pytest 15 + 16 + logger = logging.getLogger("hidtools.test.mouse") 17 + 18 + # workaround https://gitlab.freedesktop.org/libevdev/python-libevdev/issues/6 19 + try: 20 + libevdev.EV_REL.REL_WHEEL_HI_RES 21 + except AttributeError: 22 + libevdev.EV_REL.REL_WHEEL_HI_RES = libevdev.EV_REL.REL_0B 23 + libevdev.EV_REL.REL_HWHEEL_HI_RES = libevdev.EV_REL.REL_0C 24 + 25 + 26 + class InvalidHIDCommunication(Exception): 27 + pass 28 + 29 + 30 + class MouseData(object): 31 + pass 32 + 33 + 34 + class BaseMouse(base.UHIDTestDevice): 35 + def __init__(self, rdesc, name=None, input_info=None): 36 + assert rdesc is not None 37 + super().__init__(name, "Mouse", input_info=input_info, rdesc=rdesc) 38 + self.left = False 39 + self.right = False 40 + self.middle = False 41 + 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 x 47 + :param y: relative y 48 + :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 for 51 + the two wheels 52 + :param reportID: the numeric report ID for this report, if needed 53 + """ 54 + if buttons is not None: 55 + l, r, m = buttons 56 + if l is not None: 57 + self.left = l 58 + if r is not None: 59 + self.right = r 60 + if m is not None: 61 + self.middle = m 62 + left = self.left 63 + right = self.right 64 + middle = self.middle 65 + # Note: the BaseMouse doesn't actually have a wheel but the 66 + # create_report magic only fills in those fields exist, so let's 67 + # make this generic here. 68 + wheel, acpan = 0, 0 69 + if wheels is not None: 70 + if isinstance(wheels, tuple): 71 + wheel = wheels[0] 72 + acpan = wheels[1] 73 + else: 74 + wheel = wheels 75 + 76 + reportID = reportID or self.default_reportID 77 + 78 + mouse = MouseData() 79 + mouse.b1 = int(left) 80 + mouse.b2 = int(right) 81 + mouse.b3 = int(middle) 82 + mouse.x = x 83 + mouse.y = y 84 + mouse.wheel = wheel 85 + mouse.acpan = acpan 86 + 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 x 93 + :param y: relative y 94 + :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 for 97 + the two wheels 98 + """ 99 + r = self.create_report(x, y, buttons, wheels) 100 + self.call_input_event(r) 101 + return [r] 102 + 103 + 104 + class ButtonMouse(BaseMouse): 105 + # fmt: off 106 + report_descriptor = [ 107 + 0x05, 0x01, # .Usage Page (Generic Desktop) 0 108 + 0x09, 0x02, # .Usage (Mouse) 2 109 + 0xa1, 0x01, # .Collection (Application) 4 110 + 0x09, 0x02, # ..Usage (Mouse) 6 111 + 0xa1, 0x02, # ..Collection (Logical) 8 112 + 0x09, 0x01, # ...Usage (Pointer) 10 113 + 0xa1, 0x00, # ...Collection (Physical) 12 114 + 0x05, 0x09, # ....Usage Page (Button) 14 115 + 0x19, 0x01, # ....Usage Minimum (1) 16 116 + 0x29, 0x03, # ....Usage Maximum (3) 18 117 + 0x15, 0x00, # ....Logical Minimum (0) 20 118 + 0x25, 0x01, # ....Logical Maximum (1) 22 119 + 0x75, 0x01, # ....Report Size (1) 24 120 + 0x95, 0x03, # ....Report Count (3) 26 121 + 0x81, 0x02, # ....Input (Data,Var,Abs) 28 122 + 0x75, 0x05, # ....Report Size (5) 30 123 + 0x95, 0x01, # ....Report Count (1) 32 124 + 0x81, 0x03, # ....Input (Cnst,Var,Abs) 34 125 + 0x05, 0x01, # ....Usage Page (Generic Desktop) 36 126 + 0x09, 0x30, # ....Usage (X) 38 127 + 0x09, 0x31, # ....Usage (Y) 40 128 + 0x15, 0x81, # ....Logical Minimum (-127) 42 129 + 0x25, 0x7f, # ....Logical Maximum (127) 44 130 + 0x75, 0x08, # ....Report Size (8) 46 131 + 0x95, 0x02, # ....Report Count (2) 48 132 + 0x81, 0x06, # ....Input (Data,Var,Rel) 50 133 + 0xc0, # ...End Collection 52 134 + 0xc0, # ..End Collection 53 135 + 0xc0, # .End Collection 54 136 + ] 137 + # fmt: on 138 + 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 = buttons 145 + if left is None: 146 + left = self.left 147 + if right is None: 148 + right = self.right 149 + if middle is None: 150 + middle = self.middle 151 + else: 152 + left = self.left 153 + right = self.right 154 + middle = self.middle 155 + 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 + 164 + class WheelMouse(ButtonMouse): 165 + # fmt: off 166 + report_descriptor = [ 167 + 0x05, 0x01, # Usage Page (Generic Desktop) 0 168 + 0x09, 0x02, # Usage (Mouse) 2 169 + 0xa1, 0x01, # Collection (Application) 4 170 + 0x05, 0x09, # .Usage Page (Button) 6 171 + 0x19, 0x01, # .Usage Minimum (1) 8 172 + 0x29, 0x03, # .Usage Maximum (3) 10 173 + 0x15, 0x00, # .Logical Minimum (0) 12 174 + 0x25, 0x01, # .Logical Maximum (1) 14 175 + 0x95, 0x03, # .Report Count (3) 16 176 + 0x75, 0x01, # .Report Size (1) 18 177 + 0x81, 0x02, # .Input (Data,Var,Abs) 20 178 + 0x95, 0x01, # .Report Count (1) 22 179 + 0x75, 0x05, # .Report Size (5) 24 180 + 0x81, 0x03, # .Input (Cnst,Var,Abs) 26 181 + 0x05, 0x01, # .Usage Page (Generic Desktop) 28 182 + 0x09, 0x01, # .Usage (Pointer) 30 183 + 0xa1, 0x00, # .Collection (Physical) 32 184 + 0x09, 0x30, # ..Usage (X) 34 185 + 0x09, 0x31, # ..Usage (Y) 36 186 + 0x15, 0x81, # ..Logical Minimum (-127) 38 187 + 0x25, 0x7f, # ..Logical Maximum (127) 40 188 + 0x75, 0x08, # ..Report Size (8) 42 189 + 0x95, 0x02, # ..Report Count (2) 44 190 + 0x81, 0x06, # ..Input (Data,Var,Rel) 46 191 + 0xc0, # .End Collection 48 192 + 0x09, 0x38, # .Usage (Wheel) 49 193 + 0x15, 0x81, # .Logical Minimum (-127) 51 194 + 0x25, 0x7f, # .Logical Maximum (127) 53 195 + 0x75, 0x08, # .Report Size (8) 55 196 + 0x95, 0x01, # .Report Count (1) 57 197 + 0x81, 0x06, # .Input (Data,Var,Rel) 59 198 + 0xc0, # End Collection 61 199 + ] 200 + # fmt: on 201 + 202 + def __init__(self, rdesc=report_descriptor, name=None, input_info=None): 203 + super().__init__(rdesc, name, input_info) 204 + self.wheel_multiplier = 1 205 + 206 + 207 + class TwoWheelMouse(WheelMouse): 208 + # fmt: off 209 + report_descriptor = [ 210 + 0x05, 0x01, # Usage Page (Generic Desktop) 0 211 + 0x09, 0x02, # Usage (Mouse) 2 212 + 0xa1, 0x01, # Collection (Application) 4 213 + 0x09, 0x01, # .Usage (Pointer) 6 214 + 0xa1, 0x00, # .Collection (Physical) 8 215 + 0x05, 0x09, # ..Usage Page (Button) 10 216 + 0x19, 0x01, # ..Usage Minimum (1) 12 217 + 0x29, 0x10, # ..Usage Maximum (16) 14 218 + 0x15, 0x00, # ..Logical Minimum (0) 16 219 + 0x25, 0x01, # ..Logical Maximum (1) 18 220 + 0x95, 0x10, # ..Report Count (16) 20 221 + 0x75, 0x01, # ..Report Size (1) 22 222 + 0x81, 0x02, # ..Input (Data,Var,Abs) 24 223 + 0x05, 0x01, # ..Usage Page (Generic Desktop) 26 224 + 0x16, 0x01, 0x80, # ..Logical Minimum (-32767) 28 225 + 0x26, 0xff, 0x7f, # ..Logical Maximum (32767) 31 226 + 0x75, 0x10, # ..Report Size (16) 34 227 + 0x95, 0x02, # ..Report Count (2) 36 228 + 0x09, 0x30, # ..Usage (X) 38 229 + 0x09, 0x31, # ..Usage (Y) 40 230 + 0x81, 0x06, # ..Input (Data,Var,Rel) 42 231 + 0x15, 0x81, # ..Logical Minimum (-127) 44 232 + 0x25, 0x7f, # ..Logical Maximum (127) 46 233 + 0x75, 0x08, # ..Report Size (8) 48 234 + 0x95, 0x01, # ..Report Count (1) 50 235 + 0x09, 0x38, # ..Usage (Wheel) 52 236 + 0x81, 0x06, # ..Input (Data,Var,Rel) 54 237 + 0x05, 0x0c, # ..Usage Page (Consumer Devices) 56 238 + 0x0a, 0x38, 0x02, # ..Usage (AC Pan) 58 239 + 0x95, 0x01, # ..Report Count (1) 61 240 + 0x81, 0x06, # ..Input (Data,Var,Rel) 63 241 + 0xc0, # .End Collection 65 242 + 0xc0, # End Collection 66 243 + ] 244 + # fmt: on 245 + 246 + def __init__(self, rdesc=report_descriptor, name=None, input_info=None): 247 + super().__init__(rdesc, name, input_info) 248 + self.hwheel_multiplier = 1 249 + 250 + 251 + class MIDongleMIWirelessMouse(TwoWheelMouse): 252 + # fmt: off 253 + 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 Collection 283 + 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 Collection 295 + 0xc0, # End Collection 296 + 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 Collection 321 + ] 322 + # fmt: on 323 + 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_info 328 + ): 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 buttons 333 + # onto 2 distinct reports 334 + 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 rs 342 + 343 + 344 + class ResolutionMultiplierMouse(TwoWheelMouse): 345 + # fmt: off 346 + report_descriptor = [ 347 + 0x05, 0x01, # Usage Page (Generic Desktop) 83 348 + 0x09, 0x02, # Usage (Mouse) 85 349 + 0xa1, 0x01, # Collection (Application) 87 350 + 0x05, 0x01, # .Usage Page (Generic Desktop) 89 351 + 0x09, 0x02, # .Usage (Mouse) 91 352 + 0xa1, 0x02, # .Collection (Logical) 93 353 + 0x85, 0x11, # ..Report ID (17) 95 354 + 0x09, 0x01, # ..Usage (Pointer) 97 355 + 0xa1, 0x00, # ..Collection (Physical) 99 356 + 0x05, 0x09, # ...Usage Page (Button) 101 357 + 0x19, 0x01, # ...Usage Minimum (1) 103 358 + 0x29, 0x03, # ...Usage Maximum (3) 105 359 + 0x95, 0x03, # ...Report Count (3) 107 360 + 0x75, 0x01, # ...Report Size (1) 109 361 + 0x25, 0x01, # ...Logical Maximum (1) 111 362 + 0x81, 0x02, # ...Input (Data,Var,Abs) 113 363 + 0x95, 0x01, # ...Report Count (1) 115 364 + 0x81, 0x01, # ...Input (Cnst,Arr,Abs) 117 365 + 0x09, 0x05, # ...Usage (Vendor Usage 0x05) 119 366 + 0x81, 0x02, # ...Input (Data,Var,Abs) 121 367 + 0x95, 0x03, # ...Report Count (3) 123 368 + 0x81, 0x01, # ...Input (Cnst,Arr,Abs) 125 369 + 0x05, 0x01, # ...Usage Page (Generic Desktop) 127 370 + 0x09, 0x30, # ...Usage (X) 129 371 + 0x09, 0x31, # ...Usage (Y) 131 372 + 0x95, 0x02, # ...Report Count (2) 133 373 + 0x75, 0x08, # ...Report Size (8) 135 374 + 0x15, 0x81, # ...Logical Minimum (-127) 137 375 + 0x25, 0x7f, # ...Logical Maximum (127) 139 376 + 0x81, 0x06, # ...Input (Data,Var,Rel) 141 377 + 0xa1, 0x02, # ...Collection (Logical) 143 378 + 0x85, 0x12, # ....Report ID (18) 145 379 + 0x09, 0x48, # ....Usage (Resolution Multiplier) 147 380 + 0x95, 0x01, # ....Report Count (1) 149 381 + 0x75, 0x02, # ....Report Size (2) 151 382 + 0x15, 0x00, # ....Logical Minimum (0) 153 383 + 0x25, 0x01, # ....Logical Maximum (1) 155 384 + 0x35, 0x01, # ....Physical Minimum (1) 157 385 + 0x45, 0x04, # ....Physical Maximum (4) 159 386 + 0xb1, 0x02, # ....Feature (Data,Var,Abs) 161 387 + 0x35, 0x00, # ....Physical Minimum (0) 163 388 + 0x45, 0x00, # ....Physical Maximum (0) 165 389 + 0x75, 0x06, # ....Report Size (6) 167 390 + 0xb1, 0x01, # ....Feature (Cnst,Arr,Abs) 169 391 + 0x85, 0x11, # ....Report ID (17) 171 392 + 0x09, 0x38, # ....Usage (Wheel) 173 393 + 0x15, 0x81, # ....Logical Minimum (-127) 175 394 + 0x25, 0x7f, # ....Logical Maximum (127) 177 395 + 0x75, 0x08, # ....Report Size (8) 179 396 + 0x81, 0x06, # ....Input (Data,Var,Rel) 181 397 + 0xc0, # ...End Collection 183 398 + 0x05, 0x0c, # ...Usage Page (Consumer Devices) 184 399 + 0x75, 0x08, # ...Report Size (8) 186 400 + 0x0a, 0x38, 0x02, # ...Usage (AC Pan) 188 401 + 0x81, 0x06, # ...Input (Data,Var,Rel) 191 402 + 0xc0, # ..End Collection 193 403 + 0xc0, # .End Collection 194 404 + 0xc0, # End Collection 195 405 + ] 406 + # fmt: on 407 + 408 + def __init__(self, rdesc=report_descriptor, name=None, input_info=None): 409 + super().__init__(rdesc, name, input_info) 410 + self.default_reportID = 0x11 411 + 412 + # Feature Report 12, multiplier Feature value must be set to 0b01, 413 + # i.e. 1. We should extract that from the descriptor instead 414 + # 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 = 4 429 + 430 + return 0 431 + 432 + 433 + class BadResolutionMultiplierMouse(ResolutionMultiplierMouse): 434 + def set_report(self, req, rnum, rtype, data): 435 + super().set_report(req, rnum, rtype, data) 436 + 437 + self.wheel_multiplier = 1 438 + self.hwheel_multiplier = 1 439 + return 32 # EPIPE 440 + 441 + 442 + class ResolutionMultiplierHWheelMouse(TwoWheelMouse): 443 + # fmt: off 444 + report_descriptor = [ 445 + 0x05, 0x01, # Usage Page (Generic Desktop) 0 446 + 0x09, 0x02, # Usage (Mouse) 2 447 + 0xa1, 0x01, # Collection (Application) 4 448 + 0x05, 0x01, # .Usage Page (Generic Desktop) 6 449 + 0x09, 0x02, # .Usage (Mouse) 8 450 + 0xa1, 0x02, # .Collection (Logical) 10 451 + 0x85, 0x1a, # ..Report ID (26) 12 452 + 0x09, 0x01, # ..Usage (Pointer) 14 453 + 0xa1, 0x00, # ..Collection (Physical) 16 454 + 0x05, 0x09, # ...Usage Page (Button) 18 455 + 0x19, 0x01, # ...Usage Minimum (1) 20 456 + 0x29, 0x05, # ...Usage Maximum (5) 22 457 + 0x95, 0x05, # ...Report Count (5) 24 458 + 0x75, 0x01, # ...Report Size (1) 26 459 + 0x15, 0x00, # ...Logical Minimum (0) 28 460 + 0x25, 0x01, # ...Logical Maximum (1) 30 461 + 0x81, 0x02, # ...Input (Data,Var,Abs) 32 462 + 0x75, 0x03, # ...Report Size (3) 34 463 + 0x95, 0x01, # ...Report Count (1) 36 464 + 0x81, 0x01, # ...Input (Cnst,Arr,Abs) 38 465 + 0x05, 0x01, # ...Usage Page (Generic Desktop) 40 466 + 0x09, 0x30, # ...Usage (X) 42 467 + 0x09, 0x31, # ...Usage (Y) 44 468 + 0x95, 0x02, # ...Report Count (2) 46 469 + 0x75, 0x10, # ...Report Size (16) 48 470 + 0x16, 0x01, 0x80, # ...Logical Minimum (-32767) 50 471 + 0x26, 0xff, 0x7f, # ...Logical Maximum (32767) 53 472 + 0x81, 0x06, # ...Input (Data,Var,Rel) 56 473 + 0xa1, 0x02, # ...Collection (Logical) 58 474 + 0x85, 0x12, # ....Report ID (18) 60 475 + 0x09, 0x48, # ....Usage (Resolution Multiplier) 62 476 + 0x95, 0x01, # ....Report Count (1) 64 477 + 0x75, 0x02, # ....Report Size (2) 66 478 + 0x15, 0x00, # ....Logical Minimum (0) 68 479 + 0x25, 0x01, # ....Logical Maximum (1) 70 480 + 0x35, 0x01, # ....Physical Minimum (1) 72 481 + 0x45, 0x0c, # ....Physical Maximum (12) 74 482 + 0xb1, 0x02, # ....Feature (Data,Var,Abs) 76 483 + 0x85, 0x1a, # ....Report ID (26) 78 484 + 0x09, 0x38, # ....Usage (Wheel) 80 485 + 0x35, 0x00, # ....Physical Minimum (0) 82 486 + 0x45, 0x00, # ....Physical Maximum (0) 84 487 + 0x95, 0x01, # ....Report Count (1) 86 488 + 0x75, 0x10, # ....Report Size (16) 88 489 + 0x16, 0x01, 0x80, # ....Logical Minimum (-32767) 90 490 + 0x26, 0xff, 0x7f, # ....Logical Maximum (32767) 93 491 + 0x81, 0x06, # ....Input (Data,Var,Rel) 96 492 + 0xc0, # ...End Collection 98 493 + 0xa1, 0x02, # ...Collection (Logical) 99 494 + 0x85, 0x12, # ....Report ID (18) 101 495 + 0x09, 0x48, # ....Usage (Resolution Multiplier) 103 496 + 0x75, 0x02, # ....Report Size (2) 105 497 + 0x15, 0x00, # ....Logical Minimum (0) 107 498 + 0x25, 0x01, # ....Logical Maximum (1) 109 499 + 0x35, 0x01, # ....Physical Minimum (1) 111 500 + 0x45, 0x0c, # ....Physical Maximum (12) 113 501 + 0xb1, 0x02, # ....Feature (Data,Var,Abs) 115 502 + 0x35, 0x00, # ....Physical Minimum (0) 117 503 + 0x45, 0x00, # ....Physical Maximum (0) 119 504 + 0x75, 0x04, # ....Report Size (4) 121 505 + 0xb1, 0x01, # ....Feature (Cnst,Arr,Abs) 123 506 + 0x85, 0x1a, # ....Report ID (26) 125 507 + 0x05, 0x0c, # ....Usage Page (Consumer Devices) 127 508 + 0x95, 0x01, # ....Report Count (1) 129 509 + 0x75, 0x10, # ....Report Size (16) 131 510 + 0x16, 0x01, 0x80, # ....Logical Minimum (-32767) 133 511 + 0x26, 0xff, 0x7f, # ....Logical Maximum (32767) 136 512 + 0x0a, 0x38, 0x02, # ....Usage (AC Pan) 139 513 + 0x81, 0x06, # ....Input (Data,Var,Rel) 142 514 + 0xc0, # ...End Collection 144 515 + 0xc0, # ..End Collection 145 516 + 0xc0, # .End Collection 146 517 + 0xc0, # End Collection 147 518 + ] 519 + # fmt: on 520 + 521 + def __init__(self, rdesc=report_descriptor, name=None, input_info=None): 522 + super().__init__(rdesc, name, input_info) 523 + self.default_reportID = 0x1A 524 + 525 + # Feature Report 12, multiplier Feature value must be set to 0b0101, 526 + # i.e. 5. We should extract that from the descriptor instead 527 + # 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 = 12 534 + self.hwheel_multiplier = 12 535 + 536 + return 0 537 + 538 + 539 + class BaseTest: 540 + class TestMouse(base.BaseTestCase.TestUhid): 541 + def test_buttons(self): 542 + """check for button reliability.""" 543 + uhdev = self.uhdev 544 + evdev = uhdev.get_evdev() 545 + syn_event = self.syn_event 546 + 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] == 1 553 + 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] == 0 560 + 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] == 1 567 + 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] == 0 574 + 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] == 1 581 + 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] == 0 588 + 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), events 596 + ) 597 + assert evdev.value[libevdev.EV_KEY.BTN_RIGHT] == 1 598 + assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 1 599 + 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] == 1 606 + assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 0 607 + 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] == 0 614 + assert evdev.value[libevdev.EV_KEY.BTN_LEFT] == 0 615 + 616 + def test_relative(self): 617 + """Check for relative events.""" 618 + uhdev = self.uhdev 619 + 620 + syn_event = self.syn_event 621 + 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), events 641 + ) 642 + 643 + 644 + class 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 the 650 + reports according to the report descriptors. 651 + No kernel device is used here""" 652 + uhdev = self.uhdev 653 + 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 + 683 + class 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.uhdev 694 + 695 + # check if the kernel is high res wheel compatible 696 + high_res_wheel = self.is_wheel_highres(uhdev) 697 + 698 + syn_event = self.syn_event 699 + # The Resolution Multiplier is applied to the HID reports, so we 700 + # need to pre-multiply too. 701 + mult = uhdev.wheel_multiplier 702 + 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 + 733 + class 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.uhdev 744 + 745 + # check if the kernel is high res wheel compatible 746 + high_res_wheel = self.is_wheel_highres(uhdev) 747 + high_res_hwheel = self.is_hwheel_highres(uhdev) 748 + assert high_res_wheel == high_res_hwheel 749 + 750 + syn_event = self.syn_event 751 + # The Resolution Multiplier is applied to the HID reports, so we 752 + # need to pre-multiply too. 753 + hmult = uhdev.hwheel_multiplier 754 + vmult = uhdev.wheel_multiplier 755 + 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 + 802 + class 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 feature 812 + assert uhdev.wheel_multiplier == 1 813 + 814 + return high_res 815 + 816 + def test_resolution_multiplier_wheel(self): 817 + uhdev = self.uhdev 818 + 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 > 1 823 + assert 120 % uhdev.wheel_multiplier == 0 824 + 825 + def test_wheel_with_multiplier(self): 826 + uhdev = self.uhdev 827 + 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 > 1 832 + 833 + syn_event = self.syn_event 834 + mult = uhdev.wheel_multiplier 835 + 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 + 874 + class 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 == 1 882 + 883 + return high_res 884 + 885 + def test_resolution_multiplier_wheel(self): 886 + uhdev = self.uhdev 887 + 888 + assert uhdev.wheel_multiplier == 1 889 + 890 + 891 + class 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 feature 901 + assert uhdev.hwheel_multiplier == 1 902 + 903 + return high_res 904 + 905 + def test_resolution_multiplier_ac_pan(self): 906 + uhdev = self.uhdev 907 + 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 > 1 912 + assert 120 % uhdev.hwheel_multiplier == 0 913 + 914 + def test_ac_pan_with_multiplier(self): 915 + uhdev = self.uhdev 916 + 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 > 1 921 + 922 + syn_event = self.syn_event 923 + hmult = uhdev.hwheel_multiplier 924 + 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 + 963 + class 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 two 969 + # 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 the 975 + # assert below print out the real error 976 + pass 977 + assert remaining == []