Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 40 additions & 3 deletions native/boxedwine/resize-host.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <windows.h>

#define SIZE_FILE L"D:\\boxedwine-size.txt"
#define WINDOW_SIZE_FILE L"D:\\boxedwine-window-size.txt"

typedef struct {
DWORD process_id;
Expand All @@ -23,6 +24,30 @@ static BOOL parse_dimension(char **cursor, char *end, unsigned int *value) {
return TRUE;
}

static void append_number(char **cursor, unsigned int value) {
char digits[16];
unsigned int length = 0;
do {
digits[length++] = (char)('0' + value % 10);
value /= 10;
} while (value);
while (length) *(*cursor)++ = digits[--length];
}

static void write_window_size(unsigned int width, unsigned int height) {
char buffer[40];
char *cursor = buffer;
append_number(&cursor, width);
*cursor++ = ' ';
append_number(&cursor, height);
HANDLE file = CreateFileW(WINDOW_SIZE_FILE, GENERIC_WRITE, FILE_SHARE_READ,
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (file == INVALID_HANDLE_VALUE) return;
DWORD written;
WriteFile(file, buffer, (DWORD)(cursor - buffer), &written, NULL);
CloseHandle(file);
}

static BOOL read_window_size(unsigned int *width, unsigned int *height,
unsigned int *native_width,
unsigned int *native_height) {
Expand Down Expand Up @@ -76,6 +101,8 @@ static DWORD run(void) {
WCHAR application_path[MAX_PATH];
unsigned int applied_width = 0;
unsigned int applied_height = 0;
unsigned int reported_width = 0;
unsigned int reported_height = 0;
int width_adjustment = 0;
int height_adjustment = 0;
BOOL calibrated = FALSE;
Expand All @@ -99,12 +126,22 @@ static DWORD run(void) {
unsigned int native_height;
WindowSearch search = {process.dwProcessId, NULL, 0};
EnumWindows(find_process_window, (LPARAM)&search);
if (!search.window ||
!read_window_size(&width, &height, &native_width, &native_height))
if (!search.window) continue;
RECT bounds;
if (GetWindowRect(search.window, &bounds)) {
unsigned int current_width = (unsigned int)(bounds.right - bounds.left);
unsigned int current_height = (unsigned int)(bounds.bottom - bounds.top);
if (current_width != reported_width ||
current_height != reported_height) {
write_window_size(current_width, current_height);
reported_width = current_width;
reported_height = current_height;
}
}
if (!read_window_size(&width, &height, &native_width, &native_height))
continue;
if (width == applied_width && height == applied_height) continue;
if (!calibrated) {
RECT bounds;
if (!GetWindowRect(search.window, &bounds)) continue;
width_adjustment = bounds.right - bounds.left - (int)native_width;
height_adjustment = bounds.bottom - bounds.top - (int)native_height;
Expand Down
87 changes: 87 additions & 0 deletions site/apps/core/boxedwine-preload.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
const RUNTIME_ROOT = "vendor/boxedwine/26R1/";
let preloadPromise;

export const isConstrainedBoxedWineDevice = (hostWindow = window) => {
const connection = hostWindow.navigator.connection;
const mobileUserAgent =
hostWindow.navigator.userAgentData?.mobile === true ||
/Android|iPad|iPhone|iPod|Mobile/i.test(hostWindow.navigator.userAgent);
return Boolean(
connection?.saveData ||
/(?:^|-)2g$/.test(connection?.effectiveType || "") ||
(Number.isFinite(hostWindow.navigator.deviceMemory) &&
hostWindow.navigator.deviceMemory < 4) ||
hostWindow.matchMedia?.("(max-width: 600px)").matches ||
hostWindow.matchMedia?.("(pointer: coarse)").matches ||
mobileUserAgent,
);
};

const runtimeUrl = (path, hostWindow) =>
new URL(`${RUNTIME_ROOT}${path}`, hostWindow.document.baseURI).href;

export const preloadBoxedWineRuntime = async ({
automatic = false,
hostWindow = window,
} = {}) => {
if (automatic && isConstrainedBoxedWineDevice(hostWindow)) return false;
if (preloadPromise) return preloadPromise;

preloadPromise = hostWindow
.fetch(runtimeUrl("preload.json", hostWindow), {
cache: "force-cache",
credentials: "same-origin",
})
.then(async (response) => {
if (!response.ok)
throw new Error("BoxedWine preload manifest unavailable");
const manifest = await response.json();
if (
!Array.isArray(manifest.files) ||
manifest.files.some(
(file) =>
typeof file !== "string" || !/^[a-z0-9][a-z0-9.-]*$/i.test(file),
)
) {
throw new Error("Invalid BoxedWine preload manifest");
}
await Promise.all(
manifest.files.map(async (file) => {
const assetResponse = await hostWindow.fetch(
runtimeUrl(file, hostWindow),
{
cache: "force-cache",
credentials: "same-origin",
},
);
if (!assetResponse.ok) {
throw new Error("BoxedWine preload asset unavailable");
}
await assetResponse.arrayBuffer();
}),
);
return true;
})
.catch((error) => {
preloadPromise = undefined;
throw error;
});
return preloadPromise;
};

export const scheduleBoxedWinePreload = (hostWindow = window) => {
if (isConstrainedBoxedWineDevice(hostWindow)) return false;
const start = () =>
preloadBoxedWineRuntime({ automatic: true, hostWindow }).catch(() => {});
if (typeof hostWindow.requestIdleCallback === "function") {
hostWindow.requestIdleCallback(start, { timeout: 3000 });
} else {
hostWindow.setTimeout(start, 1500);
}
return true;
};

window.XPBoxedWinePreload = Object.freeze({
preload: () => preloadBoxedWineRuntime(),
schedule: () => scheduleBoxedWinePreload(),
});
8 changes: 8 additions & 0 deletions site/apps/core/boxedwine-resize.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export const installBoxedWineResizeBridge = (hostWindow, module) => {
let runtimeInitialized = false;
let windowSizeTimer = null;
let previousWindowSize = "";
let startupWindowReported = false;

const reportWindowSize = () => {
if (!runtimeInitialized || typeof module.FS?.readFile !== "function")
Expand All @@ -31,6 +32,13 @@ export const installBoxedWineResizeBridge = (hostWindow, module) => {
const match = /^(\d+) (\d+)$/.exec(text);
if (!match) return;
previousWindowSize = text;
if (!startupWindowReported) {
startupWindowReported = true;
hostWindow.BoxedWineStartup?.report("window-ready", {
width: Number(match[1]),
height: Number(match[2]),
});
}
hostWindow.parent.postMessage(
{
type: "boxedwine-window-size",
Expand Down
28 changes: 26 additions & 2 deletions site/apps/core/boxedwine.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const RUNTIME_ROOT = "vendor/boxedwine/26R1/";
const ROOT_ARCHIVE = "xp-accessories";

const packageRoot = (packageId) => {
const configuredRoot = window.ASTRO_GAME_ROOTS?.[packageId];
Expand All @@ -17,11 +18,14 @@ const runnerUrl = ({
const url = new URL(`${RUNTIME_ROOT}index.html`, document.baseURI);
url.search = new URLSearchParams({
appRoot: new URL(packageRoot(packageId), document.baseURI).href,
root: ROOT_ARCHIVE,
archive,
executable,
resolution,
frameTop: String(frameTop),
sound: String(sound),
cache: "false",
trace: "false",
});
return url.href;
};
Expand Down Expand Up @@ -52,6 +56,7 @@ export const mountBoxedWineApplication = ({
background = "#000",
scaleOnly = false,
onWindowSize,
onStartupMetric,
nativeFrameWidth = 0,
nativeFrameHeight = 0,
}) => {
Expand Down Expand Up @@ -133,10 +138,29 @@ export const mountBoxedWineApplication = ({
resizeObserver?.observe(host);
frame.addEventListener("load", updateLayout);
const handleWindowMessage = (event) => {
const { type, width, height } = event.data || {};
const { type, width, height, stage, elapsed } = event.data || {};
if (
event.source !== frame.contentWindow ||
event.origin !== new URL(frame.src).origin ||
event.origin !== new URL(frame.src).origin
)
return;
if (
type === "boxedwine-startup" &&
typeof stage === "string" &&
Number.isFinite(elapsed) &&
elapsed >= 0
) {
const metric = { ...event.data };
delete metric.type;
host.dataset.boxedwineStartupStage = metric.stage;
host.dataset.boxedwineStartupElapsed = String(metric.elapsed);
onStartupMetric?.(metric);
host.dispatchEvent(
new window.CustomEvent("boxedwine-startup-metric", { detail: metric }),
);
return;
}
if (
type !== "boxedwine-window-size" ||
!Number.isInteger(width) ||
!Number.isInteger(height) ||
Expand Down
1 change: 1 addition & 0 deletions site/apps/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { accessoryApplications } from "./catalog/accessories.js";
import { systemToolApplications } from "./catalog/system-tools.js";
import { systemApplications } from "./catalog/system-applications.js";
import { createApplicationRegistry } from "./core/registry.js";
import "./core/boxedwine-preload.js";
import { paintApplication } from "./paint/index.js";
import { notepadApplication } from "./notepad/index.js";
import { winampApplication } from "./winamp/index.js";
Expand Down
8 changes: 4 additions & 4 deletions site/iframe/freecell/SOURCES.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"sourceSha256": "fd8c8d42c1581e8767217fe800bfc0d5649c0ad20d754c927d6c763e446d1927",
"package": {
"file": "xp-freecell.zip",
"bytes": 178720,
"sha256": "11262b2bd42a0da786a0ef8facf61c9395bea97768285da2529a27bef14c3252"
"bytes": 178938,
"sha256": "13575f808874364ef95b7693a9a2b9d84fbd7139654174cf1d360a9da2a8f2f5"
},
"files": {
"freecell.exe": {
Expand All @@ -19,8 +19,8 @@
},
"resize-host.exe": {
"source": "native/boxedwine/resize-host.c",
"build": "i686-w64-mingw32-gcc -Os -s -nostdlib -mwindows native/boxedwine/resize-host.c -lkernel32 -luser32 -o resize-host.exe",
"sha256": "adf6e0075ed512a40eaa06ec9f0389f1777e0b885153a4fc1b9eedf070426f5c"
"build": "SOURCE_DATE_EPOCH=0 i686-w64-mingw32-gcc -Os -s -nostdlib -mwindows -Wl,--no-insert-timestamp native/boxedwine/resize-host.c -lkernel32 -luser32 -o resize-host.exe",
"sha256": "deca3199da0d60a398e1ed1865c8a5c0f44516e24d11e863e0b52bd0c6065601"
}
}
}
Expand Down
Binary file modified site/iframe/freecell/xp-freecell.zip
Binary file not shown.
8 changes: 4 additions & 4 deletions site/iframe/solitaire/SOURCES.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"sourceSha256": "fd8c8d42c1581e8767217fe800bfc0d5649c0ad20d754c927d6c763e446d1927",
"package": {
"file": "xp-solitaire.zip",
"bytes": 178239,
"sha256": "d4ba31c630b08b478a60bee49630e6b4fc449ed0a168ca3d4a2bda1c89c4216c"
"bytes": 178457,
"sha256": "e452928237f2a8aeb92b8d5090b604b5d697d54ee2e92b2e5778d8d7b22d9ce1"
},
"files": {
"sol.exe": {
Expand All @@ -19,8 +19,8 @@
},
"resize-host.exe": {
"source": "native/boxedwine/resize-host.c",
"build": "i686-w64-mingw32-gcc -Os -s -nostdlib -mwindows native/boxedwine/resize-host.c -lkernel32 -luser32 -o resize-host.exe",
"sha256": "adf6e0075ed512a40eaa06ec9f0389f1777e0b885153a4fc1b9eedf070426f5c"
"build": "SOURCE_DATE_EPOCH=0 i686-w64-mingw32-gcc -Os -s -nostdlib -mwindows -Wl,--no-insert-timestamp native/boxedwine/resize-host.c -lkernel32 -luser32 -o resize-host.exe",
"sha256": "deca3199da0d60a398e1ed1865c8a5c0f44516e24d11e863e0b52bd0c6065601"
}
}
}
Expand Down
Binary file modified site/iframe/solitaire/xp-solitaire.zip
Binary file not shown.
8 changes: 4 additions & 4 deletions site/iframe/spider-solitaire/SOURCES.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"sourceSha256": "fd8c8d42c1581e8767217fe800bfc0d5649c0ad20d754c927d6c763e446d1927",
"package": {
"file": "xp-spider-solitaire.zip",
"bytes": 271876,
"sha256": "a3a05d06ac7d6abd0680ba1ccdc28a2e7a0d27ca0e461fd80ae47fad7b37d847"
"bytes": 272094,
"sha256": "274230cc0219d579998bda3101784dad8a2126087903e127e6f746f1b9e8abb1"
},
"files": {
"spider.exe": {
Expand All @@ -15,8 +15,8 @@
},
"resize-host.exe": {
"source": "native/boxedwine/resize-host.c",
"build": "i686-w64-mingw32-gcc -Os -s -nostdlib -mwindows native/boxedwine/resize-host.c -lkernel32 -luser32 -o resize-host.exe",
"sha256": "adf6e0075ed512a40eaa06ec9f0389f1777e0b885153a4fc1b9eedf070426f5c"
"build": "SOURCE_DATE_EPOCH=0 i686-w64-mingw32-gcc -Os -s -nostdlib -mwindows -Wl,--no-insert-timestamp native/boxedwine/resize-host.c -lkernel32 -luser32 -o resize-host.exe",
"sha256": "deca3199da0d60a398e1ed1865c8a5c0f44516e24d11e863e0b52bd0c6065601"
}
}
}
Expand Down
Binary file modified site/iframe/spider-solitaire/xp-spider-solitaire.zip
Binary file not shown.
Loading