#!/usr/bin/env python3 """ find_codes.py - print real evdev BTN_*/ABS_* codes for the Sol-R1 grip and base, so they can replace the TODO placeholders in any game's joyful-config.yml / daemon config. Requirements: pip install evdev --break-system-packages Permissions: sudo usermod -aG input $USER # for reading the joystick (re-login after this) Usage: # Step 1: find the exact device name(s) - grip and base usually # show up as two separate evdev devices. python3 find_codes.py --list # Step 2: watch a device and exercise every physical control on it # (move every axis through its full range, press every button, # flip every switch both ways, run every rotary knob both ways). python3 find_codes.py --device "Sol-R" """ import argparse import selectors import sys try: from evdev import InputDevice, ecodes, list_devices except ImportError: sys.exit("Missing deps. Run:\n pip install evdev --break-system-packages") def list_all_devices(): devices = [InputDevice(path) for path in list_devices()] if not devices: print("No input devices found (check permissions: groups $USER).") return for dev in devices: print(f"{dev.path}\t{dev.name}") def find_devices(name_substr: str): matches = [ dev for dev in (InputDevice(path) for path in list_devices()) if name_substr.lower() in dev.name.lower() ] if not matches: sys.exit( f"No input device matching '{name_substr}' found. " f"Run --list to see available device names." ) if len(matches) > 1: print(f"Multiple devices match '{name_substr}', watching all:") for d in matches: print(f" {d.path}\t{d.name}") print() return matches def code_name(ev_type: int, code: int) -> str: """Resolve a numeric code back to its BTN_*/ABS_*/KEY_* name(s).""" names = ecodes.bytype.get(ev_type, {}).get(code) if names is None: return str(code) return names if isinstance(names, str) else "/".join(names) def watch(devices): print("Exercise every physical control now (axes through full " "range, buttons pressed, switches flipped both ways, knobs " "turned both ways). Ctrl+C to stop.\n") sel = selectors.DefaultSelector() for dev in devices: sel.register(dev, selectors.EVENT_READ, data=dev) try: while True: for key, _ in sel.select(): dev = key.data for event in dev.read(): if event.type == ecodes.EV_KEY: name = code_name(ecodes.EV_KEY, event.code) state = {0: "UP", 1: "DOWN", 2: "HOLD"}.get( event.value, f"value={event.value}" ) print(f"[{dev.name}] {name} ({event.code}) {state}") elif event.type == ecodes.EV_ABS: name = code_name(ecodes.EV_ABS, event.code) print(f"[{dev.name}] {name} ({event.code}) = {event.value}") except KeyboardInterrupt: print("\nStopped.") def main(): parser = argparse.ArgumentParser( description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter ) parser.add_argument( "--list", action="store_true", help="list all evdev input devices and exit" ) parser.add_argument( "--device", metavar="NAME", help="substring match against evdev device name(s) to watch", ) args = parser.parse_args() if args.list: list_all_devices() return if args.device: watch(find_devices(args.device)) return parser.print_help() if __name__ == "__main__": main()