From 6e573ccb6afc01e7f06de35a8aaeb6f14798eb1d Mon Sep 17 00:00:00 2001 From: Olivier Huet Date: Thu, 2 Apr 2026 19:31:30 +0200 Subject: [PATCH] Fix MDI child windows not appearing and menu corruption for Win16 apps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - message.ts: Sign-extend 16-bit MDICREATESTRUCT coordinates (same as MoveWindow/SetWindowPos) Clamp negative positions to 0 and oversized children to fill MDICLIENT area WINFILE passed y=-1026 (0xFBFE) which was stored as 64510 unsigned → off-screen - EmulatorView.tsx: Use callWndProc16 for NE apps in handleMenuOpen (was using Win32 callWndProc) Opening a menu on a Win16 app corrupted CPU state, breaking New Window and Exit - window.ts: Minor whitespace cleanup in SetWindowPos Co-Authored-By: Claude Opus 4.6 (1M context) --- src/components/EmulatorView.tsx | 7 +++++-- src/lib/emu/win16/user/message.ts | 25 ++++++++++++++++++++----- src/lib/emu/win16/user/window.ts | 1 - 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/components/EmulatorView.tsx b/src/components/EmulatorView.tsx index ea78c6c..ccaa262 100644 --- a/src/components/EmulatorView.tsx +++ b/src/components/EmulatorView.tsx @@ -1187,11 +1187,14 @@ export function EmulatorView({ arrayBuffer, peInfo, additionalFiles, exeName, co const savedEIP = emu.cpu.eip; const savedWaiting = emu.waitingForMessage; emu.waitingForMessage = false; - emu.callWndProc(wnd.wndProc, emu.mainWindow, WM_INITMENU, hMenu, 0); + const callWndProc = emu.isNE + ? (wp: number, hw: number, m: number, w: number, l: number) => emu.callWndProc16(wp, hw, m, w, l) + : (wp: number, hw: number, m: number, w: number, l: number) => emu.callWndProc(wp, hw, m, w, l); + callWndProc(wnd.wndProc, emu.mainWindow, WM_INITMENU, hMenu, 0); // Get submenu handle for INITMENUPOPUP const menuData = hMenu ? emu.handles.get<{ items: { hSubMenu: number }[] }>(hMenu) : null; const hSubMenu = menuData?.items?.[index]?.hSubMenu || 0; - emu.callWndProc(wnd.wndProc, emu.mainWindow, WM_INITMENUPOPUP, hSubMenu, index); + callWndProc(wnd.wndProc, emu.mainWindow, WM_INITMENUPOPUP, hSubMenu, index); emu.cpu.reg[4] = savedESP; emu.cpu.eip = savedEIP; emu.waitingForMessage = savedWaiting; diff --git a/src/lib/emu/win16/user/message.ts b/src/lib/emu/win16/user/message.ts index eb1590b..0171333 100644 --- a/src/lib/emu/win16/user/message.ts +++ b/src/lib/emu/win16/user/message.ts @@ -1134,7 +1134,13 @@ export function registerWin16UserMessage(emu: Emulator, user: Win16Module, h: Wi const mdiTitle = titleAddr ? emu.memory.readCString(titleAddr) : ''; const classInfo = emu.windowClasses.get(mdiClassName.toUpperCase()); - console.log(`[WIN16] WM_MDICREATE class="${mdiClassName}" title="${mdiTitle}" ${mdiCX}x${mdiCY} style=0x${mdiStyle.toString(16)} wndProc=0x${(classInfo?.wndProc||0).toString(16)} cbExtra=${classInfo?.cbWndExtra||0} lParam=0x${mdiLParam.toString(16)}`); + // Sign-extend 16-bit coordinates (same as MoveWindow/SetWindowPos) + const CW_USEDEFAULT = -32768; // 0x8000 sign-extended + const mdiXs = (mdiX << 16) >> 16; + const mdiYs = (mdiY << 16) >> 16; + const mdiCXs = (mdiCX << 16) >> 16; + const mdiCYs = (mdiCY << 16) >> 16; + console.log(`[WIN16] WM_MDICREATE class="${mdiClassName}" title="${mdiTitle}" ${mdiCXs}x${mdiCYs} style=0x${mdiStyle.toString(16)} wndProc=0x${(classInfo?.wndProc||0).toString(16)} cbExtra=${classInfo?.cbWndExtra||0} lParam=0x${mdiLParam.toString(16)}`); const WS_CHILD = 0x40000000; const WS_VISIBLE = 0x10000000; @@ -1152,15 +1158,24 @@ export function registerWin16UserMessage(emu: Emulator, user: Win16Module, h: Wi const { cw: mdiCW, ch: mdiCH } = getClientSize(wnd.style, false, wnd.width, wnd.height, true); const clientW = mdiCW || (emu.canvas?.width ?? 320); const clientH = mdiCH || (emu.canvas?.height ?? 200); + // Resolve position and size: CW_USEDEFAULT or negative → fill MDICLIENT area; + // if requested size exceeds client area, fill it entirely (Windows 3.1 behavior) + let childX = (mdiXs === CW_USEDEFAULT || mdiXs < 0) ? 0 : mdiXs; + let childY = (mdiYs === CW_USEDEFAULT || mdiYs < 0) ? 0 : mdiYs; + let childW = (mdiCXs === CW_USEDEFAULT || mdiCXs <= 0) ? clientW : mdiCXs; + let childH = (mdiCYs === CW_USEDEFAULT || mdiCYs <= 0) ? clientH : mdiCYs; + if (childW > clientW || childH > clientH) { + childX = 0; childY = 0; childW = clientW; childH = clientH; + } const childHwnd = emu.handles.alloc('window', { classInfo: classInfo || { className: mdiClassName, wndProc: 0, rawWndProc: 0, style: 0, hbrBackground: 0, hIcon: 0, hCursor: 0, cbWndExtra: 0 }, title: mdiTitle, style: childStyle, exStyle: 0, - x: mdiX === 0x8000 ? 0 : mdiX, - y: mdiY === 0x8000 ? 0 : mdiY, - width: (mdiCX === 0x8000 || mdiCX === 0) ? clientW : mdiCX, - height: (mdiCY === 0x8000 || mdiCY === 0) ? clientH : mdiCY, + x: childX, + y: childY, + width: childW, + height: childH, hMenu: 0, parent: hWnd, wndProc: classInfo?.wndProc || 0, diff --git a/src/lib/emu/win16/user/window.ts b/src/lib/emu/win16/user/window.ts index acacafd..3cb6774 100644 --- a/src/lib/emu/win16/user/window.ts +++ b/src/lib/emu/win16/user/window.ts @@ -624,7 +624,6 @@ export function registerWin16UserWindow(emu: Emulator, user: Win16Module, h: Win const [hWnd, _hInsertAfter, x, y, cx, cy, uFlags] = emu.readPascalArgs16([2, 2, 2, 2, 2, 2, 2]); const wnd = emu.handles.get(hWnd); if (!wnd) return 0; - const SWP_NOSIZE = 0x1, SWP_NOMOVE = 0x2; const SWP_SHOWWINDOW = 0x40, SWP_HIDEWINDOW = 0x80; let sizeChanged = false;