10#include "absl/status/status.h"
11#include "absl/strings/str_format.h"
16#include "imgui/backends/imgui_impl_sdl3.h"
17#include "imgui/backends/imgui_impl_sdlrenderer3.h"
18#include "imgui/imgui.h"
32#define g_window_is_resizing yaze::core::g_window_is_resizing
43 event.type = SDL_EVENT_USER;
46 return SDL_PushEvent(&event);
50 return event.type == SDL_EVENT_USER &&
event.user.code ==
kWakeEventCode;
53SDL3WindowBackend::~SDL3WindowBackend() {
59absl::Status SDL3WindowBackend::Initialize(
const WindowConfig& config) {
61 LOG_WARN(
"SDL3WindowBackend",
"Already initialized, shutting down first");
67 if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_EVENTS)) {
68 return absl::InternalError(
69 absl::StrFormat(
"SDL_Init failed: %s", SDL_GetError()));
73 int screen_width = config.width;
74 int screen_height = config.height;
76 if (screen_width == 0 || screen_height == 0) {
79 SDL_DisplayID display_id = SDL_GetPrimaryDisplay();
80 const SDL_DisplayMode* mode = SDL_GetCurrentDisplayMode(display_id);
83 screen_width =
static_cast<int>(mode->w * config.display_scale);
84 screen_height =
static_cast<int>(mode->h * config.display_scale);
90 "Failed to get display mode, using defaults: %dx%d",
91 screen_width, screen_height);
97 SDL_WindowFlags flags = 0;
98 if (config.resizable) {
99 flags |= SDL_WINDOW_RESIZABLE;
101 if (config.maximized) {
102 flags |= SDL_WINDOW_MAXIMIZED;
104 if (config.fullscreen) {
105 flags |= SDL_WINDOW_FULLSCREEN;
107 if (config.high_dpi) {
108 flags |= SDL_WINDOW_HIGH_PIXEL_DENSITY;
111 flags |= SDL_WINDOW_HIDDEN;
116 SDL_Window* raw_window = SDL_CreateWindow(config.title.c_str(), screen_width,
117 screen_height, flags);
121 return absl::InternalError(
122 absl::StrFormat(
"SDL_CreateWindow failed: %s", SDL_GetError()));
125 window_ = std::unique_ptr<SDL_Window, SDL3WindowDeleter>(raw_window);
128 const int audio_frequency = 48000;
129 const size_t buffer_size = (audio_frequency / 50) * 2;
130 audio_buffer_ = std::shared_ptr<int16_t>(
new int16_t[buffer_size],
131 std::default_delete<int16_t[]>());
133 LOG_INFO(
"SDL3WindowBackend",
"Initialized: %dx%d, audio buffer: %zu samples",
134 screen_width, screen_height, buffer_size);
138 return absl::OkStatus();
141absl::Status SDL3WindowBackend::Shutdown() {
143 return absl::OkStatus();
147 if (imgui_initialized_) {
152 LOG_INFO(
"SDL3WindowBackend",
"Shutting down graphics arena...");
157 LOG_INFO(
"SDL3WindowBackend",
"Destroying window...");
162 LOG_INFO(
"SDL3WindowBackend",
"Shutting down SDL...");
165 initialized_ =
false;
166 LOG_INFO(
"SDL3WindowBackend",
"Shutdown complete");
167 return absl::OkStatus();
170bool SDL3WindowBackend::PollEvent(WindowEvent& out_event) {
172 if (SDL_PollEvent(&sdl_event)) {
174 if (imgui_initialized_) {
175 ImGui_ImplSDL3_ProcessEvent(&sdl_event);
179 out_event = ConvertSDL3Event(sdl_event);
180 out_event.has_native_event =
true;
181 out_event.native_event = sdl_event;
187bool SDL3WindowBackend::WaitForEvent(
int timeout_ms) {
189 return SDL_WaitEventTimeout(
nullptr, timeout_ms);
192void SDL3WindowBackend::ProcessNativeEvent(
void* native_event) {
193 if (native_event && imgui_initialized_) {
194 ImGui_ImplSDL3_ProcessEvent(
static_cast<SDL_Event*
>(native_event));
198WindowEvent SDL3WindowBackend::ConvertSDL3Event(
const SDL_Event& sdl_event) {
202 switch (sdl_event.type) {
214 case SDL_EVENT_KEY_DOWN:
216 event.key_code = sdl_event.key.key;
217 event.scan_code = sdl_event.key.scancode;
218 UpdateModifierState();
219 event.key_shift = key_shift_;
220 event.key_ctrl = key_ctrl_;
221 event.key_alt = key_alt_;
222 event.key_super = key_super_;
225 case SDL_EVENT_KEY_UP:
227 event.key_code = sdl_event.key.key;
228 event.scan_code = sdl_event.key.scancode;
229 UpdateModifierState();
230 event.key_shift = key_shift_;
231 event.key_ctrl = key_ctrl_;
232 event.key_alt = key_alt_;
233 event.key_super = key_super_;
240 case SDL_EVENT_MOUSE_MOTION:
242 event.mouse_x = sdl_event.motion.x;
243 event.mouse_y = sdl_event.motion.y;
246 case SDL_EVENT_MOUSE_BUTTON_DOWN:
248 event.mouse_x = sdl_event.button.x;
249 event.mouse_y = sdl_event.button.y;
250 event.mouse_button = sdl_event.button.button;
253 case SDL_EVENT_MOUSE_BUTTON_UP:
255 event.mouse_x = sdl_event.button.x;
256 event.mouse_y = sdl_event.button.y;
257 event.mouse_button = sdl_event.button.button;
260 case SDL_EVENT_MOUSE_WHEEL:
262 event.wheel_x = sdl_event.wheel.x;
263 event.wheel_y = sdl_event.wheel.y;
269 case SDL_EVENT_DROP_FILE:
271 if (sdl_event.drop.data) {
272 event.dropped_file = sdl_event.drop.data;
282 case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
286 case SDL_EVENT_WINDOW_RESIZED:
288 event.window_width = sdl_event.window.data1;
289 event.window_height = sdl_event.window.data2;
294 case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
297 event.window_width = sdl_event.window.data1;
298 event.window_height = sdl_event.window.data2;
303 case SDL_EVENT_WINDOW_MINIMIZED:
305 is_resizing_ =
false;
309 case SDL_EVENT_WINDOW_MAXIMIZED:
313 case SDL_EVENT_WINDOW_RESTORED:
315 is_resizing_ =
false;
319 case SDL_EVENT_WINDOW_SHOWN:
321 is_resizing_ =
false;
325 case SDL_EVENT_WINDOW_HIDDEN:
327 is_resizing_ =
false;
331 case SDL_EVENT_WINDOW_EXPOSED:
333 is_resizing_ =
false;
337 case SDL_EVENT_WINDOW_FOCUS_GAINED:
341 case SDL_EVENT_WINDOW_FOCUS_LOST:
349void SDL3WindowBackend::UpdateModifierState() {
351 SDL_Keymod mod = SDL_GetModState();
352 key_shift_ = (mod & SDL_KMOD_SHIFT) != 0;
353 key_ctrl_ = (mod & SDL_KMOD_CTRL) != 0;
354 key_alt_ = (mod & SDL_KMOD_ALT) != 0;
355 key_super_ = (mod & SDL_KMOD_GUI) != 0;
358WindowStatus SDL3WindowBackend::GetStatus()
const {
360 status.is_active = active_;
361 status.is_resizing = is_resizing_;
364 SDL_WindowFlags flags = SDL_GetWindowFlags(window_.get());
365 status.is_minimized = (flags & SDL_WINDOW_MINIMIZED) != 0;
366 status.is_maximized = (flags & SDL_WINDOW_MAXIMIZED) != 0;
367 status.is_fullscreen = (flags & SDL_WINDOW_FULLSCREEN) != 0;
368 status.is_focused = (flags & SDL_WINDOW_INPUT_FOCUS) != 0;
370 SDL_GetWindowSize(window_.get(), &status.width, &status.height);
376void SDL3WindowBackend::GetSize(
int* width,
int* height)
const {
378 SDL_GetWindowSize(window_.get(), width, height);
387void SDL3WindowBackend::SetSize(
int width,
int height) {
389 SDL_SetWindowSize(window_.get(), width, height);
393std::string SDL3WindowBackend::GetTitle()
const {
395 const char* title = SDL_GetWindowTitle(window_.get());
396 return title ? title :
"";
401void SDL3WindowBackend::SetTitle(
const std::string& title) {
403 SDL_SetWindowTitle(window_.get(), title.c_str());
407void SDL3WindowBackend::ShowWindow() {
409 SDL_ShowWindow(window_.get());
413void SDL3WindowBackend::HideWindow() {
415 SDL_HideWindow(window_.get());
419bool SDL3WindowBackend::InitializeRenderer(gfx::IRenderer* renderer) {
420 if (!window_ || !renderer) {
424 if (renderer->GetBackendRenderer()) {
429 return renderer->Initialize(window_.get());
432absl::Status SDL3WindowBackend::InitializeImGui(gfx::IRenderer* renderer) {
433 if (imgui_initialized_) {
434 return absl::OkStatus();
438 return absl::InvalidArgumentError(
"Renderer is null");
441 IMGUI_CHECKVERSION();
442 ImGui::CreateContext();
444 ImGuiIO& io = ImGui::GetIO();
446 io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
447 io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
454 io.ConfigMacOSXBehaviors =
true;
458 static std::string ini_path_str;
459 if (ini_path_str.empty()) {
460 ini_path_str = ini_path->string();
462 io.IniFilename = ini_path_str.c_str();
464 io.IniFilename =
nullptr;
465 LOG_WARN(
"SDL3WindowBackend",
"Failed to resolve ImGui ini path: %s",
466 ini_path.status().ToString().c_str());
470 SDL_Renderer* sdl_renderer =
471 static_cast<SDL_Renderer*
>(renderer->GetBackendRenderer());
474 return absl::InternalError(
"Failed to get SDL renderer from IRenderer");
478 if (!ImGui_ImplSDL3_InitForSDLRenderer(window_.get(), sdl_renderer)) {
479 return absl::InternalError(
"ImGui_ImplSDL3_InitForSDLRenderer failed");
482 if (!ImGui_ImplSDLRenderer3_Init(sdl_renderer)) {
483 ImGui_ImplSDL3_Shutdown();
484 return absl::InternalError(
"ImGui_ImplSDLRenderer3_Init failed");
493 imgui_initialized_ =
true;
494 LOG_INFO(
"SDL3WindowBackend",
"ImGui initialized successfully");
495 return absl::OkStatus();
498void SDL3WindowBackend::ShutdownImGui() {
499 if (!imgui_initialized_) {
503 LOG_INFO(
"SDL3WindowBackend",
"Shutting down ImGui implementations...");
504 ImGui_ImplSDLRenderer3_Shutdown();
505 ImGui_ImplSDL3_Shutdown();
507 LOG_INFO(
"SDL3WindowBackend",
"Destroying ImGui context...");
508 ImGui::DestroyContext();
510 imgui_initialized_ =
false;
513void SDL3WindowBackend::NewImGuiFrame() {
514 if (!imgui_initialized_) {
518 ImGui_ImplSDLRenderer3_NewFrame();
519 ImGui_ImplSDL3_NewFrame();
522void SDL3WindowBackend::RenderImGui(gfx::IRenderer* renderer) {
523 if (!imgui_initialized_) {
531 SDL_Renderer* sdl_renderer =
532 static_cast<SDL_Renderer*
>(renderer->GetBackendRenderer());
534 ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), sdl_renderer);
539 ImGuiIO& io = ImGui::GetIO();
540 if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
541 ImGui::UpdatePlatformWindows();
542 ImGui::RenderPlatformWindowsDefault();
#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
#define RETURN_IF_ERROR(expr)