From c9a5c2dfc717f6f87981d693904eb20dbd54f98b Mon Sep 17 00:00:00 2001 From: astrovm <~@4st.li> Date: Thu, 13 Aug 2026 02:37:13 -0300 Subject: [PATCH] Run the original Windows XP Calculator --- native/calculator/window-host.c | 82 +++++ site/apps/calculator/index.js | 36 ++ site/apps/catalog/accessories.js | 9 - site/apps/core/boxedwine-resize.js | 37 +- site/apps/core/boxedwine.js | 51 ++- site/apps/index.js | 2 + site/apps/programs/define-program.js | 1 - site/apps/programs/renderers/calculator.js | 393 --------------------- site/apps/programs/renderers/index.js | 1 - site/css/shell/themes.css | 210 +---------- site/iframe/calculator/SOURCES.json | 27 ++ site/iframe/calculator/xp-calculator.zip | Bin 0 -> 69002 bytes tests/calculator.test.ts | 252 ++++++++----- tests/shell-behavior.test.ts | 17 +- 14 files changed, 395 insertions(+), 723 deletions(-) create mode 100644 native/calculator/window-host.c create mode 100644 site/apps/calculator/index.js delete mode 100644 site/apps/programs/renderers/calculator.js create mode 100644 site/iframe/calculator/SOURCES.json create mode 100644 site/iframe/calculator/xp-calculator.zip diff --git a/native/calculator/window-host.c b/native/calculator/window-host.c new file mode 100644 index 00000000..eb2e11b6 --- /dev/null +++ b/native/calculator/window-host.c @@ -0,0 +1,82 @@ +#define WIN32_LEAN_AND_MEAN +#include + +#define SIZE_FILE L"D:\\boxedwine-window-size.txt" + +typedef struct { + DWORD process_id; + HWND window; +} WindowSearch; + +static BOOL CALLBACK find_process_window(HWND window, LPARAM parameter) { + WindowSearch *search = (WindowSearch *)parameter; + DWORD process_id = 0; + GetWindowThreadProcessId(window, &process_id); + if (process_id == search->process_id && IsWindowVisible(window) && + !GetWindow(window, GW_OWNER)) { + search->window = window; + return FALSE; + } + 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(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 DWORD run(void) { + STARTUPINFOW startup = {0}; + PROCESS_INFORMATION process = {0}; + WCHAR calculator_path[MAX_PATH]; + unsigned int previous_width = 0; + unsigned int previous_height = 0; + + startup.cb = sizeof(startup); + DWORD path_length = GetModuleFileNameW(NULL, calculator_path, MAX_PATH); + if (!path_length || path_length == MAX_PATH) return 1; + WCHAR *filename = calculator_path + path_length; + while (filename > calculator_path && filename[-1] != L'\\') --filename; + if (filename == calculator_path) return 1; + lstrcpyW(filename, L"calc.exe"); + if (!CreateProcessW(calculator_path, NULL, NULL, NULL, FALSE, 0, NULL, NULL, + &startup, &process)) + return 1; + + CloseHandle(process.hThread); + while (WaitForSingleObject(process.hProcess, 50) == WAIT_TIMEOUT) { + WindowSearch search = {process.dwProcessId, NULL}; + RECT bounds; + EnumWindows(find_process_window, (LPARAM)&search); + if (!search.window || !GetWindowRect(search.window, &bounds)) continue; + unsigned int width = (unsigned int)(bounds.right - bounds.left); + unsigned int height = (unsigned int)(bounds.bottom - bounds.top); + if (width == previous_width && height == previous_height) continue; + write_window_size(width, height); + previous_width = width; + previous_height = height; + } + CloseHandle(process.hProcess); + return 0; +} + +void WinMainCRTStartup(void) { ExitProcess(run()); } diff --git a/site/apps/calculator/index.js b/site/apps/calculator/index.js new file mode 100644 index 00000000..37215897 --- /dev/null +++ b/site/apps/calculator/index.js @@ -0,0 +1,36 @@ +import { defineApplication } from "../core/application.js"; +import { mountBoxedWineApplication } from "../core/boxedwine.js"; + +export const calculatorApplication = defineApplication({ + id: "__calculator", + title: "Calculator", + icon: "Calculator.png", + kind: "native-game", + window: { + width: 260, + height: 260, + resizable: false, + maximizable: false, + className: "xp-calculator-window", + }, + mount: (context) => + mountBoxedWineApplication({ + title: "Windows XP Calculator", + packageId: "calculator", + archive: "xp-calculator", + executable: "window-host.exe", + nativeWidth: 254, + nativeHeight: 229, + frameTop: 32, + nativeFrameWidth: 6, + nativeFrameHeight: 31, + background: "#ece9d8", + onWindowSize(width, height) { + const workArea = context.getDesktopSize(); + context.setSize( + workArea.width > 0 ? Math.min(width, workArea.width) : width, + workArea.height > 0 ? Math.min(height, workArea.height) : height, + ); + }, + }), +}); diff --git a/site/apps/catalog/accessories.js b/site/apps/catalog/accessories.js index beabf173..dae59504 100644 --- a/site/apps/catalog/accessories.js +++ b/site/apps/catalog/accessories.js @@ -4,15 +4,6 @@ const program = (id, title, icon, kind, extra = {}) => defineProgram({ id, title, icon, kind, ...extra }); export const accessoryApplications = [ - program("__calculator", "Calculator", "Calculator.png", "calculator", { - window: { - width: 260, - height: 260, - resizable: false, - maximizable: false, - className: "xp-calculator-window", - }, - }), program( "__command-prompt", "Command Prompt", diff --git a/site/apps/core/boxedwine-resize.js b/site/apps/core/boxedwine-resize.js index 9802aafc..6ec2cc5a 100644 --- a/site/apps/core/boxedwine-resize.js +++ b/site/apps/core/boxedwine-resize.js @@ -17,6 +17,32 @@ const isResizeMessage = (event, origin) => { export const installBoxedWineResizeBridge = (hostWindow, module) => { let pendingResize = null; let runtimeInitialized = false; + let windowSizeTimer = null; + let previousWindowSize = ""; + + const reportWindowSize = () => { + if (!runtimeInitialized || typeof module.FS?.readFile !== "function") + return; + try { + const text = module.FS.readFile("/d_drive/boxedwine-window-size.txt", { + encoding: "utf8", + }).trim(); + if (text === previousWindowSize) return; + const match = /^(\d+) (\d+)$/.exec(text); + if (!match) return; + previousWindowSize = text; + hostWindow.parent.postMessage( + { + type: "boxedwine-window-size", + width: Number(match[1]), + height: Number(match[2]), + }, + hostWindow.location.origin, + ); + } catch { + // Most applications do not publish their native window size. + } + }; const applyResize = () => { if ( @@ -53,8 +79,17 @@ export const installBoxedWineResizeBridge = (hostWindow, module) => { onRuntimeInitialized?.(); runtimeInitialized = true; applyResize(); + if ( + typeof module.FS?.readFile === "function" && + typeof hostWindow.setInterval === "function" + ) { + windowSizeTimer = hostWindow.setInterval(reportWindowSize, 100); + } }; hostWindow.addEventListener("message", onMessage); - return () => hostWindow.removeEventListener("message", onMessage); + return () => { + hostWindow.removeEventListener("message", onMessage); + if (windowSizeTimer !== null) hostWindow.clearInterval(windowSizeTimer); + }; }; diff --git a/site/apps/core/boxedwine.js b/site/apps/core/boxedwine.js index 5ceec910..6c19d5d4 100644 --- a/site/apps/core/boxedwine.js +++ b/site/apps/core/boxedwine.js @@ -51,7 +51,12 @@ export const mountBoxedWineApplication = ({ sound = true, background = "#000", scaleOnly = false, + onWindowSize, + nativeFrameWidth = 0, + nativeFrameHeight = 0, }) => { + let activeNativeWidth = nativeWidth; + let activeNativeHeight = nativeHeight; const host = document.createElement("div"); host.className = "window-content boxedwine-app-host"; host.style.background = background; @@ -80,26 +85,29 @@ export const mountBoxedWineApplication = ({ host.appendChild(frame); const updateLayout = () => { - const hostSize = getHostSize(host, nativeWidth, nativeHeight); + const hostSize = getHostSize(host, activeNativeWidth, activeNativeHeight); const shouldScale = scaleOnly || - hostSize.width < nativeWidth || - hostSize.height < nativeHeight; + hostSize.width < activeNativeWidth || + hostSize.height < activeNativeHeight; const scale = shouldScale ? Math.min( 1, - hostSize.width / nativeWidth, - hostSize.height / nativeHeight, + hostSize.width / activeNativeWidth, + hostSize.height / activeNativeHeight, ) : 1; const framebuffer = shouldScale - ? { width: nativeWidth, height: nativeHeight + frameTop } + ? { + width: activeNativeWidth, + height: activeNativeHeight + frameTop, + } : { width: hostSize.width, height: hostSize.height + frameTop }; if (shouldScale) { - frame.style.width = `${nativeWidth}px`; - frame.style.height = `${nativeHeight}px`; - frame.style.left = `${Math.max(0, (hostSize.width - nativeWidth * scale) / 2)}px`; + frame.style.width = `${activeNativeWidth}px`; + frame.style.height = `${activeNativeHeight}px`; + frame.style.left = `${Math.max(0, (hostSize.width - activeNativeWidth * scale) / 2)}px`; frame.style.transform = `scale(${scale})`; frame.style.transformOrigin = "top left"; } else { @@ -113,8 +121,8 @@ export const mountBoxedWineApplication = ({ { type: "boxedwine-framebuffer-resize", ...framebuffer, - baseWidth: nativeWidth, - baseHeight: nativeHeight + frameTop, + baseWidth: activeNativeWidth, + baseHeight: activeNativeHeight + frameTop, }, new URL(frame.src).origin, ); @@ -124,12 +132,33 @@ export const mountBoxedWineApplication = ({ : null; resizeObserver?.observe(host); frame.addEventListener("load", updateLayout); + const handleWindowMessage = (event) => { + const { type, width, height } = event.data || {}; + if ( + event.source !== frame.contentWindow || + event.origin !== new URL(frame.src).origin || + type !== "boxedwine-window-size" || + !Number.isInteger(width) || + !Number.isInteger(height) || + width < 100 || + height < 100 + ) + return; + if (nativeFrameWidth || nativeFrameHeight) { + activeNativeWidth = Math.max(1, width - nativeFrameWidth); + activeNativeHeight = Math.max(1, height - nativeFrameHeight); + } + onWindowSize?.(width, height); + updateLayout(); + }; + window.addEventListener("message", handleWindowMessage); updateLayout(); return { element: host, unmount() { resizeObserver?.disconnect(); + window.removeEventListener("message", handleWindowMessage); frame.removeEventListener("load", updateLayout); frame.src = "about:blank"; frame.remove(); diff --git a/site/apps/index.js b/site/apps/index.js index 90bb2bf9..25fc45ce 100644 --- a/site/apps/index.js +++ b/site/apps/index.js @@ -9,11 +9,13 @@ import { minesweeperApplication } from "./minesweeper/index.js"; import { solitaireApplication } from "./solitaire/index.js"; import { pinballApplication } from "./pinball/index.js"; import { xpCardGameApplications } from "./xp-card-games/index.js"; +import { calculatorApplication } from "./calculator/index.js"; export const applicationRegistry = createApplicationRegistry([ ...accessoryApplications, ...systemToolApplications, ...systemApplications, + calculatorApplication, minesweeperApplication, notepadApplication, paintApplication, diff --git a/site/apps/programs/define-program.js b/site/apps/programs/define-program.js index 34df2a82..5234074b 100644 --- a/site/apps/programs/define-program.js +++ b/site/apps/programs/define-program.js @@ -2,7 +2,6 @@ import { defineApplication } from "../core/application.js"; import * as renderers from "./renderers/index.js"; const PROGRAM_RENDERERS = Object.freeze({ - calculator: renderers.renderCalculator, terminal: renderers.renderTerminal, volume: renderers.renderVolume, }); diff --git a/site/apps/programs/renderers/calculator.js b/site/apps/programs/renderers/calculator.js deleted file mode 100644 index 9a42e645..00000000 --- a/site/apps/programs/renderers/calculator.js +++ /dev/null @@ -1,393 +0,0 @@ -import { createProgramRoot } from "../ui.js"; - -const MAX_DIGITS = 32; - -const formatNumber = (value) => { - if (!Number.isFinite(value)) return "Cannot divide by zero."; - if (Object.is(value, -0)) return "0"; - const absoluteValue = Math.abs(value); - if (absoluteValue >= 1e16 || (absoluteValue > 0 && absoluteValue < 1e-15)) { - return value - .toExponential(15) - .replace(/\.0+e/, "e") - .replace(/(\.\d*?)0+e/, "$1e"); - } - return String(Number(value.toPrecision(15))); -}; - -const BUTTONS = [ - ["MC", "memory-clear", "memory"], - ["7", "7", "number"], - ["8", "8", "number"], - ["9", "9", "number"], - ["/", "divide", "operator"], - ["sqrt", "square-root", "function"], - ["MR", "memory-recall", "memory"], - ["4", "4", "number"], - ["5", "5", "number"], - ["6", "6", "number"], - ["*", "multiply", "operator"], - ["%", "percent", "function"], - ["MS", "memory-store", "memory"], - ["1", "1", "number"], - ["2", "2", "number"], - ["3", "3", "number"], - ["-", "subtract", "operator"], - ["1/x", "reciprocal", "function"], - ["M+", "memory-add", "memory"], - ["0", "0", "number"], - ["+/-", "sign", "number"], - [".", "decimal", "number"], - ["+", "add", "operator"], - ["=", "equals", "operator"], -]; - -const createMenu = (label, items) => { - const group = document.createElement("div"); - group.className = "xp-calculator-menu-group"; - const trigger = document.createElement("button"); - trigger.type = "button"; - trigger.className = "xp-calculator-menu-trigger"; - trigger.textContent = label; - trigger.setAttribute("aria-haspopup", "menu"); - trigger.setAttribute("aria-expanded", "false"); - const popup = document.createElement("div"); - popup.className = "xp-calculator-popup"; - popup.hidden = true; - popup.setAttribute("role", "menu"); - for (const item of items) { - if (item.separator) { - const separator = document.createElement("hr"); - separator.setAttribute("role", "separator"); - popup.appendChild(separator); - continue; - } - const button = document.createElement("button"); - button.type = "button"; - button.dataset.command = item.command; - button.disabled = Boolean(item.disabled); - button.setAttribute("role", item.role || "menuitem"); - if (item.checked !== undefined) { - button.setAttribute("aria-checked", String(item.checked)); - } - button.innerHTML = `${item.checked ? "●" : ""}${item.label}${item.key || ""}`; - popup.appendChild(button); - } - group.append(trigger, popup); - return group; -}; - -export const renderCalculator = (context, program) => { - const content = createProgramRoot(program); - content.tabIndex = -1; - content.innerHTML = ` - -
- -
- - - - -
-
-
`; - - const display = content.querySelector(".xp-calculator-display"); - const keys = content.querySelector(".xp-calculator-keys"); - const memoryIndicator = content.querySelector( - ".xp-calculator-memory-indicator", - ); - let entry = "0"; - let accumulator = null; - let pendingOperator = null; - let replaceEntry = true; - let lastOperator = null; - let lastOperand = null; - let memory = 0; - let error = false; - - const showEntry = () => { - display.value = error ? entry : entry.includes(".") ? entry : `${entry}.`; - }; - - const setValue = (value) => { - entry = formatNumber(value); - error = !Number.isFinite(value); - replaceEntry = true; - showEntry(); - }; - - const currentValue = () => Number(entry); - - const calculate = (left, operator, right) => { - if (operator === "add") return left + right; - if (operator === "subtract") return left - right; - if (operator === "multiply") return left * right; - if (operator === "divide") return right === 0 ? Number.NaN : left / right; - return right; - }; - - const clearAll = () => { - entry = "0"; - accumulator = null; - pendingOperator = null; - lastOperator = null; - lastOperand = null; - replaceEntry = true; - error = false; - showEntry(); - }; - - const inputDigit = (digit) => { - if (error) clearAll(); - if (replaceEntry) { - entry = digit; - replaceEntry = false; - } else if (entry === "0") { - entry = digit; - } else if (entry.replace(/[-.]/g, "").length < MAX_DIGITS) { - entry += digit; - } - showEntry(); - }; - - const inputDecimal = () => { - if (error) clearAll(); - if (replaceEntry) { - entry = "0."; - replaceEntry = false; - } else if (!entry.includes(".")) { - entry += "."; - } - showEntry(); - }; - - const selectOperator = (operator) => { - if (error) return; - const value = currentValue(); - if (pendingOperator && !replaceEntry) { - const result = calculate(accumulator, pendingOperator, value); - setValue(result); - accumulator = result; - } else if (accumulator === null) { - accumulator = value; - } - pendingOperator = operator; - replaceEntry = true; - }; - - const equals = () => { - if (error) return; - let operator = pendingOperator; - let operand = currentValue(); - if (!operator && lastOperator) { - operator = lastOperator; - operand = lastOperand; - } - if (!operator) return; - const left = accumulator ?? currentValue(); - const result = calculate(left, operator, operand); - lastOperator = operator; - lastOperand = operand; - pendingOperator = null; - accumulator = result; - setValue(result); - }; - - const runCommand = (command) => { - if (/^\d$/.test(command)) { - inputDigit(command); - return; - } - if (command === "decimal") { - inputDecimal(); - return; - } - if (command === "clear") { - clearAll(); - return; - } - if (command === "clear-entry") { - entry = "0"; - replaceEntry = true; - error = false; - showEntry(); - return; - } - if (command === "backspace") { - if (!replaceEntry && !error) { - entry = entry.length > 1 ? entry.slice(0, -1) : "0"; - if (entry === "-") entry = "0"; - showEntry(); - } - return; - } - if (["add", "subtract", "multiply", "divide"].includes(command)) { - selectOperator(command); - return; - } - if (command === "equals") { - equals(); - return; - } - if (error) return; - const value = currentValue(); - if (command === "sign") { - if (value !== 0) - entry = entry.startsWith("-") ? entry.slice(1) : `-${entry}`; - showEntry(); - } else if (command === "square-root") { - setValue(value < 0 ? Number.NaN : Math.sqrt(value)); - } else if (command === "reciprocal") { - setValue(value === 0 ? Number.NaN : 1 / value); - } else if (command === "percent") { - const percentValue = - pendingOperator === "add" || pendingOperator === "subtract" - ? ((accumulator ?? 0) * value) / 100 - : value / 100; - setValue(percentValue); - } else if (command === "memory-clear") { - memory = 0; - memoryIndicator.textContent = ""; - } else if (command === "memory-recall") { - setValue(memory); - } else if (command === "memory-store") { - memory = value; - memoryIndicator.textContent = memory === 0 ? "" : "M"; - replaceEntry = true; - } else if (command === "memory-add") { - memory += value; - memoryIndicator.textContent = memory === 0 ? "" : "M"; - replaceEntry = true; - } - }; - - for (const [label, command, type] of BUTTONS) { - const button = document.createElement("button"); - button.type = "button"; - button.textContent = label; - button.dataset.command = command; - button.dataset.keyType = type; - keys.appendChild(button); - } - - content.querySelectorAll("[data-command]").forEach((button) => { - button.addEventListener("click", () => runCommand(button.dataset.command)); - }); - - const menuBar = content.querySelector(".xp-calculator-menu-bar"); - const menus = [ - createMenu("Edit", [ - { label: "Copy", command: "copy", key: "Ctrl+C" }, - { label: "Paste", command: "paste", key: "Ctrl+V" }, - ]), - createMenu("View", [ - { - label: "Standard", - command: "standard", - role: "menuitemradio", - checked: true, - }, - { - label: "Scientific", - command: "scientific", - role: "menuitemradio", - checked: false, - disabled: true, - }, - { separator: true }, - { - label: "Digit grouping", - command: "grouping", - role: "menuitemcheckbox", - checked: false, - }, - ]), - createMenu("Help", [ - { label: "Help Topics", command: "help-topics" }, - { separator: true }, - { label: "About Calculator", command: "about" }, - ]), - ]; - menuBar.append(...menus); - - const closeMenus = () => { - content.querySelectorAll(".xp-calculator-popup").forEach((popup) => { - popup.hidden = true; - }); - content - .querySelectorAll(".xp-calculator-menu-trigger") - .forEach((trigger) => { - trigger.setAttribute("aria-expanded", "false"); - }); - }; - - content.querySelectorAll(".xp-calculator-menu-trigger").forEach((trigger) => { - trigger.addEventListener("click", () => { - const popup = trigger.nextElementSibling; - const open = popup.hidden; - closeMenus(); - popup.hidden = !open; - trigger.setAttribute("aria-expanded", String(open)); - }); - }); - - content.querySelectorAll(".xp-calculator-popup button").forEach((button) => { - button.addEventListener("click", async () => { - closeMenus(); - if (button.dataset.command === "copy") { - await navigator.clipboard?.writeText(entry); - } else if (button.dataset.command === "paste") { - const value = await navigator.clipboard?.readText(); - if (value && Number.isFinite(Number(value.trim()))) { - entry = String(Number(value.trim())); - replaceEntry = true; - error = false; - showEntry(); - } - } else if (button.dataset.command === "help-topics") { - context.dialogs.alert( - "Use the number and operation buttons to perform a calculation.", - "Calculator Help", - "info", - ); - } else if (button.dataset.command === "about") { - context.dialogs.alert( - "Microsoft Calculator\nVersion 5.1", - "About Calculator", - "info", - ); - } - }); - }); - - const keyboardCommands = { - Enter: "equals", - "=": "equals", - Escape: "clear", - Backspace: "backspace", - Delete: "clear-entry", - "+": "add", - "-": "subtract", - "*": "multiply", - "/": "divide", - "%": "percent", - ".": "decimal", - ",": "decimal", - }; - content.addEventListener("keydown", (event) => { - const command = /^\d$/.test(event.key) - ? event.key - : keyboardCommands[event.key]; - if (!command) return; - event.preventDefault(); - runCommand(command); - }); - - content.addEventListener("pointerdown", (event) => { - if (!event.target.closest(".xp-calculator-menu-group")) closeMenus(); - }); - showEntry(); - return content; -}; diff --git a/site/apps/programs/renderers/index.js b/site/apps/programs/renderers/index.js index 76f921a0..7c86ef97 100644 --- a/site/apps/programs/renderers/index.js +++ b/site/apps/programs/renderers/index.js @@ -1,3 +1,2 @@ -export { renderCalculator } from "./calculator.js"; export { renderTerminal } from "./terminal.js"; export { renderVolume } from "./volume.js"; diff --git a/site/css/shell/themes.css b/site/css/shell/themes.css index 9ee27511..3d885147 100644 --- a/site/css/shell/themes.css +++ b/site/css/shell/themes.css @@ -694,217 +694,11 @@ html[data-xp-appearance="classic"] .start-program-flyout button:focus-visible { min-height: min(260px, calc(100% - 8px)); } -.xp-native-calculator { - position: relative; - height: 100%; - padding: 0; - overflow: hidden; - background: #ece9d8; - user-select: none; -} - -.xp-native-calculator button { - font: - 11px Tahoma, - sans-serif; -} - -.xp-calculator-menu-bar { - position: relative; - z-index: 3; - display: flex; - height: 21px; - padding-left: 0; +.xp-calculator-window .window-content, +.xp-calculator-window .boxedwine-app-host { background: #ece9d8; } -.xp-calculator-menu-group { - position: relative; -} - -.xp-calculator-menu-trigger { - box-sizing: border-box; - height: 21px; - padding: 1px 3px 2px; - border: 1px solid transparent; - background: transparent; - color: #000; -} - -.xp-calculator-menu-group:nth-child(1) .xp-calculator-menu-trigger { - width: 30px; -} - -.xp-calculator-menu-group:nth-child(2) .xp-calculator-menu-trigger, -.xp-calculator-menu-group:nth-child(3) .xp-calculator-menu-trigger { - width: 34px; -} - -.xp-calculator-menu-trigger:hover, -.xp-calculator-menu-trigger[aria-expanded="true"] { - border-color: #316ac5; - background: #316ac5; - color: #fff; -} - -.xp-calculator-popup { - position: absolute; - top: 20px; - left: 0; - min-width: 164px; - padding: 2px; - border: 1px solid #808080; - background: #fff; - box-shadow: 2px 2px 3px rgb(0 0 0 / 35%); -} - -.xp-calculator-popup[hidden] { - display: none; -} - -.xp-calculator-popup button { - display: grid; - grid-template-columns: 15px 1fr auto; - width: 100%; - min-height: 20px; - padding: 2px 7px 2px 2px; - border: 0; - background: transparent; - color: #000; - text-align: left; -} - -.xp-calculator-popup button:hover:not(:disabled) { - background: #316ac5; - color: #fff; -} - -.xp-calculator-popup button:disabled { - color: #aca899; -} - -.xp-calculator-popup kbd { - margin-left: 20px; - font: inherit; -} - -.xp-calculator-popup hr { - height: 1px; - margin: 2px 1px 2px 17px; - border: 0; - background: #aca899; -} - -.xp-calculator-body { - position: relative; - height: 209px; - padding: 0; -} - -.xp-calculator-display { - box-sizing: border-box; - position: absolute; - top: 0; - left: 7px; - width: 239px; - height: 23px; - margin: 0; - padding: 2px 4px; - border: 1px solid #7f9db9; - border-radius: 0; - background: #fff; - color: #000; - text-align: right; - font: - 11px Tahoma, - sans-serif; -} - -.xp-calculator-clear-row { - position: absolute; - top: 36px; - left: 11px; - width: 234px; - height: 28px; -} - -.xp-calculator-clear-row .xp-calculator-memory-indicator { - box-sizing: border-box; - width: 26px; - height: 27px; - margin: 1px 0 0; - border: 1px solid; - border-color: #7f7f7f #fff #fff #7f7f7f; - color: #f00; - text-align: center; - font: - 11px/24px Tahoma, - sans-serif; -} - -.xp-calculator-clear-row button { - position: absolute; - top: 0; - width: 61px; - height: 28px; -} - -.xp-calculator-clear-row button:nth-of-type(1) { - left: 43px; -} - -.xp-calculator-clear-row button:nth-of-type(2) { - left: 108px; -} - -.xp-calculator-clear-row button:nth-of-type(3) { - left: 173px; -} - -.xp-calculator-clear-row button, -.xp-calculator-keys button { - box-sizing: border-box; - height: 27px; - padding: 1px 2px; - border: 1px solid #003c74; - border-radius: 3px; - background: linear-gradient(180deg, #fff, #f7f6f1 8%, #ece9d8 88%, #d6d0c4); - box-shadow: inset 1px 1px #fff; -} - -.xp-calculator-clear-row button:active, -.xp-calculator-keys button:active { - padding: 2px 1px 0 3px; - background: #e3e0d2; - box-shadow: inset 1px 1px #aca899; -} - -.xp-calculator-clear-row button { - color: #f00; -} - -.xp-calculator-keys { - position: absolute; - top: 72px; - left: 9px; - display: grid; - grid-template-columns: 39px repeat(5, 34px); - gap: 6px; -} - -.xp-calculator-keys button { - width: 34px; -} - -.xp-calculator-keys button[data-key-type="number"] { - color: #00f; -} - -.xp-calculator-keys button[data-key-type="operator"], -.xp-calculator-keys button[data-key-type="memory"] { - color: #f00; -} - .xp-command-prompt-window { min-width: min(320px, calc(100% - 8px)); min-height: min(180px, calc(100% - 8px)); diff --git a/site/iframe/calculator/SOURCES.json b/site/iframe/calculator/SOURCES.json new file mode 100644 index 00000000..67a5d281 --- /dev/null +++ b/site/iframe/calculator/SOURCES.json @@ -0,0 +1,27 @@ +{ + "runtime": "boxedwine", + "windowsXp": { + "source": "User-supplied Windows XP Professional SP3 source-media ISO", + "sourceSha256": "fd8c8d42c1581e8767217fe800bfc0d5649c0ad20d754c927d6c763e446d1927", + "package": { + "file": "xp-calculator.zip", + "bytes": 69002, + "sha256": "8fb2df72143efdd104e1c2fe4508a5bf6e379d59f2c2648cd8753d85f21036d2" + }, + "files": { + "calc.exe": { + "member": "I386/CALC.EX_", + "sha256": "37121ecb7c1e112b735bd21b0dfe3e526352ecb98c434c5f40e6a2a582380cdd" + }, + "calc.chm": { + "member": "I386/CALC.CH_", + "sha256": "180e7b08e5a72ab907e4c5372530df107097de6b3cd6e6f4a63519fc171bd3bb" + }, + "window-host.exe": { + "source": "native/calculator/window-host.c", + "build": "SOURCE_DATE_EPOCH=0 i686-w64-mingw32-gcc -Os -s -nostdlib -mwindows -Wl,--no-insert-timestamp native/calculator/window-host.c -lkernel32 -luser32 -o window-host.exe", + "sha256": "3d1180fc264d7d026ea53f6b58542d61da6927216dc08a92850e29c50d07020e" + } + } + } +} diff --git a/site/iframe/calculator/xp-calculator.zip b/site/iframe/calculator/xp-calculator.zip new file mode 100644 index 0000000000000000000000000000000000000000..04671514aa2a79f598d40d34212e4ddf6da6548d GIT binary patch literal 69002 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 T=`Q|#fR;MTsd0n}KwbX>F~dp7 literal 0 HcmV?d00001 diff --git a/tests/calculator.test.ts b/tests/calculator.test.ts index 2a56efac..fc30cb0f 100644 --- a/tests/calculator.test.ts +++ b/tests/calculator.test.ts @@ -1,6 +1,25 @@ // @ts-nocheck -- Happy DOM's element types intentionally replace lib.dom here. import { afterEach, describe, expect, test } from "bun:test"; +import { createHash } from "node:crypto"; +import { readFile } from "node:fs/promises"; +import { createRequire } from "node:module"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; + import { cleanupShells, loadShell, login } from "./helpers/shell-harness"; +import { installBoxedWineResizeBridge } from "../site/apps/core/boxedwine-resize.js"; + +const require = createRequire(import.meta.url); +const { unzipSync } = require("fflate"); +const projectDirectory = join(dirname(fileURLToPath(import.meta.url)), ".."); +const calculatorDirectory = join( + projectDirectory, + "site", + "iframe", + "calculator", +); +const sha256 = (content: Uint8Array) => + createHash("sha256").update(content).digest("hex"); afterEach(cleanupShells); @@ -14,103 +33,162 @@ const launchCalculator = async () => { const calculator = shell.document.querySelector( '.xp-window[data-game="__calculator"]', ); - const display = calculator.querySelector(".xp-calculator-display"); - const press = (...labels) => { - for (const label of labels) { - [...calculator.querySelectorAll("button")] - .find((button) => button.textContent === label) - .click(); - } - }; - return { shell, calculator, display, press }; + return { shell, calculator }; }; -describe("Windows XP Calculator", () => { - test("matches the fixed standard-mode layout from XP", async () => { - const { calculator, display } = await launchCalculator(); +describe("original Windows XP Calculator through BoxedWine", () => { + test("launches the authentic XP executable in its fixed Standard window", async () => { + const { calculator } = await launchCalculator(); + const frame = calculator.querySelector(".boxedwine-app-frame"); + const url = new URL(frame.src); expect(calculator.style.width).toBe("260px"); expect(calculator.style.height).toBe("260px"); expect(calculator.querySelector(".maximize-btn").disabled).toBeTrue(); - expect(display.value).toBe("0."); - expect( - [...calculator.querySelectorAll(".xp-calculator-menu-trigger")].map( - (button) => button.textContent, - ), - ).toEqual(["Edit", "View", "Help"]); - expect( - [...calculator.querySelectorAll(".xp-calculator-keys button")].map( - (button) => button.textContent, - ), - ).toEqual([ - "MC", - "7", - "8", - "9", - "/", - "sqrt", - "MR", - "4", - "5", - "6", - "*", - "%", - "MS", - "1", - "2", - "3", - "-", - "1/x", - "M+", - "0", - "+/-", - ".", - "+", - "=", - ]); + expect(calculator.querySelector(".resize-handle")).toBeNull(); + expect(url.searchParams.get("archive")).toBe("xp-calculator"); + expect(url.searchParams.get("executable")).toBe("window-host.exe"); + expect(url.searchParams.get("resolution")).toBe("254x261"); + expect(url.searchParams.get("frameTop")).toBe("32"); }); - test("performs chained, repeated, percent, and unary calculations", async () => { - const { display, press } = await launchCalculator(); - - press("2", "+", "3", "="); - expect(display.value).toBe("5."); - press("="); - expect(display.value).toBe("8."); - press("C", "2", "0", "0", "+", "1", "0", "%", "="); - expect(display.value).toBe("220."); - press("C", "9", "sqrt"); - expect(display.value).toBe("3."); - press("1/x"); - expect(display.value).toBe("0.333333333333333"); + test("follows the real Calculator window when XP switches to Scientific mode", async () => { + const { shell, calculator } = await launchCalculator(); + const frame = calculator.querySelector(".boxedwine-app-frame"); + + shell.window.dispatchEvent( + new shell.window.MessageEvent("message", { + data: { type: "boxedwine-window-size", width: 480, height: 260 }, + origin: new URL(frame.src).origin, + source: frame.contentWindow, + }), + ); + + expect(calculator.style.width).toBe("480px"); + expect(calculator.style.height).toBe("260px"); + + shell.window.dispatchEvent( + new shell.window.MessageEvent("message", { + data: { type: "boxedwine-window-size", width: 260, height: 260 }, + origin: new URL(frame.src).origin, + source: frame.contentWindow, + }), + ); + expect(calculator.style.width).toBe("260px"); }); - test("supports memory, correction controls, errors, and keyboard input", async () => { - const { shell, calculator, display, press } = await launchCalculator(); - - press("4", "2", "MS", "C", "MR"); - expect(display.value).toBe("42."); - expect( - calculator.querySelector(".xp-calculator-memory-indicator").textContent, - ).toBe("M"); - press("MC"); - expect( - calculator.querySelector(".xp-calculator-memory-indicator").textContent, - ).toBe(""); - - press("C", "8", "9", "Backspace"); - expect(display.value).toBe("8."); - press("/", "0", "="); - expect(display.value).toBe("Cannot divide by zero."); - press("C"); - - for (const key of ["6", "*", "7", "Enter"]) { - calculator - .querySelector(".xp-native-calculator") - .dispatchEvent( - new shell.window.KeyboardEvent("keydown", { key, bubbles: true }), - ); + test("scales the complete Scientific surface into a phone window", async () => { + const shell = await login(await loadShell()); + const desktop = shell.document.getElementById("desktop"); + Object.defineProperties(desktop, { + clientWidth: { configurable: true, value: 390 }, + clientHeight: { configurable: true, value: 814 }, + }); + let resize; + shell.window.ResizeObserver = class ResizeObserver { + constructor(callback) { + resize = callback; + } + observe() {} + disconnect() {} + }; + + shell.document.getElementById("start-button").click(); + shell.document.getElementById("all-programs-button").click(); + const flyouts = shell.document.getElementById("start-menu-flyouts"); + flyouts.querySelector('[data-program-id="accessories"]').click(); + flyouts.querySelector('[data-program-id="calculator"]').click(); + const calculator = shell.document.querySelector( + '.xp-window[data-game="__calculator"]', + ); + const host = calculator.querySelector(".boxedwine-app-host"); + const frame = host.querySelector(".boxedwine-app-frame"); + + shell.window.dispatchEvent( + new shell.window.MessageEvent("message", { + data: { type: "boxedwine-window-size", width: 480, height: 260 }, + origin: new URL(frame.src).origin, + source: frame.contentWindow, + }), + ); + Object.defineProperties(host, { + clientWidth: { configurable: true, value: 384 }, + clientHeight: { configurable: true, value: 229 }, + }); + resize(); + + expect(calculator.style.width).toBe("390px"); + expect(calculator.style.height).toBe("260px"); + expect(frame.style.width).toBe("474px"); + expect(frame.style.height).toBe("229px"); + expect(frame.style.transform).toBe(`scale(${384 / 474})`); + }); + + test("packages the original executable, complete help, and window host", async () => { + const manifest = JSON.parse( + await readFile(join(calculatorDirectory, "SOURCES.json"), "utf8"), + ); + const packageContent = new Uint8Array( + await readFile(join(calculatorDirectory, "xp-calculator.zip")), + ); + const files = unzipSync(packageContent); + + expect(packageContent.byteLength).toBe(manifest.windowsXp.package.bytes); + expect(sha256(packageContent)).toBe(manifest.windowsXp.package.sha256); + expect(Object.keys(files).sort()).toEqual([ + "calc.chm", + "calc.exe", + "window-host.exe", + ]); + for (const [name, source] of Object.entries(manifest.windowsXp.files)) { + expect(sha256(files[name])).toBe(source.sha256); } - expect(display.value).toBe("42."); + }); + + test("publishes native Standard and Scientific window sizes to the shell", () => { + let pollWindowSize; + const posted = []; + const runnerWindow = { + location: { origin: "https://flash.test" }, + parent: { + postMessage(message, origin) { + posted.push({ message, origin }); + }, + }, + addEventListener() {}, + removeEventListener() {}, + setInterval(callback) { + pollWindowSize = callback; + return 1; + }, + clearInterval() {}, + }; + let nativeSize = "260 260"; + const module = { + _boxedwine_resize_screen() {}, + FS: { + readFile() { + return nativeSize; + }, + writeFile() {}, + unlink() {}, + rename() {}, + }, + }; + installBoxedWineResizeBridge(runnerWindow, module); + module.onRuntimeInitialized(); + + pollWindowSize(); + expect(posted.at(-1)).toEqual({ + message: { type: "boxedwine-window-size", width: 260, height: 260 }, + origin: "https://flash.test", + }); + + nativeSize = "480 260"; + pollWindowSize(); + expect(posted.at(-1)).toEqual({ + message: { type: "boxedwine-window-size", width: 480, height: 260 }, + origin: "https://flash.test", + }); }); }); diff --git a/tests/shell-behavior.test.ts b/tests/shell-behavior.test.ts index 71a1c683..e9e29314 100644 --- a/tests/shell-behavior.test.ts +++ b/tests/shell-behavior.test.ts @@ -506,18 +506,11 @@ test("All Programs exposes system applications and games in the XP hierarchy", a const calculatorWindow = shell.document.querySelector( '.xp-window[data-game="__calculator"]', )!; - const calculatorKeys = [ - ...calculatorWindow.querySelectorAll( - ".xp-calculator-keys button", - ), - ]; - for (const key of ["2", "+", "3", "="]) { - calculatorKeys.find((button) => button.textContent === key)!.click(); - } - expect( - calculatorWindow.querySelector(".xp-calculator-display")! - .value, - ).toBe("5."); + const calculatorUrl = new URL( + calculatorWindow.querySelector("iframe")!.src, + ); + expect(calculatorUrl.searchParams.get("archive")).toBe("xp-calculator"); + expect(calculatorUrl.searchParams.get("executable")).toBe("window-host.exe"); shell.document.getElementById("start-button")!.click(); shell.document.getElementById("all-programs-button")!.click();