#include "Bootstrap/Application.h" #include "Bootstrap/ApplicationBootstrapSupport.h" #include "State/EditorContext.h" #include "Platform/Win32/EditorWindow.h" #include "Platform/Win32/EditorWindowManager.h" #include "Support/EnvironmentFlags.h" #ifndef XCUIEDITOR_REPO_ROOT #define XCUIEDITOR_REPO_ROOT "." #endif namespace XCEngine::UI::Editor { using namespace BootstrapSupport; bool Application::Initialize(HINSTANCE hInstance, int nCmdShow) { m_hInstance = hInstance; m_repoRoot = ResolveRepoRootPath(); EnableDpiAwareness(); InitializeSmokeTestConfig(); const std::filesystem::path logRoot = GetExecutableDirectory() / "logs"; InitializeUIEditorRuntimeTrace(logRoot); SetUnhandledExceptionFilter(&Application::HandleUnhandledException); AppendUIEditorRuntimeTrace("app", "initialize begin"); if (!m_editorContext->Initialize(m_repoRoot)) { AppendUIEditorRuntimeTrace( "app", "shell asset validation failed: " + m_editorContext->GetValidationMessage()); return false; } if (!RegisterWindowClass()) { return false; } App::EditorWindowHostConfig hostConfig = {}; hostConfig.hInstance = m_hInstance; hostConfig.windowClassName = kWindowClassName; hostConfig.windowStyle = kBorderlessWindowStyle; hostConfig.primaryWindowTitle = kWindowTitle; hostConfig.windowUserData = this; m_windowManager = std::make_unique( hostConfig, m_repoRoot, *m_editorContext); m_editorContext->SetExitRequestHandler([this]() { if (m_windowManager == nullptr) { return; } if (App::EditorWindow* primaryWindow = m_windowManager->FindPrimaryWindow(); primaryWindow != nullptr && primaryWindow->GetHwnd() != nullptr) { PostMessageW(primaryWindow->GetHwnd(), WM_CLOSE, 0, 0); } }); m_editorContext->SetReadyStatus(); App::EditorWindowManager::CreateParams createParams = {}; createParams.windowId = "main"; createParams.title = kWindowTitle; createParams.showCommand = nCmdShow; createParams.primary = true; createParams.autoCaptureOnStartup = !m_smokeTestEnabled; if (m_windowManager->CreateEditorWindow( m_editorContext->BuildWorkspaceController(), createParams) == nullptr) { AppendUIEditorRuntimeTrace("app", "primary window creation failed"); return false; } AppendUIEditorRuntimeTrace("app", "initialize end"); return true; } void Application::InitializeSmokeTestConfig() { m_smokeTestEnabled = Support::IsEnvironmentFlagEnabled("XCUIEDITOR_SMOKE_TEST"); m_smokeTestFrameLimit = 0; m_smokeTestRenderedFrames = 0; m_smokeTestCloseRequested = false; if (!m_smokeTestEnabled) { return; } constexpr int kDefaultSmokeFrameLimit = 4; const std::optional frameLimit = Support::TryGetEnvironmentInt("XCUIEDITOR_SMOKE_TEST_FRAME_LIMIT"); m_smokeTestFrameLimit = frameLimit.has_value() && frameLimit.value() > 0 ? frameLimit.value() : kDefaultSmokeFrameLimit; AppendUIEditorRuntimeTrace( "smoke", "enabled with frame limit=" + std::to_string(m_smokeTestFrameLimit)); } void Application::Shutdown() { AppendUIEditorRuntimeTrace("app", "shutdown begin"); if (m_windowManager != nullptr) { m_windowManager->Shutdown(); m_windowManager.reset(); } if (m_windowClassAtom != 0 && m_hInstance != nullptr) { UnregisterClassW(kWindowClassName, m_hInstance); m_windowClassAtom = 0; } AppendUIEditorRuntimeTrace("app", "shutdown end"); ShutdownUIEditorRuntimeTrace(); } std::filesystem::path Application::ResolveRepoRootPath() { std::string root = XCUIEDITOR_REPO_ROOT; if (root.size() >= 2u && root.front() == '"' && root.back() == '"') { root = root.substr(1u, root.size() - 2u); } return std::filesystem::path(root).lexically_normal(); } LONG WINAPI Application::HandleUnhandledException(EXCEPTION_POINTERS* exceptionInfo) { if (exceptionInfo != nullptr && exceptionInfo->ExceptionRecord != nullptr) { AppendUIEditorCrashTrace( exceptionInfo->ExceptionRecord->ExceptionCode, exceptionInfo->ExceptionRecord->ExceptionAddress); } else { AppendUIEditorCrashTrace(0u, nullptr); } return EXCEPTION_EXECUTE_HANDLER; } } // namespace XCEngine::UI::Editor