diff --git a/sidecar/prefix.ts b/sidecar/prefix.ts index 051c528..d96feea 100644 --- a/sidecar/prefix.ts +++ b/sidecar/prefix.ts @@ -50,7 +50,19 @@ export type PrefixResult = { ok: boolean; detail: string }; * `winetricks -q vb6run` fetches Microsoft's own `VB6.0-KB290887-X86.exe` and verifies its checksum before * unpacking, which is why this does not carry a redistributable of its own. */ -export async function ensurePrefix(log: (line: string) => void): Promise { +export async function ensurePrefix(log: (line: string) => void, display: string): Promise { + // THE DISPLAY IS NOT OPTIONAL, and this cost a cold install to learn. + // + // `winetricks -q vb6run` runs Microsoft's redistributable, and that installer creates a window even + // when told to be quiet. With no `DISPLAY` wine cannot load a graphics driver, the installer dies, and + // winetricks reports `returned status 43` — which names neither wine nor a display and reads like a + // corrupt download. + // + // This used to run BEFORE Xvfb started, because building a prefix looks like preparation rather than + // something that needs a screen. It never showed up here: this machine's prefix was seeded from an + // older one, so the build path only ever ran on a prefix that was already complete. The first genuine + // cold install — alpha — failed immediately. + const env = { ...WINE_ENV, DISPLAY: display }; // Before the early return as well: an existing prefix may predate this, or have had `z:` restored by a // wine upgrade. It is a few `unlink` calls, so it is cheaper to re-assert than to detect. if (prefixReady()) { @@ -69,14 +81,14 @@ export async function ensurePrefix(log: (line: string) => void): Promise void): Promise { 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 { } } + // 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 ──