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
+2
View File
@@ -619,3 +619,5 @@
{"date": "2026-09-30", "unit": "EterLib/BlockTexture.cpp", "action": "Render ADAPTED (UI image of the CPU texture), rest PORTED verbatim", "evidence": "port.text_bar"}
{"date": "2026-09-30", "unit": "UserInterface/ProcessCRC.cpp", "action": "port", "evidence": "verbatim; process introspection ADAPTED (no module snapshot -> ProcCRC 0); port.process_crc"}
{"date": "2026-09-30", "unit": "UserInterface/WiseLogicXTrap.cpp", "action": "port", "evidence": "verbatim non-XTRAP build; mapped PORTED"}
{"date": "2026-09-30", "unit": "EterLib/MSWindow.cpp", "action": "port", "evidence": "verbatim over new winuser.h shim (platform/Win32User.cpp); 64-bit pointer casts ADAPTED; port.ms_window"}
{"date": "2026-09-30", "unit": "EterLib/MSApplication.cpp", "action": "port", "evidence": "verbatim over new winuser.h shim (platform/Win32User.cpp); 64-bit pointer casts ADAPTED; port.ms_window"}
@@ -0,0 +1,78 @@
{
"reference": "EterLib/MSApplication.cpp",
"reference_sha256": "d3be4da92ba850fd1e043c39d32cf1402928e2be84ec95fd875f9981538d52e9",
"priority": "P4",
"contracts": [],
"functions": {
"CMSApplication::CMSApplication": {
"status": "PORTED",
"impl": [
"src/platform/EterLib/MSApplication.cpp:CMSApplication::CMSApplication"
],
"test": [
"tests/port/port_ms_window_test.cpp"
],
"note": "verbatim over the <winuser.h> subset in src/platform/Win32User.cpp (host-side window records + message queue)"
},
"CMSApplication::~CMSApplication": {
"status": "PORTED",
"impl": [
"src/platform/EterLib/MSApplication.cpp:CMSApplication::~CMSApplication"
],
"test": [
"tests/port/port_ms_window_test.cpp"
],
"note": "verbatim over the <winuser.h> subset in src/platform/Win32User.cpp (host-side window records + message queue)"
},
"CMSApplication::Initialize": {
"status": "PORTED",
"impl": [
"src/platform/EterLib/MSApplication.cpp:CMSApplication::Initialize"
],
"test": [
"tests/port/port_ms_window_test.cpp"
],
"note": "verbatim over the <winuser.h> subset in src/platform/Win32User.cpp (host-side window records + message queue)"
},
"CMSApplication::MessageLoop": {
"status": "PORTED",
"impl": [
"src/platform/EterLib/MSApplication.cpp:CMSApplication::MessageLoop"
],
"test": [
"tests/port/port_ms_window_test.cpp"
],
"note": "verbatim over the <winuser.h> subset in src/platform/Win32User.cpp (host-side window records + message queue)"
},
"CMSApplication::IsMessage": {
"status": "PORTED",
"impl": [
"src/platform/EterLib/MSApplication.cpp:CMSApplication::IsMessage"
],
"test": [
"tests/port/port_ms_window_test.cpp"
],
"note": "verbatim over the <winuser.h> subset in src/platform/Win32User.cpp (host-side window records + message queue)"
},
"CMSApplication::MessageProcess": {
"status": "PORTED",
"impl": [
"src/platform/EterLib/MSApplication.cpp:CMSApplication::MessageProcess"
],
"test": [
"tests/port/port_ms_window_test.cpp"
],
"note": "verbatim over the <winuser.h> subset in src/platform/Win32User.cpp (host-side window records + message queue)"
},
"CMSApplication::WindowProcedure": {
"status": "PORTED",
"impl": [
"src/platform/EterLib/MSApplication.cpp:CMSApplication::WindowProcedure"
],
"test": [
"tests/port/port_ms_window_test.cpp"
],
"note": "verbatim over the <winuser.h> subset in src/platform/Win32User.cpp (host-side window records + message queue)"
}
}
}
+258
View File
@@ -0,0 +1,258 @@
{
"reference": "EterLib/MSWindow.cpp",
"reference_sha256": "e1e01221043a37eb4394cf339716e17dfb4afc662426973c1ab7a9337898fff9",
"priority": "P4",
"contracts": [],
"functions": {
"MSWindowProcedure": {
"status": "ADAPTED",
"impl": [
"src/platform/EterLib/MSWindow.cpp:MSWindowProcedure"
],
"test": [
"tests/port/port_ms_window_test.cpp"
],
"note": "40250: GetWindowLong(GWL_USERDATA) gives the CMSWindow*. Port: GetWindowLongPtr(GWLP_USERDATA). Invariant: the pointer Create stored comes back whole on 64-bit."
},
"CMSWindow::WindowProcedure": {
"status": "PORTED",
"impl": [
"src/platform/EterLib/MSWindow.cpp:CMSWindow::WindowProcedure"
],
"test": [
"tests/port/port_ms_window_test.cpp"
],
"note": "verbatim over the <winuser.h> subset in src/platform/Win32User.cpp (host-side window records + message queue)"
},
"CMSWindow::OnSize": {
"status": "PORTED",
"impl": [
"src/platform/EterLib/MSWindow.cpp:CMSWindow::OnSize"
],
"test": [
"tests/port/port_ms_window_test.cpp"
],
"note": "verbatim over the <winuser.h> subset in src/platform/Win32User.cpp (host-side window records + message queue)"
},
"CMSWindow::Destroy": {
"status": "PORTED",
"impl": [
"src/platform/EterLib/MSWindow.cpp:CMSWindow::Destroy"
],
"test": [
"tests/port/port_ms_window_test.cpp"
],
"note": "verbatim over the <winuser.h> subset in src/platform/Win32User.cpp (host-side window records + message queue)"
},
"CMSWindow::Create": {
"status": "ADAPTED",
"impl": [
"src/platform/EterLib/MSWindow.cpp:CMSWindow::Create"
],
"test": [
"tests/port/port_ms_window_test.cpp"
],
"note": "40250: SetWindowLong(GWL_USERDATA, (DWORD) this). Port: SetWindowLongPtr(GWLP_USERDATA, (LONG_PTR) this). Invariant: MSWindowProcedure reaches this object's WindowProcedure."
},
"CMSWindow::SetVisibleMode": {
"status": "PORTED",
"impl": [
"src/platform/EterLib/MSWindow.cpp:CMSWindow::SetVisibleMode"
],
"test": [
"tests/port/port_ms_window_test.cpp"
],
"note": "verbatim over the <winuser.h> subset in src/platform/Win32User.cpp (host-side window records + message queue)"
},
"CMSWindow::Show": {
"status": "PORTED",
"impl": [
"src/platform/EterLib/MSWindow.cpp:CMSWindow::Show"
],
"test": [
"tests/port/port_ms_window_test.cpp"
],
"note": "verbatim over the <winuser.h> subset in src/platform/Win32User.cpp (host-side window records + message queue)"
},
"CMSWindow::Hide": {
"status": "PORTED",
"impl": [
"src/platform/EterLib/MSWindow.cpp:CMSWindow::Hide"
],
"test": [
"tests/port/port_ms_window_test.cpp"
],
"note": "verbatim over the <winuser.h> subset in src/platform/Win32User.cpp (host-side window records + message queue)"
},
"CMSWindow::IsVisible": {
"status": "PORTED",
"impl": [
"src/platform/EterLib/MSWindow.cpp:CMSWindow::IsVisible"
],
"test": [
"tests/port/port_ms_window_test.cpp"
],
"note": "verbatim over the <winuser.h> subset in src/platform/Win32User.cpp (host-side window records + message queue)"
},
"CMSWindow::IsActive": {
"status": "PORTED",
"impl": [
"src/platform/EterLib/MSWindow.cpp:CMSWindow::IsActive"
],
"test": [
"tests/port/port_ms_window_test.cpp"
],
"note": "verbatim over the <winuser.h> subset in src/platform/Win32User.cpp (host-side window records + message queue)"
},
"CMSWindow::GetInstance": {
"status": "PORTED",
"impl": [
"src/platform/EterLib/MSWindow.cpp:CMSWindow::GetInstance"
],
"test": [
"tests/port/port_ms_window_test.cpp"
],
"note": "verbatim over the <winuser.h> subset in src/platform/Win32User.cpp (host-side window records + message queue)"
},
"CMSWindow::GetWindowHandle": {
"status": "PORTED",
"impl": [
"src/platform/EterLib/MSWindow.cpp:CMSWindow::GetWindowHandle"
],
"test": [
"tests/port/port_ms_window_test.cpp"
],
"note": "verbatim over the <winuser.h> subset in src/platform/Win32User.cpp (host-side window records + message queue)"
},
"CMSWindow::GetScreenWidth": {
"status": "PORTED",
"impl": [
"src/platform/EterLib/MSWindow.cpp:CMSWindow::GetScreenWidth"
],
"test": [
"tests/port/port_ms_window_test.cpp"
],
"note": "verbatim over the <winuser.h> subset in src/platform/Win32User.cpp (host-side window records + message queue)"
},
"CMSWindow::GetScreenHeight": {
"status": "PORTED",
"impl": [
"src/platform/EterLib/MSWindow.cpp:CMSWindow::GetScreenHeight"
],
"test": [
"tests/port/port_ms_window_test.cpp"
],
"note": "verbatim over the <winuser.h> subset in src/platform/Win32User.cpp (host-side window records + message queue)"
},
"CMSWindow::GetWindowRect": {
"status": "PORTED",
"impl": [
"src/platform/EterLib/MSWindow.cpp:CMSWindow::GetWindowRect"
],
"test": [
"tests/port/port_ms_window_test.cpp"
],
"note": "verbatim over the <winuser.h> subset in src/platform/Win32User.cpp (host-side window records + message queue)"
},
"CMSWindow::GetClientRect": {
"status": "PORTED",
"impl": [
"src/platform/EterLib/MSWindow.cpp:CMSWindow::GetClientRect"
],
"test": [
"tests/port/port_ms_window_test.cpp"
],
"note": "verbatim over the <winuser.h> subset in src/platform/Win32User.cpp (host-side window records + message queue)"
},
"CMSWindow::GetMousePosition": {
"status": "PORTED",
"impl": [
"src/platform/EterLib/MSWindow.cpp:CMSWindow::GetMousePosition"
],
"test": [
"tests/port/port_ms_window_test.cpp"
],
"note": "verbatim over the <winuser.h> subset in src/platform/Win32User.cpp (host-side window records + message queue)"
},
"CMSWindow::SetPosition": {
"status": "PORTED",
"impl": [
"src/platform/EterLib/MSWindow.cpp:CMSWindow::SetPosition"
],
"test": [
"tests/port/port_ms_window_test.cpp"
],
"note": "verbatim over the <winuser.h> subset in src/platform/Win32User.cpp (host-side window records + message queue)"
},
"CMSWindow::SetCenterPosition": {
"status": "PORTED",
"impl": [
"src/platform/EterLib/MSWindow.cpp:CMSWindow::SetCenterPosition"
],
"test": [
"tests/port/port_ms_window_test.cpp"
],
"note": "verbatim over the <winuser.h> subset in src/platform/Win32User.cpp (host-side window records + message queue)"
},
"CMSWindow::AdjustSize": {
"status": "PORTED",
"impl": [
"src/platform/EterLib/MSWindow.cpp:CMSWindow::AdjustSize"
],
"test": [
"tests/port/port_ms_window_test.cpp"
],
"note": "verbatim over the <winuser.h> subset in src/platform/Win32User.cpp (host-side window records + message queue)"
},
"CMSWindow::SetText": {
"status": "PORTED",
"impl": [
"src/platform/EterLib/MSWindow.cpp:CMSWindow::SetText"
],
"test": [
"tests/port/port_ms_window_test.cpp"
],
"note": "verbatim over the <winuser.h> subset in src/platform/Win32User.cpp (host-side window records + message queue)"
},
"CMSWindow::SetSize": {
"status": "PORTED",
"impl": [
"src/platform/EterLib/MSWindow.cpp:CMSWindow::SetSize"
],
"test": [
"tests/port/port_ms_window_test.cpp"
],
"note": "verbatim over the <winuser.h> subset in src/platform/Win32User.cpp (host-side window records + message queue)"
},
"CMSWindow::RegisterWindowClass": {
"status": "ADAPTED",
"impl": [
"src/platform/EterLib/MSWindow.cpp:CMSWindow::RegisterWindowClass"
],
"test": [
"tests/port/port_ms_window_test.cpp"
],
"note": "40250: formats (DWORD) pfnWndProc into the class name. Port: (DWORD)(uintptr_t), the low 32 bits. Invariant: one class per style/brush/procedure, registered once."
},
"CMSWindow::CMSWindow": {
"status": "PORTED",
"impl": [
"src/platform/EterLib/MSWindow.cpp:CMSWindow::CMSWindow"
],
"test": [
"tests/port/port_ms_window_test.cpp"
],
"note": "verbatim over the <winuser.h> subset in src/platform/Win32User.cpp (host-side window records + message queue)"
},
"CMSWindow::~CMSWindow": {
"status": "PORTED",
"impl": [
"src/platform/EterLib/MSWindow.cpp:CMSWindow::~CMSWindow"
],
"test": [
"tests/port/port_ms_window_test.cpp"
],
"note": "verbatim over the <winuser.h> subset in src/platform/Win32User.cpp (host-side window records + message queue)"
}
}
}
+31 -22
View File
@@ -1,49 +1,58 @@
// Platform skeleton for EterLib/MSApplication.h (40250 EterLib/MSApplication.cpp), generated by platform_stub.py.
// Every MT_PLATFORM_STUB() body is unimplemented: replace it with the platform implementation.
// 40250 EterLib/MSApplication.cpp, verbatim over the <winuser.h> subset of platform/Win32User.cpp.
// ClearWindowClass is declared in MSApplication.h but not defined in 40250.
#include "EterLib/StdAfx.h"
#include "EterLib/MSApplication.h"
#include "../PlatformStub.h"
CMSApplication::CMSApplication()
{
MT_PLATFORM_STUB();
}
CMSApplication::~CMSApplication()
{
MT_PLATFORM_STUB();
// for (TWindowClassSet::iterator i=ms_stWCSet.begin(); i!=ms_stWCSet.end(); ++i)
// UnregisterClass(*i, ms_hInstance);
}
auto CMSApplication::Initialize(HINSTANCE) -> void
void CMSApplication::Initialize(HINSTANCE hInstance)
{
MT_PLATFORM_STUB();
ms_hInstance = hInstance;
}
auto CMSApplication::MessageLoop() -> void
void CMSApplication::MessageLoop()
{
MT_PLATFORM_STUB();
while (MessageProcess());
}
auto CMSApplication::IsMessage() -> bool
bool CMSApplication::IsMessage()
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<bool>();
MSG msg;
if (!PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
return false;
return true;
}
auto CMSApplication::MessageProcess() -> bool
bool CMSApplication::MessageProcess()
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<bool>();
MSG msg;
if (!GetMessage(&msg, NULL, 0, 0))
return false;
TranslateMessage(&msg);
DispatchMessage(&msg);
return true;
}
auto CMSApplication::ClearWindowClass() -> void
LRESULT CMSApplication::WindowProcedure(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
MT_PLATFORM_STUB();
switch (uiMsg)
{
case WM_CLOSE:
PostQuitMessage(0);
break;
}
auto CMSApplication::WindowProcedure(HWND, UINT, WPARAM, LPARAM) -> LRESULT
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<LRESULT>();
return CMSWindow::WindowProcedure(hWnd, uiMsg, wParam, lParam);
}
+243 -139
View File
@@ -1,150 +1,254 @@
// Platform skeleton for EterLib/MSWindow.h (40250 EterLib/MSWindow.cpp), generated by platform_stub.py.
// Every MT_PLATFORM_STUB() body is unimplemented: replace it with the platform implementation.
// 40250 EterLib/MSWindow.cpp, verbatim over the <winuser.h> subset of platform/Win32User.cpp, except the
// two pointer-to-DWORD casts (PORT below), which do not fit a 64-bit pointer.
#include "EterLib/StdAfx.h"
#include "EterLib/MSWindow.h"
#include "../PlatformStub.h"
#include "HostCursor.h"
#include <windowsx.h>
CMSWindow::TWindowClassSet CMSWindow::ms_stWCSet;
HINSTANCE CMSWindow::ms_hInstance = NULL;
LRESULT CALLBACK MSWindowProcedure(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
// PORT: 40250 reads GetWindowLong(GWL_USERDATA), the 32-bit pointer SetWindowLong stored in Create.
CMSWindow * pWnd = (CMSWindow *) GetWindowLongPtr(hWnd, GWLP_USERDATA);
if (pWnd)
return pWnd->WindowProcedure(hWnd, uiMsg, wParam, lParam);
return DefWindowProc(hWnd, uiMsg, wParam, lParam);
}
LRESULT CMSWindow::WindowProcedure(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
switch (uiMsg)
{
case WM_SIZE:
OnSize(wParam, lParam);
break;
case WM_ACTIVATEAPP:
m_isActive = (wParam == WA_ACTIVE) || (wParam == WA_CLICKACTIVE);
break;
}
return DefWindowProc(hWnd, uiMsg, wParam, lParam);
}
void CMSWindow::OnSize(WPARAM wParam, LPARAM /*lParam*/)
{
if (wParam == SIZE_MINIMIZED)
{
InvalidateRect(m_hWnd, NULL, true);
m_isActive = false;
m_isVisible = false;
}
else
{
m_isActive = true;
m_isVisible = true;
}
}
void CMSWindow::Destroy()
{
if (!m_hWnd)
return;
if (IsWindow(m_hWnd))
DestroyWindow(m_hWnd);
m_hWnd = NULL;
m_isVisible = false;
}
bool CMSWindow::Create(const char* c_szName, int brush, DWORD cs, DWORD ws, HICON hIcon, int iCursorResource)
{
//assert(ms_hInstance != NULL);
Destroy();
const char* c_szClassName = RegisterWindowClass(cs, brush, MSWindowProcedure, hIcon, iCursorResource);
m_hWnd = CreateWindow(
c_szClassName,
c_szName,
ws,
0, 0, 0, 0,
NULL,
NULL,
ms_hInstance,
NULL);
if (!m_hWnd)
return false;
// PORT: 40250 stores (DWORD) this with SetWindowLong(GWL_USERDATA); a 64-bit pointer needs the LONG_PTR slot.
SetWindowLongPtr(m_hWnd, GWLP_USERDATA, (LONG_PTR) this);
//DestroyWindow(ImmGetDefaultIMEWnd(m_hWnd));
return true;
}
void CMSWindow::SetVisibleMode(bool isVisible)
{
m_isVisible = isVisible;
if (m_isVisible)
{
ShowWindow(m_hWnd, SW_SHOW);
}
else
{
ShowWindow(m_hWnd, SW_HIDE);
}
}
void CMSWindow::Show()
{
m_isVisible = true;
ShowWindow(m_hWnd, SW_SHOW);
}
void CMSWindow::Hide()
{
m_isVisible = false;
ShowWindow(m_hWnd, SW_HIDE);
}
bool CMSWindow::IsVisible()
{
return m_isVisible;
}
bool CMSWindow::IsActive()
{
return m_isActive;
}
HINSTANCE CMSWindow::GetInstance()
{
return ms_hInstance;
}
HWND CMSWindow::GetWindowHandle()
{
return m_hWnd;
}
int CMSWindow::GetScreenWidth()
{
return GetSystemMetrics(SM_CXSCREEN);
}
int CMSWindow::GetScreenHeight()
{
return GetSystemMetrics(SM_CYSCREEN);
}
void CMSWindow::GetWindowRect(RECT* prc)
{
::GetWindowRect(m_hWnd, prc);
}
void CMSWindow::GetClientRect(RECT* prc)
{
::GetClientRect(m_hWnd, prc);
}
void CMSWindow::GetMousePosition(POINT* ppt)
{
GetCursorPos(ppt);
ScreenToClient(m_hWnd, ppt);
}
void CMSWindow::SetPosition(int x, int y)
{
SetWindowPos(m_hWnd, NULL, x, y, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
}
void CMSWindow::SetCenterPosition()
{
RECT rc;
GetClientRect(&rc);
int windowWidth = rc.right - rc.left;
int windowHeight = rc.bottom - rc.top;
SetPosition((GetScreenWidth()-windowWidth)/2, (GetScreenHeight()-windowHeight)/2);
}
void CMSWindow::AdjustSize(int width, int height)
{
SetRect(&m_rect, 0, 0, width, height);
AdjustWindowRectEx(&m_rect,
GetWindowStyle(m_hWnd),
GetMenu(m_hWnd ) != NULL,
GetWindowExStyle(m_hWnd ) );
MoveWindow
(
m_hWnd,
0,
0,
m_rect.right - m_rect.left,
m_rect.bottom - m_rect.top,
FALSE
);
}
void CMSWindow::SetText(const char* c_szText)
{
SetWindowText(m_hWnd, c_szText);
}
void CMSWindow::SetSize(int width, int height)
{
SetWindowPos(m_hWnd, NULL, 0, 0, width, height, SWP_NOZORDER|SWP_NOMOVE);
}
const char * CMSWindow::RegisterWindowClass(DWORD style, int brush, WNDPROC pfnWndProc, HICON hIcon, int iCursorResource)
{
char szClassName[1024];
// PORT: 40250 casts the procedure straight to DWORD; the low 32 bits of the 64-bit address, as there.
sprintf(szClassName, "eter - s%x:b%x:p:%x", style, brush, (DWORD) (uintptr_t) pfnWndProc);
TWindowClassSet::iterator f = ms_stWCSet.find((char*) szClassName);
if (f != ms_stWCSet.end())
return *f;
const char* c_szStaticClassName = stl_static_string(szClassName).c_str();
ms_stWCSet.insert((char * const) c_szStaticClassName);
WNDCLASS wc;
wc.style = 0;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.lpfnWndProc = pfnWndProc;
wc.hCursor = LoadCursor(ms_hInstance, MAKEINTRESOURCE(iCursorResource));
wc.hIcon = hIcon ? hIcon : LoadIcon(ms_hInstance, IDI_APPLICATION);
wc.hbrBackground = (HBRUSH) GetStockObject(brush);
wc.hInstance = ms_hInstance;
wc.lpszClassName = c_szStaticClassName;
wc.lpszMenuName = "";
if (!RegisterClass(&wc))
return "";
return c_szStaticClassName;
}
CMSWindow::CMSWindow()
{
MT_PLATFORM_STUB();
m_hWnd=NULL;
m_isVisible=false;
}
CMSWindow::~CMSWindow()
{
MT_PLATFORM_STUB();
}
auto CMSWindow::Destroy() -> void
{
MT_PLATFORM_STUB();
}
auto CMSWindow::Create(const char *, int, DWORD, DWORD, HICON, int) -> bool
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<bool>();
}
auto CMSWindow::Show() -> void
{
MT_PLATFORM_STUB();
}
auto CMSWindow::Hide() -> void
{
MT_PLATFORM_STUB();
}
auto CMSWindow::SetVisibleMode(bool) -> void
{
MT_PLATFORM_STUB();
}
auto CMSWindow::SetPosition(int, int) -> void
{
MT_PLATFORM_STUB();
}
auto CMSWindow::SetCenterPosition() -> void
{
MT_PLATFORM_STUB();
}
auto CMSWindow::SetText(const char *) -> void
{
MT_PLATFORM_STUB();
}
auto CMSWindow::AdjustSize(int, int) -> void
{
MT_PLATFORM_STUB();
}
auto CMSWindow::SetSize(int, int) -> void
{
MT_PLATFORM_STUB();
}
auto CMSWindow::IsVisible() -> bool
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<bool>();
}
auto CMSWindow::IsActive() -> bool
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<bool>();
}
namespace {
POINT g_host_cursor = {0, 0};
}
void MtHostSetCursor(int x, int y)
{
g_host_cursor.x = x;
g_host_cursor.y = y;
}
auto CMSWindow::GetMousePosition(POINT * ppt) -> void
{
*ppt = g_host_cursor;
}
auto CMSWindow::GetClientRect(RECT *) -> void
{
MT_PLATFORM_STUB();
}
auto CMSWindow::GetWindowRect(RECT *) -> void
{
MT_PLATFORM_STUB();
}
auto CMSWindow::GetScreenWidth() -> int
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<int>();
}
auto CMSWindow::GetScreenHeight() -> int
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<int>();
}
auto CMSWindow::GetWindowHandle() -> HWND
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<HWND>();
}
auto CMSWindow::GetInstance() -> HINSTANCE
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<HINSTANCE>();
}
auto CMSWindow::WindowProcedure(HWND, UINT, WPARAM, LPARAM) -> LRESULT
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<LRESULT>();
}
auto CMSWindow::OnSize(WPARAM, LPARAM) -> void
{
MT_PLATFORM_STUB();
}
auto CMSWindow::RegisterWindowClass(DWORD, int, WNDPROC, HICON, int) -> const char *
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<const char *>();
}
decltype(CMSWindow::ms_stWCSet) CMSWindow::ms_stWCSet{};
decltype(CMSWindow::ms_hInstance) CMSWindow::ms_hInstance{};
+4 -3
View File
@@ -1084,9 +1084,10 @@ void Stop()
}
// winuser.h (declared in the port's windows.h shim). WM_QUIT ends 40250's CPythonApplication::Loop;
// here the script fiber's loop ends the same way.
void PostQuitMessage(int)
// winuser.h (common/shim/win32/winuser.h). WM_QUIT ends 40250's CPythonApplication::Loop; here the script
// fiber's loop ends the same way. The WM_QUIT is also queued for a GetMessage loop (CMSApplication).
void PostQuitMessage(int nExitCode)
{
PostMessage(NULL, WM_QUIT, WPARAM(nExitCode), 0);
PythonBoot::AppRequestQuit();
}
+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;
}
+3
View File
@@ -202,6 +202,9 @@ if(BUILD_TESTING AND CMAKE_SYSTEM_NAME STREQUAL CMAKE_HOST_SYSTEM_NAME)
add_executable(port_process_crc_test ${PROJECT_SOURCE_DIR}/tests/port/port_process_crc_test.cpp)
target_link_libraries(port_process_crc_test PRIVATE port_platform)
add_test(NAME port.process_crc COMMAND $<TARGET_FILE:port_process_crc_test>)
add_executable(port_ms_window_test ${PROJECT_SOURCE_DIR}/tests/port/port_ms_window_test.cpp)
target_link_libraries(port_ms_window_test PRIVATE port_platform)
add_test(NAME port.ms_window COMMAND $<TARGET_FILE:port_ms_window_test>)
# MilesLib: the 40250 sound managers over the Miles emulation (platform/MilesLib/MilesAil.cpp).
add_executable(port_miles_test ${PROJECT_SOURCE_DIR}/tests/port/port_miles_test.cpp)
+3 -5
View File
@@ -8,6 +8,7 @@
#include "../../Win32Types.h"
#include "../../Win32Crt.h"
#include "wingdi.h"
#include "winuser.h"
// winuser.h / wingdi.h constants named in 40250 declarations (default arguments).
#define WS_OVERLAPPED 0x00000000L
@@ -39,10 +40,7 @@
inline int GetSystemMetrics(int nIndex)
{
if (nIndex == SM_CXFULLSCREEN) return 1920;
if (nIndex == SM_CYFULLSCREEN) return 1080;
if (nIndex == SM_CXSCREEN || nIndex == SM_CXFULLSCREEN) return 1920;
if (nIndex == SM_CYSCREEN || nIndex == SM_CYFULLSCREEN) return 1080;
return 0;
}
// winuser.h functions the platform layer implements (platform/ScriptLib/PythonBoot.cpp).
void PostQuitMessage(int nExitCode);
+3
View File
@@ -1,2 +1,5 @@
#pragma once
#include <windows.h>
#define GetWindowStyle(hwnd) ((DWORD)GetWindowLong(hwnd, GWL_STYLE))
#define GetWindowExStyle(hwnd) ((DWORD)GetWindowLong(hwnd, GWL_EXSTYLE))
+1
View File
@@ -54,6 +54,7 @@ int SetDIBitsToDevice(HDC hdc, int xDest, int yDest, DWORD w, DWORD h, int xSrc,
HFONT CreateFontIndirect(const LOGFONT* lplf);
BOOL DeleteObject(HGDIOBJ ho);
HGDIOBJ SelectObject(HDC hdc, HGDIOBJ h);
HGDIOBJ GetStockObject(int i); // platform/Win32User.cpp (the window background brush)
COLORREF SetTextColor(HDC hdc, COLORREF color);
COLORREF SetBkColor(HDC hdc, COLORREF color);
int SetBkMode(HDC hdc, int mode);
+107
View File
@@ -0,0 +1,107 @@
#pragma once
// The <winuser.h> window and message-queue subset 40250 EterLib/MSWindow.cpp and MSApplication.cpp call,
// implemented by platform/Win32User.cpp over host-side window records (the host owns the real surface).
// The A/W pairs are declared under their plain names: no 40250 caller spells the A or W form.
#include "../../Win32Types.h"
#include "wingdi.h"
struct HMENU__;
typedef HMENU__* HMENU;
typedef WORD ATOM;
#define WM_CREATE 0x0001
#define WM_DESTROY 0x0002
#define WM_MOVE 0x0003
#define WM_SIZE 0x0005
#define WM_CLOSE 0x0010
#define WM_QUIT 0x0012
#define WM_ACTIVATEAPP 0x001C
#define WA_INACTIVE 0
#define WA_ACTIVE 1
#define WA_CLICKACTIVE 2
#define SIZE_RESTORED 0
#define SIZE_MINIMIZED 1
#define SW_HIDE 0
#define SW_SHOW 5
#define SWP_NOSIZE 0x0001
#define SWP_NOMOVE 0x0002
#define SWP_NOZORDER 0x0004
#define PM_NOREMOVE 0x0000
#define PM_REMOVE 0x0001
#define GWL_STYLE (-16)
#define GWL_EXSTYLE (-20)
#define GWL_USERDATA (-21)
#define GWLP_USERDATA (-21)
#define SM_CXSCREEN 0
#define SM_CYSCREEN 1
#define MAKELPARAM(l, h) ((LPARAM)(DWORD)MAKELONG(l, h))
#define MAKEINTRESOURCE(i) ((LPCSTR)((ULONG_PTR)((WORD)(i))))
#define IDI_APPLICATION MAKEINTRESOURCE(32512)
typedef struct tagMSG
{
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
} MSG, *PMSG, *LPMSG;
typedef struct tagWNDCLASS
{
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HINSTANCE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCSTR lpszMenuName;
LPCSTR lpszClassName;
} WNDCLASS, *PWNDCLASS, *LPWNDCLASS;
ATOM RegisterClass(const WNDCLASS* lpWndClass);
HWND CreateWindow(LPCSTR lpClassName, LPCSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight,
HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam);
BOOL DestroyWindow(HWND hWnd);
BOOL IsWindow(HWND hWnd);
LRESULT DefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
LONG GetWindowLong(HWND hWnd, int nIndex);
LONG SetWindowLong(HWND hWnd, int nIndex, LONG dwNewLong);
LONG_PTR GetWindowLongPtr(HWND hWnd, int nIndex);
LONG_PTR SetWindowLongPtr(HWND hWnd, int nIndex, LONG_PTR dwNewLong);
BOOL ShowWindow(HWND hWnd, int nCmdShow);
BOOL SetWindowPos(HWND hWnd, HWND hWndInsertAfter, int X, int Y, int cx, int cy, UINT uFlags);
BOOL MoveWindow(HWND hWnd, int X, int Y, int nWidth, int nHeight, BOOL bRepaint);
BOOL AdjustWindowRectEx(LPRECT lpRect, DWORD dwStyle, BOOL bMenu, DWORD dwExStyle);
BOOL GetWindowRect(HWND hWnd, LPRECT lpRect);
BOOL GetClientRect(HWND hWnd, LPRECT lpRect);
BOOL SetWindowText(HWND hWnd, LPCSTR lpString);
BOOL InvalidateRect(HWND hWnd, const RECT* lpRect, BOOL bErase);
HMENU GetMenu(HWND hWnd);
BOOL SetRect(LPRECT lprc, int xLeft, int yTop, int xRight, int yBottom);
BOOL GetCursorPos(LPPOINT lpPoint);
BOOL ScreenToClient(HWND hWnd, LPPOINT lpPoint);
HCURSOR LoadCursor(HINSTANCE hInstance, LPCSTR lpCursorName);
HICON LoadIcon(HINSTANCE hInstance, LPCSTR lpIconName);
BOOL PostMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
BOOL PeekMessage(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg);
BOOL GetMessage(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax);
BOOL TranslateMessage(const MSG* lpMsg);
LRESULT DispatchMessage(const MSG* lpMsg);
void PostQuitMessage(int nExitCode);
+91
View File
@@ -0,0 +1,91 @@
// 40250 CMSWindow / CMSApplication over the <winuser.h> subset of platform/Win32User.cpp.
#include "EterLib/StdAfx.h"
#include "EterLib/MSApplication.h"
#include "../../src/platform/EterLib/HostCursor.h"
#include <cstdio>
#include <vector>
namespace {
struct App : CMSApplication
{
std::vector<UINT> seen;
LRESULT WindowProcedure(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam) override
{
seen.push_back(uiMsg);
return CMSApplication::WindowProcedure(hWnd, uiMsg, wParam, lParam);
}
bool Active() const { return m_isActive; }
const char* Register() { return RegisterWindowClass(0, BLACK_BRUSH, DefWindowProc); }
};
}
int main()
{
int failures = 0;
auto check = [&failures](bool ok, const char *name) {
if (!ok) {
std::fprintf(stderr, "FAIL %s\n", name);
++failures;
}
};
App app;
app.Initialize(reinterpret_cast<HINSTANCE>(0x400000));
check(app.GetInstance() == reinterpret_cast<HINSTANCE>(0x400000), "Initialize sets the shared instance");
check(app.GetWindowHandle() == NULL && !app.IsVisible(), "constructor: no window, hidden");
const char* first = app.Register();
check(first[0] != '\0' && app.Register() == first, "RegisterWindowClass reuses the class name");
check(app.Create("Metin2") && IsWindow(app.GetWindowHandle()), "Create");
check(!app.IsVisible(), "Create leaves the window hidden");
// SetSize: Win32 sends WM_SIZE (SIZE_RESTORED) synchronously -> OnSize marks the window active and visible.
app.SetSize(800, 600);
RECT rc;
app.GetClientRect(&rc);
check(rc.left == 0 && rc.top == 0 && rc.right == 800 && rc.bottom == 600, "SetSize client rect");
check(app.IsActive() && app.IsVisible(), "WM_SIZE restored -> active, visible");
check(!app.seen.empty() && app.seen.back() == WM_SIZE, "WM_SIZE reaches the window procedure");
app.SetCenterPosition();
app.GetWindowRect(&rc);
check(rc.left == (1920 - 800) / 2 && rc.top == (1080 - 600) / 2 && rc.right - rc.left == 800, "SetCenterPosition");
// The host cursor is in client coordinates; GetCursorPos + ScreenToClient round-trips it.
MtHostSetCursor(5, 7);
POINT pt;
app.GetMousePosition(&pt);
check(pt.x == 5 && pt.y == 7, "GetMousePosition");
app.AdjustSize(640, 480);
app.GetWindowRect(&rc);
check(rc.left == 0 && rc.top == 0 && rc.right == 640 && rc.bottom == 480, "AdjustSize moves to 0,0 at the client size");
app.Hide();
check(!app.IsVisible(), "Hide");
app.SetVisibleMode(true);
check(app.IsVisible(), "SetVisibleMode");
// The message queue: IsMessage peeks, MessageProcess dispatches to CMSWindow::WindowProcedure.
check(!app.IsMessage(), "IsMessage on an empty queue");
PostMessage(app.GetWindowHandle(), WM_ACTIVATEAPP, WA_INACTIVE, 0);
check(app.IsMessage() && app.IsMessage(), "IsMessage does not remove");
check(app.MessageProcess() && !app.Active(), "WM_ACTIVATEAPP inactive");
PostMessage(app.GetWindowHandle(), WM_ACTIVATEAPP, WA_CLICKACTIVE, 0);
check(app.MessageProcess() && app.Active(), "WM_ACTIVATEAPP click-active");
PostMessage(app.GetWindowHandle(), WM_SIZE, SIZE_MINIMIZED, 0);
check(app.MessageProcess() && !app.Active() && !app.IsVisible(), "WM_SIZE minimized -> inactive, hidden");
// WM_CLOSE: CMSApplication posts WM_QUIT, DefWindowProc destroys the window, MessageLoop ends on WM_QUIT.
HWND hWnd = app.GetWindowHandle();
PostMessage(hWnd, WM_CLOSE, 0, 0);
app.seen.clear();
app.MessageLoop();
check(app.seen.size() == 2 && app.seen[0] == WM_CLOSE && app.seen[1] == WM_DESTROY, "WM_CLOSE then WM_DESTROY");
check(!IsWindow(hWnd) && !app.IsMessage(), "window destroyed, queue drained");
app.Destroy();
check(app.GetWindowHandle() == NULL, "Destroy clears the handle");
return failures ? 1 : 0;
}