build the wine prefix after the display exists, not before

A cold install could never work. `winetricks -q vb6run` runs Microsoft's
redistributable, and that installer creates a window even when told to be
quiet — so with no DISPLAY wine cannot load a graphics driver, the installer
dies, and winetricks reports `returned status 43`. That message names neither
wine nor a display, and reads like a corrupt download.

ensurePrefix ran before Xvfb started, because building a prefix looks like
preparation rather than something that needs a screen.

It never showed up in testing here: this machine's prefix was seeded from the
pre-extraction one, so the build path only ever ran against a prefix that was
already complete. The first genuine cold install — alpha — failed on the first
Start. Diagnosed from `err:winediag:nodrv_CreateWindow` in the wine output,
which is the only line that says what is actually wrong.

Order is now display, prefix, game. ensurePrefix takes the display explicitly
rather than reading the ambient environment, so it cannot silently run without
one again, and a failed build now tears the display down instead of leaving an
Xvfb behind.

Verified by moving this machine's prefix aside and starting cold: prefix built
from nothing, msvbvm60.dll installed, session up with the window at 152x214,
Z: confined, socket mode 700, and RFB 003.008 through the platform relay.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Officer-Session: 656f5c85-e49a-4f1f-b9f3-042a04b4d043
This commit is contained in:
2026-08-23 20:21:32 +00:00
co-authored by Claude Opus 5
parent 4fe9fa2e97
commit 41020b1b65
2 changed files with 24 additions and 9 deletions
+9 -6
View File
@@ -131,12 +131,6 @@ async function startInner(log: (line: string) => void): Promise<SessionStatus> {
return status();
}
const built = await ensurePrefix(log);
if (!built.ok) {
lastError = built.detail;
return status();
}
// ── the display ──
if (!alive(xvfb)) {
display = pickDisplay();
@@ -156,6 +150,15 @@ async function startInner(log: (line: string) => void): Promise<SessionStatus> {
}
}
// The prefix is built HERE, after the display exists and before the game needs it. Not earlier: the VB6
// installer opens a window, so a prefix built without a display fails in a way that names neither.
const built = await ensurePrefix(log, display!);
if (!built.ok) {
lastError = built.detail;
await stop();
return status();
}
const env = { ...WINE_ENV, DISPLAY: display! };
// ── the game ──