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