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
3#include "app/application.h"
5
6#if defined(__APPLE__)
7#include <TargetConditionals.h>
8#endif
9
10#include <chrono>
11#include <cstdio>
12#include <cstdlib>
13#include <string>
14#include <vector>
15
16#include "absl/status/status.h"
21#include "app/emu/emulator.h"
28#include "app/platform/timing.h"
30#include "imgui/imgui.h"
31#include "imgui/imgui_internal.h"
32#if defined(YAZE_ENABLE_IMGUI_TEST_ENGINE) && YAZE_ENABLE_IMGUI_TEST_ENGINE
34#include "imgui_test_engine/imgui_te_engine.h"
35#endif
36#if defined(__APPLE__) && \
37 (TARGET_OS_IPHONE == 1 || TARGET_IPHONE_SIMULATOR == 1)
39#endif
40
41namespace yaze {
42
43namespace {
44
45// ---------------------------------------------------------------------------
46// Input diagnostics (off unless YAZE_DEBUG_INPUT_LOG is set).
47//
48// YAZE_DEBUG_INPUT_LOG=<path> appends one line per SDL mouse/key/window event
49// and one line per frame in which ImGui's mouse state changed: buttons down,
50// clicked/released, hovered window and ID, active ID, key mods and the open
51// popup stack. Used to find where a click is lost between SDL and ImGui.
52//
53// YAZE_DEBUG_INPUT_SCRIPT=<path> replays "<ms> <action> <x> <y>" lines
54// (action: move, ldown, lup, rdown, rup) through SDL_PushEvent, relative to
55// the first frame, so a hidden instance can be clicked through the real SDL
56// queue, event filter and ImGui SDL backend.
57// ---------------------------------------------------------------------------
58struct InputDebug {
59 FILE* log = nullptr;
60 struct Step {
61 int at_ms;
62 std::string action;
63 float x;
64 float y;
65 };
66 std::vector<Step> script;
67 size_t next_step = 0;
68 std::chrono::steady_clock::time_point start;
69 bool started = false;
70 std::string last_frame_line;
71};
72
74 static InputDebug* debug = [] {
75 auto* d = new InputDebug();
76 if (const char* path = std::getenv("YAZE_DEBUG_INPUT_LOG");
77 path != nullptr && path[0] != '\0') {
78 d->log = std::fopen(path, "a");
79 }
80 if (const char* path = std::getenv("YAZE_DEBUG_INPUT_SCRIPT");
81 path != nullptr && path[0] != '\0') {
82 if (FILE* f = std::fopen(path, "r")) {
83 char action[32];
84 int at = 0;
85 float x = 0, y = 0;
86 while (std::fscanf(f, "%d %31s %f %f", &at, action, &x, &y) == 4) {
87 d->script.push_back({at, action, x, y});
88 }
89 std::fclose(f);
90 }
91 }
92 return d;
93 }();
94 return *debug;
95}
96
97double InputDebugMs() {
98 auto& d = GetInputDebug();
99 return std::chrono::duration<double, std::milli>(
100 std::chrono::steady_clock::now() - d.start)
101 .count();
102}
103
104#ifndef YAZE_USE_SDL3
105void InputDebugLogSdlEvent(const SDL_Event& e) {
106 auto& d = GetInputDebug();
107 if (d.log == nullptr) {
108 return;
109 }
110 switch (e.type) {
111 case SDL_MOUSEBUTTONDOWN:
112 case SDL_MOUSEBUTTONUP:
113 std::fprintf(d.log, "%9.1f sdl %s btn=%d x=%d y=%d win=%u clicks=%d\n",
114 InputDebugMs(),
115 e.type == SDL_MOUSEBUTTONDOWN ? "mousedown" : "mouseup",
116 e.button.button, e.button.x, e.button.y, e.button.windowID,
117 e.button.clicks);
118 break;
119 case SDL_KEYDOWN:
120 case SDL_KEYUP:
121 std::fprintf(d.log, "%9.1f sdl %s sym=0x%x mod=0x%x\n", InputDebugMs(),
122 e.type == SDL_KEYDOWN ? "keydown" : "keyup",
123 e.key.keysym.sym, e.key.keysym.mod);
124 break;
125 case SDL_WINDOWEVENT:
126 std::fprintf(d.log, "%9.1f sdl window event=%d\n", InputDebugMs(),
127 e.window.event);
128 break;
129 default:
130 break;
131 }
132}
133
135 auto& d = GetInputDebug();
136 if (d.next_step >= d.script.size()) {
137 return;
138 }
139 const ImGuiViewport* vp = ImGui::GetMainViewport();
140 const Uint32 window_id =
141 static_cast<Uint32>(reinterpret_cast<intptr_t>(vp->PlatformHandle));
142 while (d.next_step < d.script.size() &&
143 d.script[d.next_step].at_ms <= InputDebugMs()) {
144 const auto& step = d.script[d.next_step++];
145 SDL_Event ev{};
146 const int x = static_cast<int>(step.x);
147 const int y = static_cast<int>(step.y);
148 if (step.action == "move") {
149 ev.type = SDL_MOUSEMOTION;
150 ev.motion.windowID = window_id;
151 ev.motion.x = x;
152 ev.motion.y = y;
153 } else {
154 const bool down = step.action == "ldown" || step.action == "rdown";
155 ev.type = down ? SDL_MOUSEBUTTONDOWN : SDL_MOUSEBUTTONUP;
156 ev.button.windowID = window_id;
157 ev.button.button =
158 step.action[0] == 'r' ? SDL_BUTTON_RIGHT : SDL_BUTTON_LEFT;
159 ev.button.state = down ? SDL_PRESSED : SDL_RELEASED;
160 ev.button.clicks = 1;
161 ev.button.x = x;
162 ev.button.y = y;
163 }
164 SDL_PushEvent(&ev);
165 if (d.log != nullptr) {
166 std::fprintf(d.log, "%9.1f script %s %d %d\n", InputDebugMs(),
167 step.action.c_str(), x, y);
168 }
169 }
170}
171#endif // YAZE_USE_SDL3
172
174 auto& d = GetInputDebug();
175 if (d.log == nullptr) {
176 return;
177 }
178 const ImGuiContext& g = *ImGui::GetCurrentContext();
179 const ImGuiIO& io = g.IO;
180 std::string popups;
181 for (const ImGuiPopupData& p : g.OpenPopupStack) {
182 char id[48];
183 std::snprintf(id, sizeof(id), "(pending id=%08X parent=%08X f=%d)",
184 p.PopupId, p.OpenParentId, p.OpenFrameCount);
185 popups += p.Window ? p.Window->Name : id;
186 popups += "|";
187 }
188 char line[1024];
189 std::snprintf(
190 line, sizeof(line),
191 "down=%d%d%d clk=%d%d rel=%d%d pos=%.0f,%.0f hovwin=%s hovid=%08X "
192 "active=%08X(%s) nav=%s text=%d mods=0x%x focus=%d popups=[%s]",
193 io.MouseDown[0], io.MouseDown[1], io.MouseDown[2], io.MouseClicked[0],
194 io.MouseClicked[1], io.MouseReleased[0], io.MouseReleased[1],
195 io.MousePos.x, io.MousePos.y,
196 g.HoveredWindow ? g.HoveredWindow->Name : "-", g.HoveredIdPreviousFrame,
197 g.ActiveId, g.ActiveIdWindow ? g.ActiveIdWindow->Name : "-",
198 g.NavWindow ? g.NavWindow->Name : "-", io.WantTextInput ? 1 : 0,
199 static_cast<unsigned>(io.KeyMods), io.AppFocusLost ? 0 : 1,
200 popups.c_str());
201 const bool edge = io.MouseClicked[0] || io.MouseClicked[1] ||
202 io.MouseReleased[0] || io.MouseReleased[1];
203 if (edge || d.last_frame_line != line) {
204 std::fprintf(d.log, "%9.1f frame=%d %s\n", InputDebugMs(), g.FrameCount,
205 line);
206 std::fflush(d.log);
207 d.last_frame_line = line;
208 }
209}
210
211} // namespace
212
213absl::Status Controller::OnEntry(std::string filename) {
214 // Create window backend using factory (auto-selects SDL2 or SDL3)
216 auto renderer_type = gfx::RendererFactory::GetDefaultBackendType();
217
218 const auto& app_config = Application::Instance().GetConfig();
219
220 if (app_config.headless) {
221 LOG_INFO("Controller", "Using Null Window Backend (Headless Mode)");
223 renderer_type = gfx::RendererBackendType::Null;
224 }
225
226#if defined(__APPLE__) && \
227 (TARGET_OS_IPHONE == 1 || TARGET_IPHONE_SIMULATOR == 1)
229 renderer_type = gfx::RendererBackendType::Metal;
230#endif
231
233 if (!window_backend_) {
234 return absl::InternalError("Failed to create window backend");
235 }
236
238 config.title = "Yet Another Zelda3 Editor";
239 config.resizable = true;
240 config.high_dpi =
241 false; // Disabled to match legacy behavior (SDL_WINDOW_RESIZABLE only)
242
243 if (app_config.service_mode) {
244 LOG_INFO("Controller", "Starting in Service Mode (Hidden Window)");
245 config.hidden = true;
246 }
247
248 RETURN_IF_ERROR(window_backend_->Initialize(config));
249
250 // Create renderer via factory (auto-selects SDL2 or SDL3)
252 if (!window_backend_->InitializeRenderer(renderer_.get())) {
253 return absl::InternalError("Failed to initialize renderer");
254 }
255
256 // Initialize ImGui via backend (handles SDL2/SDL3 automatically)
257 RETURN_IF_ERROR(window_backend_->InitializeImGui(renderer_.get()));
258
259 // The test manager is linked at the controller/executable layer rather than
260 // the platform window library. Initialize its engine here, after ImGui has a
261 // context, so remote harness actions use the real engine. The dedicated GUI
262 // test runner owns a separate engine and initializes it below its OnEntry().
263#if defined(YAZE_ENABLE_IMGUI_TEST_ENGINE) && YAZE_ENABLE_IMGUI_TEST_ENGINE && \
264 !defined(YAZE_GUI_TEST_TARGET)
266#endif
267
268 // Initialize the graphics Arena with the renderer
270
271 // Set up audio for emulator (using backend's audio resources)
272 auto audio_buffer = window_backend_->GetAudioBuffer();
273 if (audio_buffer) {
274 editor_manager_.emulator().set_audio_buffer(audio_buffer.get());
275 }
276 editor_manager_.emulator().set_audio_device_id(
277 window_backend_->GetAudioDevice());
278
280 Application::Instance().GetConfig().asset_load_mode);
281
282 // Initialize editor manager with renderer
283 editor_manager_.Initialize(renderer_.get(), filename);
284
285 active_ = true;
286 return absl::OkStatus();
287}
288
289void Controller::SetStartupEditor(const std::string& editor_name,
290 const std::string& panels) {
291 // Process command-line flags for editor and panels
292 // Example: --editor=Dungeon --open_panels="dungeon.room_list,Room 0"
293 if (!editor_name.empty()) {
295 }
296}
297
299 if (!window_backend_)
300 return;
301
302 // Idle pacing. With nothing to animate and no input for a while, wait for
303 // the next event instead of drawing at the display rate: 10 fps after
304 // kIdleAfter of quiet, 4 fps while the window is hidden or minimized. Any
305 // event (mouse move, key, window change) wakes the loop at once, and work
306 // that needs frames (emulator, music, texture uploads, UI tests,
307 // screenshots) keeps full rate.
308 {
309 constexpr auto kIdleAfter = std::chrono::milliseconds(1500);
311 {
312 std::lock_guard<std::mutex> lock(screenshot_mutex_);
313 busy = busy || !screenshot_requests_.empty();
314 }
315#if defined(YAZE_ENABLE_IMGUI_TEST_ENGINE) && YAZE_ENABLE_IMGUI_TEST_ENGINE
316 auto& tests = test::TestManager::Get();
317 busy = busy || tests.IsTestRunning();
318 // Harness RPCs queue engine tests that advance one frame at a time; a
319 // task stays in the queue until it finishes.
320 if (auto* engine = tests.GetUITestEngine()) {
321 busy = busy || !ImGuiTestEngine_IsTestQueueEmpty(engine);
322 }
323#endif
324 if (!busy) {
325 // Write batched log lines before going quiet, so they are on disk if
326 // the app then crashes or is killed.
328 const auto quiet = std::chrono::steady_clock::now() - last_event_time_;
329 if (window_hidden_) {
330 window_backend_->WaitForEvent(250);
331 } else if (quiet >= kIdleAfter) {
332 window_backend_->WaitForEvent(100);
333 }
334 }
335 }
336
337 {
338 auto& debug = GetInputDebug();
339 if (!debug.started) {
340 debug.started = true;
341 debug.start = std::chrono::steady_clock::now();
342 }
343#ifndef YAZE_USE_SDL3
344 if (!debug.script.empty() && ImGui::GetCurrentContext() != nullptr) {
345 InputDebugRunScript();
346 }
347#endif
348 }
349
351 while (window_backend_->PollEvent(event)) {
352#ifndef YAZE_USE_SDL3
353 if (event.has_native_event) {
354 InputDebugLogSdlEvent(event.native_event);
355 }
356#endif
357 last_event_time_ = std::chrono::steady_clock::now();
358 switch (event.type) {
361 // Native close requests must follow the same guarded quit path as the
362 // menu and keyboard shortcut. EditorManager keeps the application
363 // alive while the user resolves any unsaved-session prompt.
365 break;
366
369 window_hidden_ = true;
371 break;
372
375 break;
376
381 window_hidden_ = false;
383 break;
384
385 default:
386 // Other events are handled by ImGui via ProcessNativeEvent
387 // which is called inside PollEvent
388 break;
389 }
390
391 // Forward native SDL events to emulator input for event-based paths
392 if (event.has_native_event) {
393 editor_manager_.emulator().input_manager().ProcessEvent(
394 static_cast<void*>(&event.native_event));
395 }
396 }
397}
398
399absl::Status Controller::OnLoad() {
400 if (!window_backend_) {
401 return absl::InternalError("Window backend not initialized");
402 }
403
404 if (editor_manager_.quit() || !window_backend_->IsActive()) {
405 active_ = false;
406 return absl::OkStatus();
407 }
408
409 // Start new ImGui frame via backend (handles SDL2/SDL3 automatically)
410 window_backend_->NewImGuiFrame();
411 ImGui::NewFrame();
412 InputDebugLogFrame();
413
414 // Advance any in-progress theme color transitions
416
417 const ImGuiViewport* viewport = ImGui::GetMainViewport();
418
419 // Calculate layout offsets for sidebars and status bar
420 const float left_offset = editor_manager_.GetLeftLayoutOffset();
421 const float right_offset = editor_manager_.GetRightLayoutOffset();
422 float bottom_offset = editor_manager_.GetBottomLayoutOffset();
423
424 float top_offset = 0.0f;
425#if defined(__APPLE__) && \
426 (TARGET_OS_IPHONE == 1 || TARGET_IPHONE_SIMULATOR == 1)
427 // On iOS, inset the dockspace by the safe area so content doesn't render
428 // behind the notch/Dynamic Island (top) or home indicator (bottom).
429 {
430 const auto safe = platform::ios::GetSafeAreaInsets();
431 top_offset = std::max(safe.top, platform::ios::GetOverlayTopInset());
432 bottom_offset += safe.bottom;
433 }
434#endif
435
436 // Adjust dockspace position and size for sidebars and status bar
437 ImVec2 dockspace_pos = viewport->WorkPos;
438 ImVec2 dockspace_size = viewport->WorkSize;
439
440 dockspace_pos.x += left_offset;
441 dockspace_pos.y += top_offset;
442 dockspace_size.x -= (left_offset + right_offset);
443 dockspace_size.y -= (bottom_offset + top_offset);
444
445 ImGui::SetNextWindowPos(dockspace_pos);
446 ImGui::SetNextWindowSize(dockspace_size);
447 ImGui::SetNextWindowViewport(viewport->ID);
448
449 // Check if menu bar should be visible (WASM can hide it for clean UI)
450 bool show_menu_bar = true;
453 }
454#if defined(__APPLE__) && \
455 (TARGET_OS_IPHONE == 1 || TARGET_IPHONE_SIMULATOR == 1)
456 show_menu_bar = false;
457#endif
458
459 ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDocking;
460 if (show_menu_bar) {
461 window_flags |= ImGuiWindowFlags_MenuBar;
462 }
463 window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse |
464 ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove;
465 window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus |
466 ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_NoBackground;
467
468 ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
469 ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
470 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
471 ImGui::Begin("DockSpaceWindow", nullptr, window_flags);
472 ImGui::PopStyleVar(3);
473
474 // Create DockSpace with adjusted size.
475 // NOTE: ImGui IDs are salted by the current window's ID stack, so this
476 // particular `GetID("MainDockSpace")` is only meaningful while
477 // DockSpaceWindow is the active Begin. Cache it on LayoutManager so
478 // cross-scope callers (e.g. the Layout Designer panel, which lives in
479 // its own PanelWindow) can apply docktrees against the real dockspace
480 // instead of a differently-salted hash.
481 ImGuiID dockspace_id = ImGui::GetID("MainDockSpace");
483 mgr->SetMainDockspaceId(dockspace_id);
484 // Phase 8.2: one-shot startup reapply of the user's last applied
485 // named layout. Idempotent — fires once per session on the first
486 // frame the dockspace is bound and `last_applied_layout_name` is
487 // set. Errors are logged inside the manager; we deliberately ignore
488 // the return so a stale or removed layout doesn't block the editor
489 // from coming up.
490 (void)mgr->MaybeReapplyStartupLayout(
492 }
494 dockspace_id, ImVec2(0.0f, 0.0f), ImGuiDockNodeFlags_PassthruCentralNode);
495
496 if (show_menu_bar) {
497 editor_manager_.DrawMainMenuBar(); // Draw the fixed menu bar at the top
498 }
499
501
503 bus->Publish(editor::FrameGuiBeginEvent::Create(ImGui::GetIO().DeltaTime));
504 }
505 ImGui::End();
506
507#if !defined(__APPLE__) || \
508 (TARGET_OS_IPHONE != 1 && TARGET_IPHONE_SIMULATOR != 1)
509 // Draw menu bar restore button when menu is hidden (WASM)
510 if (!show_menu_bar && editor_manager_.ui_coordinator()) {
512 }
513#endif
515 absl::Status update_status = editor_manager_.Update();
517 RETURN_IF_ERROR(update_status);
518
519#if defined(__APPLE__) && \
520 (TARGET_OS_IPHONE == 1 || TARGET_IPHONE_SIMULATOR == 1)
521 {
523 auto* editor = editor_manager_.GetCurrentEditor();
524 auto* rom = editor_manager_.GetCurrentRom();
525 if (editor) {
526 snap.can_undo = editor->undo_manager().CanUndo();
527 snap.can_redo = editor->undo_manager().CanRedo();
528 snap.editor_type =
530 }
531 if (rom && rom->is_loaded()) {
532 snap.can_save = true;
533 snap.is_dirty = rom->dirty();
534 snap.rom_title = rom->title();
535 }
537 }
538#endif
539
540 return absl::OkStatus();
541}
542
544 if (!window_backend_ || !renderer_)
545 return;
546
547 // Process pending texture commands.
548 // During layout transitions, use a time-budgeted approach to reduce GPU
549 // pressure and avoid Metal crashes from too many texture uploads per frame.
550 // Every 30th frame during transitions, do a full queue pass to prevent starvation.
551 {
552 const auto sync_state = editor_manager_.GetUiSyncStateSnapshot();
553 const bool in_transition = sync_state.layout_rebuild_pending ||
554 sync_state.pending_layout_actions > 0;
555 if (in_transition && (sync_state.frame_id % 30 != 0)) {
557 } else {
559 }
560 }
561
562 if (Application::Instance().GetConfig().headless) {
563 // In HEADLESS mode, we MUST still end the ImGui frame to satisfy assertions
564 // even if we don't render to a window.
565 ImGui::Render();
568 return;
569 }
570
571 renderer_->Clear();
572
573 // Render ImGui draw data and handle viewports via backend
574 // This MUST be called even in headless mode to end the ImGui frame
575 window_backend_->RenderImGui(renderer_.get());
576
577 // Capture the rendered backbuffer before Present invalidates its contents.
579 renderer_->Present();
581
582#if defined(YAZE_ENABLE_IMGUI_TEST_ENGINE) && YAZE_ENABLE_IMGUI_TEST_ENGINE
584#endif
585
586 // Gentle cap for renderers without vsync (software fallback): if the frame
587 // took under 7 ms (> ~144 fps), yield 1 ms. TimingManager is updated once
588 // per frame by EditorManager; updating it here too halved its delta time
589 // and made this check fire almost every frame.
590 const auto frame_end = std::chrono::steady_clock::now();
591 const auto frame_time = frame_end - last_frame_end_;
592 last_frame_end_ = frame_end;
593 if (frame_time < std::chrono::milliseconds(7)) {
594#if TARGET_OS_IPHONE != 1
595 SDL_Delay(1);
596#endif
597 }
598}
599
601#if defined(YAZE_ENABLE_IMGUI_TEST_ENGINE) && YAZE_ENABLE_IMGUI_TEST_ENGINE && \
602 !defined(YAZE_GUI_TEST_TARGET)
603 // Stop the test engine while its bound ImGui context is still alive.
605#endif
606
607 if (renderer_) {
608 // Retired handles contain no Bitmap pointers and are safe to destroy once
609 // the final submitted frame is no longer using them.
611 gfx::Arena::Get().Initialize(nullptr);
612 renderer_->Shutdown();
613 }
614 if (window_backend_) {
615 window_backend_->Shutdown();
616 }
617
618#if defined(YAZE_ENABLE_IMGUI_TEST_ENGINE) && YAZE_ENABLE_IMGUI_TEST_ENGINE && \
619 !defined(YAZE_GUI_TEST_TARGET)
620 // The backend owns and destroys ImGui; release the engine context afterward.
622#endif
623}
624
625absl::Status Controller::LoadRomForTesting(const std::string& rom_path) {
626 // Use EditorManager's OpenRomOrProject which handles the full initialization:
627 // 1. Load ROM file into session
628 // 2. ConfigureEditorDependencies()
629 // 3. LoadAssetsForMode() - initializes all editors and loads graphics
630 // 4. Updates UI state (hides welcome screen, etc.)
631 auto previous_mode = editor_manager_.asset_load_mode();
633 auto status = editor_manager_.OpenRomOrProject(rom_path);
634 editor_manager_.SetAssetLoadMode(previous_mode);
635 return status;
636}
637
639 std::lock_guard<std::mutex> lock(screenshot_mutex_);
640 screenshot_requests_.push(request);
641}
642
644#ifdef YAZE_WITH_GRPC
645 std::queue<ScreenshotRequest> requests;
646 {
647 std::lock_guard<std::mutex> lock(screenshot_mutex_);
648 requests.swap(screenshot_requests_);
649 }
650 while (!requests.empty()) {
651 auto request = std::move(requests.front());
652 requests.pop();
653
654 absl::StatusOr<test::ScreenshotArtifact> result =
655 absl::FailedPreconditionError(
656 "Screenshots require a rendered GUI frame; use service mode "
657 "instead of headless mode");
658 if (!Application::Instance().GetConfig().headless) {
659 result = request.window_title.empty()
660 ? test::CaptureHarnessScreenshot(request.preferred_path,
661 request.reveal_to_user,
662 request.format)
663 : test::CaptureWindowByName(
664 request.window_title, request.preferred_path,
665 request.reveal_to_user, request.format);
666 }
667 if (request.callback) {
668 request.callback(std::move(result));
669 }
670 }
671#endif
672}
673
674} // namespace yaze
static Application & Instance()
const AppConfig & GetConfig() const
Definition application.h:84
std::chrono::steady_clock::time_point last_frame_end_
Definition controller.h:112
std::queue< ScreenshotRequest > screenshot_requests_
Definition controller.h:120
editor::EditorManager editor_manager_
Definition controller.h:115
absl::Status LoadRomForTesting(const std::string &rom_path)
absl::Status OnEntry(std::string filename="")
std::chrono::steady_clock::time_point last_event_time_
Definition controller.h:108
void SetStartupEditor(const std::string &editor_name, const std::string &cards)
std::unique_ptr< platform::IWindowBackend > window_backend_
Definition controller.h:114
void ProcessScreenshotRequests() const
absl::Status OnLoad()
void DoRender() const
void RequestScreenshot(const ScreenshotRequest &request)
std::unique_ptr< gfx::IRenderer > renderer_
Definition controller.h:116
std::mutex screenshot_mutex_
Definition controller.h:119
bool dirty() const
Definition rom.h:188
bool is_loaded() const
Definition rom.h:187
auto title() const
Definition rom.h:199
Rom * GetCurrentRom() const override
UICoordinator * ui_coordinator()
void Initialize(gfx::IRenderer *renderer, const std::string &filename="")
auto GetCurrentEditor() const -> Editor *override
void HandleHostVisibilityChanged(bool visible)
void SetAssetLoadMode(AssetLoadMode mode)
void OpenEditorAndPanelsFromFlags(const std::string &editor_name, const std::string &panels_str)
absl::Status Update()
Main update loop for the editor application.
AssetLoadMode asset_load_mode() const
auto emulator() -> emu::Emulator &
absl::Status OpenRomOrProject(const std::string &filename)
UiSyncState GetUiSyncStateSnapshot() const
void Initialize(IRenderer *renderer)
Definition arena.cc:17
bool ProcessTextureQueueWithBudget(IRenderer *renderer, float budget_ms)
Process texture queue with a time budget.
Definition arena.cc:372
size_t DrainRetiredBitmaps(IRenderer *renderer)
Destroy texture handles retired earlier in the frame.
Definition arena.cc:91
void ProcessTextureQueue(IRenderer *renderer)
Definition arena.cc:211
static Arena & Get()
Definition arena.cc:24
static RendererBackendType GetDefaultBackendType()
static std::unique_ptr< IRenderer > Create(RendererBackendType type=RendererBackendType::kDefault)
static void BeginEnhancedDockSpace(ImGuiID dockspace_id, const ImVec2 &size=ImVec2(0, 0), ImGuiDockNodeFlags flags=0)
static ThemeManager & Get()
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.
static TestManager & Get()
static LogManager & instance()
Definition log.cc:66
void Flush()
Writes buffered file output now. log() flushes warnings and errors at once but batches other lines fo...
Definition log.cc:139
#define LOG_INFO(category, format,...)
Definition log.h:136
void InputDebugLogSdlEvent(const SDL_Event &e)
::yaze::EventBus * event_bus()
Get the current EventBus instance.
UserSettings * user_settings()
Get the current UserSettings instance.
LayoutManager * layout_manager()
Get the shared LayoutManager instance.
constexpr std::array< const char *, 15 > kEditorNames
Definition editor.h:229
size_t EditorTypeIndex(EditorType type)
Definition editor.h:239
@ Null
Null renderer for headless/server mode.
@ Metal
Metal renderer backend (Apple platforms)
void PostEditorStateUpdate(const EditorStateSnapshot &state)
SafeAreaInsets GetSafeAreaInsets()
SDL2/SDL3 compatibility layer.
#define RETURN_IF_ERROR(expr)
Definition snes.cc:22
std::chrono::steady_clock::time_point start
Definition controller.cc:68
static FrameGuiBeginEvent Create(float dt)
Window configuration parameters.
Definition iwindow.h:24
Platform-agnostic window event data.
Definition iwindow.h:65
WindowEventType type
Definition iwindow.h:66