关键节点
This commit is contained in:
352
editor/app/Bootstrap/Application.cpp
Normal file
352
editor/app/Bootstrap/Application.cpp
Normal file
@@ -0,0 +1,352 @@
|
||||
#include "Bootstrap/Application.h"
|
||||
#include "Bootstrap/EditorResources.h"
|
||||
#include "System/SystemInteractionService.h"
|
||||
#include "Composition/EditorContext.h"
|
||||
#include "Platform/Win32/Windowing/EditorWindowManager.h"
|
||||
#include "Platform/Win32/Windowing/EditorWindow.h"
|
||||
#include "Platform/Win32/Content/EditorWorkspaceWindowContentController.h"
|
||||
#include "Platform/Win32/System/Win32SystemInteractionHost.h"
|
||||
#include "Support/EnvironmentFlags.h"
|
||||
#include "Support/ExecutablePath.h"
|
||||
|
||||
#include <XCEditor/Foundation/UIEditorRuntimeTrace.h>
|
||||
#include <XCEngine/Core/Asset/ResourceManager.h>
|
||||
|
||||
#include <shellscalingapi.h>
|
||||
#include <utility>
|
||||
|
||||
namespace XCEngine::UI::Editor {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr const wchar_t* kWindowClassName = L"XCEditorShellHost";
|
||||
constexpr const wchar_t* kWindowTitle = L"Main Scene * - Main.xx - XCEngine Editor";
|
||||
constexpr DWORD kBorderlessWindowStyle = WS_POPUP | WS_THICKFRAME;
|
||||
|
||||
void EnableDpiAwareness() {
|
||||
const HMODULE user32 = GetModuleHandleW(L"user32.dll");
|
||||
if (user32 != nullptr) {
|
||||
using SetProcessDpiAwarenessContextFn = BOOL(WINAPI*)(DPI_AWARENESS_CONTEXT);
|
||||
const auto setProcessDpiAwarenessContext =
|
||||
reinterpret_cast<SetProcessDpiAwarenessContextFn>(
|
||||
GetProcAddress(user32, "SetProcessDpiAwarenessContext"));
|
||||
if (setProcessDpiAwarenessContext != nullptr) {
|
||||
if (setProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)) {
|
||||
return;
|
||||
}
|
||||
if (GetLastError() == ERROR_ACCESS_DENIED) {
|
||||
return;
|
||||
}
|
||||
if (setProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE)) {
|
||||
return;
|
||||
}
|
||||
if (GetLastError() == ERROR_ACCESS_DENIED) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const HMODULE shcore = LoadLibraryW(L"shcore.dll");
|
||||
if (shcore != nullptr) {
|
||||
using SetProcessDpiAwarenessFn = HRESULT(WINAPI*)(PROCESS_DPI_AWARENESS);
|
||||
const auto setProcessDpiAwareness =
|
||||
reinterpret_cast<SetProcessDpiAwarenessFn>(GetProcAddress(shcore, "SetProcessDpiAwareness"));
|
||||
if (setProcessDpiAwareness != nullptr) {
|
||||
const HRESULT hr = setProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
|
||||
FreeLibrary(shcore);
|
||||
if (SUCCEEDED(hr) || hr == E_ACCESSDENIED) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
FreeLibrary(shcore);
|
||||
}
|
||||
}
|
||||
|
||||
if (user32 != nullptr) {
|
||||
using SetProcessDPIAwareFn = BOOL(WINAPI*)();
|
||||
const auto setProcessDPIAware =
|
||||
reinterpret_cast<SetProcessDPIAwareFn>(GetProcAddress(user32, "SetProcessDPIAware"));
|
||||
if (setProcessDPIAware != nullptr) {
|
||||
setProcessDPIAware();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Application::Application()
|
||||
: m_editorContext(std::make_unique<App::EditorContext>()) {}
|
||||
|
||||
Application::~Application() = default;
|
||||
|
||||
int RunXCUIEditorApp(HINSTANCE hInstance, int nCmdShow) {
|
||||
Application application;
|
||||
return application.Run(hInstance, nCmdShow);
|
||||
}
|
||||
|
||||
} // namespace XCEngine::UI::Editor
|
||||
|
||||
#ifndef XCUIEDITOR_REPO_ROOT
|
||||
#define XCUIEDITOR_REPO_ROOT "."
|
||||
#endif
|
||||
|
||||
namespace XCEngine::UI::Editor {
|
||||
|
||||
using App::GetExecutableDirectory;
|
||||
|
||||
bool Application::Initialize(HINSTANCE hInstance, int nCmdShow) {
|
||||
m_hInstance = hInstance;
|
||||
m_repoRoot = ResolveRepoRootPath();
|
||||
EnableDpiAwareness();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
m_systemInteractionHost = std::make_unique<Host::Win32SystemInteractionHost>();
|
||||
m_editorContext->SetSystemInteractionHost(m_systemInteractionHost.get());
|
||||
|
||||
App::EditorWindowHostConfig hostConfig = {};
|
||||
hostConfig.hInstance = m_hInstance;
|
||||
hostConfig.windowClassName = kWindowClassName;
|
||||
hostConfig.windowStyle = kBorderlessWindowStyle;
|
||||
hostConfig.primaryWindowTitle = kWindowTitle;
|
||||
hostConfig.windowUserData = this;
|
||||
m_windowManager = std::make_unique<App::EditorWindowManager>(
|
||||
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 =
|
||||
App::IsEnvironmentFlagEnabled("XCUI_AUTO_CAPTURE_ON_STARTUP");
|
||||
if (m_windowManager->CreateEditorWindow(
|
||||
App::CreateEditorWorkspaceWindowContentController(
|
||||
m_editorContext->BuildWorkspaceController()),
|
||||
createParams) == nullptr) {
|
||||
AppendUIEditorRuntimeTrace("app", "primary window creation failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
AppendUIEditorRuntimeTrace("app", "initialize end");
|
||||
return true;
|
||||
}
|
||||
|
||||
void Application::Shutdown() {
|
||||
AppendUIEditorRuntimeTrace("app", "shutdown begin");
|
||||
|
||||
if (m_windowManager != nullptr) {
|
||||
m_windowManager->Shutdown();
|
||||
m_windowManager.reset();
|
||||
}
|
||||
|
||||
if (m_editorContext != nullptr) {
|
||||
m_editorContext->SetSystemInteractionHost(nullptr);
|
||||
}
|
||||
m_systemInteractionHost.reset();
|
||||
|
||||
::XCEngine::Resources::ResourceManager::Get().Shutdown();
|
||||
|
||||
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,
|
||||
exceptionInfo);
|
||||
} else {
|
||||
AppendUIEditorCrashTrace(0u, nullptr);
|
||||
}
|
||||
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
}
|
||||
|
||||
} // namespace XCEngine::UI::Editor
|
||||
|
||||
namespace XCEngine::UI::Editor {
|
||||
|
||||
int Application::Run(HINSTANCE hInstance, int nCmdShow) {
|
||||
if (!Initialize(hInstance, nCmdShow)) {
|
||||
Shutdown();
|
||||
return 1;
|
||||
}
|
||||
|
||||
constexpr int kMaxMessagesPerTick = 64;
|
||||
MSG message = {};
|
||||
while (true) {
|
||||
int processedMessageCount = 0;
|
||||
while (processedMessageCount < kMaxMessagesPerTick &&
|
||||
PeekMessageW(&message, nullptr, 0U, 0U, PM_REMOVE)) {
|
||||
if (message.message == WM_QUIT) {
|
||||
Shutdown();
|
||||
return static_cast<int>(message.wParam);
|
||||
}
|
||||
|
||||
TranslateMessage(&message);
|
||||
DispatchMessageW(&message);
|
||||
++processedMessageCount;
|
||||
}
|
||||
|
||||
if (m_windowManager != nullptr) {
|
||||
::XCEngine::Resources::ResourceManager::Get().UpdateAsyncLoads();
|
||||
m_windowManager->DestroyClosedWindows();
|
||||
if (!m_windowManager->HasWindows()) {
|
||||
break;
|
||||
}
|
||||
|
||||
m_windowManager->RenderAllWindows();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Shutdown();
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace XCEngine::UI::Editor
|
||||
|
||||
namespace XCEngine::UI::Editor {
|
||||
|
||||
namespace {
|
||||
|
||||
void TryEnableNonClientDpiScaling(HWND hwnd) {
|
||||
if (hwnd == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const HMODULE user32 = GetModuleHandleW(L"user32.dll");
|
||||
if (user32 == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
using EnableNonClientDpiScalingFn = BOOL(WINAPI*)(HWND);
|
||||
const auto enableNonClientDpiScaling =
|
||||
reinterpret_cast<EnableNonClientDpiScalingFn>(
|
||||
GetProcAddress(user32, "EnableNonClientDpiScaling"));
|
||||
if (enableNonClientDpiScaling != nullptr) {
|
||||
enableNonClientDpiScaling(hwnd);
|
||||
}
|
||||
}
|
||||
|
||||
Application* GetApplicationFromWindowUserData(HWND hwnd) {
|
||||
return reinterpret_cast<Application*>(GetWindowLongPtrW(hwnd, GWLP_USERDATA));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool Application::RegisterWindowClass() {
|
||||
WNDCLASSEXW windowClass = {};
|
||||
windowClass.cbSize = sizeof(windowClass);
|
||||
windowClass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
|
||||
windowClass.lpfnWndProc = &Application::WndProc;
|
||||
windowClass.cbClsExtra = 0;
|
||||
windowClass.cbWndExtra = 0;
|
||||
windowClass.hInstance = m_hInstance;
|
||||
windowClass.hCursor = LoadCursorW(nullptr, IDC_ARROW);
|
||||
windowClass.hbrBackground = nullptr;
|
||||
windowClass.lpszMenuName = nullptr;
|
||||
windowClass.hIcon = static_cast<HICON>(
|
||||
LoadImageW(
|
||||
m_hInstance,
|
||||
MAKEINTRESOURCEW(IDI_APP_ICON),
|
||||
IMAGE_ICON,
|
||||
0,
|
||||
0,
|
||||
LR_DEFAULTSIZE));
|
||||
windowClass.hIconSm = static_cast<HICON>(
|
||||
LoadImageW(
|
||||
m_hInstance,
|
||||
MAKEINTRESOURCEW(IDI_APP_ICON_SMALL),
|
||||
IMAGE_ICON,
|
||||
GetSystemMetrics(SM_CXSMICON),
|
||||
GetSystemMetrics(SM_CYSMICON),
|
||||
LR_DEFAULTCOLOR));
|
||||
windowClass.lpszClassName = kWindowClassName;
|
||||
m_windowClassAtom = RegisterClassExW(&windowClass);
|
||||
if (m_windowClassAtom == 0) {
|
||||
AppendUIEditorRuntimeTrace("app", "window class registration failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
LRESULT CALLBACK Application::WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
|
||||
if (message == WM_NCCREATE) {
|
||||
TryEnableNonClientDpiScaling(hwnd);
|
||||
const auto* createStruct = reinterpret_cast<const CREATESTRUCTW*>(lParam);
|
||||
Application* application =
|
||||
createStruct != nullptr
|
||||
? reinterpret_cast<Application*>(createStruct->lpCreateParams)
|
||||
: nullptr;
|
||||
SetWindowLongPtrW(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(application));
|
||||
if (application != nullptr && application->m_windowManager != nullptr) {
|
||||
application->m_windowManager->HandlePendingNativeWindowCreated(hwnd);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
Application* application = GetApplicationFromWindowUserData(hwnd);
|
||||
LRESULT dispatcherResult = 0;
|
||||
if (application != nullptr &&
|
||||
application->m_windowManager != nullptr &&
|
||||
application->m_windowManager->TryDispatchWindowMessage(
|
||||
hwnd,
|
||||
message,
|
||||
wParam,
|
||||
lParam,
|
||||
dispatcherResult)) {
|
||||
return dispatcherResult;
|
||||
}
|
||||
|
||||
return DefWindowProcW(hwnd, message, wParam, lParam);
|
||||
}
|
||||
|
||||
} // namespace XCEngine::UI::Editor
|
||||
|
||||
Reference in New Issue
Block a user