sol-r1-linux-game-mappings/find_codes.py

123 lines
3.7 KiB
Python
Executable File

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