9#include "absl/status/status.h"
10#include "absl/strings/str_format.h"
15#include "imgui/backends/imgui_impl_sdl2.h"
16#include "imgui/backends/imgui_impl_sdlrenderer2.h"
17#include "imgui/imgui.h"
31#define g_window_is_resizing yaze::core::g_window_is_resizing
42 event.type = SDL_USEREVENT;
43 event.user.code = kWakeEventCode;
45 return SDL_PushEvent(&event) == 1;
49 return event.type == SDL_USEREVENT &&
event.user.code == kWakeEventCode;
60 LOG_WARN(
"SDL2WindowBackend",
"Already initialized, shutting down first");
67 SDL_SetHint(SDL_HINT_WINDOWS_DPI_SCALING,
"1");
71 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER |
72 SDL_INIT_GAMECONTROLLER) != 0) {
73 return absl::InternalError(
74 absl::StrFormat(
"SDL_Init failed: %s", SDL_GetError()));
78 int screen_width = config.
width;
79 int screen_height = config.
height;
81 if (screen_width == 0 || screen_height == 0) {
84 SDL_Rect display_bounds;
85 if (SDL_GetDisplayBounds(0, &display_bounds) == 0) {
86 screen_width =
static_cast<int>(display_bounds.w * config.
display_scale);
87 screen_height =
static_cast<int>(display_bounds.h * config.
display_scale);
93 "Failed to get display bounds, using defaults: %dx%d",
94 screen_width, screen_height);
101 flags |= SDL_WINDOW_RESIZABLE;
104 flags |= SDL_WINDOW_MAXIMIZED;
107 flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
110 flags |= SDL_WINDOW_ALLOW_HIGHDPI;
113 flags |= SDL_WINDOW_HIDDEN;
117 window_ = std::unique_ptr<SDL_Window, util::SDL_Deleter>(
118 SDL_CreateWindow(config.
title.c_str(), SDL_WINDOWPOS_UNDEFINED,
119 SDL_WINDOWPOS_UNDEFINED, screen_width, screen_height,
125 return absl::InternalError(
126 absl::StrFormat(
"SDL_CreateWindow failed: %s", SDL_GetError()));
130 const int audio_frequency = 48000;
131 const size_t buffer_size = (audio_frequency / 50) * 2;
132 audio_buffer_ = std::shared_ptr<int16_t>(
new int16_t[buffer_size],
133 std::default_delete<int16_t[]>());
135 LOG_INFO(
"SDL2WindowBackend",
"Initialized: %dx%d, audio buffer: %zu samples",
136 screen_width, screen_height, buffer_size);
140 return absl::OkStatus();
145 return absl::OkStatus();
161 LOG_INFO(
"SDL2WindowBackend",
"Shutting down graphics arena...");
166 LOG_INFO(
"SDL2WindowBackend",
"Destroying window...");
171 LOG_INFO(
"SDL2WindowBackend",
"Shutting down SDL...");
175 LOG_INFO(
"SDL2WindowBackend",
"Shutdown complete");
176 return absl::OkStatus();
181 if (SDL_PollEvent(&sdl_event)) {
184 ImGui_ImplSDL2_ProcessEvent(&sdl_event);
198 return SDL_WaitEventTimeout(
nullptr, timeout_ms) == 1;
203 ImGui_ImplSDL2_ProcessEvent(
static_cast<SDL_Event*
>(native_event));
211 switch (sdl_event.type) {
218 event.key_code = sdl_event.key.keysym.sym;
219 event.scan_code = sdl_event.key.keysym.scancode;
229 event.key_code = sdl_event.key.keysym.sym;
230 event.scan_code = sdl_event.key.keysym.scancode;
238 case SDL_MOUSEMOTION:
240 event.mouse_x =
static_cast<float>(sdl_event.motion.x);
241 event.mouse_y =
static_cast<float>(sdl_event.motion.y);
244 case SDL_MOUSEBUTTONDOWN:
246 event.mouse_x =
static_cast<float>(sdl_event.button.x);
247 event.mouse_y =
static_cast<float>(sdl_event.button.y);
248 event.mouse_button = sdl_event.button.button;
251 case SDL_MOUSEBUTTONUP:
253 event.mouse_x =
static_cast<float>(sdl_event.button.x);
254 event.mouse_y =
static_cast<float>(sdl_event.button.y);
255 event.mouse_button = sdl_event.button.button;
260 event.wheel_x =
static_cast<float>(sdl_event.wheel.x);
261 event.wheel_y =
static_cast<float>(sdl_event.wheel.y);
266 if (sdl_event.drop.file) {
267 event.dropped_file = sdl_event.drop.file;
268 SDL_free(sdl_event.drop.file);
272 case SDL_WINDOWEVENT:
273 switch (sdl_event.window.event) {
274 case SDL_WINDOWEVENT_CLOSE:
278 case SDL_WINDOWEVENT_SIZE_CHANGED:
279 case SDL_WINDOWEVENT_RESIZED:
281 event.window_width = sdl_event.window.data1;
282 event.window_height = sdl_event.window.data2;
287 case SDL_WINDOWEVENT_MINIMIZED:
293 case SDL_WINDOWEVENT_MAXIMIZED:
297 case SDL_WINDOWEVENT_RESTORED:
303 case SDL_WINDOWEVENT_SHOWN:
309 case SDL_WINDOWEVENT_HIDDEN:
315 case SDL_WINDOWEVENT_EXPOSED:
321 case SDL_WINDOWEVENT_FOCUS_GAINED:
325 case SDL_WINDOWEVENT_FOCUS_LOST:
336 SDL_Keymod mod = SDL_GetModState();
349 Uint32 flags = SDL_GetWindowFlags(
window_.get());
350 status.
is_minimized = (flags & SDL_WINDOW_MINIMIZED) != 0;
351 status.
is_maximized = (flags & SDL_WINDOW_MAXIMIZED) != 0;
353 (flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_FULLSCREEN_DESKTOP)) != 0;
354 status.
is_focused = (flags & SDL_WINDOW_INPUT_FOCUS) != 0;
364 SDL_GetWindowSize(
window_.get(), width, height);
375 SDL_SetWindowSize(
window_.get(), width, height);
381 const char* title = SDL_GetWindowTitle(
window_.get());
382 return title ? title :
"";
389 SDL_SetWindowTitle(
window_.get(), title.c_str());
420 return absl::OkStatus();
424 return absl::InvalidArgumentError(
"Renderer is null");
427 IMGUI_CHECKVERSION();
428 ImGui::CreateContext();
430 ImGuiIO& io = ImGui::GetIO();
432 io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
433 io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
440 io.ConfigMacOSXBehaviors =
true;
441 LOG_INFO(
"SDL2WindowBackend",
"Enabled ConfigMacOSXBehaviors for macOS");
445 static std::string ini_path_str;
446 if (ini_path_str.empty()) {
447 ini_path_str = ini_path->string();
449 io.IniFilename = ini_path_str.c_str();
451 io.IniFilename =
nullptr;
452 LOG_WARN(
"SDL2WindowBackend",
"Failed to resolve ImGui ini path: %s",
453 ini_path.status().ToString().c_str());
457 SDL_Renderer* sdl_renderer =
461 return absl::InternalError(
"Failed to get SDL renderer from IRenderer");
464 if (!ImGui_ImplSDL2_InitForSDLRenderer(
window_.get(), sdl_renderer)) {
465 return absl::InternalError(
"ImGui_ImplSDL2_InitForSDLRenderer failed");
469 ImGui_ImplSDL2_Shutdown();
470 return absl::InternalError(
"ImGui_ImplSDLRenderer2_Init failed");
480 LOG_INFO(
"SDL2WindowBackend",
"ImGui initialized successfully");
481 return absl::OkStatus();
489 LOG_INFO(
"SDL2WindowBackend",
"Shutting down ImGui implementations...");
491 ImGui_ImplSDL2_Shutdown();
493 LOG_INFO(
"SDL2WindowBackend",
"Destroying ImGui context...");
494 ImGui::DestroyContext();
505 ImGui_ImplSDL2_NewFrame();
520 SDL_Renderer* sdl_renderer =
525 ImDrawData* draw_data = ImGui::GetDrawData();
526 SDL_RenderSetScale(sdl_renderer, draw_data->FramebufferScale.x,
527 draw_data->FramebufferScale.y);
529 SDL_RenderSetScale(sdl_renderer, 1.0f, 1.0f);
534 ImGuiIO& io = ImGui::GetIO();
535 if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
536 ImGui::UpdatePlatformWindows();
537 ImGui::RenderPlatformWindowsDefault();
Defines an abstract interface for all rendering operations.
virtual bool Initialize(SDL_Window *window)=0
Initializes the renderer with a given window.
virtual void * GetBackendRenderer()=0
Provides an escape hatch to get the underlying, concrete renderer object.
void ImGui_ImplSDLRenderer2_NewFrame()
void ImGui_ImplSDLRenderer2_RenderDrawData(ImDrawData *draw_data, SDL_Renderer *renderer)
bool ImGui_ImplSDLRenderer2_Init(SDL_Renderer *renderer)
void ImGui_ImplSDLRenderer2_Shutdown()
#define LOG_WARN(category, format,...)
#define LOG_INFO(category, format,...)
bool g_window_is_resizing
void ConfigureImGuiDiagnostics(ImGuiIO &io, ImGuiDiagnosticsBuild build)
Set Dear ImGui's error-recovery and ID-conflict options.
absl::Status LoadPackageFonts()
#define g_window_is_resizing
SDL2/SDL3 compatibility layer.
#define RETURN_IF_ERROR(expr)
Deleter for SDL_Window and SDL_Renderer.