"""Show what the pad is doing, with no dataflow and nothing attached to it. `dora-joy-probe` opens the gamepad and prints, live: every axis SDL reports and every button it sees pressed, by index. It exists because a consumer's axis and button indices are almost always SDL's documented layout rather than an observed one, and a pad that numbers its controls differently is otherwise discovered by watching a real machine move the wrong way. Push one control at a time and read off the index it moves. This shows the reading and nothing else — it has no idea what any index is *for*, which is the same thing that makes this node reusable. Nothing here can move anything. There is no dora node and no connection to anything; this only reads. """ import argparse import time from dora_joy.config import Config from dora_joy.gamepad import Gamepad CLEAR = "\033[H\033[J" def bar(value: float, width: int = 21) -> str: """Draw a centred bar for a value between -1 and 1. Seeing the bar sit off-centre with the stick released is how a worn pad announces itself, which is most of the point of looking at raw numbers. """ middle = width // 2 position = middle + round(max(-1.0, min(1.0, value)) * middle) cells = ["-"] * width cells[middle] = "|" cells[position] = "#" return "".join(cells) def render(sample, config: Config) -> str: """Build the whole display for one reading.""" lines = ["dora-joy probe — Ctrl-C to stop", ""] if not sample.present: lines += [ " no gamepad found.", "", f" looking for joystick index {config.joystick_index};", " plug one in and this will pick it up.", ] return "\n".join(lines) lines.append(f" axes ({len(sample.axes)} reported)") for index, value in enumerate(sample.axes): lines.append(f" {index} {value:+.3f} {bar(value)}") held = [str(index) for index, down in enumerate(sample.buttons) if down] lines += ["", f" buttons ({len(sample.buttons)} reported)", f" {', '.join(held) or 'none pressed'}"] lines += ["", " push one control at a time and note the index it moves."] return "\n".join(lines) def main() -> None: """Open the pad and print what it is doing until interrupted.""" parser = argparse.ArgumentParser(description="Show what the gamepad is doing. Moves nothing.") parser.add_argument("--hz", type=float, default=10.0, help="Redraw rate. Defaults to 10.") args = parser.parse_args() config = Config.from_env() gamepad = Gamepad(config.joystick_index) gamepad.start() try: while True: print(CLEAR + render(gamepad.read(), config), flush=True) time.sleep(1.0 / args.hz) except KeyboardInterrupt: print() finally: gamepad.shutdown() if __name__ == "__main__": main()