F1 — measure the held-direction repeat: initial delay and interval, in frames #1
Notifications
Due Date
No due date set.
Blocks
Reference: fabi/Sylpheed#1
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Play-test 2026-09-02, the human, on the real game:
The existence half is settled and settled against us: it repeats. The rate is not measured, and must not be guessed — that description bounds it and supplies no number.
What to produce. Two numbers, as frame counts at a stated present rate, not a stopwatch reading (
docs/agents/TEMPORAL-VERIFICATION.md):And two yes/no answers: does the d-pad differ from the stick, and does the rate accelerate while held or stay flat?
Pass: counted in Canary, reproduced across two runs, gated on an observable rather than a wall-clock delay — the trap
f5_snap_or_accelerate.shalready hit once.Fail: a plausible interval with no capture behind it.
Blocks the Port's implementation.
Not answered yet, but the premise conflict is resolved enough to say why no
number exists:
docs/re/f1-no-repeat-was-the-harness.md,in #23 (
9141741).This corpus already had a contradicting result on file —
menu-navigation-semantics.mdrecorded "no auto-repeat, measured" on2026-08-30, which flatly disagrees with the play-test this issue quotes.
Checked the instrument (⟨canary-source⟩,
/canary/src/xenia/hid/file/file_input_driver.h):that measurement drove input through our own scripted pad driver, whose
GetKeystroke()is deliberately built to deliver exactly one event perheld press — "repeat is what makes menu steps overshoot" is the driver's own
comment. The game reads menu input through this same Keystroke API
(
input-pad-read-path.md), so a driver engineered to prevent repeat cannotbe evidence the game lacks it. Refutation attempt against that 2026-08-30
claim, recorded — it doesn't survive as stated.
Not a clean reversal, though, and I didn't force it into one: the same
driver's
GetState()holds a button continuously, andpad.py's owndocstring warns a longer hold "auto-repeats and overshoots" — an observed
effect through this exact tool. That's in tension with the driver-can't-
repeat finding and I haven't resolved which wins.
A concrete, testable prediction, from Canary's source, not yet measured:
the real-controller (SDL) input driver auto-repeats keystrokes at 400 ms
initial delay then 100 ms interval, guest time — upstream Xenia, not
project code. If the menu treats a repeat-flagged keystroke as one nav step,
that predicts almost exactly the "medium pace, slow enough to see" the
play-test describes.
What would close it, cheapest first: (1) trace
C_PAD_RINGBUF'sproducer — keystroke ring or polled state — no emulator needed; (2) add an
opt-in repeat mode to the file driver and read cursor position per frame off
the draw log, the instrument
f1-menu-repeat-harness-built-not-answered.mdalready validated for this. Neither run this iteration — reversing a
standing claim is enough for one unit.
Stays
state/approved; the numbers issue #1 wants are still open.Continued from the last update, not a new question: traced
C_PAD_RINGBUFrather than reasoning from its name. Update appended to
docs/re/f1-no-repeat-was-the-harness.md,in #23 (
dfbefff).C_PAD_DECODER's update reads that ring at offsets 12 (the button wordalready known) and 36, 40, 44, 48 — four consecutive fields read
together through an int→double conversion an analog axis would use.
XamInputGetKeystrokeExhas no field for a stick position, so this cannotbe a keystroke queue; it reads as a periodically-refreshed polled-state
snapshot. My own prior reading of "ring buffer" as implying a queue was
wrong — refuted by tracing it, recorded per adversarial duty either way.
Practical effect: this favours the file driver's
GetState()(alreadycapable of holding a button continuously, no changes needed) over the
Keystroke/400-100ms route, and makes
nav_repeat_and_b.py's null resultmore likely a sampling artifact (its ~4–5 fps screen-diff detector
against a possibly-fast repeat) than a structural driver limit. Revised
"what would close it" in the page: re-run the existing draw-log
position-tracking instrument, properly gated on the menu, before reaching
for a driver change.
Not found yet: the actual producer writing into the ring each frame —
narrowed to "reachable from the input-manager singleton fetch in
sub_821A9DC8," not traced to completion. Still no number.state/approvedstays.
Actually tried to run this measurement — first dynamic attempt on this
issue.
docs/re/f1-hold-capture-harness-debugged.md,in #23 (
089aa8d). Still no number.Built
tools/re-capture/f1_hold_capture.py: boot to the settled menu viathe proven glyph gate, arm F10's UI-draw capture, hold a direction, read
cursor position per frame off the draw log. Four bugs across four boot
attempts, three fixed and confirmed:
tap()shelled out topad.pywithout this script'senv, so the press silently went to the wrong pad file. Fixed, confirmed
(title 154.5s, menu 163.0s next run).
ui_draw_capture_frames/maxat 3/20000 from a prior session—
log_ui_drawsis now a documented no-op, command-line didn't visiblyoverride these. Bumped in the container's
xenia-canary.config.toml.after a restart), which reproduces
structures/title-a-press-fault.md'salready-diagnosed crash — verified byte-for-byte against that page's own
addresses, looping continuously before F10 was ever pressed.
boot_menu.shalready handles this; my script didn't. Fixed, confirmed (zero crashes
with a profile signed in vs. dozens per run without).
frame from a killed prior run gave a false "TITLE" read at 2.6s.
Fixed in the script.
Stopped after 4 boot attempts (~9 min of emulator time) rather than chase a
fifth. The harness should be correct now; next attempt is the actual
measurement. Flagged loudly (REFUTED.md, HANDOFF) since bug 3 will hit any
bare
run-canarycall in any fresh container, not just mine.state/approvedstays — this is infrastructure progress, not an answer.
Clean run landed — the harness debugged last iteration worked end to end.
docs/re/f1-held-down-measured-no-repeat-via-file-driver.md,in #23 (
2939fa2). Still no initial-delay/interval number, but now a sharpreason why not.
Held ⬇ for 2.5s wall-clock (≈8s of guest time — this run went ~3.2× real
time, a cheap static menu with nothing pacing it to a display refresh).
Tracked one quad shape's position per frame off the draw log, not a screen
diff: one cursor step, caught within 133ms of arming, then nothing for
the remaining ~14s of guest time captured. Broadened the match threshold
and found no other moving element.
This is a stronger negative than the 2026-08-30 result, not a different
one — it directly tests and refutes my own leading theory from last
iteration (
C_PAD_RINGBUF's analog fields → coarse sampling was probablyhiding a fast polled-state repeat). A per-frame instrument over ~4× the
held-time still finds nothing. Refutation attempt against my own prior
lean, recorded — it doesn't survive.
Reconciled with the standing conflict rather than left hanging: the
human's play-test and
pad.py's "auto-repeats" warning almost certainlywent through a real controller — Canary's SDL driver, which auto-repeats
keystrokes at a documented upstream 400ms delay / 100ms interval (guest
time) via the Keystroke
REPEATflag. Our scriptedfiledriverdeliberately never emits that flag. So: the file driver structurally cannot
show repeat, at any hold length, through any measurement method — which is
exactly what both the 2026-08-30 result and this one show.
What would actually answer it: add opt-in
REPEAT-flag support to thefile driver (matching the SDL driver's constants), gated so scripted work
elsewhere keeps today's one-event-per-press behavior, then re-run this exact
capture. Not attempted this iteration — a second unit on an already-complete
one.
state/approvedstays.Answered.
docs/re/f1-repeat-measured-via-driver-patch.md,in #23 (
e61937f).Built the fix the previous comment named: patched Canary's
--hid=filedriver to emit Keystroke
REPEATat the SDL driver's own constants(400ms delay / 100ms interval, guest time — opt-in
--pad_file_repeat,off by default, doesn't touch any other scripted script). Rebuilt
(incremental, ~1 minute), re-ran the identical held-⬇ capture that
previously showed one move and nothing else.
The cursor moved continuously this time — 19 distinct positions,
cycling and wrapping through the whole 5-item menu for the length of the
hold. At this run's achieved 29.87 fps guest rate:
~100 ms for the other 2)
Use 12 and 4 frames. Flagging honestly rather than smoothing it over:
the interval is measurably slower than the raw 100 ms constant driving it
(100 ms ÷ a ~33.5 ms frame ≈ 3, not 4) — the game likely consumes drained
keystrokes at its own per-frame pace rather than instantly, and I haven't
traced exactly where the extra time goes. The 4-frame number is what
matters for the port: it's the visible on-screen cadence.
What this is not: a capture through an actual physical controller —
none exists in this container. This measures what the game does when fed
REPEATevents shaped like the SDL driver's, the closest available stand-in.Classified
measured, notdecoded. One run only; the corpus's two-runminimum isn't met, and the 3-vs-4-frame split is itself worth a second look
if anyone wants to firm this up further.
Moving to
state/needs-human— this is as far as I can verify it myself,and issue #2 (the Port's implementation) can use these numbers now.
Small addendum, same PR (
891c38e): found thatHANDOFF.md's originalQ1–Q10 summary table (near the top of the file) still quoted the withdrawn
"no auto-repeat" finding as current, three commits after this session
measured the real numbers. A reader who only checks that summary table —
which a 6600+ line file trains you to do — would get the wrong answer.
Fixed with a pointer to the current entries rather than silently editing
the number in place. No new question here, just closing a gap I left in my
own prior edit.
Confirmed on a real controller, 2026-09-13, by the human who reported the original defect:
That settles the question this issue could not answer from inside a container.
REPEAT_INTERVAL = 0.134— ~7.5 steps a second — reads as the "medium pace… slow enough to see which item is selected" of the 2026-09-02 play-test, from the same person, on the same hands.📌 What that does and does not establish. It confirms the interval is not contradicted by the one oracle that matters. It does not promote the finding: still
measured, notdecoded; still one run against the corpus's two-run minimum; and the delay is still the constant that was fed in (Canary's SDL 400 ms), confirmed by the instrument rather than by the game. A human saying "good" cannot distinguish 0.402 from 0.38.The port adopted it in #27, which also fixed a defect this measurement flushed out:
held_direction()read the stick's position from the event history instead of polling the device, so the rate was correct and never ran.Closing — the two numbers asked for exist, are cited, and are in use.