yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
window.cc
Go to the documentation of this file.
1#include "app/core/window.h"
2
3#include <filesystem>
4
5#include "absl/status/status.h"
6#include "absl/strings/str_format.h"
8#include "util/sdl_deleter.h"
9#include "util/log.h"
10#include "app/gfx/arena.h"
11#include "app/gui/style.h"
12#include "imgui/backends/imgui_impl_sdl2.h"
13#include "imgui/backends/imgui_impl_sdlrenderer2.h"
14#include "imgui/imgui.h"
15
16namespace {
17// Custom ImGui assertion handler to prevent crashes
18void ImGuiAssertionHandler(const char* expr, const char* file, int line,
19 const char* msg) {
20 // Log the assertion instead of crashing
21 LOG_ERROR("ImGui", "Assertion failed: %s\nFile: %s:%d\nMessage: %s",
22 expr, file, line, msg ? msg : "");
23
24 // Try to recover by resetting ImGui state
25 static int error_count = 0;
26 error_count++;
27
28 if (error_count > 5) {
29 LOG_ERROR("ImGui", "Too many assertions, resetting workspace settings...");
30
31 // Backup and reset imgui.ini
32 try {
33 if (std::filesystem::exists("imgui.ini")) {
34 std::filesystem::copy("imgui.ini", "imgui.ini.backup",
35 std::filesystem::copy_options::overwrite_existing);
36 std::filesystem::remove("imgui.ini");
37 LOG_INFO("ImGui", "Workspace settings reset. Backup saved to imgui.ini.backup");
38 }
39 } catch (const std::exception& e) {
40 LOG_ERROR("ImGui", "Failed to reset workspace: %s", e.what());
41 }
42
43 error_count = 0; // Reset counter
44 }
45
46 // Don't abort - let the program continue
47 // The assertion is logged and workspace can be reset if needed
48}
49} // namespace
50
51namespace yaze {
52namespace core {
53
54// Global flag for window resize state (used by emulator to pause)
56
57absl::Status CreateWindow(Window& window, gfx::IRenderer* renderer, int flags) {
58 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER) != 0) {
59 return absl::InternalError(
60 absl::StrFormat("SDL_Init: %s\n", SDL_GetError()));
61 }
62
63 SDL_DisplayMode display_mode;
64 SDL_GetCurrentDisplayMode(0, &display_mode);
65 int screen_width = display_mode.w * 0.8;
66 int screen_height = display_mode.h * 0.8;
67
68 window.window_ = std::unique_ptr<SDL_Window, util::SDL_Deleter>(
69 SDL_CreateWindow("Yet Another Zelda3 Editor", SDL_WINDOWPOS_UNDEFINED,
70 SDL_WINDOWPOS_UNDEFINED, screen_width, screen_height,
71 flags),
73 if (window.window_ == nullptr) {
74 return absl::InternalError(
75 absl::StrFormat("SDL_CreateWindow: %s\n", SDL_GetError()));
76 }
77
78 // Only initialize renderer if one is provided and not already initialized
79 if (renderer && !renderer->GetBackendRenderer()) {
80 if (!renderer->Initialize(window.window_.get())) {
81 return absl::InternalError("Failed to initialize renderer");
82 }
83 }
84
85 IMGUI_CHECKVERSION();
86 ImGui::CreateContext();
87 ImGuiIO& io = ImGui::GetIO();
88 io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
89 io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
90
91 // Set custom assertion handler to prevent crashes
92#ifdef IMGUI_DISABLE_DEFAULT_ASSERT_HANDLER
93 ImGui::SetAssertHandler(ImGuiAssertionHandler);
94#else
95 // For release builds, assertions are already disabled
96 LOG_INFO("Window", "ImGui assertions are disabled in this build");
97#endif
98
99 // Initialize ImGuiTestEngine after ImGui context is created
100#if defined(YAZE_ENABLE_IMGUI_TEST_ENGINE) && YAZE_ENABLE_IMGUI_TEST_ENGINE
102#endif
103
104 // Initialize ImGui backends if renderer is provided
105 if (renderer) {
106 SDL_Renderer* sdl_renderer = static_cast<SDL_Renderer*>(renderer->GetBackendRenderer());
107 ImGui_ImplSDL2_InitForSDLRenderer(window.window_.get(), sdl_renderer);
108 ImGui_ImplSDLRenderer2_Init(sdl_renderer);
109 }
110
112
113 // Apply original YAZE colors as fallback, then try to load theme system
115
116 // Audio is now handled by IAudioBackend in Emulator class
117 // Keep legacy buffer allocation for backwards compatibility
118 if (window.audio_device_ == 0) {
119 const int audio_frequency = 48000;
120 const size_t buffer_size = (audio_frequency / 50) * 2; // 1920 int16_t for stereo PAL
121
122 // CRITICAL FIX: Allocate buffer as ARRAY, not single value
123 // Use new[] with shared_ptr custom deleter for proper array allocation
124 window.audio_buffer_ = std::shared_ptr<int16_t>(
125 new int16_t[buffer_size],
126 std::default_delete<int16_t[]>());
127
128 // Note: Actual audio device is created by Emulator's IAudioBackend
129 // This maintains compatibility with existing code paths
130 LOG_INFO("Window", "Audio buffer allocated: %zu int16_t samples (backend in Emulator)",
131 buffer_size);
132 }
133
134 return absl::OkStatus();
135}
136
137absl::Status ShutdownWindow(Window& window) {
138 SDL_PauseAudioDevice(window.audio_device_, 1);
139 SDL_CloseAudioDevice(window.audio_device_);
140
141 // Stop test engine WHILE ImGui context is still valid
142#ifdef YAZE_ENABLE_IMGUI_TEST_ENGINE
144#endif
145
146 // TODO: BAD FIX, SLOW SHUTDOWN TAKES TOO LONG NOW
147 // CRITICAL FIX: Shutdown graphics arena FIRST
148 // This ensures all textures are destroyed while renderer is still valid
149 LOG_INFO("Window", "Shutting down graphics arena...");
151
152 // Shutdown ImGui implementations (after Arena but before context)
153 LOG_INFO("Window", "Shutting down ImGui implementations...");
154 ImGui_ImplSDL2_Shutdown();
155 ImGui_ImplSDLRenderer2_Shutdown();
156
157 // Destroy ImGui context
158 LOG_INFO("Window", "Destroying ImGui context...");
159 ImGui::DestroyContext();
160
161 // NOW destroy test engine context (after ImGui context is destroyed)
162#ifdef YAZE_ENABLE_IMGUI_TEST_ENGINE
164#endif
165
166 // Finally destroy window
167 LOG_INFO("Window", "Destroying window...");
168 SDL_DestroyWindow(window.window_.get());
169
170 LOG_INFO("Window", "Shutting down SDL...");
171 SDL_Quit();
172
173 LOG_INFO("Window", "Shutdown complete");
174 return absl::OkStatus();
175}
176
177absl::Status HandleEvents(Window& window) {
178 SDL_Event event;
179 ImGuiIO& io = ImGui::GetIO();
180
181 // Protect SDL_PollEvent from crashing the app
182 // macOS NSPersistentUIManager corruption can crash during event polling
183 while (SDL_PollEvent(&event)) {
184 ImGui_ImplSDL2_ProcessEvent(&event);
185 switch (event.type) {
186 case SDL_KEYDOWN:
187 case SDL_KEYUP: {
188 io.KeyShift = ((SDL_GetModState() & KMOD_SHIFT) != 0);
189 io.KeyCtrl = ((SDL_GetModState() & KMOD_CTRL) != 0);
190 io.KeyAlt = ((SDL_GetModState() & KMOD_ALT) != 0);
191 io.KeySuper = ((SDL_GetModState() & KMOD_GUI) != 0);
192 break;
193 }
194 case SDL_WINDOWEVENT:
195 switch (event.window.event) {
196 case SDL_WINDOWEVENT_CLOSE:
197 window.active_ = false;
198 break;
199 case SDL_WINDOWEVENT_SIZE_CHANGED:
200 case SDL_WINDOWEVENT_RESIZED:
201 // Update display size for both resize and size_changed events
202 io.DisplaySize.x = static_cast<float>(event.window.data1);
203 io.DisplaySize.y = static_cast<float>(event.window.data2);
205 break;
206 case SDL_WINDOWEVENT_MINIMIZED:
207 case SDL_WINDOWEVENT_HIDDEN:
208 // Window is minimized/hidden
209 g_window_is_resizing = false;
210 break;
211 case SDL_WINDOWEVENT_RESTORED:
212 case SDL_WINDOWEVENT_SHOWN:
213 case SDL_WINDOWEVENT_EXPOSED:
214 // Window is restored - clear resize flag
215 g_window_is_resizing = false;
216 break;
217 }
218 break;
219 }
220 }
221 int mouseX;
222 int mouseY;
223 const int buttons = SDL_GetMouseState(&mouseX, &mouseY);
224
225 io.DeltaTime = 1.0f / 60.0f;
226 io.MousePos = ImVec2(static_cast<float>(mouseX), static_cast<float>(mouseY));
227 io.MouseDown[0] = buttons & SDL_BUTTON(SDL_BUTTON_LEFT);
228 io.MouseDown[1] = buttons & SDL_BUTTON(SDL_BUTTON_RIGHT);
229 io.MouseDown[2] = buttons & SDL_BUTTON(SDL_BUTTON_MIDDLE);
230
231 int wheel = 0;
232 io.MouseWheel = static_cast<float>(wheel);
233 return absl::OkStatus();
234}
235
236} // namespace core
237} // namespace yaze
void Shutdown()
Definition arena.cc:164
static Arena & Get()
Definition arena.cc:15
Defines an abstract interface for all rendering operations.
Definition irenderer.h:35
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.
static TestManager & Get()
#define LOG_ERROR(category, format,...)
Definition log.h:110
#define LOG_INFO(category, format,...)
Definition log.h:106
#define RETURN_IF_ERROR(expression)
Definition macro.h:53
void ImGuiAssertionHandler(const char *expr, const char *file, int line, const char *msg)
Definition window.cc:18
absl::Status ShutdownWindow(Window &window)
Definition window.cc:137
bool g_window_is_resizing
Definition window.cc:55
absl::Status HandleEvents(Window &window)
Definition window.cc:177
absl::Status CreateWindow(Window &window, gfx::IRenderer *renderer, int flags)
Definition window.cc:57
void ColorsYaze()
Definition style.cc:32
Main namespace for the application.
absl::Status LoadPackageFonts()
std::shared_ptr< SDL_Window > window_
Definition window.h:18
std::shared_ptr< int16_t > audio_buffer_
Definition window.h:20
SDL_AudioDeviceID audio_device_
Definition window.h:19
Deleter for SDL_Window and SDL_Renderer.
Definition sdl_deleter.h:12