82 lines
3.0 KiB
Python
Executable File
82 lines
3.0 KiB
Python
Executable File
#!/usr/bin/env python3
|
||
"""
|
||
volume_remapper.py
|
||
------------------
|
||
Listen for Volume‑Up key events and emit a different key code
|
||
(“Ctrl+Shift+M” by default) so the focused application receives it.
|
||
|
||
Dependencies: python‑evdev (pip install evdev)
|
||
"""
|
||
|
||
import sys
|
||
import os
|
||
import argparse
|
||
from evdev import InputDevice, categorize, ecodes, UInput, list_devices
|
||
import time
|
||
|
||
# ------------------------------------------------------------------ #
|
||
# Helpers
|
||
# ------------------------------------------------------------------ #
|
||
|
||
def find_device(name_substr="volume"):
|
||
"""Return the first InputDevice that contains a KEY_VOLUMEUP event."""
|
||
for dev_path in [d.path for d in list_devices()]:
|
||
dev = InputDevice(dev_path)
|
||
caps = dev.capabilities()
|
||
if ecodes.EV_KEY in caps and ecodes.KEY_VOLUMEUP in caps[ecodes.EV_KEY]:
|
||
if name_substr.lower() in dev.name.lower():
|
||
return dev
|
||
return None
|
||
|
||
# ------------------------------------------------------------------ #
|
||
# Core logic
|
||
# ------------------------------------------------------------------ #
|
||
|
||
def main(remap_keys, device=None):
|
||
"""
|
||
:param remap_keys: tuple of ecodes.KEY_* codes to emit (e.g. (ecodes.KEY_LCTRL, ecodes.KEY_LSHIFT, ecodes.KEY_M))
|
||
:param device: path to /dev/input/eventX or None to auto‑detect
|
||
"""
|
||
if device is None:
|
||
dev = find_device()
|
||
if dev is None:
|
||
print("⚠️ No device with a Volume‑Up key found.", file=sys.stderr)
|
||
sys.exit(1)
|
||
else:
|
||
dev = InputDevice(device)
|
||
|
||
print(f"[INFO] Listening on {dev.path} ({dev.name}) ...")
|
||
print(f"[INFO] Remapping to: {', '.join(ecodes.KEY.get(k) for k in remap_keys)}")
|
||
|
||
# Create a virtual keyboard (uinput)
|
||
ui = UInput(events=[
|
||
ecodes.EV_KEY, # only key events
|
||
ecodes.EV_SYN
|
||
], name='volume‑remapper')
|
||
|
||
try:
|
||
for event in dev.read_loop():
|
||
if event.type == ecodes.EV_KEY:
|
||
if event.code == ecodes.KEY_VOLUMEUP and event.value == 1: # key_down
|
||
# Emit the mapped key sequence
|
||
for key in remap_keys:
|
||
ui.write(ecodes.EV_KEY, key, 1) # key down
|
||
ui.write(ecodes.EV_KEY, key, 0) # key up
|
||
ui.syn()
|
||
print(f"✅ Mapped Volume‑Up → {', '.join(ecodes.KEY.get(k) for k in remap_keys)}")
|
||
except KeyboardInterrupt:
|
||
print("\n[INFO] Stopping …")
|
||
finally:
|
||
ui.close()
|
||
|
||
|
||
if __name__ == "__main__":
|
||
parser = argparse.ArgumentParser(description="Remap Volume‑Up to any key combo.")
|
||
parser.add_argument("--device", help="Optional /dev/input/eventX path to listen on.")
|
||
parser.add_argument("--to", nargs="+", default=["KEY_LCTRL", "KEY_LSHIFT", "KEY_M"],
|
||
help="Space‑separated key names to emit. Default: Ctrl+Shift+M")
|
||
args = parser.parse_args()
|
||
|
||
# Convert key names to ecodes
|
||
remap = [ecodes.KEY[code.upper()] for code in args.to]
|
||
main(remap_keys=remap, device=args.device) |