port: MSWindow/MSApplication verbatim over a winuser.h shim (Win32User.cpp)

Host-side window records + message queue; WM_SIZE/WM_MOVE/WM_DESTROY sent
synchronously as Win32 does; PostQuitMessage also queues WM_QUIT. 64-bit
pointer casts ADAPTED. port.ms_window.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
shenlei
2026-09-30 13:19:36 +09:00
co-authored by Claude Opus 5.5
parent 80c5a1e39f
commit 5d64311a4d
13 changed files with 1186 additions and 170 deletions
+361
View File
@@ -0,0 +1,361 @@
// The <winuser.h> subset of common/shim/win32/winuser.h: window records and a thread-shared message queue,
// enough for 40250 CMSWindow / CMSApplication to run unchanged. The host (Godot/SDL) owns the real surface
// and its events; a window here is the bookkeeping 40250 reads back (class, text, styles, rect, visibility,
// user data) plus its window procedure, which the calls below invoke where Win32 sends a message
// synchronously (WM_SIZE / WM_MOVE on a resize or move, WM_DESTROY, WM_CLOSE in DefWindowProc).
//
// No non-client area: the client rect is the window rect's size (AdjustWindowRectEx leaves the rect
// unchanged). The host cursor (MtHostSetCursor, client coordinates of the host surface) is placed on the
// screen at the first created window's origin, so GetCursorPos + ScreenToClient of that window gives it back.
#include "EterLib/StdAfx.h"
#include "EterLib/HostCursor.h"
#include <condition_variable>
#include <deque>
#include <map>
#include <mutex>
#include <set>
#include <string>
struct HWND__
{
std::string className;
std::string text;
DWORD style = 0;
DWORD exStyle = 0;
RECT rect = {0, 0, 0, 0};
bool visible = false;
WNDPROC proc = NULL;
LONG_PTR userData = 0;
};
namespace {
std::recursive_mutex g_mutex;
std::condition_variable_any g_queueReady;
std::map<std::string, WNDCLASS> g_classes;
std::set<HWND> g_windows;
HWND g_mainWindow = NULL;
std::deque<MSG> g_queue;
ATOM g_nextAtom = 0xC000;
POINT g_hostCursor = {0, 0};
bool Alive(HWND hWnd)
{
return hWnd && g_windows.count(hWnd);
}
LRESULT Send(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
WNDPROC proc = NULL;
{
std::lock_guard<std::recursive_mutex> lock(g_mutex);
if (Alive(hWnd))
proc = hWnd->proc;
}
return proc ? proc(hWnd, msg, wParam, lParam) : 0;
}
bool Matches(const MSG& msg, HWND hWnd, UINT wMin, UINT wMax)
{
if (hWnd && msg.hwnd != hWnd)
return false;
if (wMin == 0 && wMax == 0)
return true;
return msg.message >= wMin && msg.message <= wMax;
}
}
void MtHostSetCursor(int x, int y)
{
std::lock_guard<std::recursive_mutex> lock(g_mutex);
g_hostCursor.x = x;
g_hostCursor.y = y;
}
ATOM RegisterClass(const WNDCLASS* lpWndClass)
{
if (!lpWndClass || !lpWndClass->lpszClassName)
return 0;
std::lock_guard<std::recursive_mutex> lock(g_mutex);
if (!g_classes.emplace(lpWndClass->lpszClassName, *lpWndClass).second)
return 0; // ERROR_CLASS_ALREADY_EXISTS
return g_nextAtom++;
}
// Win32 also sends WM_NCCREATE/WM_CREATE here, before the caller can set GWL_USERDATA; a 40250 window
// procedure passes them to DefWindowProc, so they are not sent.
HWND CreateWindow(LPCSTR lpClassName, LPCSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight,
HWND, HMENU, HINSTANCE, LPVOID)
{
std::lock_guard<std::recursive_mutex> lock(g_mutex);
auto cls = g_classes.find(lpClassName ? lpClassName : "");
if (cls == g_classes.end())
return NULL;
HWND hWnd = new HWND__;
hWnd->className = cls->first;
hWnd->text = lpWindowName ? lpWindowName : "";
hWnd->style = dwStyle;
hWnd->rect = {x, y, x + nWidth, y + nHeight};
hWnd->proc = cls->second.lpfnWndProc;
g_windows.insert(hWnd);
if (!g_mainWindow)
g_mainWindow = hWnd;
return hWnd;
}
BOOL DestroyWindow(HWND hWnd)
{
if (!IsWindow(hWnd))
return FALSE;
Send(hWnd, WM_DESTROY, 0, 0);
std::lock_guard<std::recursive_mutex> lock(g_mutex);
if (!g_windows.erase(hWnd))
return FALSE;
if (g_mainWindow == hWnd)
g_mainWindow = NULL;
for (auto i = g_queue.begin(); i != g_queue.end();)
i = i->hwnd == hWnd ? g_queue.erase(i) : i + 1;
delete hWnd;
return TRUE;
}
BOOL IsWindow(HWND hWnd)
{
std::lock_guard<std::recursive_mutex> lock(g_mutex);
return Alive(hWnd) ? TRUE : FALSE;
}
LRESULT DefWindowProc(HWND hWnd, UINT Msg, WPARAM, LPARAM)
{
if (Msg == WM_CLOSE)
DestroyWindow(hWnd);
return 0;
}
LONG_PTR GetWindowLongPtr(HWND hWnd, int nIndex)
{
std::lock_guard<std::recursive_mutex> lock(g_mutex);
if (!Alive(hWnd))
return 0;
switch (nIndex)
{
case GWL_STYLE: return LONG_PTR(hWnd->style);
case GWL_EXSTYLE: return LONG_PTR(hWnd->exStyle);
case GWLP_USERDATA: return hWnd->userData;
}
return 0;
}
LONG_PTR SetWindowLongPtr(HWND hWnd, int nIndex, LONG_PTR dwNewLong)
{
std::lock_guard<std::recursive_mutex> lock(g_mutex);
if (!Alive(hWnd))
return 0;
LONG_PTR old = GetWindowLongPtr(hWnd, nIndex);
switch (nIndex)
{
case GWL_STYLE: hWnd->style = DWORD(dwNewLong); break;
case GWL_EXSTYLE: hWnd->exStyle = DWORD(dwNewLong); break;
case GWLP_USERDATA: hWnd->userData = dwNewLong; break;
}
return old;
}
LONG GetWindowLong(HWND hWnd, int nIndex)
{
return LONG(GetWindowLongPtr(hWnd, nIndex));
}
LONG SetWindowLong(HWND hWnd, int nIndex, LONG dwNewLong)
{
return LONG(SetWindowLongPtr(hWnd, nIndex, dwNewLong));
}
BOOL ShowWindow(HWND hWnd, int nCmdShow)
{
std::lock_guard<std::recursive_mutex> lock(g_mutex);
if (!Alive(hWnd))
return FALSE;
const BOOL wasVisible = hWnd->visible ? TRUE : FALSE;
hWnd->visible = nCmdShow != SW_HIDE;
return wasVisible;
}
// Win32 sends WM_MOVE (client origin) and WM_SIZE (SIZE_RESTORED, client size) after a change.
BOOL SetWindowPos(HWND hWnd, HWND, int X, int Y, int cx, int cy, UINT uFlags)
{
bool moved = false, sized = false;
RECT rc;
{
std::lock_guard<std::recursive_mutex> lock(g_mutex);
if (!Alive(hWnd))
return FALSE;
rc = hWnd->rect;
const int w = (uFlags & SWP_NOSIZE) ? rc.right - rc.left : cx;
const int h = (uFlags & SWP_NOSIZE) ? rc.bottom - rc.top : cy;
const int x = (uFlags & SWP_NOMOVE) ? rc.left : X;
const int y = (uFlags & SWP_NOMOVE) ? rc.top : Y;
moved = x != rc.left || y != rc.top;
sized = w != rc.right - rc.left || h != rc.bottom - rc.top;
rc = {x, y, x + w, y + h};
hWnd->rect = rc;
}
if (moved)
Send(hWnd, WM_MOVE, 0, MAKELPARAM(rc.left, rc.top));
if (sized)
Send(hWnd, WM_SIZE, SIZE_RESTORED, MAKELPARAM(rc.right - rc.left, rc.bottom - rc.top));
return TRUE;
}
BOOL MoveWindow(HWND hWnd, int X, int Y, int nWidth, int nHeight, BOOL)
{
return SetWindowPos(hWnd, NULL, X, Y, nWidth, nHeight, SWP_NOZORDER);
}
BOOL AdjustWindowRectEx(LPRECT lpRect, DWORD, BOOL, DWORD)
{
return lpRect ? TRUE : FALSE;
}
BOOL GetWindowRect(HWND hWnd, LPRECT lpRect)
{
std::lock_guard<std::recursive_mutex> lock(g_mutex);
if (!Alive(hWnd) || !lpRect)
return FALSE;
*lpRect = hWnd->rect;
return TRUE;
}
BOOL GetClientRect(HWND hWnd, LPRECT lpRect)
{
std::lock_guard<std::recursive_mutex> lock(g_mutex);
if (!Alive(hWnd) || !lpRect)
return FALSE;
*lpRect = {0, 0, hWnd->rect.right - hWnd->rect.left, hWnd->rect.bottom - hWnd->rect.top};
return TRUE;
}
BOOL SetWindowText(HWND hWnd, LPCSTR lpString)
{
std::lock_guard<std::recursive_mutex> lock(g_mutex);
if (!Alive(hWnd))
return FALSE;
hWnd->text = lpString ? lpString : "";
return TRUE;
}
// The host repaints every frame.
BOOL InvalidateRect(HWND hWnd, const RECT*, BOOL)
{
return hWnd == NULL || IsWindow(hWnd);
}
HMENU GetMenu(HWND)
{
return NULL;
}
BOOL SetRect(LPRECT lprc, int xLeft, int yTop, int xRight, int yBottom)
{
if (!lprc)
return FALSE;
*lprc = {xLeft, yTop, xRight, yBottom};
return TRUE;
}
BOOL GetCursorPos(LPPOINT lpPoint)
{
std::lock_guard<std::recursive_mutex> lock(g_mutex);
if (!lpPoint)
return FALSE;
*lpPoint = g_hostCursor;
if (g_mainWindow)
{
lpPoint->x += g_mainWindow->rect.left;
lpPoint->y += g_mainWindow->rect.top;
}
return TRUE;
}
BOOL ScreenToClient(HWND hWnd, LPPOINT lpPoint)
{
std::lock_guard<std::recursive_mutex> lock(g_mutex);
if (!Alive(hWnd) || !lpPoint)
return FALSE;
lpPoint->x -= hWnd->rect.left;
lpPoint->y -= hWnd->rect.top;
return TRUE;
}
// No executable resources: windows take the host's cursor and icon.
HCURSOR LoadCursor(HINSTANCE, LPCSTR)
{
return NULL;
}
HICON LoadIcon(HINSTANCE, LPCSTR)
{
return NULL;
}
// The host clears the surface; no stock brush is needed.
HGDIOBJ GetStockObject(int)
{
return NULL;
}
BOOL PostMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
std::lock_guard<std::recursive_mutex> lock(g_mutex);
if (hWnd && !Alive(hWnd))
return FALSE;
MSG msg = {};
msg.hwnd = hWnd;
msg.message = Msg;
msg.wParam = wParam;
msg.lParam = lParam;
msg.time = timeGetTime();
msg.pt = g_hostCursor;
g_queue.push_back(msg);
g_queueReady.notify_all();
return TRUE;
}
BOOL PeekMessage(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg)
{
std::lock_guard<std::recursive_mutex> lock(g_mutex);
for (auto i = g_queue.begin(); i != g_queue.end(); ++i)
{
if (!Matches(*i, hWnd, wMsgFilterMin, wMsgFilterMax))
continue;
if (lpMsg)
*lpMsg = *i;
if (wRemoveMsg & PM_REMOVE)
g_queue.erase(i);
return TRUE;
}
return FALSE;
}
BOOL GetMessage(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax)
{
std::unique_lock<std::recursive_mutex> lock(g_mutex);
MSG msg;
g_queueReady.wait(lock, [&] { return PeekMessage(&msg, hWnd, wMsgFilterMin, wMsgFilterMax, PM_REMOVE); });
if (lpMsg)
*lpMsg = msg;
return msg.message == WM_QUIT ? FALSE : TRUE;
}
// Character input reaches the window manager from the host already translated (PythonBoot).
BOOL TranslateMessage(const MSG*)
{
return FALSE;
}
LRESULT DispatchMessage(const MSG* lpMsg)
{
return lpMsg ? Send(lpMsg->hwnd, lpMsg->message, lpMsg->wParam, lpMsg->lParam) : 0;
}