#!/usr/bin/env python3 import sys import usb.core import usb.util # Script to set the DPI of the Microsoft Classic Intellimouse # Taken from https://gist.github.com/K-Visscher/22561ca69a64339a7383d67db6e79818 VID = 0x045E PID = 0x0823 DPI_WRITE_PROPERTY = 0x96 WRITE_REPORT_ID = 0x24 WRITE_REPORT_LENGTH = 0x20 INTERFACE = 0x01 class IntelliMouse(): def __init__(self): self.reattach = False self.device = usb.core.find(idVendor=VID, idProduct=PID) if self.device is None: raise ValueError("couldn't find the intellimouse...") def set_configuration(self): if sys.platform.startswith("linux"): for config in self.device: for interface in config: if self.device.is_kernel_driver_active(interface.bInterfaceNumber): self.device.detach_kernel_driver(interface.bInterfaceNumber) self.reattach = True self.device.set_configuration() def detach(self): usb.util.dispose_resources(self.device) if self.reattach: for config in self.device: for interface in config: if not self.device.is_kernel_driver_active(interface.bInterfaceNumber): self.device.attach_kernel_driver(interface.bInterfaceNumber) def __enter__(self): self.set_configuration() def __exit__(self, exc_type, exc_value, exc_traceback): self.detach() def __write_property(self, property, data): if not isinstance(property, int): raise TypeError("please make sure to pass a integer for the property argument...") if not isinstance(data, list) or not all(isinstance(x, int) for x in data): raise TypeError("please make sure to pass a list of integers for the data argument...") report = self.__pad_right([WRITE_REPORT_ID, property, len(data)] + data, WRITE_REPORT_LENGTH) self.device.ctrl_transfer(0x21, 0x09, 0x03 << 8 | WRITE_REPORT_ID, INTERFACE, report) def __pad_right(self, data, until): if not isinstance(data, list) or not all(isinstance(x, int) for x in data): raise TypeError("please make sure to pass a list of integers for the data argument...") if not isinstance(until, int): raise TypeError("please make sure to pass a integer for the until argument...") if until <= 0: raise ValueError("please pass a positive integer for the until argument...") if len(data) >= until: return return data + ((until - len(data)) * [0x00]) def set_dpi(self, dpi): if not isinstance(dpi, int): raise TypeError("please make sure to pass an integer...") if dpi % 200 != 0 or not (dpi >= 400 and dpi <= 3200): raise ValueError("please make sure to pass a valid value (dpi % 200 == 0 and (dpi >= 400 and dpi <= 3200))") self.__write_property(DPI_WRITE_PROPERTY, [0x00] + list(dpi.to_bytes(2, byteorder="little"))) if __name__ == "__main__": if len(sys.argv) < 2: sys.stderr.write("Usage: {} [400 — 3200 DPI]\n".format(sys.argv[0])) sys.exit(1) try: dpi = int(sys.argv[1]) if dpi < 400 or dpi > 3200: raise ValueError except ValueError: sys.stderr.write("DPI value must be a number between 400 and 3200 inclusively") exit(1) mouse = IntelliMouse() with mouse: mouse.set_dpi(dpi)