#include "pch.h" #include "platform_windows/windows_runtime_shell.h" #include "app.h" #include "log.h" #include "platform_windows/windows_bootstrap_helpers.h" #include "platform_windows/windows_lifecycle_shell.h" #include "platform_windows/windows_main_window_session.h" #include "platform_windows/windows_platform_services.h" #include "platform_windows/windows_stylus_input.h" #include "platform_windows/windows_window_shell.h" #include "wacom.h" #include "../resource.h" namespace pp::platform::windows { namespace { struct RetainedWindowsRuntimeState final { std::unique_ptr owned_app; WacomTablet tablet; }; [[nodiscard]] RetainedWindowsRuntimeState& retained_runtime_state() { static RetainedWindowsRuntimeState state; return state; } void register_touch_window(HWND hWnd) { // link: https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-registertouchwindow if (RegisterTouchWindow(hWnd, 0) == 0) { LOG("RegisterTouchWindow error: %s", GetLastErrorAsString().c_str()); } } void initialize_runtime_threads() { auto* app = bound_app(); mark_lifecycle_running(); app->runtime().render_thread_start(*app); app->runtime().ui_thread_start(*app); } void install_debug_gl_callbacks() { #ifdef _DEBUG glad_set_pre_callback(_pre_call_callback); glad_set_post_callback(_post_call_callback); #endif } void initialize_wintab(HWND hWnd, bool sandboxed) { auto* tablet = bound_wacom_tablet(); if (!sandboxed) { LOG("init WinTab"); tablet->init(hWnd); } else { LOG("SKIP init WinTab"); } } void set_main_window_icon(HWND hWnd) { LOG("change icon"); SendMessage( hWnd, WM_SETICON, ICON_SMALL, (LPARAM)LoadIcon(GetModuleHandle(0), MAKEINTRESOURCE(IDI_ICON1))); } void restore_window_placement(HWND hWnd, int show_command) { WINDOWPLACEMENT wp; GetWindowPlacement(hWnd, &wp); wp.showCmd = show_command; SetWindowPlacement(hWnd, &wp); } void run_main_message_loop() { MSG msg; auto* app = bound_app(); LOG("start main loop"); while (lifecycle_is_running()) { auto present = app->animate || app->redraw ? PeekMessage(&msg, 0, 0, 0, PM_REMOVE) : GetMessage(&msg, 0, 0, 0); if (msg.message == WM_QUIT) mark_lifecycle_stopped(); if (present) { DispatchMessage(&msg); TranslateMessage(&msg); } drain_main_thread_tasks(); } } void shutdown_main_window_runtime(const MainWindowStartupState& startup, HINSTANCE hInst) { retained_runtime_state().tablet.terminate(); UnregisterClass(startup.window_class.lpszClassName, hInst); LogRemote::I.stop(); } } void bind_app(App* app) noexcept { App::I = app; } App* bound_app() noexcept { return App::I; } void release_bound_app() noexcept { bind_app(nullptr); retained_runtime_state().owned_app.reset(); } WacomTablet* bound_wacom_tablet() noexcept { return &retained_runtime_state().tablet; } int run_main_application(int argc, char** argv) { const auto instance = GetModuleHandle(NULL); auto& runtime_state = retained_runtime_state(); runtime_state.owned_app = std::make_unique(); auto* app = runtime_state.owned_app.get(); bind_app(app); app->set_platform_services(&pp::platform::windows::platform_services()); app->initLog(); pp::platform::windows::init_shcore_API(); pp::platform::windows::initialize_stylus_input(); if (pp::platform::windows::SetProcessDpiAwareness_fn) pp::platform::windows::SetProcessDpiAwareness_fn(PROCESS_PER_MONITOR_DPI_AWARE); pp::platform::windows::ensure_runtime_data_directory(); pp::platform::windows::SplashScreen splash(instance); pp::platform::windows::initialize_retained_input_state(); pp::platform::windows::setup_exception_handler(*app); pp::platform::windows::read_WMI_info(); app->create(); auto startup = pp::platform::windows::initialize_main_window_startup_state(*app); auto context = pp::platform::windows::OpenGlWindowContext {}; switch (pp::platform::windows::initialize_main_window_and_gl( startup, retained_main_window_handle_ref(), instance, retained_main_window_title_buffer(), context)) { case pp::platform::windows::MainStartupResult::Ok: break; case pp::platform::windows::MainStartupResult::GladLoadFailure: release_bound_app(); return 0; case pp::platform::windows::MainStartupResult::MissingCoreContextSupport: release_bound_app(); return -1; } //wglSwapIntervalEXT(1); bool start_in_vr = false; if (argc > 1) { switch (const_hash(argv[1])) { case const_hash("convert"): app->initShaders(); app->cmd_convert(argv[2], argv[3]); release_bound_app(); return 0; case const_hash("-vrmode"): start_in_vr = true; break; default: break; } } pp::platform::windows::run_main_window_runtime(startup, start_in_vr, instance, splash); release_bound_app(); return 0; } void run_main_window_runtime(const MainWindowStartupState& startup, bool start_in_vr, HINSTANCE instance, SplashScreen& splash) { auto* app = bound_app(); register_touch_window(retained_main_window_handle_ref()); wglMakeCurrent(NULL, NULL); initialize_runtime_threads(); install_debug_gl_callbacks(); initialize_wintab(retained_main_window_handle_ref(), retained_main_window_sandboxed()); set_main_window_icon(retained_main_window_handle_ref()); app->ui_sync(); restore_window_placement(retained_main_window_handle_ref(), startup.show_command); if (start_in_vr) app->vr_start(); LOG("show main window"); SetForegroundWindow(retained_main_window_handle_ref()); splash.dismiss(); run_main_message_loop(); shutdown_main_window_runtime(startup, instance); } }