Advertisement
DeaD_EyE

rotate_mouse

Oct 3rd, 2022
594
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 2.96 KB | None | 0 0
  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]:
  22.     a = radians(degrees)
  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()
  47.     sub = parser.add_subparsers(title="Commands", dest="command")
  48.     ag_list = sub.add_parser("list", help="Command")
  49.     ag_get = sub.add_parser("get")
  50.     ag_get.add_argument("name", help="Name of the mouse pointer")
  51.     ag_set = sub.add_parser("set")
  52.     ag_reset = sub.add_parser("reset")
  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.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement