2026-02-02 12:24:50 -08:00
|
|
|
#include "core/window.hpp"
|
|
|
|
|
#include "core/logger.hpp"
|
2026-02-21 19:41:21 -08:00
|
|
|
#include "rendering/vk_context.hpp"
|
|
|
|
|
#include <SDL2/SDL_vulkan.h>
|
2026-02-23 16:30:49 +01:00
|
|
|
#ifdef _WIN32
|
|
|
|
|
#include <cstdlib>
|
|
|
|
|
#endif
|
2026-02-02 12:24:50 -08:00
|
|
|
|
|
|
|
|
namespace wowee {
|
|
|
|
|
namespace core {
|
|
|
|
|
|
|
|
|
|
Window::Window(const WindowConfig& config)
|
|
|
|
|
: config(config)
|
|
|
|
|
, width(config.width)
|
2026-02-05 16:11:00 -08:00
|
|
|
, height(config.height)
|
|
|
|
|
, windowedWidth(config.width)
|
|
|
|
|
, windowedHeight(config.height)
|
|
|
|
|
, fullscreen(config.fullscreen)
|
|
|
|
|
, vsync(config.vsync) {
|
2026-02-02 12:24:50 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Window::~Window() {
|
|
|
|
|
shutdown();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Window::initialize() {
|
|
|
|
|
LOG_INFO("Initializing window: ", config.title);
|
|
|
|
|
|
|
|
|
|
// Initialize SDL
|
|
|
|
|
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) {
|
|
|
|
|
LOG_ERROR("Failed to initialize SDL: ", SDL_GetError());
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-23 16:30:49 +01:00
|
|
|
// Explicitly load the Vulkan library before creating the window.
|
|
|
|
|
// SDL_CreateWindow with SDL_WINDOW_VULKAN fails on some platforms/drivers
|
|
|
|
|
// if the Vulkan loader hasn't been located yet; calling this first gives a
|
|
|
|
|
// clear error and avoids the misleading "not configured in SDL" message.
|
|
|
|
|
// SDL 2.28+ uses LoadLibraryExW(LOAD_LIBRARY_SEARCH_DEFAULT_DIRS) which does
|
|
|
|
|
// not search System32, so fall back to the explicit path on Windows if needed.
|
|
|
|
|
bool vulkanLoaded = (SDL_Vulkan_LoadLibrary(nullptr) == 0);
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
if (!vulkanLoaded) {
|
|
|
|
|
const char* sysRoot = std::getenv("SystemRoot");
|
|
|
|
|
if (sysRoot && *sysRoot) {
|
|
|
|
|
std::string fallbackPath = std::string(sysRoot) + "\\System32\\vulkan-1.dll";
|
|
|
|
|
vulkanLoaded = (SDL_Vulkan_LoadLibrary(fallbackPath.c_str()) == 0);
|
|
|
|
|
if (vulkanLoaded) {
|
|
|
|
|
LOG_INFO("Loaded Vulkan library via explicit path: ", fallbackPath);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
if (!vulkanLoaded) {
|
|
|
|
|
LOG_ERROR("Failed to load Vulkan library: ", SDL_GetError());
|
|
|
|
|
LOG_ERROR("Ensure the Vulkan runtime (vulkan-1.dll) is installed. "
|
|
|
|
|
"Install the latest GPU drivers or the Vulkan Runtime from https://vulkan.lunarg.com/");
|
|
|
|
|
SDL_Quit();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-21 19:41:21 -08:00
|
|
|
// Create Vulkan window (no GL attributes needed)
|
|
|
|
|
Uint32 flags = SDL_WINDOW_VULKAN | SDL_WINDOW_SHOWN;
|
2026-02-02 12:24:50 -08:00
|
|
|
if (config.fullscreen) {
|
|
|
|
|
flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
|
|
|
|
|
}
|
|
|
|
|
if (config.resizable) {
|
|
|
|
|
flags |= SDL_WINDOW_RESIZABLE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
window = SDL_CreateWindow(
|
|
|
|
|
config.title.c_str(),
|
|
|
|
|
SDL_WINDOWPOS_CENTERED,
|
|
|
|
|
SDL_WINDOWPOS_CENTERED,
|
|
|
|
|
width,
|
|
|
|
|
height,
|
|
|
|
|
flags
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!window) {
|
|
|
|
|
LOG_ERROR("Failed to create window: ", SDL_GetError());
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-21 19:41:21 -08:00
|
|
|
// Initialize Vulkan context
|
|
|
|
|
vkContext = std::make_unique<rendering::VkContext>();
|
|
|
|
|
if (!vkContext->initialize(window)) {
|
|
|
|
|
LOG_ERROR("Failed to initialize Vulkan context");
|
2026-02-02 12:24:50 -08:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-21 19:41:21 -08:00
|
|
|
LOG_INFO("Window initialized successfully (Vulkan)");
|
2026-02-02 12:24:50 -08:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Window::shutdown() {
|
2026-02-21 19:41:21 -08:00
|
|
|
if (vkContext) {
|
|
|
|
|
vkContext->shutdown();
|
|
|
|
|
vkContext.reset();
|
2026-02-02 12:24:50 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (window) {
|
|
|
|
|
SDL_DestroyWindow(window);
|
|
|
|
|
window = nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-23 16:30:49 +01:00
|
|
|
SDL_Vulkan_UnloadLibrary();
|
2026-02-02 12:24:50 -08:00
|
|
|
SDL_Quit();
|
|
|
|
|
LOG_INFO("Window shutdown complete");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Window::pollEvents() {
|
|
|
|
|
SDL_Event event;
|
|
|
|
|
while (SDL_PollEvent(&event)) {
|
|
|
|
|
if (event.type == SDL_QUIT) {
|
|
|
|
|
shouldCloseFlag = true;
|
|
|
|
|
}
|
|
|
|
|
else if (event.type == SDL_WINDOWEVENT) {
|
|
|
|
|
if (event.window.event == SDL_WINDOWEVENT_RESIZED) {
|
|
|
|
|
width = event.window.data1;
|
|
|
|
|
height = event.window.data2;
|
2026-02-21 19:41:21 -08:00
|
|
|
if (vkContext) {
|
2026-02-22 03:49:44 -08:00
|
|
|
vkContext->markSwapchainDirty();
|
2026-02-21 19:41:21 -08:00
|
|
|
}
|
2026-02-02 12:24:50 -08:00
|
|
|
LOG_DEBUG("Window resized to ", width, "x", height);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-05 16:11:00 -08:00
|
|
|
void Window::setFullscreen(bool enable) {
|
|
|
|
|
if (!window) return;
|
|
|
|
|
if (enable == fullscreen) return;
|
|
|
|
|
if (enable) {
|
|
|
|
|
windowedWidth = width;
|
|
|
|
|
windowedHeight = height;
|
|
|
|
|
if (SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP) != 0) {
|
|
|
|
|
LOG_WARNING("Failed to enter fullscreen: ", SDL_GetError());
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
fullscreen = true;
|
|
|
|
|
SDL_GetWindowSize(window, &width, &height);
|
|
|
|
|
} else {
|
|
|
|
|
if (SDL_SetWindowFullscreen(window, 0) != 0) {
|
|
|
|
|
LOG_WARNING("Failed to exit fullscreen: ", SDL_GetError());
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
fullscreen = false;
|
|
|
|
|
SDL_SetWindowSize(window, windowedWidth, windowedHeight);
|
|
|
|
|
width = windowedWidth;
|
|
|
|
|
height = windowedHeight;
|
|
|
|
|
}
|
2026-02-21 19:41:21 -08:00
|
|
|
if (vkContext) {
|
2026-02-22 03:49:44 -08:00
|
|
|
vkContext->markSwapchainDirty();
|
2026-02-21 19:41:21 -08:00
|
|
|
}
|
2026-02-05 16:11:00 -08:00
|
|
|
}
|
|
|
|
|
|
2026-02-21 19:41:21 -08:00
|
|
|
void Window::setVsync([[maybe_unused]] bool enable) {
|
|
|
|
|
// VSync in Vulkan is controlled by present mode (set at swapchain creation)
|
|
|
|
|
// For now, store the preference — applied on next swapchain recreation
|
2026-02-05 16:11:00 -08:00
|
|
|
vsync = enable;
|
2026-02-21 19:41:21 -08:00
|
|
|
LOG_INFO("VSync preference set to ", enable ? "on" : "off", " (applied on swapchain recreation)");
|
2026-02-05 16:11:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Window::applyResolution(int w, int h) {
|
|
|
|
|
if (!window) return;
|
|
|
|
|
if (w <= 0 || h <= 0) return;
|
|
|
|
|
if (fullscreen) {
|
|
|
|
|
windowedWidth = w;
|
|
|
|
|
windowedHeight = h;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
SDL_SetWindowSize(window, w, h);
|
|
|
|
|
width = w;
|
|
|
|
|
height = h;
|
|
|
|
|
windowedWidth = w;
|
|
|
|
|
windowedHeight = h;
|
2026-02-21 19:41:21 -08:00
|
|
|
if (vkContext) {
|
2026-02-22 03:49:44 -08:00
|
|
|
vkContext->markSwapchainDirty();
|
2026-02-21 19:41:21 -08:00
|
|
|
}
|
2026-02-05 16:11:00 -08:00
|
|
|
}
|
|
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
} // namespace core
|
|
|
|
|
} // namespace wowee
|