# rotate_mouse

Oct 3rd, 2022
690
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
1. #!/usr/bin/env python3
2. """
3. Inspired from here:
4. https://luator.de/linux/2022/03/05/xinput-rotate-mouse-axes.html
5.
6. The “Coordinate Transformation Matrix” is a affine transformation matrix. To rotate by an angle α, you need the matrix
7. ⎡cos(α)  -sin(α)  0⎤
8. ⎢sin(α)   cos(α)  0⎥
9. ⎣  0        0     1⎦
10. """
11. import re
12. from argparse import ArgumentParser, Namespace
13. from math import acos, cos, degrees, radians, sin
14. from subprocess import run
15.
16. LIST_REG = re.compile(
17.     r"(?P<name>\w[_\w\s-]+?)\s+id=(?P<id>\d+)\s+\[(master|slave)\s+pointer"
18. )
19.
20.
21. def rotate(degrees) -> list[float]:
23.     return [
24.         cos(a),
25.         -sin(a),
26.         0.0,
27.         sin(a),
28.         cos(a),
29.         0.0,
30.         0.0,
31.         0.0,
32.         1.0,
33.     ]
34.
35.
36. def get_rotation(name):
37.     proc = run(["xinput", "list-props", name], capture_output=True, encoding="utf8")
38.     for line in proc.stdout.splitlines():
39.         if "Coordinate Transformation Matrix" in line:
40.             matrix = tuple(map(float, line.partition(":")[2].split(",")))
41.             deg = degrees(acos(matrix[0]))
42.             return deg
43.
44.
45. def get_args() -> Namespace:
46.     parser = ArgumentParser()
50.     ag_get.add_argument("name", help="Name of the mouse pointer")
53.     ag_set.add_argument("name", help="Name of the mouse")
54.     ag_set.add_argument("angle", help="Angle in degrees", type=float)
55.     return parser.parse_args()
56.
57.
58. def list_dev(show=True):
59.     proc = run(["xinput", "list"], capture_output=True, encoding="utf8")
60.     devices = {}
61.
62.     for line in proc.stdout.splitlines():
63.         if match := LIST_REG.search(line):
64.             name = match["name"]
65.             dev_id = int(match["id"])
66.             devices[dev_id] = name
67.
68.     if not show:
69.         return devices
70.
71.     for dev_id, name in sorted(devices.items()):
72.         rot = get_rotation(str(dev_id)) or 0.0
73.         print(f"[ {dev_id:3d} ] {name:<30} {rot:0.2f} °")
74.
75.
76. def set_prop(name: str | int, angle: float):
77.     name = str(name)
78.     transf_matrix = [str(v) for v in rotate(angle)]
79.     command = [
80.         "xinput",
81.         "set-prop",
82.         name,
83.         "Coordinate Transformation Matrix",
84.         *transf_matrix,
85.     ]
86.     run(command)
87.
88.
89. def main():
90.     args = get_args()
91.
92.     match args.command:
93.         case "list":
94.             list_dev()
95.
96.         case "get":
97.             deg = get_rotation(args.name)
98.             print(f"{deg:.2f} °")
99.
100.         case "reset":
101.             for dev_id in list_dev(show=False):
102.                 set_prop(dev_id, 0.0)
103.
104.             list_dev()
105.
106.         case "set":
107.             set_prop(args.name, args.angle)
108.
109.
110. if __name__ == "__main__":
111.     try:
112.         main()
113.     except Exception as e:
114.         print(e)
115.