Files
Sylpheed/docs/re/f1-held-down-measured-no-repeat-via-file-driver.md
sylph-decoder db830a1e75 re: F1 measured -- held DOWN moves the cursor once and no more, via the file driver
Fifth boot attempt this session, first clean one, using the harness debugged
last iteration (env-safe input, wide capture window, signed-in profile,
blanked X root). Reached the menu, held DOWN 2.5s wall-clock, armed the F10
draw capture, released, killed -- zero crashes.

Achieved vs requested: 433 of 600 requested frames, spanning 14.43s of GUEST
time against ~4.5s wall-clock for the hold+tail window -- this run went at
~3.2x real time (a cheap static menu, nothing pacing it to a display
refresh). The 2.5s wall-clock hold covers roughly 8s of that guest time.

Method: track one quad shape's position per frame via read_draws.py, not a
screen diff -- the approach f1-menu-repeat-harness-built-not-answered.md
already named as correct. Exactly one shape-group moves at all: one jump of
~0.21-0.225 NDC units (matching the known one-menu-item pitch), complete
within 4 frames / 133ms guest time of arming, then flat (a ~0.0125 pulse
wobble only) for the remaining 427 frames / 14.3s of guest time. Broadening
the match threshold finds no other moving element.

This is a STRONGER negative than the 2026-08-30 result it agrees with (that
one sampled a screen diff at ~4-5fps for 2.0s; this reads every quad every
frame for ~8s of guest-time held) -- it survives, rather than confirms, last
iteration's "coarse sampling hid a fast repeat" alternative, which does not
survive a per-frame instrument over a much longer window. Refutation
attempt against my own prior lean, recorded: it does not survive.

