// 40250 CMSWindow / CMSApplication over the subset of platform/Win32User.cpp. #include "EterLib/StdAfx.h" #include "EterLib/MSApplication.h" #include "../../src/platform/EterLib/HostCursor.h" #include #include namespace { struct App : CMSApplication { std::vector 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(0x400000)); check(app.GetInstance() == reinterpret_cast(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; }