#include "window.hpp" #include "imgui.hpp" #include "webgpu/gpu.hpp" #include "input.hpp" #include "internal.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace aurora::window { namespace { Module Log("aurora::window"); SDL_Window* g_window; SDL_Renderer* g_renderer; AuroraWindowSize g_windowSize; std::vector g_events; inline bool operator==(const AuroraWindowSize& lhs, const AuroraWindowSize& rhs) { return lhs.width == rhs.width && lhs.height == rhs.height && lhs.fb_width == rhs.fb_width && lhs.fb_height == rhs.fb_height && lhs.scale == rhs.scale; } void resize_swapchain() noexcept { const auto size = get_window_size(); if (size == g_windowSize) { return; } if (size.scale != g_windowSize.scale) { if (g_windowSize.scale > 0.f) { Log.info("Display scale changed to {}", size.scale); } } g_windowSize = size; webgpu::resize_swapchain(size.fb_width, size.fb_height); } void set_window_icon() noexcept { if (g_config.iconRGBA8 == nullptr) { return; } auto* iconSurface = SDL_CreateSurfaceFrom(static_cast(g_config.iconWidth), static_cast(g_config.iconHeight), SDL_GetPixelFormatForMasks(32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000), g_config.iconRGBA8, static_cast(4 * g_config.iconWidth)); ASSERT(iconSurface != nullptr, "Failed to create icon surface: {}", SDL_GetError()); TRY_WARN(SDL_SetWindowIcon(g_window, iconSurface), "Failed to set window icon: {}", SDL_GetError()); SDL_DestroySurface(iconSurface); } } // namespace const AuroraEvent* poll_events() { g_events.clear(); SDL_Event event; while (SDL_PollEvent(&event)) { imgui::process_event(event); switch (event.type) { case SDL_EVENT_WINDOW_MINIMIZED: { // Android/iOS: Application backgrounded g_events.push_back(AuroraEvent{ .type = AURORA_PAUSED, }); break; } case SDL_EVENT_WINDOW_RESTORED: { // Android/iOS: Application focused g_events.push_back(AuroraEvent{ .type = AURORA_UNPAUSED, }); break; } case SDL_EVENT_WINDOW_MOVED: { g_events.push_back(AuroraEvent{ .type = AURORA_WINDOW_MOVED, .windowPos = {.x = event.window.data1, .y = event.window.data2}, }); break; } case SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED: { resize_swapchain(); g_events.push_back(AuroraEvent{ .type = AURORA_DISPLAY_SCALE_CHANGED, .windowSize = get_window_size(), }); break; } case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED: { resize_swapchain(); g_events.push_back(AuroraEvent{ .type = AURORA_WINDOW_RESIZED, .windowSize = get_window_size(), }); break; } case SDL_EVENT_GAMEPAD_ADDED: { auto instance = input::add_controller(event.gdevice.which); g_events.push_back(AuroraEvent{ .type = AURORA_CONTROLLER_ADDED, .controller = instance, }); break; } case SDL_EVENT_GAMEPAD_REMOVED: { input::remove_controller(event.gdevice.which); g_events.push_back(AuroraEvent{ .type = AURORA_CONTROLLER_REMOVED, .controller = event.gdevice.which, }); break; } case SDL_EVENT_QUIT: g_events.push_back(AuroraEvent{ .type = AURORA_EXIT, }); default: break; } g_events.push_back(AuroraEvent{ .type = AURORA_SDL_EVENT, .sdl = event, }); } g_events.push_back(AuroraEvent{ .type = AURORA_NONE, }); return g_events.data(); } bool create_window(AuroraBackend backend) { SDL_WindowFlags flags = SDL_WINDOW_HIGH_PIXEL_DENSITY; #if TARGET_OS_IOS || TARGET_OS_TV flags |= SDL_WINDOW_FULLSCREEN; #else flags |= SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE; if (g_config.startFullscreen) { flags |= SDL_WINDOW_FULLSCREEN; } #endif switch (backend) { #ifdef DAWN_ENABLE_BACKEND_VULKAN case BACKEND_VULKAN: flags |= SDL_WINDOW_VULKAN; break; #endif #ifdef DAWN_ENABLE_BACKEND_METAL case BACKEND_METAL: flags |= SDL_WINDOW_METAL; break; #endif #ifdef DAWN_ENABLE_BACKEND_OPENGL case BACKEND_OPENGL: case BACKEND_OPENGLES: flags |= SDL_WINDOW_OPENGL; break; #endif default: break; } #ifdef __SWITCH__ Sint32 width = 1280; Sint32 height = 720; #else auto width = static_cast(g_config.windowWidth); auto height = static_cast(g_config.windowHeight); if (width == 0 || height == 0) { width = 1280; height = 960; } Sint32 posX = g_config.windowPosX; Sint32 posY = g_config.windowPosY; if (posX < 0 || posY < 0) { posX = SDL_WINDOWPOS_UNDEFINED; posY = SDL_WINDOWPOS_UNDEFINED; } #endif const auto props = SDL_CreateProperties(); TRY(SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, g_config.appName), "Failed to set {}: {}", SDL_PROP_WINDOW_CREATE_TITLE_STRING, SDL_GetError()); TRY(SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, posX), "Failed to set {}: {}", SDL_PROP_WINDOW_CREATE_X_NUMBER, SDL_GetError()); TRY(SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, posY), "Failed to set {}: {}", SDL_PROP_WINDOW_CREATE_Y_NUMBER, SDL_GetError()); TRY(SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, width), "Failed to set {}: {}", SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, SDL_GetError()); TRY(SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, height), "Failed to set {}: {}", SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, SDL_GetError()); TRY(SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_FLAGS_NUMBER, flags), "Failed to set {}: {}", SDL_PROP_WINDOW_CREATE_FLAGS_NUMBER, SDL_GetError()); g_window = SDL_CreateWindowWithProperties(props); if (g_window == nullptr) { Log.error("Failed to create window: {}", SDL_GetError()); return false; } set_window_icon(); return true; } bool create_renderer() { if (g_window == nullptr) { return false; } const auto props = SDL_CreateProperties(); TRY(SDL_SetPointerProperty(props, SDL_PROP_RENDERER_CREATE_WINDOW_POINTER, g_window), "Failed to set {}: {}", SDL_PROP_RENDERER_CREATE_WINDOW_POINTER, SDL_GetError()); TRY(SDL_SetNumberProperty(props, SDL_PROP_RENDERER_CREATE_PRESENT_VSYNC_NUMBER, SDL_RENDERER_VSYNC_ADAPTIVE), "Failed to set {}: {}", SDL_PROP_RENDERER_CREATE_PRESENT_VSYNC_NUMBER, SDL_GetError()); g_renderer = SDL_CreateRendererWithProperties(props); if (g_renderer == nullptr) { Log.error("Failed to create renderer: {}", SDL_GetError()); return false; } return true; } void destroy_window() { if (g_renderer != nullptr) { SDL_DestroyRenderer(g_renderer); g_renderer = nullptr; } if (g_window != nullptr) { SDL_DestroyWindow(g_window); g_window = nullptr; } } void show_window() { if (g_window != nullptr) { TRY_WARN(SDL_ShowWindow(g_window), "Failed to show window: {}", SDL_GetError()); } } bool initialize() { /* We don't want to initialize anything input related here, otherwise the add events will get lost to the void */ TRY(SDL_InitSubSystem(SDL_INIT_EVENTS | SDL_INIT_VIDEO), "Error initializing SDL: {}", SDL_GetError()); #if !defined(_WIN32) && !defined(__APPLE__) TRY(SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0"), "Error setting {}: {}", SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, SDL_GetError()); #endif TRY(SDL_SetHint(SDL_HINT_SCREENSAVER_INHIBIT_ACTIVITY_NAME, g_config.appName), "Error setting {}: {}", SDL_HINT_SCREENSAVER_INHIBIT_ACTIVITY_NAME, SDL_GetError()); TRY(SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE_RUMBLE_BRAKE, "1"), "Error setting {}: {}", SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE_RUMBLE_BRAKE, SDL_GetError()); TRY(SDL_DisableScreenSaver(), "Error disabling screensaver: {}", SDL_GetError()); if (g_config.allowJoystickBackgroundEvents) { TRY(SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"), "Error setting {}: {}", SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, SDL_GetError()); } return true; } void shutdown() { destroy_window(); TRY_WARN(SDL_EnableScreenSaver(), "Error enabling screensaver: {}", SDL_GetError()); SDL_Quit(); } AuroraWindowSize get_window_size() { int width = 0; int height = 0; int fb_w = 0; int fb_h = 0; ASSERT(SDL_GetWindowSize(g_window, &width, &height), "Failed to get window size: {}", SDL_GetError()); ASSERT(SDL_GetWindowSizeInPixels(g_window, &fb_w, &fb_h), "Failed to get window size in pixels: {}", SDL_GetError()); const float scale = SDL_GetWindowDisplayScale(g_window); return { .width = static_cast(width), .height = static_cast(height), .fb_width = static_cast(fb_w), .fb_height = static_cast(fb_h), .scale = scale, }; } SDL_Window* get_sdl_window() { return g_window; } SDL_Renderer* get_sdl_renderer() { return g_renderer; } void set_title(const char* title) { TRY_WARN(SDL_SetWindowTitle(g_window, title), "Failed to set window title: {}", SDL_GetError()); } void set_fullscreen(bool fullscreen) { TRY_WARN(SDL_SetWindowFullscreen(g_window, fullscreen), "Failed to set window fullscreen: {}", SDL_GetError()); } bool get_fullscreen() { return (SDL_GetWindowFlags(g_window) & SDL_WINDOW_FULLSCREEN) != 0u; } } // namespace aurora::window