10#include "absl/status/status.h"
11#include "absl/strings/str_format.h"
15#include "imgui/backends/imgui_impl_sdl3.h"
16#include "imgui/backends/imgui_impl_sdlrenderer3.h"
17#include "imgui/imgui.h"
31#define g_window_is_resizing yaze::core::g_window_is_resizing
33SDL3WindowBackend::~SDL3WindowBackend() {
39absl::Status SDL3WindowBackend::Initialize(
const WindowConfig& config) {
41 LOG_WARN(
"SDL3WindowBackend",
"Already initialized, shutting down first");
47 if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_EVENTS)) {
48 return absl::InternalError(
49 absl::StrFormat(
"SDL_Init failed: %s", SDL_GetError()));
53 int screen_width = config.width;
54 int screen_height = config.height;
56 if (screen_width == 0 || screen_height == 0) {
59 SDL_DisplayID display_id = SDL_GetPrimaryDisplay();
60 const SDL_DisplayMode* mode = SDL_GetCurrentDisplayMode(display_id);
63 screen_width =
static_cast<int>(mode->w * config.display_scale);
64 screen_height =
static_cast<int>(mode->h * config.display_scale);
70 "Failed to get display mode, using defaults: %dx%d",
71 screen_width, screen_height);
77 SDL_WindowFlags flags = 0;
78 if (config.resizable) {
79 flags |= SDL_WINDOW_RESIZABLE;
81 if (config.maximized) {
82 flags |= SDL_WINDOW_MAXIMIZED;
84 if (config.fullscreen) {
85 flags |= SDL_WINDOW_FULLSCREEN;
87 if (config.high_dpi) {
88 flags |= SDL_WINDOW_HIGH_PIXEL_DENSITY;
91 flags |= SDL_WINDOW_HIDDEN;
96 SDL_Window* raw_window = SDL_CreateWindow(config.title.c_str(), screen_width,
97 screen_height, flags);
101 return absl::InternalError(
102 absl::StrFormat(
"SDL_CreateWindow failed: %s", SDL_GetError()));
105 window_ = std::unique_ptr<SDL_Window, SDL3WindowDeleter>(raw_window);
108 const int audio_frequency = 48000;
109 const size_t buffer_size = (audio_frequency / 50) * 2;
110 audio_buffer_ = std::shared_ptr<int16_t>(
new int16_t[buffer_size],
111 std::default_delete<int16_t[]>());
113 LOG_INFO(
"SDL3WindowBackend",
"Initialized: %dx%d, audio buffer: %zu samples",
114 screen_width, screen_height, buffer_size);
118 return absl::OkStatus();
121absl::Status SDL3WindowBackend::Shutdown() {
123 return absl::OkStatus();
127 if (imgui_initialized_) {
132 LOG_INFO(
"SDL3WindowBackend",
"Shutting down graphics arena...");
137 LOG_INFO(
"SDL3WindowBackend",
"Destroying window...");
142 LOG_INFO(
"SDL3WindowBackend",
"Shutting down SDL...");
145 initialized_ =
false;
146 LOG_INFO(
"SDL3WindowBackend",
"Shutdown complete");
147 return absl::OkStatus();
150bool SDL3WindowBackend::PollEvent(WindowEvent& out_event) {
152 if (SDL_PollEvent(&sdl_event)) {
154 if (imgui_initialized_) {
155 ImGui_ImplSDL3_ProcessEvent(&sdl_event);
159 out_event = ConvertSDL3Event(sdl_event);
160 out_event.has_native_event =
true;
161 out_event.native_event = sdl_event;
167bool SDL3WindowBackend::WaitForEvent(
int timeout_ms) {
169 return SDL_WaitEventTimeout(
nullptr, timeout_ms);
172void SDL3WindowBackend::ProcessNativeEvent(
void* native_event) {
173 if (native_event && imgui_initialized_) {
174 ImGui_ImplSDL3_ProcessEvent(
static_cast<SDL_Event*
>(native_event));
178WindowEvent SDL3WindowBackend::ConvertSDL3Event(
const SDL_Event& sdl_event) {
182 switch (sdl_event.type) {
194 case SDL_EVENT_KEY_DOWN:
196 event.key_code = sdl_event.key.key;
197 event.scan_code = sdl_event.key.scancode;
198 UpdateModifierState();
199 event.key_shift = key_shift_;
200 event.key_ctrl = key_ctrl_;
201 event.key_alt = key_alt_;
202 event.key_super = key_super_;
205 case SDL_EVENT_KEY_UP:
207 event.key_code = sdl_event.key.key;
208 event.scan_code = sdl_event.key.scancode;
209 UpdateModifierState();
210 event.key_shift = key_shift_;
211 event.key_ctrl = key_ctrl_;
212 event.key_alt = key_alt_;
213 event.key_super = key_super_;
220 case SDL_EVENT_MOUSE_MOTION:
222 event.mouse_x = sdl_event.motion.x;
223 event.mouse_y = sdl_event.motion.y;
226 case SDL_EVENT_MOUSE_BUTTON_DOWN:
228 event.mouse_x = sdl_event.button.x;
229 event.mouse_y = sdl_event.button.y;
230 event.mouse_button = sdl_event.button.button;
233 case SDL_EVENT_MOUSE_BUTTON_UP:
235 event.mouse_x = sdl_event.button.x;
236 event.mouse_y = sdl_event.button.y;
237 event.mouse_button = sdl_event.button.button;
240 case SDL_EVENT_MOUSE_WHEEL:
242 event.wheel_x = sdl_event.wheel.x;
243 event.wheel_y = sdl_event.wheel.y;
249 case SDL_EVENT_DROP_FILE:
251 if (sdl_event.drop.data) {
252 event.dropped_file = sdl_event.drop.data;
262 case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
266 case SDL_EVENT_WINDOW_RESIZED:
268 event.window_width = sdl_event.window.data1;
269 event.window_height = sdl_event.window.data2;
274 case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
277 event.window_width = sdl_event.window.data1;
278 event.window_height = sdl_event.window.data2;
283 case SDL_EVENT_WINDOW_MINIMIZED:
285 is_resizing_ =
false;
289 case SDL_EVENT_WINDOW_MAXIMIZED:
293 case SDL_EVENT_WINDOW_RESTORED:
295 is_resizing_ =
false;
299 case SDL_EVENT_WINDOW_SHOWN:
301 is_resizing_ =
false;
305 case SDL_EVENT_WINDOW_HIDDEN:
307 is_resizing_ =
false;
311 case SDL_EVENT_WINDOW_EXPOSED:
313 is_resizing_ =
false;
317 case SDL_EVENT_WINDOW_FOCUS_GAINED:
321 case SDL_EVENT_WINDOW_FOCUS_LOST:
329void SDL3WindowBackend::UpdateModifierState() {
331 SDL_Keymod mod = SDL_GetModState();
332 key_shift_ = (mod & SDL_KMOD_SHIFT) != 0;
333 key_ctrl_ = (mod & SDL_KMOD_CTRL) != 0;
334 key_alt_ = (mod & SDL_KMOD_ALT) != 0;
335 key_super_ = (mod & SDL_KMOD_GUI) != 0;
338WindowStatus SDL3WindowBackend::GetStatus()
const {
340 status.is_active = active_;
341 status.is_resizing = is_resizing_;
344 SDL_WindowFlags flags = SDL_GetWindowFlags(window_.get());
345 status.is_minimized = (flags & SDL_WINDOW_MINIMIZED) != 0;
346 status.is_maximized = (flags & SDL_WINDOW_MAXIMIZED) != 0;
347 status.is_fullscreen = (flags & SDL_WINDOW_FULLSCREEN) != 0;
348 status.is_focused = (flags & SDL_WINDOW_INPUT_FOCUS) != 0;
350 SDL_GetWindowSize(window_.get(), &status.width, &status.height);
356void SDL3WindowBackend::GetSize(
int* width,
int* height)
const {
358 SDL_GetWindowSize(window_.get(), width, height);
367void SDL3WindowBackend::SetSize(
int width,
int height) {
369 SDL_SetWindowSize(window_.get(), width, height);
373std::string SDL3WindowBackend::GetTitle()
const {
375 const char* title = SDL_GetWindowTitle(window_.get());
376 return title ? title :
"";
381void SDL3WindowBackend::SetTitle(
const std::string& title) {
383 SDL_SetWindowTitle(window_.get(), title.c_str());
387void SDL3WindowBackend::ShowWindow() {
389 SDL_ShowWindow(window_.get());
393void SDL3WindowBackend::HideWindow() {
395 SDL_HideWindow(window_.get());
399bool SDL3WindowBackend::InitializeRenderer(gfx::IRenderer* renderer) {
400 if (!window_ || !renderer) {
404 if (renderer->GetBackendRenderer()) {
409 return renderer->Initialize(window_.get());
412absl::Status SDL3WindowBackend::InitializeImGui(gfx::IRenderer* renderer) {
413 if (imgui_initialized_) {
414 return absl::OkStatus();
418 return absl::InvalidArgumentError(
"Renderer is null");
421 IMGUI_CHECKVERSION();
422 ImGui::CreateContext();
424 ImGuiIO& io = ImGui::GetIO();
425 io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
426 io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
427 io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;
431 io.ConfigMacOSXBehaviors =
true;
435 static std::string ini_path_str;
436 if (ini_path_str.empty()) {
437 ini_path_str = ini_path->string();
439 io.IniFilename = ini_path_str.c_str();
441 io.IniFilename =
nullptr;
442 LOG_WARN(
"SDL3WindowBackend",
"Failed to resolve ImGui ini path: %s",
443 ini_path.status().ToString().c_str());
447 SDL_Renderer* sdl_renderer =
448 static_cast<SDL_Renderer*
>(renderer->GetBackendRenderer());
451 return absl::InternalError(
"Failed to get SDL renderer from IRenderer");
455 if (!ImGui_ImplSDL3_InitForSDLRenderer(window_.get(), sdl_renderer)) {
456 return absl::InternalError(
"ImGui_ImplSDL3_InitForSDLRenderer failed");
459 if (!ImGui_ImplSDLRenderer3_Init(sdl_renderer)) {
460 ImGui_ImplSDL3_Shutdown();
461 return absl::InternalError(
"ImGui_ImplSDLRenderer3_Init failed");
470 imgui_initialized_ =
true;
471 LOG_INFO(
"SDL3WindowBackend",
"ImGui initialized successfully");
472 return absl::OkStatus();
475void SDL3WindowBackend::ShutdownImGui() {
476 if (!imgui_initialized_) {
480 LOG_INFO(
"SDL3WindowBackend",
"Shutting down ImGui implementations...");
481 ImGui_ImplSDLRenderer3_Shutdown();
482 ImGui_ImplSDL3_Shutdown();
484 LOG_INFO(
"SDL3WindowBackend",
"Destroying ImGui context...");
485 ImGui::DestroyContext();
487 imgui_initialized_ =
false;
490void SDL3WindowBackend::NewImGuiFrame() {
491 if (!imgui_initialized_) {
495 ImGui_ImplSDLRenderer3_NewFrame();
496 ImGui_ImplSDL3_NewFrame();
499void SDL3WindowBackend::RenderImGui(gfx::IRenderer* renderer) {
500 if (!imgui_initialized_) {
508 SDL_Renderer* sdl_renderer =
509 static_cast<SDL_Renderer*
>(renderer->GetBackendRenderer());
511 ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), sdl_renderer);
516 ImGuiIO& io = ImGui::GetIO();
517 if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
518 ImGui::UpdatePlatformWindows();
519 ImGui::RenderPlatformWindowsDefault();
#define LOG_WARN(category, format,...)
#define LOG_INFO(category, format,...)
bool g_window_is_resizing
absl::Status LoadPackageFonts()
#define g_window_is_resizing
#define RETURN_IF_ERROR(expr)