86 lines
2.8 KiB
Python
86 lines
2.8 KiB
Python
"""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()
|