7#include <TargetConditionals.h>
16#include "absl/status/status.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"
36#if defined(__APPLE__) && \
37 (TARGET_OS_IPHONE == 1 || TARGET_IPHONE_SIMULATOR == 1)
68 std::chrono::steady_clock::time_point
start;
76 if (
const char* path = std::getenv(
"YAZE_DEBUG_INPUT_LOG");
77 path !=
nullptr && path[0] !=
'\0') {
78 d->log = std::fopen(path,
"a");
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")) {
86 while (std::fscanf(f,
"%d %31s %f %f", &at, action, &x, &y) == 4) {
87 d->script.push_back({at, action, x, y});
99 return std::chrono::duration<double, std::milli>(
100 std::chrono::steady_clock::now() - d.start)
107 if (d.log ==
nullptr) {
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",
115 e.type == SDL_MOUSEBUTTONDOWN ?
"mousedown" :
"mouseup",
116 e.button.button, e.button.x, e.button.y, e.button.windowID,
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);
125 case SDL_WINDOWEVENT:
126 std::fprintf(d.log,
"%9.1f sdl window event=%d\n",
InputDebugMs(),
136 if (d.next_step >= d.script.size()) {
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() &&
144 const auto& step = d.script[d.next_step++];
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;
154 const bool down = step.action ==
"ldown" || step.action ==
"rdown";
155 ev.type = down ? SDL_MOUSEBUTTONDOWN : SDL_MOUSEBUTTONUP;
156 ev.button.windowID = window_id;
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;
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);
175 if (d.log ==
nullptr) {
178 const ImGuiContext& g = *ImGui::GetCurrentContext();
179 const ImGuiIO& io = g.IO;
181 for (
const ImGuiPopupData& p : g.OpenPopupStack) {
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;
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,
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,
207 d.last_frame_line = line;
220 if (app_config.headless) {
221 LOG_INFO(
"Controller",
"Using Null Window Backend (Headless Mode)");
226#if defined(__APPLE__) && \
227 (TARGET_OS_IPHONE == 1 || TARGET_IPHONE_SIMULATOR == 1)
234 return absl::InternalError(
"Failed to create window backend");
238 config.
title =
"Yet Another Zelda3 Editor";
243 if (app_config.service_mode) {
244 LOG_INFO(
"Controller",
"Starting in Service Mode (Hidden Window)");
253 return absl::InternalError(
"Failed to initialize renderer");
263#if defined(YAZE_ENABLE_IMGUI_TEST_ENGINE) && YAZE_ENABLE_IMGUI_TEST_ENGINE && \
264 !defined(YAZE_GUI_TEST_TARGET)
286 return absl::OkStatus();
290 const std::string& panels) {
293 if (!editor_name.empty()) {
309 constexpr auto kIdleAfter = std::chrono::milliseconds(1500);
315#if defined(YAZE_ENABLE_IMGUI_TEST_ENGINE) && YAZE_ENABLE_IMGUI_TEST_ENGINE
317 busy = busy || tests.IsTestRunning();
320 if (
auto* engine = tests.GetUITestEngine()) {
321 busy = busy || !ImGuiTestEngine_IsTestQueueEmpty(engine);
331 }
else if (quiet >= kIdleAfter) {
338 auto& debug = GetInputDebug();
339 if (!debug.started) {
340 debug.started =
true;
341 debug.start = std::chrono::steady_clock::now();
344 if (!debug.script.empty() && ImGui::GetCurrentContext() !=
nullptr) {
345 InputDebugRunScript();
358 switch (event.
type) {
401 return absl::InternalError(
"Window backend not initialized");
406 return absl::OkStatus();
412 InputDebugLogFrame();
417 const ImGuiViewport* viewport = ImGui::GetMainViewport();
424 float top_offset = 0.0f;
425#if defined(__APPLE__) && \
426 (TARGET_OS_IPHONE == 1 || TARGET_IPHONE_SIMULATOR == 1)
432 bottom_offset += safe.bottom;
437 ImVec2 dockspace_pos = viewport->WorkPos;
438 ImVec2 dockspace_size = viewport->WorkSize;
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);
445 ImGui::SetNextWindowPos(dockspace_pos);
446 ImGui::SetNextWindowSize(dockspace_size);
447 ImGui::SetNextWindowViewport(viewport->ID);
450 bool show_menu_bar =
true;
454#if defined(__APPLE__) && \
455 (TARGET_OS_IPHONE == 1 || TARGET_IPHONE_SIMULATOR == 1)
456 show_menu_bar =
false;
459 ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDocking;
461 window_flags |= ImGuiWindowFlags_MenuBar;
463 window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse |
464 ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove;
465 window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus |
466 ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_NoBackground;
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);
481 ImGuiID dockspace_id = ImGui::GetID(
"MainDockSpace");
483 mgr->SetMainDockspaceId(dockspace_id);
490 (void)mgr->MaybeReapplyStartupLayout(
494 dockspace_id, ImVec2(0.0f, 0.0f), ImGuiDockNodeFlags_PassthruCentralNode);
507#if !defined(__APPLE__) || \
508 (TARGET_OS_IPHONE != 1 && TARGET_IPHONE_SIMULATOR != 1)
519#if defined(__APPLE__) && \
520 (TARGET_OS_IPHONE == 1 || TARGET_IPHONE_SIMULATOR == 1)
526 snap.
can_undo = editor->undo_manager().CanUndo();
527 snap.
can_redo = editor->undo_manager().CanRedo();
540 return absl::OkStatus();
554 sync_state.pending_layout_actions > 0;
555 if (in_transition && (sync_state.frame_id % 30 != 0)) {
582#if defined(YAZE_ENABLE_IMGUI_TEST_ENGINE) && YAZE_ENABLE_IMGUI_TEST_ENGINE
590 const auto frame_end = std::chrono::steady_clock::now();
593 if (frame_time < std::chrono::milliseconds(7)) {
594#if TARGET_OS_IPHONE != 1
601#if defined(YAZE_ENABLE_IMGUI_TEST_ENGINE) && YAZE_ENABLE_IMGUI_TEST_ENGINE && \
602 !defined(YAZE_GUI_TEST_TARGET)
618#if defined(YAZE_ENABLE_IMGUI_TEST_ENGINE) && YAZE_ENABLE_IMGUI_TEST_ENGINE && \
619 !defined(YAZE_GUI_TEST_TARGET)
645 std::queue<ScreenshotRequest> requests;
650 while (!requests.empty()) {
651 auto request = std::move(requests.front());
654 absl::StatusOr<test::ScreenshotArtifact> result =
655 absl::FailedPreconditionError(
656 "Screenshots require a rendered GUI frame; use service mode "
657 "instead of headless mode");
659 result = request.window_title.empty()
660 ? test::CaptureHarnessScreenshot(request.preferred_path,
661 request.reveal_to_user,
663 : test::CaptureWindowByName(
664 request.window_title, request.preferred_path,
665 request.reveal_to_user, request.format);
667 if (request.callback) {
668 request.callback(std::move(result));
static Application & Instance()
const AppConfig & GetConfig() const
std::chrono::steady_clock::time_point last_frame_end_
std::queue< ScreenshotRequest > screenshot_requests_
editor::EditorManager editor_manager_
absl::Status LoadRomForTesting(const std::string &rom_path)
absl::Status OnEntry(std::string filename="")
std::chrono::steady_clock::time_point last_event_time_
void SetStartupEditor(const std::string &editor_name, const std::string &cards)
std::unique_ptr< platform::IWindowBackend > window_backend_
void ProcessScreenshotRequests() const
void RequestScreenshot(const ScreenshotRequest &request)
std::unique_ptr< gfx::IRenderer > renderer_
std::mutex screenshot_mutex_
float GetRightLayoutOffset() const
Rom * GetCurrentRom() const override
UICoordinator * ui_coordinator()
void Initialize(gfx::IRenderer *renderer, const std::string &filename="")
float GetBottomLayoutOffset() const
auto GetCurrentEditor() const -> Editor *override
void HandleHostVisibilityChanged(bool visible)
bool WantsContinuousFrames() const
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
float GetLeftLayoutOffset() const
bool IsMenuBarVisible() const
void DrawMenuBarRestoreButton()
void Initialize(IRenderer *renderer)
bool ProcessTextureQueueWithBudget(IRenderer *renderer, float budget_ms)
Process texture queue with a time budget.
size_t DrainRetiredBitmaps(IRenderer *renderer)
Destroy texture handles retired earlier in the frame.
void ProcessTextureQueue(IRenderer *renderer)
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 void EndEnhancedDockSpace()
static ThemeManager & Get()
void DestroyUITestingContext()
static TestManager & Get()
void InitializeUITesting()
static LogManager & instance()
void Flush()
Writes buffered file output now. log() flushes warnings and errors at once but batches other lines fo...
#define LOG_INFO(category, format,...)
void InputDebugLogSdlEvent(const SDL_Event &e)
InputDebug & GetInputDebug()
void InputDebugRunScript()
void InputDebugLogFrame()
::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
size_t EditorTypeIndex(EditorType type)
@ Null
Null renderer for headless/server mode.
@ Metal
Metal renderer backend (Apple platforms)
SDL2/SDL3 compatibility layer.
#define RETURN_IF_ERROR(expr)
bool layout_rebuild_pending
static FrameGuiBeginEvent Create(float dt)