diff --git a/docs/modernization/roadmap.md b/docs/modernization/roadmap.md index f28e21b5..d7779198 100644 --- a/docs/modernization/roadmap.md +++ b/docs/modernization/roadmap.md @@ -70,6 +70,15 @@ What is already real: - `pp_app_core` Latest slice: +- `src/platform_windows/windows_runtime_shell.cpp` now explicitly owns the + Windows `App` lifetime through a retained `std::unique_ptr` instead of + raw `new`/`delete` plus shutdown-side manual cleanup in the lifecycle shell. +- `src/platform_windows/windows_lifecycle_shell.cpp` now releases the bound + Windows app through `release_bound_app()` after runtime shutdown instead of + deleting it directly through the global shutdown path. +- `src/platform_windows/windows_window_shell.cpp` now routes the touched + key-map and VR-state reads through narrow helpers instead of keeping the + broader retained-state bundle live across the main window-proc body. - `scripts/automation/quiet-validate.ps1` is now the bundled checkpoint path for Windows build/test plus optional platform and Apple remote validation, with one compact JSON summary under `out/logs/quiet-validation`. diff --git a/docs/modernization/tasks.md b/docs/modernization/tasks.md index 28302b4c..c68808b0 100644 --- a/docs/modernization/tasks.md +++ b/docs/modernization/tasks.md @@ -78,6 +78,15 @@ Completed, blocked, and superseded task history moved to the queue is now ordered by code movement instead. Current slice: +- `src/platform_windows/windows_runtime_shell.cpp` now owns the Windows `App` + through a retained `std::unique_ptr`, so startup/early-return/convert + paths no longer manage raw `new`/`delete` app lifetime manually. +- `src/platform_windows/windows_lifecycle_shell.cpp` now releases the bound + app through `release_bound_app()` after runtime shutdown instead of deleting + it directly through the lifecycle shell. +- `src/platform_windows/windows_window_shell.cpp` now routes the touched + key-map synchronization and VR close state through narrow helpers instead of + carrying the broader retained window bundle live across the window-proc path. - `scripts/automation/quiet-validate.ps1` now owns the recommended quiet checkpoint path and can bundle Windows build/test, Android/platform sweeps, and Apple remote compile gates into one compact JSON summary with diff --git a/src/platform_windows/windows_lifecycle_shell.cpp b/src/platform_windows/windows_lifecycle_shell.cpp index e9b558cf..64717543 100644 --- a/src/platform_windows/windows_lifecycle_shell.cpp +++ b/src/platform_windows/windows_lifecycle_shell.cpp @@ -53,9 +53,7 @@ void handle_window_close_message(VrShellState& vr) runtime->ui_thread_stop(); runtime->render_thread_stop(); app->terminate(); - bind_app(nullptr); - bind_runtime(nullptr); - delete app; + release_bound_app(); PostQuitMessage(0); } diff --git a/src/platform_windows/windows_runtime_shell.cpp b/src/platform_windows/windows_runtime_shell.cpp index 63769ed6..630732dc 100644 --- a/src/platform_windows/windows_runtime_shell.cpp +++ b/src/platform_windows/windows_runtime_shell.cpp @@ -18,6 +18,7 @@ namespace pp::platform::windows { namespace { struct RetainedWindowsRuntimeState final { + std::unique_ptr owned_app; App* app = nullptr; AppRuntime* runtime = nullptr; WacomTablet* tablet = nullptr; @@ -132,6 +133,14 @@ App* bound_app() noexcept return retained_runtime_state().app; } +void release_bound_app() noexcept +{ + auto& state = retained_runtime_state(); + bind_runtime(nullptr); + bind_app(nullptr); + state.owned_app.reset(); +} + void bind_runtime(AppRuntime* runtime) noexcept { retained_runtime_state().runtime = runtime; @@ -177,7 +186,9 @@ int run_main_application(int argc, char** argv) auto& state = retained_state(); state.hInst = GetModuleHandle(NULL); - auto* app = new App(); + auto& runtime_state = retained_runtime_state(); + runtime_state.owned_app = std::make_unique(); + auto* app = runtime_state.owned_app.get(); bind_app(app); bind_runtime(&app->runtime()); bind_wacom_tablet(&WacomTablet::I); @@ -209,13 +220,11 @@ int run_main_application(int argc, char** argv) case pp::platform::windows::MainStartupResult::Ok: break; case pp::platform::windows::MainStartupResult::GladLoadFailure: - bind_app(nullptr); - bind_runtime(nullptr); + release_bound_app(); bind_wacom_tablet(nullptr); return 0; case pp::platform::windows::MainStartupResult::MissingCoreContextSupport: - bind_app(nullptr); - bind_runtime(nullptr); + release_bound_app(); bind_wacom_tablet(nullptr); return -1; } @@ -230,8 +239,7 @@ int run_main_application(int argc, char** argv) case const_hash("convert"): app->initShaders(); app->cmd_convert(argv[2], argv[3]); - bind_app(nullptr); - bind_runtime(nullptr); + release_bound_app(); bind_wacom_tablet(nullptr); return 0; case const_hash("-vrmode"): @@ -243,6 +251,7 @@ int run_main_application(int argc, char** argv) } pp::platform::windows::run_main_window_runtime(startup, start_in_vr, splash); + release_bound_app(); bind_wacom_tablet(nullptr); return 0; } diff --git a/src/platform_windows/windows_runtime_shell.h b/src/platform_windows/windows_runtime_shell.h index ba02858b..e2e5c357 100644 --- a/src/platform_windows/windows_runtime_shell.h +++ b/src/platform_windows/windows_runtime_shell.h @@ -13,6 +13,7 @@ int run_main_application(int argc, char** argv); void run_main_window_runtime(const MainWindowStartupState& startup, bool start_in_vr, SplashScreen& splash); void bind_app(App* app) noexcept; [[nodiscard]] App* bound_app() noexcept; +void release_bound_app() noexcept; void bind_runtime(AppRuntime* runtime) noexcept; [[nodiscard]] AppRuntime* bound_runtime() noexcept; void bind_wacom_tablet(WacomTablet* tablet) noexcept; diff --git a/src/platform_windows/windows_window_shell.cpp b/src/platform_windows/windows_window_shell.cpp index 1018145c..ef6e3a4c 100644 --- a/src/platform_windows/windows_window_shell.cpp +++ b/src/platform_windows/windows_window_shell.cpp @@ -34,11 +34,42 @@ namespace { return pointer_source; } +[[nodiscard]] std::map& retained_virtual_key_map() +{ + return retained_state().vkey_map; +} + +[[nodiscard]] pp::platform::windows::VrShellState& retained_vr_shell_state() +{ + return retained_state().vr; +} + +void synchronize_app_key_state_from_keyboard(App& app) +{ + static BYTE keys[256]; + if (!GetKeyboardState(keys)) + return; + + const bool alt = (keys[VK_MENU] & 0x80) != 0; + for (const auto& [key, vk] : retained_virtual_key_map()) + { + if (alt && key == kKey::KeyTab) + continue; + + const bool down = (keys[vk] & 0x80) != 0; + if (app.keys[(int)key] && !down) + app.key_up(key); + else if (!app.keys[(int)key] && down) + app.key_down(key); + } +} + } void initialize_retained_input_state() { auto& state = retained_state(); + auto& vkey_map = retained_virtual_key_map(); memset(&state.keys, 0, sizeof(state.keys)); for (int k = 1; k < 256; ++k) // ignore kKey::Unknown = 0 { @@ -48,14 +79,14 @@ void initialize_retained_input_state() continue; auto key = (kKey)k; - if (state.vkey_map.find(key) == state.vkey_map.end()) + if (vkey_map.find(key) == vkey_map.end()) { - state.vkey_map.insert({ key, vk }); + vkey_map.insert({ key, vk }); } else { LOG("KEY MAP COLLISION %d and %d maps to %d", - vk, state.vkey_map[key], k); + vk, vkey_map[key], k); } } } @@ -69,7 +100,6 @@ RetainedState& retained_state() LRESULT CALLBACK main_window_proc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { - auto& state = retained_state(); auto* app = bound_app(); static glm::vec2 lastPoint; @@ -78,7 +108,7 @@ LRESULT CALLBACK main_window_proc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) switch (msg) { case kUserCloseMessage: - handle_window_close_message(state.vr); + handle_window_close_message(retained_vr_shell_state()); return 0; case WM_PAINT: app->redraw = true; @@ -114,24 +144,10 @@ LRESULT CALLBACK main_window_proc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) case WM_ACTIVATE: { platform_services().set_cursor_visible(true); - app->ui_task_async([app, &state, wp, lp] { + app->ui_task_async([app, wp, lp] { int active = GET_WM_ACTIVATE_STATE(wp, lp); active_wacom_tablet().set_focus(active); - static BYTE keys[256]; - if (GetKeyboardState(keys)) - { - bool alt = keys[VK_MENU] & 0x80; - for (auto k : state.vkey_map) - { - if (alt && k.first == kKey::KeyTab) - continue; - bool down = keys[k.second] & 0x80; - if (app->keys[(int)k.first] && !down) - app->key_up(k.first); - else if (!app->keys[(int)k.first] && down) - app->key_down(k.first); - } - } + synchronize_app_key_state_from_keyboard(*app); }); break; }