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
Original file line number Diff line number Diff line change
@@ -1,21 +1,13 @@
#define WIN32_LEAN_AND_MEAN
#include <windows.h>

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

#if defined(RESIZE_FREECELL)
#define TARGET_EXECUTABLE L"freecell.exe"
#define TARGET_TITLE L"FreeCell"
#define NATIVE_SCREEN_WIDTH 640
#define NATIVE_SCREEN_HEIGHT 480
#elif defined(RESIZE_SPIDER_SOLITAIRE)
#define TARGET_EXECUTABLE L"spider.exe"
#define TARGET_TITLE L"Spider Solitaire"
#define NATIVE_SCREEN_WIDTH 794
#define NATIVE_SCREEN_HEIGHT 601
#else
#error A supported card game must be selected.
#endif
typedef struct {
DWORD process_id;
HWND window;
LONG area;
} WindowSearch;

static BOOL parse_dimension(char **cursor, char *end, unsigned int *value) {
unsigned int result = 0;
Expand All @@ -31,7 +23,9 @@ static BOOL parse_dimension(char **cursor, char *end, unsigned int *value) {
return TRUE;
}

static BOOL read_window_size(unsigned int *width, unsigned int *height) {
static BOOL read_window_size(unsigned int *width, unsigned int *height,
unsigned int *native_width,
unsigned int *native_height) {
char buffer[64];
DWORD bytes_read = 0;
HANDLE file = CreateFileW(SIZE_FILE, GENERIC_READ,
Expand All @@ -43,8 +37,37 @@ static BOOL read_window_size(unsigned int *width, unsigned int *height) {
char *cursor = buffer;
char *end = buffer + bytes_read;
return read && parse_dimension(&cursor, end, width) &&
parse_dimension(&cursor, end, height) && *width >= 100 &&
*height >= 100;
parse_dimension(&cursor, end, height) &&
parse_dimension(&cursor, end, native_width) &&
parse_dimension(&cursor, end, native_height) && *width >= 100 &&
*height >= 100 && *native_width >= 100 && *native_height >= 100;
}

static BOOL CALLBACK find_process_window(HWND window, LPARAM parameter) {
WindowSearch *search = (WindowSearch *)parameter;
DWORD process_id = 0;
RECT bounds;
GetWindowThreadProcessId(window, &process_id);
if (process_id != search->process_id || !IsWindowVisible(window) ||
GetWindow(window, GW_OWNER) || !GetWindowRect(window, &bounds))
return TRUE;
LONG area = (bounds.right - bounds.left) * (bounds.bottom - bounds.top);
if (area > search->area) {
search->window = window;
search->area = area;
}
return TRUE;
}

static BOOL find_target(WCHAR *application_path, WCHAR *filename) {
static const WCHAR *targets[] = {L"sol.exe", L"freecell.exe", L"spider.exe"};
for (unsigned int index = 0; index < sizeof(targets) / sizeof(targets[0]);
++index) {
lstrcpyW(filename, targets[index]);
if (GetFileAttributesW(application_path) != INVALID_FILE_ATTRIBUTES)
return TRUE;
}
return FALSE;
}

static DWORD run(void) {
Expand All @@ -62,8 +85,8 @@ static DWORD run(void) {
if (!path_length || path_length == MAX_PATH) return 1;
WCHAR *filename = application_path + path_length;
while (filename > application_path && filename[-1] != L'\\') --filename;
if (filename == application_path) return 1;
lstrcpyW(filename, TARGET_EXECUTABLE);
if (filename == application_path || !find_target(application_path, filename))
return 1;
if (!CreateProcessW(application_path, NULL, NULL, NULL, FALSE, 0, NULL, NULL,
&startup, &process))
return 1;
Expand All @@ -72,17 +95,22 @@ static DWORD run(void) {
while (WaitForSingleObject(process.hProcess, 50) == WAIT_TIMEOUT) {
unsigned int width;
unsigned int height;
HWND application = FindWindowW(NULL, TARGET_TITLE);
if (!application || !read_window_size(&width, &height)) continue;
unsigned int native_width;
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))
continue;
if (width == applied_width && height == applied_height) continue;
if (!calibrated) {
RECT bounds;
if (!GetWindowRect(application, &bounds)) continue;
width_adjustment = bounds.right - bounds.left - NATIVE_SCREEN_WIDTH;
height_adjustment = bounds.bottom - bounds.top - NATIVE_SCREEN_HEIGHT;
if (!GetWindowRect(search.window, &bounds)) continue;
width_adjustment = bounds.right - bounds.left - (int)native_width;
height_adjustment = bounds.bottom - bounds.top - (int)native_height;
calibrated = TRUE;
}
if (SetWindowPos(application, NULL, 0, 0, (int)width + width_adjustment,
if (SetWindowPos(search.window, NULL, 0, 0, (int)width + width_adjustment,
(int)height + height_adjustment,
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE)) {
applied_width = width;
Expand Down
84 changes: 0 additions & 84 deletions native/solitaire/resize-host.c

This file was deleted.

39 changes: 39 additions & 0 deletions site/apps/core/boxedwine-card-game.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { defineApplication } from "./application.js";
import { mountBoxedWineApplication } from "./boxedwine.js";

export const defineBoxedWineCardGame = ({
id,
title,
icon,
archive,
width,
height,
nativeWidth = width - 6,
nativeHeight = height - 32,
}) =>
defineApplication({
id: `__${id}`,
title,
icon,
kind: "native-game",
deepLinkId: id,
offlineGameId: id,
window: {
width,
height,
fitToWorkArea: true,
resizable: true,
maximizable: true,
},
mount: () =>
mountBoxedWineApplication({
title: `Windows XP ${title}`,
packageId: id,
archive,
executable: "resize-host.exe",
nativeWidth,
nativeHeight,
frameTop: 32,
background: "#27811f",
}),
});
17 changes: 12 additions & 5 deletions site/apps/core/boxedwine-resize.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
const isResizeMessage = (event, origin) => {
const { type, width, height } = event.data || {};
const { type, width, height, baseWidth, baseHeight } = event.data || {};
return (
event.origin === origin &&
type === "boxedwine-framebuffer-resize" &&
Number.isInteger(width) &&
Number.isInteger(height) &&
Number.isInteger(baseWidth) &&
Number.isInteger(baseHeight) &&
width > 0 &&
height > 0
height > 0 &&
baseWidth > 0 &&
baseHeight > 0
);
};

Expand All @@ -22,12 +26,15 @@ export const installBoxedWineResizeBridge = (hostWindow, module) => {
typeof module.FS?.writeFile !== "function"
)
return;
const { width, height } = pendingResize;
const sizePath = "/d_drive/solitaire-size.txt";
const { width, height, baseWidth, baseHeight } = pendingResize;
const sizePath = "/d_drive/boxedwine-size.txt";
const temporaryPath = `${sizePath}.tmp`;

module._boxedwine_resize_screen(width, height);
module.FS.writeFile(temporaryPath, `${width} ${height}`);
module.FS.writeFile(
temporaryPath,
`${width} ${height} ${baseWidth} ${baseHeight}`,
);
try {
module.FS.unlink(sizePath);
} catch {
Expand Down
7 changes: 6 additions & 1 deletion site/apps/core/boxedwine.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,12 @@ export const mountBoxedWineApplication = ({
frame.style.transformOrigin = "";
}
frame.contentWindow?.postMessage(
{ type: "boxedwine-framebuffer-resize", ...framebuffer },
{
type: "boxedwine-framebuffer-resize",
...framebuffer,
baseWidth: nativeWidth,
baseHeight: nativeHeight + frameTop,
},
new URL(frame.src).origin,
);
};
Expand Down
36 changes: 8 additions & 28 deletions site/apps/solitaire/index.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,12 @@
import { mountBoxedWineApplication } from "../core/boxedwine.js";
import { defineApplication } from "../core/application.js";
import { defineBoxedWineCardGame } from "../core/boxedwine-card-game.js";

const mountSolitaire = () =>
mountBoxedWineApplication({
title: "Windows XP Solitaire",
packageId: "solitaire",
archive: "xp-solitaire",
executable: "resize-host.exe",
nativeWidth: 586,
nativeHeight: 406,
frameTop: 32,
background: "#27811f",
});

export const solitaireApplication = defineApplication({
id: "__solitaire",
export const solitaireApplication = defineBoxedWineCardGame({
id: "solitaire",
title: "Solitaire",
icon: "Solitaire.png",
kind: "native-game",
deepLinkId: "solitaire",
offlineGameId: "solitaire",
window: {
width: 592,
height: 438,
className: "xp-native-solitaire-window",
fitToWorkArea: true,
resizable: true,
maximizable: true,
},
mount: mountSolitaire,
archive: "xp-solitaire",
width: 592,
height: 438,
nativeWidth: 586,
nativeHeight: 406,
});
45 changes: 4 additions & 41 deletions site/apps/xp-card-games/index.js
Original file line number Diff line number Diff line change
@@ -1,45 +1,7 @@
import { defineApplication } from "../core/application.js";
import { mountBoxedWineApplication } from "../core/boxedwine.js";

const defineXpCardGame = ({
id,
title,
icon,
archive,
width,
height,
nativeWidth = width - 6,
nativeHeight = height - 31,
}) =>
defineApplication({
id: `__${id}`,
title,
icon,
kind: "native-game",
deepLinkId: id,
offlineGameId: id,
window: {
width,
height,
fitToWorkArea: true,
resizable: true,
maximizable: true,
},
mount: () =>
mountBoxedWineApplication({
title: `Windows XP ${title}`,
packageId: id,
archive,
executable: "resize-host.exe",
nativeWidth,
nativeHeight,
frameTop: 32,
background: "#27811f",
}),
});
import { defineBoxedWineCardGame } from "../core/boxedwine-card-game.js";

export const xpCardGameApplications = [
defineXpCardGame({
defineBoxedWineCardGame({
id: "freecell",
title: "FreeCell",
icon: "FreeCell.png",
Expand All @@ -49,12 +11,13 @@ export const xpCardGameApplications = [
nativeWidth: 640,
nativeHeight: 448,
}),
defineXpCardGame({
defineBoxedWineCardGame({
id: "spider-solitaire",
title: "Spider Solitaire",
icon: "SpiderSolitaire.png",
archive: "xp-spider-solitaire",
width: 800,
height: 600,
nativeHeight: 569,
}),
];
Loading