The previous entry named the next step: "tap (A) once per second and see whether the success rate goes to 1". It was run. The answer is no. 40 presses at 1/s on the title change nothing, and neither does one each of START, B, BACK, X and Y. Every press was delivered (the driver logs down/up for each) and the new [RE-INPUT] diagnostic never fired, so nothing was swallowed — the guest received them and ignored them. Also recorded, because it destroys a run rather than merely wasting it: tapping (A) through the boot (88 presses over the intro) ends on a permanent black screen with no crash and no throw. What is left is a difference in STATE, not in input: the one success came on a title that appeared ~83 s into a warm boot, the failures on titles that appeared after a full attract cycle. "The attract-loop title is a non-interactive presentation" is written down as a hypothesis with the experiment that would settle it — a log_ui_draws capture in each state, looking for ptbtn00.
8.6 KiB
Getting past the title screen in the container — three traps and one blocker
Status: ✅ CONFIRMED for the three traps (each reproduced, and two of them
fixed in the toolkit). 🔴 blocked on reaching the main menu: the guest reads
Ⓐ at the title, and the step behind Ⓐ either crashes it or stalls it.
Written down because each of these presents as "the pad is dead" or "the game ignores input", and each has a completely different cause.
1. F10 opens the emulator's menu bar, and that swallows every guest keystroke
F10 is the RE hotkey (ship capture, and now log_ui_draws). It is also the
toolkit key that opens the application menu bar, and it does both. With that menu
open, XamState::IsUIActive() is true, so XamInputGetKeystrokeEx returns
X_ERROR_SUCCESS with a zeroed keystroke before the driver is ever asked
(xam_input.cc). The guest polls, gets an empty event, and does nothing —
forever, and with no error anywhere.
This cost most of an iteration. Symptom: a run presses F10 to arm a capture,
then every later Ⓐ does nothing; the title sits there while the pad log happily
prints [file-pad] keystroke vk=5800 down. Screenshot
captures/title-with-emulator-menu-open.png
shows the giveaway — Open… / Open Recent / Install Content… / Exit over the
title art. It is easy to miss because the menu is at the far left of the window
and the game is still animating behind it.
Fix: after any F10, click once on the game surface to dismiss the menu.
Escape does not close it. ui_draw_capture.sh now does this.
2. The pad is fine — the driver was never the problem
Worth stating because the file-pad's own history (--hid=file, GetKeystroke
implemented specifically so "PRESS Ⓐ BUTTON" would work) makes it the natural
suspect. It delivers: the log shows vk=5800 down/up per tap, and Ⓐ during a
movie skips the movie, every time. When the title ignores Ⓐ, the pad is not why.
3. Ⓐ at the title needs a signed-in profile — and this container cannot type one in
With no profile, Ⓐ is handled: the guest calls XamShowSigninUI and Xenia
pops its Sign In dialog (captures/title-signin-dialog.png),
ProfileManager: Found 0 Profiles. That dialog is the proof the guest reads the
button.
The dialog cannot be completed from here: "Create Profile" opens an ImGui text
field for a gamertag, and synthetic X key events do not reach it. Tried, all
with the window focused (xdotool getwindowfocus = the game window):
xdotool type --window, xdotool type via XTEST, single xdotool key presses,
clicking the field first. The field stays empty; Create with an empty gamertag
is refused. Mouse clicks do work (they opened the dialog and the sub-panel), so
it is text entry specifically.
Xenia's own Profile ▸ Show Profile Menu panel does have a working Create Profile button, and mouse clicks reach it — but the gamertag it then asks for is
the same ImGui field, so the UI route dead-ends in the same place.
Fix, landed in Canary (auto/re-ui-draw-order): a --create_profile_if_none=<gamertag>
cvar that calls ProfileManager::CreateProfile(tag, autologin, default_xuid) at
startup when no profile exists. default_xuid is fixed at B13EBABEBABEBABE, so
a script can then pass --logged_profile_slot_0_xuid=B13EBABEBABEBABE and be
deterministic. Verified: ProfileManager: RE bootstrap profile 'SylphRE' -> created, then on the next boot Found 1 Profiles / Loaded SylphRE (GUID: B13EBABEBABEBABE) to slot 0, and the Profiles Menu panel shows the user
(captures/xenia-profile-panel-signed-in.png).
The profile persists on disk, so later runs need only
--logged_profile_slot_0_xuid=B13EBABEBABEBABE.
Withdrawn: Ⓐ is not blocked — it works, intermittently
The main menu has been reached
(captures/main-menu-reached.png — NEW GAME /
LOAD GAME / TUTORIAL / OPTIONS / EXTRAS). So the previous version of this entry,
which called the title's Ⓐ a hard blocker, is withdrawn. What is true is
narrower and more annoying: Ⓐ advances the title only sometimes — one success
in roughly four attempts, with the press verifiably delivered every time
([file-pad] keystroke vk=5800 down/up in the log) and no Xenia UI active.
The successful press landed on the first title after boot, before any attract cycle; the failures were on a title that had come round again. That is a hypothesis, not a rule — a later attempt on a first title also failed (the screen flickered to something else for one sample and came back), so timing is involved and the window has not been pinned down.
What was eliminated on the way, so nobody re-walks it
IsUIActive()is now observable.XamInputGetKeystrokeExreturnsX_ERROR_SUCCESSwith a zeroed keystroke before consulting any driver when a Xenia dialog is up — an invisible input blackout. Canary now logs[RE-INPUT] XamInputGetKeystrokeEx swallowed by IsUIActive (dialogs=N …)when that happens. On the runs where Ⓐ failed, it never fires, so the blackout is not the cause there. (It is the cause after F10 — trap 1 above.)- The driver filter is fine. The game polls with
flags = 3, andInputSystem::FilterDriverskeeps a driver whenflags & InputTypeis non-zero; the file pad reportsController = 1, so it is selected. - The game does not query sign-in on the press. Tracing every
Xam*call around a press shows onlyXamInputGetCapabilities,XamInputGetKeystrokeExandXamInputGetState— no content, user or signin call at all.
Two traps in the measuring rig itself, which cost more than the bug
- A FIFO trace consumer that exits stalls the emulator.
--phase_a_event_log_pathwrites 1.5 GB a minute, so it was pointed at a named pipe with agrepon the other end. When that reader goes away, the emulator blocks on the write — and the symptom is exactly "the pad is dead": the guest stops polling, the driver never refreshes, nothing is logged. Two runs' worth of "the driver never reacts" was this, not the game. - The trace's
return_valueis a placeholder.phase_a_bridge::EmitReturnis called asEmitReturn(name, 0)— literally zero — so everykernel.returnevent reads"status": "0x00000000". "Every keystroke call returns SUCCESS" looked like hard evidence for theIsUIActivetheory and means nothing. The theory had to be tested with a real log line instead.
The scripted reproduction was tried, and it failed
"Tap Ⓐ once per second and see whether the success rate goes to 1" was the next
step this entry named. It was run (tools/re-capture/menu_draw_capture.sh), and
the answer is no:
| run | what was pressed | result |
|---|---|---|
| E | one Ⓐ on the first title after boot | main menu |
| F | 40 × Ⓐ at 1/s on the title, then one each of START, B, BACK, X, Y | title, unchanged |
| G | Ⓐ every 4 s through the whole boot (88 presses) | permanent black screen, no crash, never recovers |
In run F every press was delivered — the driver logs keystroke vk=5800 down/up
for each — and the new [RE-INPUT] line never fired, so nothing was swallowed.
The guest received them and ignored them. No button does anything on that
title: START, B, BACK, X and Y are as inert as Ⓐ
(captures/title-unresponsive-attract.png).
Run G is worth its own warning: hammering Ⓐ through the boot is not merely useless, it breaks the run — 88 presses left a black screen that never came back (no crash, no throw, the emulator healthy). The script no longer taps during boot; the intro takes ~3.5 minutes and gets to the title by itself.
So the difference is not the number of presses or the button. The one success came on a title that appeared ~83 s into a warm boot; the failures came on titles that appeared after 292 s and a full attract cycle. The obvious reading — that the attract-loop title is a non-interactive presentation and the post-boot title is the interactive one — is a hypothesis, unconfirmed, and it is what to test next.
Still open
Which title state accepts input, and how to tell them apart from outside. A
log_ui_draws capture in each state would say whether the two composite
differently (the interactive one draws ptbtn00, the PRESS Ⓐ BUTTON plate,
from build 2 — if the attract title omits or replaces it, that is the tell and it
is visible in one capture). Until that is pinned down, reaching the main menu is
a matter of luck, which is not a basis for the second screen's paint-order
capture.