#!/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)