add dora-joy node publishing gamepad axes and buttons
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
"""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()
|
||||
Reference in New Issue
Block a user