re: F1 answered -- 12 frames initial delay, 4 frames interval, by patching in the repeat the driver lacked

Direct follow-through on this session's own named next step. The prior
result (f1-held-down-measured-no-repeat-via-file-driver.md) concluded the
file driver cannot show menu repeat because its GetKeystroke() never emits
a REPEAT-flagged event, and that the menu's repeat is very likely driven by
that flag rather than raw polled state. Testable, so tested: patched
/canary/src/xenia/hid/file/file_input_driver.h to add opt-in repeat behind
a new --pad_file_repeat cvar (off by default, every other scripted script
unaffected), using the SDL driver's own constants verbatim
(HID_SDL_REPEAT_DELAY/_RATE = 400/100, guest-time ms via
Clock::QueryGuestUptimeMillis) rather than re-deriving them. Incremental
rebuild, ~1 minute (only xenia_main.cc needed recompiling).

Control: the driver's own log confirms repeated keystroke events fire as
designed, zero crashes.

Result: re-ran the identical held-DOWN capture. The cursor that moved once
and stopped in the null result now cycles continuously through the whole
5-item menu, wrapping, for as long as the button is held -- the null result
was real for that driver path, and giving the driver the one thing it
lacked reverses it completely.

Measured at this run's achieved 29.87 fps guest rate: 12 frames (~402ms)
initial delay from the press-triggered step to the first repeat step; 4
frames (~133ms) steady-state interval for 13 of 15 gaps, 3 frames (~100ms)
for the other 2 -- slower than the raw 100ms constant driving it, which
this page flags but does not trace further (most likely the game batches
drained keystrokes per its own frame tick rather than reacting to each one
instantly). The 4-frame figure is what matters for the port: it's what the
cursor visibly does.

Honestly scoped: this measures what the game does when FED repeat events
shaped like the SDL driver's, not a capture through an actual physical
controller (none exists in this container) -- classified measured, not
decoded, for exactly that reason. One run only; the corpus's two-run
minimum isn't met, flagged rather than overclaimed.

f1_hold_capture.py gains an optional `repeat` argument. The Canary source
patch itself lives in /canary, outside this repo (Canary source, not
sylpheed-formats) -- fully described inline in the finding doc so it can be
reapplied if that tree doesn't persist across a container reset.

Reference data: docs/re/data/f1-repeat-cursor-transitions.tsv -- every
transition's frame, guest tick and Y position, not the raw draw log.
This commit is contained in:
sylph-decoder
2026-09-12 12:31:05 +00:00
parent db830a1e75
commit ed9a7d702d
5 changed files with 168 additions and 4 deletions

View File

@@ -15,7 +15,13 @@ Reuses nav_repeat_and_b.py's proven boot-to-menu gate (glyph counting over a
live x11grab pipe) verbatim in spirit -- that gate is the part of this
apparatus already known to work -- and replaces its *measurement* half.
f1_hold_capture.py OUTDIR [hold_secs]
f1_hold_capture.py OUTDIR [hold_secs] [repeat]
`repeat` passes --pad_file_repeat=true, which only exists on a Canary build
carrying the patch described in docs/re/f1-repeat-measured-via-driver-patch.md
(file_input_driver.h: opt-in Keystroke REPEAT at the SDL driver's own 400ms/
100ms constants). Without that patch this flag is simply unrecognised --
check `run-canary --help` output before relying on it after a rebuild.
"""
import os
import subprocess
@@ -26,6 +32,7 @@ import numpy as np
OUT = sys.argv[1]
HOLD_S = float(sys.argv[2]) if len(sys.argv) > 2 else 2.5
REPEAT = len(sys.argv) > 3 and sys.argv[3] in ("1", "true", "repeat")
os.makedirs(OUT, exist_ok=True)
SD = os.path.dirname(os.path.abspath(__file__))
PAD = os.path.join(SD, "pad.py")
@@ -123,10 +130,12 @@ def main():
"run: run-canary --create_profile_if_none=Tag, wait ~5s, kill it",
flush=True)
return
print(f"signing in profile {xuid}", flush=True)
print(f"signing in profile {xuid}, pad_file_repeat={REPEAT}", flush=True)
args = ["run-canary", f"--logged_profile_slot_0_xuid={xuid}"]
if REPEAT:
args.append("--pad_file_repeat=true")
proc = subprocess.Popen(
["run-canary", f"--logged_profile_slot_0_xuid={xuid}"],
cwd=OUT, env=env, stdout=canary_log, stderr=subprocess.STDOUT)
args, cwd=OUT, env=env, stdout=canary_log, stderr=subprocess.STDOUT)
print(f"canary pid={proc.pid}, waiting for window", flush=True)
T0 = time.time()