1#!/bin/env python3 2# -*- coding: utf-8 -*- 3# 4# Copyright (c) 2017 Benjamin Tissoires <benjamin.tissoires@gmail.com> 5# Copyright (c) 2017 Red Hat, Inc. 6# 7# This program is free software: you can redistribute it and/or modify 8# it under the terms of the GNU General Public License as published by 9# the Free Software Foundation; either version 2 of the License, or 10# (at your option) any later version. 11# 12# This program is distributed in the hope that it will be useful, 13# but WITHOUT ANY WARRANTY; without even the implied warranty of 14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15# GNU General Public License for more details. 16# 17# You should have received a copy of the GNU General Public License 18# along with this program. If not, see <http://www.gnu.org/licenses/>. 19# 20 21import base 22import libevdev 23import sys 24import hidtools.hid 25import unittest 26from base import main, setUpModule, tearDownModule # noqa 27 28import logging 29logger = logging.getLogger('hidtools.test.mouse') 30 31 32class InvalidHIDCommunication(Exception): 33 pass 34 35 36class MouseData(object): 37 pass 38 39 40class BaseMouse(base.UHIDTestDevice): 41 def __init__(self, rdesc, name=None, info=None): 42 assert rdesc is not None 43 super().__init__(name, 'Mouse', info=info, rdesc=rdesc) 44 self.left = False 45 self.right = False 46 self.middle = False 47 48 def create_report(self, x, y, buttons=None, wheels=None, reportID=None): 49 """ 50 Return an input report for this device. 51 52 :param x: relative x 53 :param y: relative y 54 :param buttons: a (l, r, m) tuple of bools for the button states, 55 where ``None`` is "leave unchanged" 56 :param wheels: a single value for the vertical wheel or a (vertical, horizontal) tuple for 57 the two wheels 58 :param reportID: the numeric report ID for this report, if needed 59 """ 60 if buttons is not None: 61 l, r, m = buttons 62 if l is not None: 63 self.left = l 64 if r is not None: 65 self.right = r 66 if m is not None: 67 self.middle = m 68 left = self.left 69 right = self.right 70 middle = self.middle 71 # Note: the BaseMouse doesn't actually have a wheel but the 72 # create_report magic only fills in those fields exist, so let's 73 # make this generic here. 74 wheel, acpan = 0, 0 75 if wheels is not None: 76 if isinstance(wheels, tuple): 77 wheel = wheels[0] 78 acpan = wheels[1] 79 else: 80 wheel = wheels 81 82 reportID = reportID or self.default_reportID 83 84 mouse = MouseData() 85 mouse.b1 = int(left) 86 mouse.b2 = int(right) 87 mouse.b3 = int(middle) 88 mouse.x = x 89 mouse.y = y 90 mouse.wheel = wheel 91 mouse.acpan = acpan 92 return super().create_report(mouse, reportID=reportID) 93 94 def event(self, x, y, buttons=None, wheels=None): 95 """ 96 Send an input event on the default report ID. 97 98 :param x: relative x 99 :param y: relative y 100 :param buttons: a (l, r, m) tuple of bools for the button states, 101 where ``None`` is "leave unchanged" 102 :param wheels: a single value for the vertical wheel or a (vertical, horizontal) tuple for 103 the two wheels 104 """ 105 r = self.create_report(x, y, buttons, wheels) 106 self.call_input_event(r) 107 return [r] 108 109 110class ButtonMouse(BaseMouse): 111 report_descriptor = [ 112 0x05, 0x01, # .Usage Page (Generic Desktop) 0 113 0x09, 0x02, # .Usage (Mouse) 2 114 0xa1, 0x01, # .Collection (Application) 4 115 0x09, 0x02, # ..Usage (Mouse) 6 116 0xa1, 0x02, # ..Collection (Logical) 8 117 0x09, 0x01, # ...Usage (Pointer) 10 118 0xa1, 0x00, # ...Collection (Physical) 12 119 0x05, 0x09, # ....Usage Page (Button) 14 120 0x19, 0x01, # ....Usage Minimum (1) 16 121 0x29, 0x03, # ....Usage Maximum (3) 18 122 0x15, 0x00, # ....Logical Minimum (0) 20 123 0x25, 0x01, # ....Logical Maximum (1) 22 124 0x75, 0x01, # ....Report Size (1) 24 125 0x95, 0x03, # ....Report Count (3) 26 126 0x81, 0x02, # ....Input (Data,Var,Abs) 28 127 0x75, 0x05, # ....Report Size (5) 30 128 0x95, 0x01, # ....Report Count (1) 32 129 0x81, 0x03, # ....Input (Cnst,Var,Abs) 34 130 0x05, 0x01, # ....Usage Page (Generic Desktop) 36 131 0x09, 0x30, # ....Usage (X) 38 132 0x09, 0x31, # ....Usage (Y) 40 133 0x15, 0x81, # ....Logical Minimum (-127) 42 134 0x25, 0x7f, # ....Logical Maximum (127) 44 135 0x75, 0x08, # ....Report Size (8) 46 136 0x95, 0x02, # ....Report Count (2) 48 137 0x81, 0x06, # ....Input (Data,Var,Rel) 50 138 0xc0, # ...End Collection 52 139 0xc0, # ..End Collection 53 140 0xc0, # .End Collection 54 141 ] 142 143 def __init__(self, rdesc=report_descriptor, name=None, info=None): 144 super().__init__(rdesc, name, info) 145 146 def fake_report(self, x, y, buttons): 147 if buttons is not None: 148 left, right, middle = buttons 149 if left is None: 150 left = self.left 151 if right is None: 152 right = self.right 153 if middle is None: 154 middle = self.middle 155 else: 156 left = self.left 157 right = self.right 158 middle = self.middle 159 160 button_mask = sum(1 << i for i, b in enumerate([left, right, middle]) if b) 161 x = max(-127, min(127, x)) 162 y = max(-127, min(127, y)) 163 x = base.to_twos_comp(x, 8) 164 y = base.to_twos_comp(y, 8) 165 return [button_mask, x, y] 166 167 168class WheelMouse(ButtonMouse): 169 report_descriptor = [ 170 0x05, 0x01, # Usage Page (Generic Desktop) 0 171 0x09, 0x02, # Usage (Mouse) 2 172 0xa1, 0x01, # Collection (Application) 4 173 0x05, 0x09, # .Usage Page (Button) 6 174 0x19, 0x01, # .Usage Minimum (1) 8 175 0x29, 0x03, # .Usage Maximum (3) 10 176 0x15, 0x00, # .Logical Minimum (0) 12 177 0x25, 0x01, # .Logical Maximum (1) 14 178 0x95, 0x03, # .Report Count (3) 16 179 0x75, 0x01, # .Report Size (1) 18 180 0x81, 0x02, # .Input (Data,Var,Abs) 20 181 0x95, 0x01, # .Report Count (1) 22 182 0x75, 0x05, # .Report Size (5) 24 183 0x81, 0x03, # .Input (Cnst,Var,Abs) 26 184 0x05, 0x01, # .Usage Page (Generic Desktop) 28 185 0x09, 0x01, # .Usage (Pointer) 30 186 0xa1, 0x00, # .Collection (Physical) 32 187 0x09, 0x30, # ..Usage (X) 34 188 0x09, 0x31, # ..Usage (Y) 36 189 0x15, 0x81, # ..Logical Minimum (-127) 38 190 0x25, 0x7f, # ..Logical Maximum (127) 40 191 0x75, 0x08, # ..Report Size (8) 42 192 0x95, 0x02, # ..Report Count (2) 44 193 0x81, 0x06, # ..Input (Data,Var,Rel) 46 194 0xc0, # .End Collection 48 195 0x09, 0x38, # .Usage (Wheel) 49 196 0x15, 0x81, # .Logical Minimum (-127) 51 197 0x25, 0x7f, # .Logical Maximum (127) 53 198 0x75, 0x08, # .Report Size (8) 55 199 0x95, 0x01, # .Report Count (1) 57 200 0x81, 0x06, # .Input (Data,Var,Rel) 59 201 0xc0, # End Collection 61 202 ] 203 204 def __init__(self, rdesc=report_descriptor, name=None, info=None): 205 super().__init__(rdesc, name, info) 206 self.wheel_multiplier = 1 207 208 209class TwoWheelMouse(WheelMouse): 210 report_descriptor = [ 211 0x05, 0x01, # Usage Page (Generic Desktop) 0 212 0x09, 0x02, # Usage (Mouse) 2 213 0xa1, 0x01, # Collection (Application) 4 214 0x09, 0x01, # .Usage (Pointer) 6 215 0xa1, 0x00, # .Collection (Physical) 8 216 0x05, 0x09, # ..Usage Page (Button) 10 217 0x19, 0x01, # ..Usage Minimum (1) 12 218 0x29, 0x10, # ..Usage Maximum (16) 14 219 0x15, 0x00, # ..Logical Minimum (0) 16 220 0x25, 0x01, # ..Logical Maximum (1) 18 221 0x95, 0x10, # ..Report Count (16) 20 222 0x75, 0x01, # ..Report Size (1) 22 223 0x81, 0x02, # ..Input (Data,Var,Abs) 24 224 0x05, 0x01, # ..Usage Page (Generic Desktop) 26 225 0x16, 0x01, 0x80, # ..Logical Minimum (-32767) 28 226 0x26, 0xff, 0x7f, # ..Logical Maximum (32767) 31 227 0x75, 0x10, # ..Report Size (16) 34 228 0x95, 0x02, # ..Report Count (2) 36 229 0x09, 0x30, # ..Usage (X) 38 230 0x09, 0x31, # ..Usage (Y) 40 231 0x81, 0x06, # ..Input (Data,Var,Rel) 42 232 0x15, 0x81, # ..Logical Minimum (-127) 44 233 0x25, 0x7f, # ..Logical Maximum (127) 46 234 0x75, 0x08, # ..Report Size (8) 48 235 0x95, 0x01, # ..Report Count (1) 50 236 0x09, 0x38, # ..Usage (Wheel) 52 237 0x81, 0x06, # ..Input (Data,Var,Rel) 54 238 0x05, 0x0c, # ..Usage Page (Consumer Devices) 56 239 0x0a, 0x38, 0x02, # ..Usage (AC Pan) 58 240 0x95, 0x01, # ..Report Count (1) 61 241 0x81, 0x06, # ..Input (Data,Var,Rel) 63 242 0xc0, # .End Collection 65 243 0xc0, # End Collection 66 244 ] 245 246 def __init__(self, rdesc=report_descriptor, name=None, info=None): 247 super().__init__(rdesc, name, info) 248 self.hwheel_multiplier = 1 249 250 251class MIDongleMIWirelessMouse(TwoWheelMouse): 252 report_descriptor = [ 253 0x05, 0x01, # Usage Page (Generic Desktop) 254 0x09, 0x02, # Usage (Mouse) 255 0xa1, 0x01, # Collection (Application) 256 0x85, 0x01, # .Report ID (1) 257 0x09, 0x01, # .Usage (Pointer) 258 0xa1, 0x00, # .Collection (Physical) 259 0x95, 0x05, # ..Report Count (5) 260 0x75, 0x01, # ..Report Size (1) 261 0x05, 0x09, # ..Usage Page (Button) 262 0x19, 0x01, # ..Usage Minimum (1) 263 0x29, 0x05, # ..Usage Maximum (5) 264 0x15, 0x00, # ..Logical Minimum (0) 265 0x25, 0x01, # ..Logical Maximum (1) 266 0x81, 0x02, # ..Input (Data,Var,Abs) 267 0x95, 0x01, # ..Report Count (1) 268 0x75, 0x03, # ..Report Size (3) 269 0x81, 0x01, # ..Input (Cnst,Arr,Abs) 270 0x75, 0x08, # ..Report Size (8) 271 0x95, 0x01, # ..Report Count (1) 272 0x05, 0x01, # ..Usage Page (Generic Desktop) 273 0x09, 0x38, # ..Usage (Wheel) 274 0x15, 0x81, # ..Logical Minimum (-127) 275 0x25, 0x7f, # ..Logical Maximum (127) 276 0x81, 0x06, # ..Input (Data,Var,Rel) 277 0x05, 0x0c, # ..Usage Page (Consumer Devices) 278 0x0a, 0x38, 0x02, # ..Usage (AC Pan) 279 0x95, 0x01, # ..Report Count (1) 280 0x81, 0x06, # ..Input (Data,Var,Rel) 281 0xc0, # .End Collection 282 0x85, 0x02, # .Report ID (2) 283 0x09, 0x01, # .Usage (Consumer Control) 284 0xa1, 0x00, # .Collection (Physical) 285 0x75, 0x0c, # ..Report Size (12) 286 0x95, 0x02, # ..Report Count (2) 287 0x05, 0x01, # ..Usage Page (Generic Desktop) 288 0x09, 0x30, # ..Usage (X) 289 0x09, 0x31, # ..Usage (Y) 290 0x16, 0x01, 0xf8, # ..Logical Minimum (-2047) 291 0x26, 0xff, 0x07, # ..Logical Maximum (2047) 292 0x81, 0x06, # ..Input (Data,Var,Rel) 293 0xc0, # .End Collection 294 0xc0, # End Collection 295 0x05, 0x0c, # Usage Page (Consumer Devices) 296 0x09, 0x01, # Usage (Consumer Control) 297 0xa1, 0x01, # Collection (Application) 298 0x85, 0x03, # .Report ID (3) 299 0x15, 0x00, # .Logical Minimum (0) 300 0x25, 0x01, # .Logical Maximum (1) 301 0x75, 0x01, # .Report Size (1) 302 0x95, 0x01, # .Report Count (1) 303 0x09, 0xcd, # .Usage (Play/Pause) 304 0x81, 0x06, # .Input (Data,Var,Rel) 305 0x0a, 0x83, 0x01, # .Usage (AL Consumer Control Config) 306 0x81, 0x06, # .Input (Data,Var,Rel) 307 0x09, 0xb5, # .Usage (Scan Next Track) 308 0x81, 0x06, # .Input (Data,Var,Rel) 309 0x09, 0xb6, # .Usage (Scan Previous Track) 310 0x81, 0x06, # .Input (Data,Var,Rel) 311 0x09, 0xea, # .Usage (Volume Down) 312 0x81, 0x06, # .Input (Data,Var,Rel) 313 0x09, 0xe9, # .Usage (Volume Up) 314 0x81, 0x06, # .Input (Data,Var,Rel) 315 0x0a, 0x25, 0x02, # .Usage (AC Forward) 316 0x81, 0x06, # .Input (Data,Var,Rel) 317 0x0a, 0x24, 0x02, # .Usage (AC Back) 318 0x81, 0x06, # .Input (Data,Var,Rel) 319 0xc0, # End Collection 320 ] 321 device_info = (0x3, 0x2717, 0x003b) 322 device_name = "uhid test MI Dongle MI Wireless Mouse" 323 324 def __init__(self, rdesc=report_descriptor, name=device_name, info=device_info): 325 super().__init__(rdesc, name, info) 326 327 def event(self, x, y, buttons=None, wheels=None): 328 # this mouse spreads the relative pointer and the mouse buttons 329 # onto 2 distinct reports 330 rs = [] 331 r = self.create_report(x, y, buttons, wheels, reportID=1) 332 self.call_input_event(r) 333 rs.append(r) 334 r = self.create_report(x, y, buttons, reportID=2) 335 self.call_input_event(r) 336 rs.append(r) 337 return rs 338 339 340class ResolutionMultiplierMouse(TwoWheelMouse): 341 report_descriptor = [ 342 0x05, 0x01, # Usage Page (Generic Desktop) 83 343 0x09, 0x02, # Usage (Mouse) 85 344 0xa1, 0x01, # Collection (Application) 87 345 0x05, 0x01, # .Usage Page (Generic Desktop) 89 346 0x09, 0x02, # .Usage (Mouse) 91 347 0xa1, 0x02, # .Collection (Logical) 93 348 0x85, 0x11, # ..Report ID (17) 95 349 0x09, 0x01, # ..Usage (Pointer) 97 350 0xa1, 0x00, # ..Collection (Physical) 99 351 0x05, 0x09, # ...Usage Page (Button) 101 352 0x19, 0x01, # ...Usage Minimum (1) 103 353 0x29, 0x03, # ...Usage Maximum (3) 105 354 0x95, 0x03, # ...Report Count (3) 107 355 0x75, 0x01, # ...Report Size (1) 109 356 0x25, 0x01, # ...Logical Maximum (1) 111 357 0x81, 0x02, # ...Input (Data,Var,Abs) 113 358 0x95, 0x01, # ...Report Count (1) 115 359 0x81, 0x01, # ...Input (Cnst,Arr,Abs) 117 360 0x09, 0x05, # ...Usage (Vendor Usage 0x05) 119 361 0x81, 0x02, # ...Input (Data,Var,Abs) 121 362 0x95, 0x03, # ...Report Count (3) 123 363 0x81, 0x01, # ...Input (Cnst,Arr,Abs) 125 364 0x05, 0x01, # ...Usage Page (Generic Desktop) 127 365 0x09, 0x30, # ...Usage (X) 129 366 0x09, 0x31, # ...Usage (Y) 131 367 0x95, 0x02, # ...Report Count (2) 133 368 0x75, 0x08, # ...Report Size (8) 135 369 0x15, 0x81, # ...Logical Minimum (-127) 137 370 0x25, 0x7f, # ...Logical Maximum (127) 139 371 0x81, 0x06, # ...Input (Data,Var,Rel) 141 372 0xa1, 0x02, # ...Collection (Logical) 143 373 0x85, 0x12, # ....Report ID (18) 145 374 0x09, 0x48, # ....Usage (Resolution Multiplier) 147 375 0x95, 0x01, # ....Report Count (1) 149 376 0x75, 0x02, # ....Report Size (2) 151 377 0x15, 0x00, # ....Logical Minimum (0) 153 378 0x25, 0x01, # ....Logical Maximum (1) 155 379 0x35, 0x01, # ....Physical Minimum (1) 157 380 0x45, 0x04, # ....Physical Maximum (4) 159 381 0xb1, 0x02, # ....Feature (Data,Var,Abs) 161 382 0x35, 0x00, # ....Physical Minimum (0) 163 383 0x45, 0x00, # ....Physical Maximum (0) 165 384 0x75, 0x06, # ....Report Size (6) 167 385 0xb1, 0x01, # ....Feature (Cnst,Arr,Abs) 169 386 0x85, 0x11, # ....Report ID (17) 171 387 0x09, 0x38, # ....Usage (Wheel) 173 388 0x15, 0x81, # ....Logical Minimum (-127) 175 389 0x25, 0x7f, # ....Logical Maximum (127) 177 390 0x75, 0x08, # ....Report Size (8) 179 391 0x81, 0x06, # ....Input (Data,Var,Rel) 181 392 0xc0, # ...End Collection 183 393 0x05, 0x0c, # ...Usage Page (Consumer Devices) 184 394 0x75, 0x08, # ...Report Size (8) 186 395 0x0a, 0x38, 0x02, # ...Usage (AC Pan) 188 396 0x81, 0x06, # ...Input (Data,Var,Rel) 191 397 0xc0, # ..End Collection 193 398 0xc0, # .End Collection 194 399 0xc0, # End Collection 195 400 ] 401 402 def __init__(self, rdesc=report_descriptor, name=None, info=None): 403 super().__init__(rdesc, name, info) 404 self.default_reportID = 0x11 405 406 # Feature Report 12, multiplier Feature value must be set to 0b01, 407 # i.e. 1. We should extract that from the descriptor instead 408 # of hardcoding it here, but meanwhile this will do. 409 self.set_feature_report = [0x12, 0x1] 410 411 def set_report(self, req, rnum, rtype, data): 412 if rtype != self.UHID_FEATURE_REPORT: 413 raise InvalidHIDCommunication(f'Unexpected report type: {rtype}') 414 if rnum != 0x12: 415 raise InvalidHIDCommunication(f'Unexpected report number: {rnum}') 416 417 if data != self.set_feature_report: 418 raise InvalidHIDCommunication(f'Unexpected data: {data}, expected {self.set_feature_report}') 419 420 self.wheel_multiplier = 4 421 422 return 0 423 424 425class ResolutionMultiplierHWheelMouse(TwoWheelMouse): 426 report_descriptor = [ 427 0x05, 0x01, # Usage Page (Generic Desktop) 0 428 0x09, 0x02, # Usage (Mouse) 2 429 0xa1, 0x01, # Collection (Application) 4 430 0x05, 0x01, # .Usage Page (Generic Desktop) 6 431 0x09, 0x02, # .Usage (Mouse) 8 432 0xa1, 0x02, # .Collection (Logical) 10 433 0x85, 0x1a, # ..Report ID (26) 12 434 0x09, 0x01, # ..Usage (Pointer) 14 435 0xa1, 0x00, # ..Collection (Physical) 16 436 0x05, 0x09, # ...Usage Page (Button) 18 437 0x19, 0x01, # ...Usage Minimum (1) 20 438 0x29, 0x05, # ...Usage Maximum (5) 22 439 0x95, 0x05, # ...Report Count (5) 24 440 0x75, 0x01, # ...Report Size (1) 26 441 0x15, 0x00, # ...Logical Minimum (0) 28 442 0x25, 0x01, # ...Logical Maximum (1) 30 443 0x81, 0x02, # ...Input (Data,Var,Abs) 32 444 0x75, 0x03, # ...Report Size (3) 34 445 0x95, 0x01, # ...Report Count (1) 36 446 0x81, 0x01, # ...Input (Cnst,Arr,Abs) 38 447 0x05, 0x01, # ...Usage Page (Generic Desktop) 40 448 0x09, 0x30, # ...Usage (X) 42 449 0x09, 0x31, # ...Usage (Y) 44 450 0x95, 0x02, # ...Report Count (2) 46 451 0x75, 0x10, # ...Report Size (16) 48 452 0x16, 0x01, 0x80, # ...Logical Minimum (-32767) 50 453 0x26, 0xff, 0x7f, # ...Logical Maximum (32767) 53 454 0x81, 0x06, # ...Input (Data,Var,Rel) 56 455 0xa1, 0x02, # ...Collection (Logical) 58 456 0x85, 0x12, # ....Report ID (18) 60 457 0x09, 0x48, # ....Usage (Resolution Multiplier) 62 458 0x95, 0x01, # ....Report Count (1) 64 459 0x75, 0x02, # ....Report Size (2) 66 460 0x15, 0x00, # ....Logical Minimum (0) 68 461 0x25, 0x01, # ....Logical Maximum (1) 70 462 0x35, 0x01, # ....Physical Minimum (1) 72 463 0x45, 0x0c, # ....Physical Maximum (12) 74 464 0xb1, 0x02, # ....Feature (Data,Var,Abs) 76 465 0x85, 0x1a, # ....Report ID (26) 78 466 0x09, 0x38, # ....Usage (Wheel) 80 467 0x35, 0x00, # ....Physical Minimum (0) 82 468 0x45, 0x00, # ....Physical Maximum (0) 84 469 0x95, 0x01, # ....Report Count (1) 86 470 0x75, 0x10, # ....Report Size (16) 88 471 0x16, 0x01, 0x80, # ....Logical Minimum (-32767) 90 472 0x26, 0xff, 0x7f, # ....Logical Maximum (32767) 93 473 0x81, 0x06, # ....Input (Data,Var,Rel) 96 474 0xc0, # ...End Collection 98 475 0xa1, 0x02, # ...Collection (Logical) 99 476 0x85, 0x12, # ....Report ID (18) 101 477 0x09, 0x48, # ....Usage (Resolution Multiplier) 103 478 0x75, 0x02, # ....Report Size (2) 105 479 0x15, 0x00, # ....Logical Minimum (0) 107 480 0x25, 0x01, # ....Logical Maximum (1) 109 481 0x35, 0x01, # ....Physical Minimum (1) 111 482 0x45, 0x0c, # ....Physical Maximum (12) 113 483 0xb1, 0x02, # ....Feature (Data,Var,Abs) 115 484 0x35, 0x00, # ....Physical Minimum (0) 117 485 0x45, 0x00, # ....Physical Maximum (0) 119 486 0x75, 0x04, # ....Report Size (4) 121 487 0xb1, 0x01, # ....Feature (Cnst,Arr,Abs) 123 488 0x85, 0x1a, # ....Report ID (26) 125 489 0x05, 0x0c, # ....Usage Page (Consumer Devices) 127 490 0x95, 0x01, # ....Report Count (1) 129 491 0x75, 0x10, # ....Report Size (16) 131 492 0x16, 0x01, 0x80, # ....Logical Minimum (-32767) 133 493 0x26, 0xff, 0x7f, # ....Logical Maximum (32767) 136 494 0x0a, 0x38, 0x02, # ....Usage (AC Pan) 139 495 0x81, 0x06, # ....Input (Data,Var,Rel) 142 496 0xc0, # ...End Collection 144 497 0xc0, # ..End Collection 145 498 0xc0, # .End Collection 146 499 0xc0, # End Collection 147 500 ] 501 502 def __init__(self, rdesc=report_descriptor, name=None, info=None): 503 super().__init__(rdesc, name, info) 504 self.default_reportID = 0x1a 505 506 # Feature Report 12, multiplier Feature value must be set to 0b0101, 507 # i.e. 5. We should extract that from the descriptor instead 508 # of hardcoding it here, but meanwhile this will do. 509 self.set_feature_report = [0x12, 0x5] 510 511 def set_report(self, req, rnum, rtype, data): 512 super().set_report(req, rnum, rtype, data) 513 514 self.wheel_multiplier = 12 515 self.hwheel_multiplier = 12 516 517 return 0 518 519 520class BaseTest: 521 class TestMouse(base.BaseTestCase.TestUhid): 522 def test_buttons(self): 523 """check for button reliability.""" 524 uhdev = self.uhdev 525 syn_event = self.syn_event 526 527 r = uhdev.event(0, 0, (None, True, None)) 528 expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 1) 529 events = uhdev.next_sync_events() 530 self.debug_reports(r, uhdev, events) 531 self.assertInputEventsIn((syn_event, expected_event), events) 532 self.assertEqual(uhdev.evdev.value[libevdev.EV_KEY.BTN_RIGHT], 1) 533 534 r = uhdev.event(0, 0, (None, False, None)) 535 expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 0) 536 events = uhdev.next_sync_events() 537 self.debug_reports(r, uhdev, events) 538 self.assertInputEventsIn((syn_event, expected_event), events) 539 self.assertEqual(uhdev.evdev.value[libevdev.EV_KEY.BTN_RIGHT], 0) 540 541 r = uhdev.event(0, 0, (None, None, True)) 542 expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_MIDDLE, 1) 543 events = uhdev.next_sync_events() 544 self.debug_reports(r, uhdev, events) 545 self.assertInputEventsIn((syn_event, expected_event), events) 546 self.assertEqual(uhdev.evdev.value[libevdev.EV_KEY.BTN_MIDDLE], 1) 547 548 r = uhdev.event(0, 0, (None, None, False)) 549 expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_MIDDLE, 0) 550 events = uhdev.next_sync_events() 551 self.debug_reports(r, uhdev, events) 552 self.assertInputEventsIn((syn_event, expected_event), events) 553 self.assertEqual(uhdev.evdev.value[libevdev.EV_KEY.BTN_MIDDLE], 0) 554 555 r = uhdev.event(0, 0, (True, None, None)) 556 expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 1) 557 events = uhdev.next_sync_events() 558 self.debug_reports(r, uhdev, events) 559 self.assertInputEventsIn((syn_event, expected_event), events) 560 self.assertEqual(uhdev.evdev.value[libevdev.EV_KEY.BTN_LEFT], 1) 561 562 r = uhdev.event(0, 0, (False, None, None)) 563 expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 0) 564 events = uhdev.next_sync_events() 565 self.debug_reports(r, uhdev, events) 566 self.assertInputEventsIn((syn_event, expected_event), events) 567 self.assertEqual(uhdev.evdev.value[libevdev.EV_KEY.BTN_LEFT], 0) 568 569 r = uhdev.event(0, 0, (True, True, None)) 570 expected_event0 = libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 1) 571 expected_event1 = libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 1) 572 events = uhdev.next_sync_events() 573 self.debug_reports(r, uhdev, events) 574 self.assertInputEventsIn((syn_event, expected_event0, expected_event1), events) 575 self.assertEqual(uhdev.evdev.value[libevdev.EV_KEY.BTN_RIGHT], 1) 576 self.assertEqual(uhdev.evdev.value[libevdev.EV_KEY.BTN_LEFT], 1) 577 578 r = uhdev.event(0, 0, (False, None, None)) 579 expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_LEFT, 0) 580 events = uhdev.next_sync_events() 581 self.debug_reports(r, uhdev, events) 582 self.assertInputEventsIn((syn_event, expected_event), events) 583 self.assertEqual(uhdev.evdev.value[libevdev.EV_KEY.BTN_RIGHT], 1) 584 self.assertEqual(uhdev.evdev.value[libevdev.EV_KEY.BTN_LEFT], 0) 585 586 r = uhdev.event(0, 0, (None, False, None)) 587 expected_event = libevdev.InputEvent(libevdev.EV_KEY.BTN_RIGHT, 0) 588 events = uhdev.next_sync_events() 589 self.debug_reports(r, uhdev, events) 590 self.assertInputEventsIn((syn_event, expected_event), events) 591 self.assertEqual(uhdev.evdev.value[libevdev.EV_KEY.BTN_RIGHT], 0) 592 self.assertEqual(uhdev.evdev.value[libevdev.EV_KEY.BTN_LEFT], 0) 593 594 def test_relative(self): 595 """Check for relative events.""" 596 uhdev = self.uhdev 597 598 syn_event = self.syn_event 599 600 r = uhdev.event(0, -1) 601 expected_event = libevdev.InputEvent(libevdev.EV_REL.REL_Y, -1) 602 events = uhdev.next_sync_events() 603 self.debug_reports(r, uhdev, events) 604 self.assertInputEvents((syn_event, expected_event), events) 605 606 r = uhdev.event(1, 0) 607 expected_event = libevdev.InputEvent(libevdev.EV_REL.REL_X, 1) 608 events = uhdev.next_sync_events() 609 self.debug_reports(r, uhdev, events) 610 self.assertInputEvents((syn_event, expected_event), events) 611 612 r = uhdev.event(-1, 2) 613 expected_event0 = libevdev.InputEvent(libevdev.EV_REL.REL_X, -1) 614 expected_event1 = libevdev.InputEvent(libevdev.EV_REL.REL_Y, 2) 615 events = uhdev.next_sync_events() 616 self.debug_reports(r, uhdev, events) 617 self.assertInputEvents((syn_event, expected_event0, expected_event1), events) 618 619 620class TestSimpleMouse(BaseTest.TestMouse): 621 def create_device(self): 622 return ButtonMouse() 623 624 def test_rdesc(self): 625 """Check that the testsuite actually manages to format the 626 reports according to the report descriptors. 627 No kernel device is used here""" 628 uhdev = self.uhdev 629 630 event = (0, 0, (None, None, None)) 631 self.assertEqual(uhdev.fake_report(*event), 632 uhdev.create_report(*event)) 633 634 event = (0, 0, (None, True, None)) 635 self.assertEqual(uhdev.fake_report(*event), 636 uhdev.create_report(*event)) 637 638 event = (0, 0, (True, True, None)) 639 self.assertEqual(uhdev.fake_report(*event), 640 uhdev.create_report(*event)) 641 642 event = (0, 0, (False, False, False)) 643 self.assertEqual(uhdev.fake_report(*event), 644 uhdev.create_report(*event)) 645 646 event = (1, 0, (True, False, True)) 647 self.assertEqual(uhdev.fake_report(*event), 648 uhdev.create_report(*event)) 649 650 event = (-1, 0, (True, False, True)) 651 self.assertEqual(uhdev.fake_report(*event), 652 uhdev.create_report(*event)) 653 654 event = (-5, 5, (True, False, True)) 655 self.assertEqual(uhdev.fake_report(*event), 656 uhdev.create_report(*event)) 657 658 event = (-127, 127, (True, False, True)) 659 self.assertEqual(uhdev.fake_report(*event), 660 uhdev.create_report(*event)) 661 662 event = (0, -128, (True, False, True)) 663 with self.assertRaises(hidtools.hid.RangeError): 664 uhdev.create_report(*event) 665 666 667class TestWheelMouse(BaseTest.TestMouse): 668 def create_device(self): 669 return WheelMouse() 670 671 def is_wheel_highres(self, uhdev): 672 self.assertTrue(uhdev.evdev.has(libevdev.EV_REL.REL_WHEEL)) 673 return uhdev.evdev.has(libevdev.EV_REL.REL_0B) 674 675 def test_wheel(self): 676 uhdev = self.uhdev 677 678 # check if the kernel is high res wheel compatible 679 high_res_wheel = self.is_wheel_highres(uhdev) 680 681 syn_event = self.syn_event 682 # The Resolution Multiplier is applied to the HID reports, so we 683 # need to pre-multiply too. 684 mult = uhdev.wheel_multiplier 685 686 r = uhdev.event(0, 0, wheels=1 * mult) 687 expected = [syn_event] 688 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, 1)) 689 if high_res_wheel: 690 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_0B, 120)) 691 events = uhdev.next_sync_events() 692 self.debug_reports(r, uhdev, events) 693 self.assertInputEvents(expected, events) 694 695 r = uhdev.event(0, 0, wheels=-1 * mult) 696 expected = [syn_event] 697 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, -1)) 698 if high_res_wheel: 699 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_0B, -120)) 700 events = uhdev.next_sync_events() 701 self.debug_reports(r, uhdev, events) 702 self.assertInputEvents(expected, events) 703 704 r = uhdev.event(-1, 2, wheels=3 * mult) 705 expected = [syn_event] 706 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, -1)) 707 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, 2)) 708 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, 3)) 709 if high_res_wheel: 710 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_0B, 360)) 711 events = uhdev.next_sync_events() 712 self.debug_reports(r, uhdev, events) 713 self.assertInputEvents(expected, events) 714 715 716class TestTwoWheelMouse(TestWheelMouse): 717 def create_device(self): 718 return TwoWheelMouse() 719 720 def is_hwheel_highres(self, uhdev): 721 self.assertTrue(uhdev.evdev.has(libevdev.EV_REL.REL_HWHEEL)) 722 return uhdev.evdev.has(libevdev.EV_REL.REL_0C) 723 724 def test_ac_pan(self): 725 uhdev = self.uhdev 726 727 # check if the kernel is high res wheel compatible 728 high_res_wheel = self.is_wheel_highres(uhdev) 729 high_res_hwheel = self.is_hwheel_highres(uhdev) 730 self.assertEqual(high_res_wheel, high_res_hwheel) 731 732 syn_event = self.syn_event 733 # The Resolution Multiplier is applied to the HID reports, so we 734 # need to pre-multiply too. 735 hmult = uhdev.hwheel_multiplier 736 vmult = uhdev.wheel_multiplier 737 738 r = uhdev.event(0, 0, wheels=(0, 1 * hmult)) 739 expected = [syn_event] 740 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, 1)) 741 if high_res_hwheel: 742 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_0C, 120)) 743 events = uhdev.next_sync_events() 744 self.debug_reports(r, uhdev, events) 745 self.assertInputEvents(expected, events) 746 747 r = uhdev.event(0, 0, wheels=(0, -1 * hmult)) 748 expected = [syn_event] 749 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, -1)) 750 if high_res_hwheel: 751 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_0C, -120)) 752 events = uhdev.next_sync_events() 753 self.debug_reports(r, uhdev, events) 754 self.assertInputEvents(expected, events) 755 756 r = uhdev.event(-1, 2, wheels=(0, 3 * hmult)) 757 expected = [syn_event] 758 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, -1)) 759 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, 2)) 760 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, 3)) 761 if high_res_hwheel: 762 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_0C, 360)) 763 events = uhdev.next_sync_events() 764 self.debug_reports(r, uhdev, events) 765 self.assertInputEvents(expected, events) 766 767 r = uhdev.event(-1, 2, wheels=(-3 * vmult, 4 * hmult)) 768 expected = [syn_event] 769 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, -1)) 770 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, 2)) 771 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, -3)) 772 if high_res_wheel: 773 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_0B, -360)) 774 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, 4)) 775 if high_res_wheel: 776 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_0C, 480)) 777 events = uhdev.next_sync_events() 778 self.debug_reports(r, uhdev, events) 779 self.assertInputEvents(expected, events) 780 781 782class TestResolutionMultiplierMouse(TestTwoWheelMouse): 783 def create_device(self): 784 return ResolutionMultiplierMouse() 785 786 def is_wheel_highres(self, uhdev): 787 high_res = super().is_wheel_highres(uhdev) 788 789 if not high_res: 790 # the kernel doesn't seem to support the high res wheel mice, 791 # make sure we haven't triggered the feature 792 assert uhdev.wheel_multiplier == 1 793 794 return high_res 795 796 def test_resolution_multiplier_wheel(self): 797 uhdev = self.uhdev 798 799 if not self.is_wheel_highres(uhdev): 800 raise unittest.SkipTest('Kernel not compatible, we can not trigger the conditions') 801 802 self.assertGreater(uhdev.wheel_multiplier, 1) 803 self.assertEqual(120 % uhdev.wheel_multiplier, 0) 804 805 def test_wheel_with_multiplier(self): 806 uhdev = self.uhdev 807 808 if not self.is_wheel_highres(uhdev): 809 raise unittest.SkipTest('Kernel not compatible, we can not trigger the conditions') 810 811 assert uhdev.wheel_multiplier > 1 812 813 syn_event = self.syn_event 814 mult = uhdev.wheel_multiplier 815 816 r = uhdev.event(0, 0, wheels=1) 817 expected = [syn_event] 818 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_0B, 120 / mult)) 819 events = uhdev.next_sync_events() 820 self.debug_reports(r, uhdev, events) 821 self.assertInputEvents(expected, events) 822 823 r = uhdev.event(0, 0, wheels=-1) 824 expected = [syn_event] 825 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_0B, -120 / mult)) 826 events = uhdev.next_sync_events() 827 self.debug_reports(r, uhdev, events) 828 self.assertInputEvents(expected, events) 829 830 expected = [syn_event] 831 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, 1)) 832 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, -2)) 833 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_0B, 120 / mult)) 834 835 for _ in range(mult - 1): 836 r = uhdev.event(1, -2, wheels=1) 837 events = uhdev.next_sync_events() 838 self.debug_reports(r, uhdev, events) 839 self.assertInputEvents(expected, events) 840 841 r = uhdev.event(1, -2, wheels=1) 842 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_WHEEL, 1)) 843 events = uhdev.next_sync_events() 844 self.debug_reports(r, uhdev, events) 845 self.assertInputEvents(expected, events) 846 847 848class TestResolutionMultiplierHWheelMouse(TestResolutionMultiplierMouse): 849 def create_device(self): 850 return ResolutionMultiplierHWheelMouse() 851 852 def is_hwheel_highres(self, uhdev): 853 high_res = super().is_hwheel_highres(uhdev) 854 855 if not high_res: 856 # the kernel doesn't seem to support the high res wheel mice, 857 # make sure we haven't triggered the feature 858 assert uhdev.hwheel_multiplier == 1 859 860 return high_res 861 862 def test_resolution_multiplier_ac_pan(self): 863 uhdev = self.uhdev 864 865 if not self.is_hwheel_highres(uhdev): 866 raise unittest.SkipTest('Kernel not compatible, we can not trigger the conditions') 867 868 self.assertGreater(uhdev.hwheel_multiplier, 1) 869 self.assertEqual(120 % uhdev.hwheel_multiplier, 0) 870 871 def test_ac_pan_with_multiplier(self): 872 uhdev = self.uhdev 873 874 if not self.is_hwheel_highres(uhdev): 875 raise unittest.SkipTest('Kernel not compatible, we can not trigger the conditions') 876 877 assert uhdev.hwheel_multiplier > 1 878 879 syn_event = self.syn_event 880 hmult = uhdev.hwheel_multiplier 881 882 r = uhdev.event(0, 0, wheels=(0, 1)) 883 expected = [syn_event] 884 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_0C, 120 / hmult)) 885 events = uhdev.next_sync_events() 886 self.debug_reports(r, uhdev, events) 887 self.assertInputEvents(expected, events) 888 889 r = uhdev.event(0, 0, wheels=(0, -1)) 890 expected = [syn_event] 891 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_0C, -120 / hmult)) 892 events = uhdev.next_sync_events() 893 self.debug_reports(r, uhdev, events) 894 self.assertInputEvents(expected, events) 895 896 expected = [syn_event] 897 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_X, 1)) 898 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_Y, -2)) 899 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_0C, 120 / hmult)) 900 901 for _ in range(hmult - 1): 902 r = uhdev.event(1, -2, wheels=(0, 1)) 903 events = uhdev.next_sync_events() 904 self.debug_reports(r, uhdev, events) 905 self.assertInputEvents(expected, events) 906 907 r = uhdev.event(1, -2, wheels=(0, 1)) 908 expected.append(libevdev.InputEvent(libevdev.EV_REL.REL_HWHEEL, 1)) 909 events = uhdev.next_sync_events() 910 self.debug_reports(r, uhdev, events) 911 self.assertInputEvents(expected, events) 912 913 914class TestMiMouse(TestWheelMouse): 915 def create_device(self): 916 return MIDongleMIWirelessMouse() 917 918 def assertInputEvents(self, expected_events, effective_events): 919 # Buttons and x/y are spread over two HID reports, so we can get two 920 # event frames for this device. 921 remaining = self.assertInputEventsIn(expected_events, effective_events) 922 try: 923 remaining.remove(libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT, 0)) 924 except ValueError: 925 # If there's no SYN_REPORT in the list, continue and let the 926 # assert below print out the real error 927 pass 928 self.assertEqual(remaining, []) 929 930 931if __name__ == "__main__": 932 main(sys.argv[1:]) 933