aboutsummaryrefslogtreecommitdiff
path: root/dotfiles/.local/bin/intellimouse-classic
blob: 3b320a3988f23b80a46f2a6f28862261d2d3a0fb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#!/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)