Reconciled, not left in conflict: the human's play-test and pad.py's
"auto-repeats" warning almost certainly went through a REAL controller
(Canary's SDL driver), which auto-repeats keystrokes at a documented
upstream 400ms/100ms (guest time) via the REPEAT keystroke flag. The file
driver deliberately never emits that flag, by design -- so it cannot show
repeat regardless of how carefully or how long it's held, which is exactly
the negative measured here. Still no F1 number; now a specific, well-
evidenced reason the file driver alone will never produce one, and a named
next step (add opt-in REPEAT-flag support to the file driver, matching the
SDL driver's constants, then re-run this exact capture) -- not attempted
this iteration, a second unit on an already-complete one.

Reference data: docs/re/data/f1-cursor-quad-y-per-frame.tsv -- the derived
per-frame Y position and guest tick, not the raw draw log itself (a capture
artifact, kept uncommitted per the corpus's game-content rule).
2026-09-12 12:13:41 +00:00

7.1 KiB
Raw Permalink Blame History

F1 — measured: held ⬇ moves the cursor once and no more, over ~8 s of guest time, through the file driver

Status: measured (⟨capture⟩), for the path this container can actually drive. still not F1's requested initial-delay/interval numbers — this result explains why they cannot come from this instrument, and names the one that could. Instrument: tools/re-capture/f1_hold_capture.py, after the four fixes in f1-hold-capture-harness-debugged.md. 2026-09-12, fifth boot attempt, first clean one.

The run

Reached the settled main menu at 159.3 s (title at 154.3 s, matching the last clean run's timing), armed the F10 draw capture, held ⬇ for 2.5 s wall-clock, released, waited 2 s, killed. Zero crashes. 433 of the requested 600 frames captured before the kill (ui_draw_capture_frames=600, bumped from the default 3 last iteration).

⚠️ Achieved vs. requested, per TEMPORAL-VERIFICATION.md: the 433 frames span 14.43 s of guest time (gtick/gfreq on each frame header, 50 000 000 Hz) against ~4.5 s of wall-clock for the hold + tail — this window ran at roughly 3.2× real time, not 1:1. A static 2D menu is cheap to render, and nothing here was pacing the emulation to a display refresh. The 2.5 s wall-clock hold itself therefore covers roughly 8 s of guest time (the hold is 2.5 of the 4.5 wall-clock seconds captured), not 2.5. Achieved guest frame rate: 29.87 fps — close to a 30 Hz internal tick, for what it's worth, but this is a rate, not a claim about which clock the menu itself runs on.

Method — track one quad shape's position, not a screen diff

Read every draw with read_draws.py, grouped quads by (texture page, width, height) — the "track one quad shape's position over time" approach f1-menu-repeat-harness-built-not-answered.md already named as the correct one. Exactly one shape-group moves at all across the whole capture: page B5B1C73032BA3FA3, a small (0.09 × ~0.16 NDC) element — the focus highlight, by elimination and by the size of its move (below).

What it shows

frame guest ms since frame 1 Y (NDC)
1 0 +0.0525
5 133 0.16
6…432 up to 14 297 ms later 0.1725 … 0.16 (wobble only)

One jump, size 0.210.225 NDC units, complete within 4 frames (133 ms guest) of arming — close to the one-item pitch computed from menu_focus.py's known button rows ((390315)/720×2 = 0.208), i.e. this reads as one menu step. Then nothing. The 0.16-ish value that follows oscillates by ≤0.0125 on a ~60-frame cycle for the remaining 427 frames / 14.3 s of guest time — a pulsing highlight glow, the same shape of animation this corpus has measured elsewhere, not a second discrete move. Broadening the shape-match threshold from ≥50 to ≥5 frames-present finds only one other group, at the same page and a rounding-adjacent height (0.17 vs 0.16, same element split by sub-pixel quantisation), showing the identical pattern. Nothing else in the capture moves at all.

This is a stronger negative than the 2026-08-30 result it agrees with: nav_repeat_and_b.py sampled a screen diff at ~45 fps for 2.0 s wall-clock; this reads every submitted quad, every frame, for a window covering ~8 s of guest time the button was actually held. A coarse-sampling explanation for "no repeat" — the leading alternative hypothesis after last iteration's C_PAD_RINGBUF trace — cannot survive a per-frame instrument that still finds nothing.

Reconciling with the standing conflict, not hand-waving past it

Three facts now sit together and are not actually in tension once separated by instrument, not by hand:

  1. The human's play-test, and pad.py's own docstring, both describe a real repeat — almost certainly observed through a real controller, i.e. Canary's SDL input driver.
  2. That driver's GetKeystroke() (f1-no-repeat-was-the-harness.md) auto-repeats keystrokes at a documented, upstream 400 ms initial delay then 100 ms interval, guest time (HID_SDL_REPEAT_DELAY/_RATE).
  3. This measurement went through the file driver, whose GetKeystroke() is deliberately built to emit exactly one event per press and never a REPEAT-flagged one — and now, measured rather than argued, holding the button via that driver's continuously-reporting GetState() for ~8 guest-seconds produces no second move either.

The straightforward reading: menu repeat is driven by REPEAT-flagged keystrokes, not by polling raw held state every frame. The file driver cannot produce that flag by design, so it cannot show repeat, however long or precisely you hold the button through it — which is exactly the negative result above. Last iteration's C_PAD_RINGBUF trace (analog-axis-shaped fields, favouring a polled-state reading) likely belongs to a different consumer — plausibly raw stick deflection for something else entirely — not to whatever specifically steps the menu cursor. That inference is not re-verified here; flagging it as probably-superseded rather than silently dropping it.

What would actually answer F1

Give the file driver the same REPEAT-flag capability the SDL driver has, gated behind a new flag so every other scripted script keeps today's one-event-per-press behaviour (this was proposed, not built, twice already — f1-no-repeat-was-the-harness.md's original "what would close it" and the harness-debugging page's "what's left"). Then re-run this exact capture. Two outcomes, both answers:

  • it now shows a genuine repeat, at some interval derived from the SDL driver's constants or the game's own consumption of them — measure the interval in frames at the achieved guest rate, not a millisecond guess;
  • it still shows nothing, which would mean the menu's repeat is driven by something other than the Keystroke REPEAT flag after all, and the question reopens from a different angle.

Not attempted this iteration — patching the driver and re-running is a second, larger unit on top of an already-complete one.

Reference data

data/f1-cursor-quad-y-per-frame.tsv — the focus-highlight quad's Y (NDC) per frame it appears in, with the guest tick/frequency for each, derived from the run's draw log (not the log itself, which is a capture artifact and stays uncommitted per the corpus's rule on game content — this is measurement output, numbers only).

Reach

⟨capture⟩ for the "no repeat via the file driver" result — direct, per-frame, quantified in guest time, with the instrument's own capability shown by the one real move it caught. Everything about why (Keystroke vs. polled state) is ⟨canary-source⟩ reasoning connecting this result to the prior static trace, not itself re-measured. One run only — the corpus's own two-run minimum for a reproducibility claim is not met, though this run's own positive control (catching the initial move at 133 ms resolution) is evidence the null isn't an instrument artifact of this specific run.