From d1e8404545076fe9fde760c1db7177379c13995f Mon Sep 17 00:00:00 2001 From: cinnaboot Date: Thu, 17 Jul 2025 12:59:07 -0400 Subject: [PATCH] Initial commit Changes to be committed: new file: .gitignore new file: README new file: mouse_mute.py new file: requirements.txt --- .gitignore | 2 + README | 2 + mouse_mute.py | 113 +++++++++++++++++++++++++++++++++++++++++++++++ requirements.txt | 2 + 4 files changed, 119 insertions(+) create mode 100644 .gitignore create mode 100644 README create mode 100644 mouse_mute.py create mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e0283ac --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +venv/ +tags diff --git a/README b/README new file mode 100644 index 0000000..85b2714 --- /dev/null +++ b/README @@ -0,0 +1,2 @@ +Did you ever need to send a mouse event to a sandboxed X11 program while using +Wayland? Me neither. But, for the weirdo that wants to, here's this thing. diff --git a/mouse_mute.py b/mouse_mute.py new file mode 100644 index 0000000..6b78462 --- /dev/null +++ b/mouse_mute.py @@ -0,0 +1,113 @@ + +import re +import select +import sys + +import evdev # https://github.com/gvalkov/python-evdev +import pulsectl # https://github.com/mk-fg/python-pulse-control + + +# NOTE: adapted from evdev/evtest.py +def select_mouse(key_code:int = -1) -> evdev.InputDevice: + def devicenum(device_path): + digits = re.findall(r"\d+$", device_path) + return [int(i) for i in digits] + + devices = sorted(evdev.list_devices('/dev/input'), key=devicenum) + devices = [evdev.InputDevice(path) for path in devices] + if not devices: + msg = "error: no input devices found (do you have rw permission on %s/*?)" + print(msg % device_dir, file=sys.stderr) + return None + + for i, dev in enumerate(devices): + print("{}: {}".format(i, dev.name)) + + selection = input("select mouse device: ") + try: + if int(selection) > len(devices): + return None + except (TypeError, ValueError): + print("invalid comparison") + return None + + dev_caps = devices[int(selection)].capabilities() + + if (evdev.ecodes.EV_KEY not in dev_caps or + key_code not in dev_caps[evdev.ecodes.EV_KEY]): + print("this device doesn't have a BTN_SIDE") + return None + + return devices[int(selection)] + +def parse_event(e:evdev.InputEvent, listen_key:int) -> int: + if e.type == evdev.ecodes.EV_KEY: + if e.code == listen_key: + return e.value + + return -1 + +def select_source() -> pulsectl.PulseSourceInfo: + with pulsectl.Pulse() as pulse: + source_list = pulse.source_list() + + for i, source in enumerate(source_list): + print("{}: {}".format(i, source.description)) + + selection = input("select sount input device: ") + try: + if int(selection) > i: + return None + except (TypeError, ValueError): + print("invalid comparison") + return None + + return source_list[int(selection)] + + +if __name__ == '__main__': + # TODO: capture initial mute state + + # TODO: select other devices/buttons + key_code = evdev.ecodes.BTN_SIDE + device = select_mouse(key_code) + + if device == None: + print("error selecting device") + # FIXME: restore initial mute state here too + sys.exit(1) + + source = select_source() + + if source is None: + print("error selecting pulse source") + # FIXME: restore initial mute state here too + sys.exit(1) + + pulse = pulsectl.Pulse() + + # NOTE: listen for the BTN_SIDE event + # TODO: an option to play a sound on mute/unmute here would be nice too: + # https://github.com/hamiltron/py-simple-audio + + try: + print("Listening for events (press ctrl-c to exit) ...") + pulse = pulsectl.Pulse() + pulse.source_list() + + while True: + for event in device.read_loop(): + ret = parse_event(event, key_code) + if ret == 0: + pulse.mute(source, True) + elif ret == 1: + pulse.mute(source, False) + else: + pass + except (KeyboardInterrupt, EOFError): + pass + + # TODO: restore initial mute state + + + sys.exit(0) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..fbc0513 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +evdev==1.9.2 +pulsectl==24.12.0