/** * @file PlatformWin32.cpp * @brief Win32 平台窗口系统实现。 * @details * 该文件实现平台窗口后端的完整行为,包括: * - Win32 窗口类注册与窗口创建; * - 窗口 ID 与 HWND 的双向关联管理; * - 大小变化、销毁、全屏切换等消息处理; * - 对 Platform.h / Window.h 暴露接口的底层支撑。 */ #ifdef _WIN64 #include "Platform.h" #include "PlatformTypes.h" namespace XEngine::platform { namespace { /** * @brief Win32 窗口实例运行时状态。 */ struct window_info { HWND hwnd{ nullptr }; RECT client_area{ 0,0,1920,1080 }; RECT fullscreen_area{}; POINT top_left{ 0,0 }; DWORD style{ WS_VISIBLE }; bool is_fullscreen{ false }; bool is_closed{ false }; }; /** * @brief 窗口对象存储池。 */ utl::free_list windows; /** * @brief 根据窗口 ID 获取窗口状态对象。 * @param id 窗口 ID。 * @return 对应窗口状态引用。 */ window_info& get_from_id(window_id id) { assert(windows[id].hwnd); return windows[id]; } /** * @brief 根据 HWND 获取窗口状态对象。 * @param handle 原生窗口句柄。 * @return 对应窗口状态引用。 */ window_info& get_from_handle(window_handle handle) { const window_id id{ (id::id_type)GetWindowLongPtr(handle, GWLP_USERDATA) }; return get_from_id(id); } /** * @brief 标记窗口是否发生了可见尺寸变更。 */ bool resized{ false }; /** * @brief 按目标客户区更新窗口尺寸与位置。 * @param info 窗口状态。 * @param area 目标客户区矩形。 */ void resize_window(const window_info& info, const RECT& area) { RECT window_rect{ area }; AdjustWindowRect(&window_rect, info.style, FALSE); const s32 width{ window_rect.right - window_rect.left }; const s32 height{ window_rect.bottom - window_rect.top }; MoveWindow(info.hwnd, info.top_left.x, info.top_left.y, width, height, true); } /// /** * @brief 查询窗口是否处于全屏状态。 * @param id 窗口 ID。 * @return 全屏返回 true。 */ bool is_window_fullscreen(window_id id) { assert(id != u32_invalid_id); return get_from_id(id).is_fullscreen; } /** * @brief 查询窗口原生句柄。 * @param id 窗口 ID。 * @return HWND 句柄。 */ window_handle get_window_handle(window_id id) { return get_from_id(id).hwnd; } /** * @brief 设置窗口标题文本。 * @param id 窗口 ID。 * @param caption 标题文本。 */ void set_window_caption(window_id id, const wchar_t* caption) { window_info& info{ get_from_id(id) }; SetWindowText(info.hwnd, caption); } /** * @brief 查询窗口矩形(客户区或全屏区)。 * @param id 窗口 ID。 * @return 左上右下坐标向量。 */ math::u32v4 get_window_size(window_id id) { window_info& info{ get_from_id(id) }; RECT& rect{ info.is_fullscreen ? info.fullscreen_area : info.client_area }; return { (u32)rect.left,(u32)rect.top,(u32)rect.right,(u32)rect.bottom }; } /** * @brief 调整窗口客户区尺寸。 * @param id 窗口 ID。 * @param width 新宽度。 * @param height 新高度。 */ void resize_window(window_id id, u32 width, u32 height) { window_info& info{ get_from_id(id) }; // when we host the window in the level editor we just update // the internal data. if (info.style & WS_CHILD) { GetClientRect(info.hwnd, &info.client_area); } else { RECT& area{ info.is_fullscreen ? info.fullscreen_area : info.client_area }; area.bottom = area.top + height; area.right = area.left + width; resize_window(info, area); } } /** * @brief 查询窗口是否已被关闭。 * @param id 窗口 ID。 * @return 已关闭返回 true。 */ bool is_window_close(window_id id) { return get_from_id(id).is_closed; } /// /** * @brief 平台内部默认窗口过程。 * @param hwnd 窗口句柄。 * @param msg 消息类型。 * @param wparam 消息参数。 * @param lparam 消息参数。 * @return 消息处理结果。 */ LRESULT CALLBACK internal_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { switch (msg) { case WM_NCCREATE: { DEBUG_OP(SetLastError(0)); const window_id id{ windows.add() }; windows[id].hwnd = hwnd; SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)id); assert(GetLastError() == 0); } break; case WM_DESTROY: get_from_handle(hwnd).is_closed = true; break; case WM_SIZE: resized = (wparam != SIZE_MINIMIZED); break; default: break; } if (resized && GetAsyncKeyState(VK_LBUTTON) >= 0) { window_info& info{ get_from_handle(hwnd) }; assert(info.hwnd); GetClientRect(info.hwnd, info.is_fullscreen ? &info.fullscreen_area : &info.client_area); resized = false; } LONG_PTR long_ptr{ GetWindowLongPtr(hwnd, 0) }; return long_ptr ? ((window_proc)long_ptr)(hwnd, msg, wparam, lparam) : DefWindowProc(hwnd, msg, wparam, lparam); assert(GetLastError() == 0); } /** * @brief 切换窗口全屏状态。 * @param id 窗口 ID。 * @param is_fullscreen 目标全屏状态。 */ void set_window_fullscreen(window_id id, bool is_fullscreen) { window_info& info{ windows[id] }; if (info.is_fullscreen != is_fullscreen) { info.is_fullscreen = is_fullscreen; if (is_fullscreen) { GetClientRect(info.hwnd, &info.client_area); RECT rect; GetWindowRect(info.hwnd, &rect); info.top_left.x = rect.left; info.top_left.y = rect.top; SetWindowLongPtr(info.hwnd, GWL_STYLE, info.style); ShowWindow(info.hwnd, SW_MAXIMIZE); } else { SetWindowLongPtr(info.hwnd, GWL_STYLE, info.style); resize_window(info, info.client_area); ShowWindow(info.hwnd, SW_SHOWNORMAL); } } } } // namespace /** * @brief 创建 Win32 窗口并注册到窗口池。 * @param init_info 可选窗口初始化参数。 * @return 创建成功的窗口句柄对象。 */ window create_window(const window_init_info* init_info /* = nullptr */) { window_proc callback{ init_info ? init_info->callback : nullptr }; // 回调窗口过程:由初始化参数提供;未提供时为 nullptr。 window_handle parent{ init_info ? init_info->parent : nullptr }; // 父窗口句柄:用于子窗口挂载;未提供时创建顶层窗口。 WNDCLASSEX wc; ZeroMemory(&wc, sizeof(WNDCLASSEX)); // 初始化 WNDCLASSEX 字段。 wc.cbSize = sizeof(WNDCLASSEX); // 结构体大小。 wc.style = CS_HREDRAW | CS_VREDRAW; // 水平/垂直尺寸变化时重绘。 wc.lpfnWndProc = internal_window_proc; // 默认窗口过程入口。 wc.cbClsExtra = 0; // 类额外字节数。 wc.cbWndExtra = callback ? sizeof(callback) : 0; // 实例额外字节数:有回调时用于存储回调指针。 wc.hInstance = 0; // 当前模块实例句柄(使用默认)。 wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); // 大图标。 wc.hCursor = LoadCursor(NULL, IDC_ARROW); // 光标。 wc.hbrBackground = CreateSolidBrush(RGB(26, 48, 76)); // 背景画刷。 wc.lpszMenuName = NULL; // 菜单名(无)。 wc.lpszClassName = L"XWindow"; // 窗口类名。 wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); // 小图标。 // 注册窗口类。 if (!RegisterClassEx(&wc)) { // 若类已注册或注册失败,后续创建窗口时由系统返回结果。 } window_info info{}; info.client_area.right = (init_info && init_info->width) ? info.client_area.left + init_info->width : info.client_area.right; info.client_area.bottom = (init_info && init_info->height) ? info.client_area.top + init_info->height : info.client_area.bottom; info.style |= parent ? WS_CHILD : WS_OVERLAPPEDWINDOW; RECT rect{ info.client_area }; AdjustWindowRect(&rect, info.style, FALSE); const wchar_t* caption{ (init_info && init_info->caption) ? init_info->caption : L"XGame" }; const s32 left{ (init_info) ? init_info->left : info.top_left.x }; const s32 top{ (init_info) ? init_info->top : info.top_left.y }; const s32 width{ rect.right - rect.left }; const s32 height{ rect.bottom - rect.top }; // 创建窗口实例。 info.hwnd = CreateWindowEx( 0, // 扩展样式。 wc.lpszClassName, // 窗口类名。 caption, // 窗口标题。 info.style, // 窗口样式。 // 尺寸与位置。 left, top, width, height, parent, // 父窗口句柄(nullptr 表示顶层窗口)。 nullptr, // 菜单句柄(未使用)。 nullptr, // 实例句柄(未显式传入)。 nullptr // 创建参数(未使用)。 ); if (info.hwnd) { DEBUG_OP(SetLastError(0)); if (callback) SetWindowLongPtr(info.hwnd, 0, (LONG_PTR)callback); assert(GetLastError() == 0); ShowWindow(info.hwnd, SW_SHOWNORMAL); UpdateWindow(info.hwnd); window_id id{ (id::id_type)GetWindowLongPtr(info.hwnd, GWLP_USERDATA) }; windows[id] = info; return window{ id }; } return {}; // 创建失败时返回无效窗口句柄。 } /** * @brief 销毁窗口并从窗口池移除。 * @param id 窗口 ID。 */ void remove_window(window_id id) { window_info& info{ get_from_id(id) }; DestroyWindow(info.hwnd); windows.remove(id); } } #include "IncludeWindowCpp.h" #endif // _WIN64