Skip to content
magmacrunchmedia edited this page Aug 24, 2026 · 3 revisions

Input

Every input source in texastoast has the same two methods, poll() and is_pressed(), so a game can swap keyboard for hardware without changing.

InputState

poll() returns an InputState — eight booleans and two derived axes:

state = keyboard.poll()

state.up, state.down, state.left, state.right
state.a, state.b, state.start, state.select

state.dx    # -1.0, 0.0 or 1.0
state.dy    # -1.0, 0.0 or 1.0
state.is_any_direction()

Opposite directions cancel: holding left and right gives dx == 0.0.

dx/dy are raw axis reads and are not normalized — a diagonal has magnitude 1.41, not 1. Entity.move normalizes for you. If you integrate position yourself, do it too:

import math

dx, dy = state.dx, state.dy
mag = math.hypot(dx, dy)
if mag > 1.0:
    dx, dy = dx / mag, dy / mag
x += dx * speed * dt
y += dy * speed * dt

KeyboardInput

from texastoast import KeyboardInput

keyboard = KeyboardInput(game.root)
game.on_close(keyboard.destroy)   # release the bindings on exit
Button Keys
up / down / left / right Arrow keys, WASD
a Z, Enter
b X, Backspace
start Escape, P
select Shift

KeyboardInput binds on construction and tracks state until told otherwise. It is a state source, not an event source: poll() tells you what is held right now. For one-shot actions — confirming a menu, advancing dialogue — bind a key event instead, because polling will fire on every frame the key is down:

def on_key(event):
    if event.keysym in ("z", "Return"):
        dialogue.dismiss()

game.bind_key("<Key>", on_key)

destroy() removes every binding and clears the state. It is idempotent.

CompositeInput

Prefers hardware when it is actually responding, and falls back to the keyboard otherwise:

from texastoast.input.magma_hub import CompositeInput, MagmaHubInput

controls = CompositeInput(keyboard, MagmaHubInput(hub))
state = controls.poll()

controls.active_source   # "magma_hub", "keyboard" or "none"

Either argument may be None. With neither, poll() returns an idle state rather than raising.

The fallback is decided per call by hub.connected, so unplugging the hardware returns control to the keyboard on the next poll — provided something is polling the hub, since connected reflects the last poll's outcome.

In 0.1.x a hub on a dead or mock bus reported itself connected, so CompositeInput latched onto it and keyboard input stopped working altogether. See Magma Hub and I2C.

Writing your own source

Anything with poll() and is_pressed() works — there is no base class to inherit. InputSource in texastoast.input.abstract is a typing.Protocol you can use for type checking:

class ReplayInput:
    def __init__(self, frames):
        self._frames = iter(frames)
        self._state = InputState()

    def poll(self):
        self._state = next(self._frames, self._state)
        return self._state

    def is_pressed(self, button):
        return getattr(self._state, button, False)

Clone this wiki locally