// The 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 #include #include #include #include #include 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 g_classes; std::set g_windows; HWND g_mainWindow = NULL; std::deque 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 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 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 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 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 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 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 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 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 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 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 lock(g_mutex); if (!Alive(hWnd) || !lpRect) return FALSE; *lpRect = hWnd->rect; return TRUE; } BOOL GetClientRect(HWND hWnd, LPRECT lpRect) { std::lock_guard 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 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 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 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 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 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 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; }