yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
controller.cc
Go to the documentation of this file.
1#include "controller.h"
2
4
5#if defined(__APPLE__)
6#include <TargetConditionals.h>
7#endif
8
9#include <string>
10
11#include "absl/status/status.h"
18#include "app/emu/emulator.h"
20#include "app/platform/timing.h"
22#include "imgui/imgui.h"
23
24namespace yaze {
25
26absl::Status Controller::OnEntry(std::string filename) {
27 // Create window backend using factory (auto-selects SDL2 or SDL3)
30#if defined(__APPLE__) && (TARGET_OS_IPHONE == 1 || TARGET_IPHONE_SIMULATOR == 1)
32 renderer_type = gfx::RendererBackendType::Metal;
33#endif
34
36 if (!window_backend_) {
37 return absl::InternalError("Failed to create window backend");
38 }
39
41 config.title = "Yet Another Zelda3 Editor";
42 config.resizable = true;
43 config.high_dpi = false; // Disabled to match legacy behavior (SDL_WINDOW_RESIZABLE only)
44
45 RETURN_IF_ERROR(window_backend_->Initialize(config));
46
47 // Create renderer via factory (auto-selects SDL2 or SDL3)
49 if (!window_backend_->InitializeRenderer(renderer_.get())) {
50 return absl::InternalError("Failed to initialize renderer");
51 }
52
53 // Initialize ImGui via backend (handles SDL2/SDL3 automatically)
54 RETURN_IF_ERROR(window_backend_->InitializeImGui(renderer_.get()));
55
56 // Initialize the graphics Arena with the renderer
58
59 // Set up audio for emulator (using backend's audio resources)
60 auto audio_buffer = window_backend_->GetAudioBuffer();
61 if (audio_buffer) {
62 editor_manager_.emulator().set_audio_buffer(audio_buffer.get());
63 }
64 editor_manager_.emulator().set_audio_device_id(window_backend_->GetAudioDevice());
65
66 // Initialize editor manager with renderer
67 editor_manager_.Initialize(renderer_.get(), filename);
68
69 active_ = true;
70 return absl::OkStatus();
71}
72
73void Controller::SetStartupEditor(const std::string& editor_name,
74 const std::string& panels) {
75 // Process command-line flags for editor and panels
76 // Example: --editor=Dungeon --open_panels="dungeon.room_list,Room 0"
77 if (!editor_name.empty()) {
79 }
80}
81
83 if (!window_backend_) return;
84
86 while (window_backend_->PollEvent(event)) {
87 switch (event.type) {
90 active_ = false;
91 break;
92 default:
93 // Other events are handled by ImGui via ProcessNativeEvent
94 // which is called inside PollEvent
95 break;
96 }
97
98 // Forward native SDL events to emulator input for event-based paths
99 if (event.has_native_event) {
100 editor_manager_.emulator().input_manager().ProcessEvent(
101 static_cast<void*>(&event.native_event));
102 }
103 }
104}
105
106absl::Status Controller::OnLoad() {
107 if (!window_backend_) {
108 return absl::InternalError("Window backend not initialized");
109 }
110
111 if (editor_manager_.quit() || !window_backend_->IsActive()) {
112 active_ = false;
113 return absl::OkStatus();
114 }
115
116#if TARGET_OS_IPHONE != 1
117 // Start new ImGui frame via backend (handles SDL2/SDL3 automatically)
118 window_backend_->NewImGuiFrame();
119 ImGui::NewFrame();
120
121 const ImGuiViewport* viewport = ImGui::GetMainViewport();
122
123 // Calculate layout offsets for sidebars and status bar
124 const float left_offset = editor_manager_.GetLeftLayoutOffset();
125 const float right_offset = editor_manager_.GetRightLayoutOffset();
126 const float bottom_offset = editor_manager_.GetBottomLayoutOffset();
127
128 // Adjust dockspace position and size for sidebars and status bar
129 ImVec2 dockspace_pos = viewport->WorkPos;
130 ImVec2 dockspace_size = viewport->WorkSize;
131
132 dockspace_pos.x += left_offset;
133 dockspace_size.x -= (left_offset + right_offset);
134 dockspace_size.y -= bottom_offset; // Reserve space for status bar at bottom
135
136 ImGui::SetNextWindowPos(dockspace_pos);
137 ImGui::SetNextWindowSize(dockspace_size);
138 ImGui::SetNextWindowViewport(viewport->ID);
139
140 // Check if menu bar should be visible (WASM can hide it for clean UI)
141 bool show_menu_bar = true;
144 }
145
146 ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDocking;
147 if (show_menu_bar) {
148 window_flags |= ImGuiWindowFlags_MenuBar;
149 }
150 window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse |
151 ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove;
152 window_flags |=
153 ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus |
154 ImGuiWindowFlags_NoBackground;
155
156 ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
157 ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
158 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
159 ImGui::Begin("DockSpaceWindow", nullptr, window_flags);
160 ImGui::PopStyleVar(3);
161
162 // Create DockSpace with adjusted size
163 ImGuiID dockspace_id = ImGui::GetID("MainDockSpace");
165 dockspace_id, ImVec2(0.0f, 0.0f), ImGuiDockNodeFlags_PassthruCentralNode);
166
167 if (show_menu_bar) {
168 editor_manager_.DrawMenuBar(); // Draw the fixed menu bar at the top
169 }
170
172 ImGui::End();
173
174 // Draw menu bar restore button when menu is hidden (WASM)
175 if (!show_menu_bar && editor_manager_.ui_coordinator()) {
177 }
178#else
179 if (window_backend_) {
180 window_backend_->NewImGuiFrame();
181 ImGui::NewFrame();
182 }
183#endif
185 absl::Status update_status = editor_manager_.Update();
187 RETURN_IF_ERROR(update_status);
188 return absl::OkStatus();
189}
190
192 if (!window_backend_ || !renderer_) return;
193
194 // Process pending texture commands (max 8 per frame for consistent performance)
196
197 renderer_->Clear();
198
199 // Render ImGui draw data and handle viewports via backend
200 window_backend_->RenderImGui(renderer_.get());
201
202 renderer_->Present();
203
204 // Process any pending screenshot requests on the main thread after present
206
207 // Get delta time AFTER render for accurate measurement
208 float delta_time = TimingManager::Get().Update();
209
210 // Gentle frame rate cap to prevent excessive CPU usage
211 // Only delay if we're rendering faster than 144 FPS (< 7ms per frame)
212 if (delta_time < 0.007f) {
213#if TARGET_OS_IPHONE != 1
214 SDL_Delay(1); // Tiny delay to yield CPU without affecting ImGui timing
215#endif
216 }
217}
218
220 if (renderer_) {
221 renderer_->Shutdown();
222 }
223 if (window_backend_) {
224 window_backend_->Shutdown();
225 }
226}
227
228absl::Status Controller::LoadRomForTesting(const std::string& rom_path) {
229 // Use EditorManager's OpenRomOrProject which handles the full initialization:
230 // 1. Load ROM file into session
231 // 2. ConfigureEditorDependencies()
232 // 3. LoadAssets() - initializes all editors and loads graphics
233 // 4. Updates UI state (hides welcome screen, etc.)
234 return editor_manager_.OpenRomOrProject(rom_path);
235}
236
238 std::lock_guard<std::mutex> lock(screenshot_mutex_);
239 screenshot_requests_.push(request);
240}
241
243#ifdef YAZE_WITH_GRPC
244 std::lock_guard<std::mutex> lock(screenshot_mutex_);
245 while (!screenshot_requests_.empty()) {
246 auto request = screenshot_requests_.front();
248
249 // Perform capture on main thread
250 auto result = test::CaptureHarnessScreenshot(request.preferred_path);
251 if (request.callback) {
252 request.callback(result);
253 }
254 }
255#endif
256}
257
258} // namespace yaze
std::queue< ScreenshotRequest > screenshot_requests_
Definition controller.h:90
editor::EditorManager editor_manager_
Definition controller.h:85
absl::Status LoadRomForTesting(const std::string &rom_path)
absl::Status OnEntry(std::string filename="")
Definition controller.cc:26
void SetStartupEditor(const std::string &editor_name, const std::string &cards)
Definition controller.cc:73
std::unique_ptr< platform::IWindowBackend > window_backend_
Definition controller.h:84
void ProcessScreenshotRequests() const
absl::Status OnLoad()
void DoRender() const
void RequestScreenshot(const ScreenshotRequest &request)
std::unique_ptr< gfx::IRenderer > renderer_
Definition controller.h:86
std::mutex screenshot_mutex_
Definition controller.h:89
static TimingManager & Get()
Definition timing.h:20
float Update()
Update the timing manager (call once per frame)
Definition timing.h:29
void DrawMenuBar()
Draw the main menu bar.
UICoordinator * ui_coordinator()
void Initialize(gfx::IRenderer *renderer, const std::string &filename="")
void OpenEditorAndPanelsFromFlags(const std::string &editor_name, const std::string &panels_str)
absl::Status Update()
Main update loop for the editor application.
auto emulator() -> emu::Emulator &
absl::Status OpenRomOrProject(const std::string &filename)
void Initialize(IRenderer *renderer)
Definition arena.cc:16
void ProcessTextureQueue(IRenderer *renderer)
Definition arena.cc:115
static Arena & Get()
Definition arena.cc:20
static RendererBackendType GetDefaultBackendType()
Get the default backend type for this build.
static std::unique_ptr< IRenderer > Create(RendererBackendType type=RendererBackendType::kDefault)
Create a renderer instance with the specified backend type.
static void BeginEnhancedDockSpace(ImGuiID dockspace_id, const ImVec2 &size=ImVec2(0, 0), ImGuiDockNodeFlags flags=0)
static WidgetIdRegistry & Instance()
static WindowBackendType GetDefaultType()
Get the default backend type for this build.
static std::unique_ptr< IWindowBackend > Create(WindowBackendType type)
Create a window backend of the specified type.
@ Metal
Metal renderer backend (Apple platforms)
SDL2/SDL3 compatibility layer.
#define RETURN_IF_ERROR(expr)
Definition snes.cc:22
Window configuration parameters.
Definition iwindow.h:24
Platform-agnostic window event data.
Definition iwindow.h:63
WindowEventType type
Definition iwindow.h:64