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