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:
co-authored by
Claude Opus 5.5
parent
80c5a1e39f
commit
5d64311a4d
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user