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
+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;
}