Controllers

orbistoun-input implements the guest's pad library itself; it wraps no host gamepad library. It models up to four ports, opening and closing a pad, and the state a title reads. The window reads the host's keyboard and, on Windows, its gamepads, and hands the result in. Buttons are named by position, never by glyph: south, east, west, north, l1, r1, l2, r2, l3, r3, up, down, left, right, select, start, and shell.

What a title reads

A pad read answers the whole 120-byte state image measured on hardware. The window's pad state is placed into it at the fields the SDK layout names (D713); with nothing pressed the title reads the measured at-rest image. A port with nothing driving it is a pad nobody is holding, which a title can enumerate like any other.

The shell button never reaches a title: the window keeps it for the shell overlay and power menu (see the user guide). While the overlay is open, the title reads a pad nobody is holding.

The controllers pane

preferences - controllers sets the ports:

Key names are the window's: ArrowUp, Enter, Backspace, Home, or a single letter or digit. Two buttons bound to one key, and a name nothing recognises, are listed in red under the ports. Save writes the mapping to config.toml; it applies to the next run.

Default keyboard mapping

Port 1 is the keyboard by default:

Button Key
up, down, left, right arrow keys
south, east, west, north K, L, J, I
l1, r1 Q, E
l2, r2 1, 3
l3, r3 Z, C
select Backspace
start Enter
shell Home
Stick Up, down, left, right
left W, S, A, D
right T, G, F, H

In config.toml:

[[pads.ports]]
source = "keyboard"

[pads.ports.keys]
south = "K"
start = "Enter"

[pads.ports.axes]
left-up = "W"

Pad scripts

A pad script drives a port from a file, so a run with no window can press buttons and two runs of one script give the same input (D707). Each step sets the pad at a moment and holds it until the next step; a press and its release are two steps.

[[step]]
at_ms = 2000            # milliseconds from the start of the run
buttons = ["start"]

[[step]]
at_ms = 2100
buttons = []            # released

[[step]]
at_ms = 3000
left_stick = [0.0, -1.0]   # x, y in -1.0..1.0, y positive down
triggers = [0.0, 1.0]      # left, right in 0.0..1.0

A step names one clock: at_ms, host milliseconds from the start of the run, or at_flip, the guest's flips since the start, which presses at the same point in the title however fast the host runs it. A script uses one clock throughout, and steps out of order are refused rather than sorted.

A step can wait for the title's phase instead of a time (D987). wait_for_draws = N holds it until the guest has closed a frame of at least N draws, and no earlier than its own time; the steps before it run until then, and the steps after it count their time from the moment it took effect. A world frame is far heavier than any menu, so in-world input lands in the world however long the menus and loads before it took:

[[step]]
at_flip = 4000          # not before flip 4000
wait_for_draws = 5000   # and not before a frame of 5000 draws

[[step]]
at_flip = 6             # six flips after that frame
right_stick = [1.0, 0.0]

The run logs input script: step N took effect at ... when a wait is met.

Name a script as a port's source in config.toml, relative to the directory holding it:

[[pads.ports]]
source = "script"
path = "inputs/press-start.toml"

or give one for a single run with orbistoun-cli run <title>/eboot.bin --input <script>.

Capturing and replaying input

The toolbar records what a title reads from its pad and plays it back (D721).

A capture and a playback end with the run, and nothing replays into the next launch unless chosen again. orbistoun-gui --title <name> --playback <file> launches a title with a capture playing, and orbistoun-cli run --input <file> replays one without a window. orbistoun-cli run --capture-input <file> captures without a window.

Checking that the title answers

orbistoun-cli motion <frames> --input <capture> says whether a held stick moves the picture (D988). Keep a sequence of frames from the title's first heavy frame (ORBISTOUN_FRAME_AT=busy:5000,busy-every:1 with ORBISTOUN_FRAME_DIR=<frames>), capture the pad the guest read, and the command measures the change between each two consecutive kept frames, split by whether a stick was held between them:

orbistoun-cli motion <frames> --input <capture> --region 0.25,0.25,0.5,0.5 --from 5900 --expect 2

--region is x,y,width,height as fractions of the frame; --expect fails the command unless the pairs with a stick held changed that many times as much as the pairs at rest.