diff --git a/engine/src/platform/windows/window_driver.cpp b/engine/src/platform/windows/window_driver.cpp index b84d822..9898a15 100644 --- a/engine/src/platform/windows/window_driver.cpp +++ b/engine/src/platform/windows/window_driver.cpp @@ -8,21 +8,127 @@ #include "platform/windows/window_driver.h" +#include "platform/windows/window_structs.h" + #ifdef NOVA_VULKAN +#include "drivers/vulkan/render_driver.h" +#include "drivers/vulkan/render_structs.h" + #include #include #endif #include +#include + +namespace { + static constexpr LPCSTR WINDOW_CLASS_NAME = "NOVA_WindowClass"; +} using namespace Nova; Win32WindowDriver::Win32WindowDriver() { NOVA_AUTO_TRACE(); + + WNDCLASSEX wc = {}; + wc.cbSize = sizeof(WNDCLASSEX); + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = _window_proc; + wc.hInstance = GetModuleHandle(nullptr); + wc.lpszClassName = WINDOW_CLASS_NAME; + + if (!RegisterClassEx(&wc)) { + throw std::runtime_error("Failed to register window class"); + } } Win32WindowDriver::~Win32WindowDriver() { NOVA_AUTO_TRACE(); + UnregisterClass(WINDOW_CLASS_NAME, GetModuleHandle(nullptr)); +} + +WindowAPI Win32WindowDriver::get_api() const { + return WindowAPI::WINDOWS; +} + +std::string Win32WindowDriver::get_api_name() const { + return "Win32"; +} + +void Win32WindowDriver::poll_events() { + MSG msg; + while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } +} + +void Win32WindowDriver::beep() { + MessageBeep(MB_OK); +} + +u32 Win32WindowDriver::get_window_count() const { + return static_cast(m_windows.size()); +} + +WindowID Win32WindowDriver::create_window(const std::string& p_title, u32 p_width, u32 p_height) { + NOVA_AUTO_TRACE(); + + RECT rect = {0, 0, static_cast(p_width), static_cast(p_height)}; + AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE); + + HWND handle = CreateWindowEx( + 0, + WINDOW_CLASS_NAME, + p_title.c_str(), + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, + CW_USEDEFAULT, + rect.right - rect.left, + rect.bottom - rect.top, + nullptr, + nullptr, + GetModuleHandle(nullptr), + this + ); + + Window* window = new Window(); + window->width = p_width; + window->height = p_height; + window->handle = handle; + + ShowWindow(handle, SW_SHOW); + UpdateWindow(handle); + + m_windows[handle] = window; + return window; +} + +void Win32WindowDriver::destroy_window(WindowID p_window) { + NOVA_AUTO_TRACE(); + NOVA_ASSERT(p_window); + DestroyWindow(p_window->handle); + m_windows.erase(p_window->handle); +} + +void Win32WindowDriver::set_window_title(WindowID p_window, const std::string& p_title) { + NOVA_AUTO_TRACE(); + NOVA_ASSERT(p_window); + SetWindowText(p_window->handle, p_title.c_str()); +} + +void Win32WindowDriver::set_window_size(WindowID p_window, u32 p_width, u32 p_height) { + NOVA_AUTO_TRACE(); + NOVA_ASSERT(p_window); + RECT rect = {0, 0, static_cast(p_width), static_cast(p_height)}; + AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE); + SetWindowPos(p_window->handle, nullptr, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER | SWP_NOMOVE); +} + +void Win32WindowDriver::set_window_position(WindowID p_window, i32 p_x, i32 p_y) { + NOVA_AUTO_TRACE(); + NOVA_ASSERT(p_window); + SetWindowPos(p_window->handle, nullptr, p_x, p_y, 0, 0, SWP_NOZORDER | SWP_NOSIZE); } const char* Win32WindowDriver::get_surface_extension() const { @@ -33,4 +139,84 @@ const char* Win32WindowDriver::get_surface_extension() const { #endif } +SurfaceID Win32WindowDriver::create_surface(WindowID p_window, RenderDriver* p_driver) { + NOVA_AUTO_TRACE(); + NOVA_ASSERT(p_window); + NOVA_ASSERT(p_driver); + NOVA_ASSERT(p_driver->get_api() == RenderAPI::VULKAN); + +#ifdef NOVA_VULKAN + VkWin32SurfaceCreateInfoKHR create {}; + create.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; + create.hinstance = GetModuleHandle(nullptr); + create.hwnd = p_window->handle; + + const auto vkrd = static_cast(p_driver); + Surface* surface = new Surface(); + + if (vkCreateWin32SurfaceKHR(vkrd->get_instance(), &create, vkrd->get_allocator(VK_OBJECT_TYPE_SURFACE_KHR), &surface->handle) + != VK_SUCCESS) { + throw std::runtime_error("Failed to create Vulkan surface"); + } + + return surface; +#else + return nullptr; +#endif +} + +LRESULT Win32WindowDriver::_handle_message(HWND p_handle, UINT p_msg, WPARAM p_wparam, LPARAM p_lparam) { + auto iter = m_windows.find(p_handle); + if (iter == m_windows.end()) { + return DefWindowProc(p_handle, p_msg, p_wparam, p_lparam); + } + + WindowID window = iter->second; + + switch (p_msg) { + case WM_DESTROY: { + NOVA_DEBUG("Window event: DESTROYED"); + if (m_windows.empty()) { + PostQuitMessage(0); + } + return 0; + } + case WM_SIZE: { + int width = LOWORD(p_lparam); + int height = HIWORD(p_lparam); + if (width != window->width || height != window->height) { + window->width = width; + window->height = height; + NOVA_DEBUG("Window event: RESIZED ({}x{})", width, height); + } + return 0; + } + case WM_CLOSE: { + NOVA_DEBUG("Window event: CLOSED"); + destroy_window(window); + return 0; + } + default: + return DefWindowProc(p_handle, p_msg, p_wparam, p_lparam); + } +} + +LRESULT CALLBACK Win32WindowDriver::_window_proc(HWND p_handle, UINT p_msg, WPARAM p_wparam, LPARAM p_lparam) { + Win32WindowDriver* driver = nullptr; + + if (p_msg == WM_NCCREATE) { + CREATESTRUCT* cs = reinterpret_cast(p_lparam); + driver = static_cast(cs->lpCreateParams); + SetWindowLongPtr(p_handle, GWLP_USERDATA, reinterpret_cast(driver)); + } else { + driver = reinterpret_cast(GetWindowLongPtr(p_handle, GWLP_USERDATA)); + } + + if (driver) { + return driver->_handle_message(p_handle, p_msg, p_wparam, p_lparam); + } + + return DefWindowProc(p_handle, p_msg, p_wparam, p_lparam); +} + #endif // NOVA_WINDOWS diff --git a/engine/src/platform/windows/window_driver.h b/engine/src/platform/windows/window_driver.h index 7d4a6c6..524ed37 100644 --- a/engine/src/platform/windows/window_driver.h +++ b/engine/src/platform/windows/window_driver.h @@ -11,13 +11,37 @@ #include #include +#include + namespace Nova { class Win32WindowDriver final : public WindowDriver { public: Win32WindowDriver(); ~Win32WindowDriver() override; + WindowAPI get_api() const override; + std::string get_api_name() const override; + + void poll_events() override; + void beep() override; + + u32 get_window_count() const override; + [[nodiscard]] WindowID create_window(const std::string& title, u32 width, u32 height) override; + void destroy_window(WindowID window) override; + + void set_window_title(WindowID window, const std::string& title) override; + void set_window_size(WindowID window, u32 width, u32 height) override; + void set_window_position(WindowID window, i32 x, i32 y) override; + [[nodiscard]] const char* get_surface_extension() const override; + [[nodiscard]] SurfaceID create_surface(WindowID window, RenderDriver* p_driver) override; + + private: + std::unordered_map m_windows; + + LRESULT _handle_message(HWND handle, UINT msg, WPARAM wparam, LPARAM lparam); + + static LRESULT CALLBACK _window_proc(HWND handle, UINT msg, WPARAM wparam, LPARAM lparam); }; } // namespace Nova diff --git a/engine/src/platform/windows/window_structs.h b/engine/src/platform/windows/window_structs.h new file mode 100644 index 0000000..99eeb4d --- /dev/null +++ b/engine/src/platform/windows/window_structs.h @@ -0,0 +1,20 @@ +/** +* Copyright (c) 2025, Jayden Grubb + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#pragma once + +/// NOTE: This header should only be included in implementation files + +#include +#include + +namespace Nova { + struct Window { + HWND handle = {}; + int width = 0; + int height = 0; + }; +} // namespace Nova