From d135835787254aa5b9c05de920355945cbbee6e4 Mon Sep 17 00:00:00 2001 From: omigamedev Date: Tue, 16 Jun 2026 11:10:31 +0200 Subject: [PATCH] Extract file menu binding and Win32 splash helper --- cmake/PanoPainterSources.cmake | 3 + docs/modernization/roadmap.md | 13 +- docs/modernization/tasks.md | 9 +- src/app_layout.cpp | 146 +----------------- src/app_layout_file_menu.cpp | 190 ++++++++++++++++++++++++ src/main.cpp | 86 +---------- src/platform_windows/windows_splash.cpp | 124 ++++++++++++++++ src/platform_windows/windows_splash.h | 30 ++++ 8 files changed, 369 insertions(+), 232 deletions(-) create mode 100644 src/app_layout_file_menu.cpp create mode 100644 src/platform_windows/windows_splash.cpp create mode 100644 src/platform_windows/windows_splash.h diff --git a/cmake/PanoPainterSources.cmake b/cmake/PanoPainterSources.cmake index 1ea4e7e7..e81bbe1c 100644 --- a/cmake/PanoPainterSources.cmake +++ b/cmake/PanoPainterSources.cmake @@ -94,6 +94,7 @@ set(PP_PANOPAINTER_APP_SOURCES src/app_dialogs.cpp src/app_events.cpp src/app_layout.cpp + src/app_layout_file_menu.cpp src/app_layout_tools_menu.cpp src/app_shaders.cpp src/app_vr.cpp @@ -161,6 +162,8 @@ set(PP_WINDOWS_PLATFORM_SOURCES src/main.cpp src/platform_windows/windows_platform_services.cpp src/platform_windows/windows_platform_services.h + src/platform_windows/windows_splash.cpp + src/platform_windows/windows_splash.h ) set(PP_WINDOWS_APP_SOURCES diff --git a/docs/modernization/roadmap.md b/docs/modernization/roadmap.md index 7f008d54..57c9c088 100644 --- a/docs/modernization/roadmap.md +++ b/docs/modernization/roadmap.md @@ -80,10 +80,10 @@ What is still carrying too much live ownership: Current hotspot files: - `src/canvas.cpp`: 2645 lines -- `src/app_layout.cpp`: 1498 lines +- `src/app_layout.cpp`: 1360 lines - `src/canvas_modes.cpp`: 1798 lines - `src/node.cpp`: 1551 lines -- `src/main.cpp`: 1374 lines +- `src/main.cpp`: 1181 lines - `src/node_panel_brush.cpp`: 1197 lines - `src/node_stroke_preview.cpp`: 933 lines - `src/node_canvas.cpp`: 888 lines @@ -127,7 +127,9 @@ Current architecture mismatches that must be treated as real blockers: rather than thin composition/binding surfaces, even though tools-menu binding plus nested panels/options submenu wiring now live in `src/app_layout_tools_menu.cpp` and `App::init_menu_tools()` is now a thin - call-through. + call-through, while file-menu binding plus the export submenu wiring now also + live in `src/app_layout_file_menu.cpp` and `App::init_menu_file()` is now a + thin call-through. - `App`, `Canvas`, `Node`, retained workers, and platform entrypoints still use global singleton reach, raw observer pointers, retained static worker ownership in several app families, and ad hoc mutex/condition-variable @@ -149,7 +151,10 @@ Current architecture mismatches that must be treated as real blockers: local worker-state helper instead of a bare static accessor, the prepared-file worker and the canvas async import/export/save/open worker now live under `AppRuntime` instead of retained static app-events/canvas - workers, `App::rec_loop()` now delegates worker-iteration orchestration into + workers, and the splash-screen dialog loop, HWND ownership, and bitmap setup + now live in `src/platform_windows/windows_splash.cpp` instead of + `src/main.cpp`, + while `App::rec_loop()` now delegates worker-iteration orchestration into the retained recording bridge, `App::update_rec_frames()` now delegates recording label refresh through that same retained recording path, and the canvas state-management cluster for picking, clear/clear-all, layer diff --git a/docs/modernization/tasks.md b/docs/modernization/tasks.md index e4a3d56a..227f8818 100644 --- a/docs/modernization/tasks.md +++ b/docs/modernization/tasks.md @@ -271,7 +271,7 @@ targets look like helpers under one old monolith. Status: In Progress Why now: -`src/app_layout.cpp` is still a 1498-line mixed file that builds menus, +`src/app_layout.cpp` is still a 1360-line mixed file that builds menus, attaches callbacks, computes planner inputs, and mutates UI state directly. Current slice: @@ -279,6 +279,10 @@ Current slice: now lives in `src/app_layout_tools_menu.cpp`, and `App::init_menu_tools()` is now a thin call-through, but file/about/layer/sidebar and broader layout composition are still inline in `src/app_layout.cpp`. +- File-menu UI binding, including the export submenu wiring, now also lives in + `src/app_layout_file_menu.cpp`, and `App::init_menu_file()` is now a thin + call-through, but about/layer/sidebar and broader layout composition are + still inline in `src/app_layout.cpp`. Write scope: - `src/app_layout.cpp` @@ -392,6 +396,9 @@ Current slice: - `main.cpp` Win32 window handles, GL task/mutex state, splash-dialog state, stylus timers, and VR worker state now sit behind one retained local state object instead of separate file-scope globals +- the splash-screen dialog loop, HWND ownership, and bitmap setup now also live + in `src/platform_windows/windows_splash.cpp` instead of `src/main.cpp`, and + `main.cpp` now just owns the helper lifecycle - Win32 async GL/context lock state now lives in `src/platform_windows/windows_platform_services.cpp` instead of `main.cpp` retained state, and `main.cpp` only seeds that platform-owned context handle diff --git a/src/app_layout.cpp b/src/app_layout.cpp index 0f220496..0dfee304 100644 --- a/src/app_layout.cpp +++ b/src/app_layout.cpp @@ -13,8 +13,6 @@ #include "app_core/canvas_tool_ui.h" #include "app_core/document_layer.h" #include "app_core/document_canvas.h" -#include "app_core/document_export.h" -#include "app_core/file_menu.h" #include "app_core/app_status.h" #include "app_core/main_toolbar.h" #include "app_core/tools_menu.h" @@ -34,6 +32,7 @@ #include namespace pp::panopainter { +void bind_legacy_file_menu(App& app); void bind_legacy_tools_menu(App& app); } @@ -54,16 +53,6 @@ bool apply_brush_preset_plan(App& app, const std::shared_ptr& brush) return pp::panopainter::apply_legacy_brush_preset_plan(app, brush); } -bool apply_document_export_menu_plan(App& app, pp::app::DocumentExportMenuKind kind) -{ - return pp::panopainter::apply_legacy_document_export_menu_plan(app, kind); -} - -void apply_file_menu_plan(App& app, pp::app::FileMenuCommand command) -{ - pp::panopainter::apply_legacy_file_menu_command(app, command); -} - std::shared_ptr add_menu_popup( App& app, const char* template_id, @@ -752,138 +741,7 @@ void App::init_toolbar_draw() void App::init_menu_file() { - if (auto* menu_file = layout[main_id]->find("menu-file")) - { - menu_file->on_click = [this, menu_file](Node*) { - auto* popup_root = layout[main_id]; - if (!popup_root) { - return; - } - const auto open_checked_menu_popup = [this, popup_root](const char* id, glm::vec2 pos, float width) - -> std::pair, pp::ui::NodeHandle> - { - auto popup = add_menu_popup(*this, id, pos, width); - if (!popup) { - return {}; - } - pp::panopainter::detach_legacy_node_from_parent(*popup); - auto popup_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_root, popup); - if (!popup_overlay) { - pp::panopainter::destroy_legacy_node(*popup); - return {}; - } - return { popup, popup_overlay.value() }; - }; - - glm::vec2 pos = menu_file->m_pos + glm::vec2(0, menu_file->m_size.y); - const auto [popup, popup_handle] = open_checked_menu_popup("file-menu", pos, menu_file->m_size.x); - if (!popup) - return; - - if (auto b = popup->find("file-newdoc")) - b->on_click = [this, popup_root, popup_handle](Node*) { - apply_file_menu_plan(*this, pp::app::FileMenuCommand::new_document); - close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); - }; - if (auto b = popup->find("file-import")) - b->on_click = [this, popup_root, popup_handle](Node*) { - apply_file_menu_plan(*this, pp::app::FileMenuCommand::import_image); - close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); - }; - if (auto b = popup->find("file-open")) - b->on_click = [this, popup_root, popup_handle](Node*) { - apply_file_menu_plan(*this, pp::app::FileMenuCommand::open_project); - close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); - }; - if (auto b = popup->find("file-browse")) - b->on_click = [this, popup_root, popup_handle](Node*) { - apply_file_menu_plan(*this, pp::app::FileMenuCommand::browse_cloud); - close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); - }; - if (auto b = popup->find("file-save")) - b->on_click = [this, popup_root, popup_handle](Node*) { - apply_file_menu_plan(*this, pp::app::FileMenuCommand::save); - close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); - }; - if (auto b = popup->find("file-save-as")) - b->on_click = [this, popup_root, popup_handle](Node*) { - apply_file_menu_plan(*this, pp::app::FileMenuCommand::save_as); - close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); - }; - if (auto b = popup->find("file-save-ver")) - b->on_click = [this, popup_root, popup_handle](Node*) { - apply_file_menu_plan(*this, pp::app::FileMenuCommand::save_version); - close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); - }; - if (auto b = popup->find("file-export")) - b->on_click = [this, popup_root, popup_handle](Node*) { - apply_file_menu_plan(*this, pp::app::FileMenuCommand::export_jpeg); - close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); - }; - if (auto b = popup->find("file-export-tick")) - b->on_click = [this, b, popup_root, popup_handle, open_checked_menu_popup](Node*) { - glm::vec2 pos = b->m_pos + glm::vec2(b->m_size.x, 0); - const auto [subpopup, subpopup_handle] = open_checked_menu_popup("file-submenu-export", pos, b->m_size.x); - if (!subpopup) - return; - subpopup->find("file-submenu-export-png")->on_click = [this, popup_root, popup_handle, subpopup_handle](Node*) { - apply_document_export_menu_plan(*this, pp::app::DocumentExportMenuKind::png); - close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); - close_legacy_overlay_handle_ignoring_status(*popup_root, subpopup_handle); - }; - subpopup->find("file-submenu-export-layers")->on_click = [this, popup_root, popup_handle, subpopup_handle](Node*) { - apply_document_export_menu_plan(*this, pp::app::DocumentExportMenuKind::layers); - close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); - close_legacy_overlay_handle_ignoring_status(*popup_root, subpopup_handle); - }; - subpopup->find("file-submenu-export-cube")->on_click = [this, popup_root, popup_handle, subpopup_handle](Node*) { - apply_document_export_menu_plan(*this, pp::app::DocumentExportMenuKind::cube_faces); - close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); - close_legacy_overlay_handle_ignoring_status(*popup_root, subpopup_handle); - }; - subpopup->find("file-submenu-export-depth")->on_click = [this, popup_root, popup_handle, subpopup_handle](Node*) { - apply_document_export_menu_plan(*this, pp::app::DocumentExportMenuKind::depth); - close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); - close_legacy_overlay_handle_ignoring_status(*popup_root, subpopup_handle); - }; - subpopup->find("file-submenu-export-anim")->on_click = [this, popup_root, popup_handle, subpopup_handle](Node*) { - apply_document_export_menu_plan(*this, pp::app::DocumentExportMenuKind::animation_frames); - close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); - close_legacy_overlay_handle_ignoring_status(*popup_root, subpopup_handle); - }; - subpopup->find("file-submenu-export-anim-mp4")->on_click = [this, popup_root, popup_handle, subpopup_handle](Node*) { - apply_document_export_menu_plan(*this, pp::app::DocumentExportMenuKind::animation_mp4); - close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); - close_legacy_overlay_handle_ignoring_status(*popup_root, subpopup_handle); - }; - subpopup->find("file-submenu-export-timelapse")->on_click = [this, popup_root, popup_handle, subpopup_handle](Node*) { - apply_document_export_menu_plan(*this, pp::app::DocumentExportMenuKind::timelapse); - close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); - close_legacy_overlay_handle_ignoring_status(*popup_root, subpopup_handle); - }; - }; - if (auto b = popup->find("file-share")) - b->on_click = [this, popup_root, popup_handle](Node*) { - apply_file_menu_plan(*this, pp::app::FileMenuCommand::share); - close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); - }; - if (auto b = popup->find("file-resize")) - b->on_click = [this, popup_root, popup_handle](Node*) { - apply_file_menu_plan(*this, pp::app::FileMenuCommand::resize); - close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); - }; - if (auto b = popup->find("file-cloud-upload")) - b->on_click = [this, popup_root, popup_handle](Node*) { - apply_file_menu_plan(*this, pp::app::FileMenuCommand::cloud_upload); - close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); - }; - if (auto b = popup->find("file-cloud-browse")) - b->on_click = [this, popup_root, popup_handle](Node*) { - apply_file_menu_plan(*this, pp::app::FileMenuCommand::cloud_browse); - close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); - }; - }; - } + pp::panopainter::bind_legacy_file_menu(*this); } void App::init_menu_edit() diff --git a/src/app_layout_file_menu.cpp b/src/app_layout_file_menu.cpp new file mode 100644 index 00000000..30097b5c --- /dev/null +++ b/src/app_layout_file_menu.cpp @@ -0,0 +1,190 @@ +#include "pch.h" +#include "app.h" +#include "app_core/document_export.h" +#include "app_core/file_menu.h" +#include "legacy_app_shell_services.h" +#include "legacy_ui_overlay_services.h" +#include "node_button_custom.h" +#include "node_popup_menu.h" + +namespace { + +void apply_document_export_menu_plan(App& app, pp::app::DocumentExportMenuKind kind) +{ + (void)pp::panopainter::apply_legacy_document_export_menu_plan(app, kind); +} + +void apply_file_menu_plan(App& app, pp::app::FileMenuCommand command) +{ + pp::panopainter::apply_legacy_file_menu_command(app, command); +} + +std::shared_ptr add_menu_popup( + App& app, + const char* template_id, + glm::vec2 position, + float rtl_anchor_width) +{ + const auto popup = pp::panopainter::add_legacy_popup_menu( + app, + template_id, + position.x, + position.y, + rtl_anchor_width); + if (!popup) { + LOG("Popup menu '%s' failed: %s", template_id ? template_id : "", popup.status().message); + return nullptr; + } + return popup.value(); +} + +void close_legacy_overlay_handle_ignoring_status( + Node& anchor, + pp::ui::NodeHandle overlay) noexcept +{ + (void)pp::panopainter::close_legacy_overlay_node(anchor, overlay); +} + +} // namespace + +namespace pp::panopainter { + +void bind_legacy_file_menu(App& app) +{ + auto main = app.layout[app.main_id]; + + if (auto* menu_file = main->find("menu-file")) + { + menu_file->on_click = [&app, menu_file](Node*) { + auto* popup_root = app.layout[app.main_id]; + if (!popup_root) { + return; + } + const auto open_checked_menu_popup = [popup_root](App& app_ref, const char* id, glm::vec2 pos, float width) + -> std::pair, pp::ui::NodeHandle> + { + auto popup = add_menu_popup(app_ref, id, pos, width); + if (!popup) { + return {}; + } + pp::panopainter::detach_legacy_node_from_parent(*popup); + auto popup_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_root, popup); + if (!popup_overlay) { + pp::panopainter::destroy_legacy_node(*popup); + return {}; + } + return { popup, popup_overlay.value() }; + }; + + glm::vec2 pos = menu_file->m_pos + glm::vec2(0, menu_file->m_size.y); + const auto [popup, popup_handle] = open_checked_menu_popup(app, "file-menu", pos, menu_file->m_size.x); + if (!popup) + return; + + if (auto b = popup->find("file-newdoc")) + b->on_click = [&app, popup_root, popup_handle](Node*) { + apply_file_menu_plan(app, pp::app::FileMenuCommand::new_document); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); + }; + if (auto b = popup->find("file-import")) + b->on_click = [&app, popup_root, popup_handle](Node*) { + apply_file_menu_plan(app, pp::app::FileMenuCommand::import_image); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); + }; + if (auto b = popup->find("file-open")) + b->on_click = [&app, popup_root, popup_handle](Node*) { + apply_file_menu_plan(app, pp::app::FileMenuCommand::open_project); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); + }; + if (auto b = popup->find("file-browse")) + b->on_click = [&app, popup_root, popup_handle](Node*) { + apply_file_menu_plan(app, pp::app::FileMenuCommand::browse_cloud); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); + }; + if (auto b = popup->find("file-save")) + b->on_click = [&app, popup_root, popup_handle](Node*) { + apply_file_menu_plan(app, pp::app::FileMenuCommand::save); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); + }; + if (auto b = popup->find("file-save-as")) + b->on_click = [&app, popup_root, popup_handle](Node*) { + apply_file_menu_plan(app, pp::app::FileMenuCommand::save_as); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); + }; + if (auto b = popup->find("file-save-ver")) + b->on_click = [&app, popup_root, popup_handle](Node*) { + apply_file_menu_plan(app, pp::app::FileMenuCommand::save_version); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); + }; + if (auto b = popup->find("file-export")) + b->on_click = [&app, popup_root, popup_handle](Node*) { + apply_file_menu_plan(app, pp::app::FileMenuCommand::export_jpeg); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); + }; + if (auto b = popup->find("file-export-tick")) + b->on_click = [&app, b, popup_root, popup_handle, open_checked_menu_popup](Node*) { + glm::vec2 pos = b->m_pos + glm::vec2(b->m_size.x, 0); + const auto [subpopup, subpopup_handle] = open_checked_menu_popup(app, "file-submenu-export", pos, b->m_size.x); + if (!subpopup) + return; + subpopup->find("file-submenu-export-png")->on_click = [&app, popup_root, popup_handle, subpopup_handle](Node*) { + apply_document_export_menu_plan(app, pp::app::DocumentExportMenuKind::png); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); + close_legacy_overlay_handle_ignoring_status(*popup_root, subpopup_handle); + }; + subpopup->find("file-submenu-export-layers")->on_click = [&app, popup_root, popup_handle, subpopup_handle](Node*) { + apply_document_export_menu_plan(app, pp::app::DocumentExportMenuKind::layers); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); + close_legacy_overlay_handle_ignoring_status(*popup_root, subpopup_handle); + }; + subpopup->find("file-submenu-export-cube")->on_click = [&app, popup_root, popup_handle, subpopup_handle](Node*) { + apply_document_export_menu_plan(app, pp::app::DocumentExportMenuKind::cube_faces); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); + close_legacy_overlay_handle_ignoring_status(*popup_root, subpopup_handle); + }; + subpopup->find("file-submenu-export-depth")->on_click = [&app, popup_root, popup_handle, subpopup_handle](Node*) { + apply_document_export_menu_plan(app, pp::app::DocumentExportMenuKind::depth); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); + close_legacy_overlay_handle_ignoring_status(*popup_root, subpopup_handle); + }; + subpopup->find("file-submenu-export-anim")->on_click = [&app, popup_root, popup_handle, subpopup_handle](Node*) { + apply_document_export_menu_plan(app, pp::app::DocumentExportMenuKind::animation_frames); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); + close_legacy_overlay_handle_ignoring_status(*popup_root, subpopup_handle); + }; + subpopup->find("file-submenu-export-anim-mp4")->on_click = [&app, popup_root, popup_handle, subpopup_handle](Node*) { + apply_document_export_menu_plan(app, pp::app::DocumentExportMenuKind::animation_mp4); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); + close_legacy_overlay_handle_ignoring_status(*popup_root, subpopup_handle); + }; + subpopup->find("file-submenu-export-timelapse")->on_click = [&app, popup_root, popup_handle, subpopup_handle](Node*) { + apply_document_export_menu_plan(app, pp::app::DocumentExportMenuKind::timelapse); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); + close_legacy_overlay_handle_ignoring_status(*popup_root, subpopup_handle); + }; + }; + if (auto b = popup->find("file-share")) + b->on_click = [&app, popup_root, popup_handle](Node*) { + apply_file_menu_plan(app, pp::app::FileMenuCommand::share); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); + }; + if (auto b = popup->find("file-resize")) + b->on_click = [&app, popup_root, popup_handle](Node*) { + apply_file_menu_plan(app, pp::app::FileMenuCommand::resize); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); + }; + if (auto b = popup->find("file-cloud-upload")) + b->on_click = [&app, popup_root, popup_handle](Node*) { + apply_file_menu_plan(app, pp::app::FileMenuCommand::cloud_upload); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); + }; + if (auto b = popup->find("file-cloud-browse")) + b->on_click = [&app, popup_root, popup_handle](Node*) { + apply_file_menu_plan(app, pp::app::FileMenuCommand::cloud_browse); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); + }; + }; + } +} + +} // namespace pp::panopainter diff --git a/src/main.cpp b/src/main.cpp index 314821c2..faa11d17 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -12,6 +12,7 @@ #include "legacy_preference_storage.h" #include "renderer_gl/opengl_capabilities.h" #include "platform_windows/windows_platform_services.h" +#include "platform_windows/windows_splash.h" #include "../resource.h" #include @@ -61,7 +62,6 @@ struct RetainedState std::mutex hmd_render_mutex; std::condition_variable hmd_render_cv; std::unique_ptr vive; - HWND splash_dialog{}; }; RetainedState& retained_state() @@ -669,84 +669,6 @@ void win32_save_window_state() p.rcNormalPosition.right, p.rcNormalPosition.bottom }); } - -HBITMAP image_to_hbitmap(const Image& img) -{ - BITMAPINFOHEADER bmih; - memset(&bmih, 0, sizeof(BITMAPINFOHEADER)); - bmih.biWidth = img.width; - bmih.biHeight = img.height; - bmih.biBitCount = 32; - bmih.biCompression = BI_RGB; - bmih.biSize = sizeof(BITMAPINFOHEADER); - bmih.biPlanes = 1; - BITMAPINFO* bmi = (BITMAPINFO*)&bmih; - return CreateDIBitmap(GetDC(NULL), &bmih, CBM_INIT, img.data(), bmi, DIB_RGB_COLORS); -} - -LRESULT CALLBACK splash_proc(HWND hWndDlg, UINT Msg, WPARAM wParam, LPARAM lParam) -{ - switch (Msg) - { - case WM_INITDIALOG: - { - auto monitor = MonitorFromWindow(0, MONITOR_DEFAULTTOPRIMARY); - auto x = unsigned{ 96 }; - auto y = unsigned{ 96 }; - if (GetDpiForMonitor_fn) - GetDpiForMonitor_fn(monitor, MDT_EFFECTIVE_DPI, &x, &y); - float z = (float)x / 96.f; - - static char base_path[MAX_PATH]; - GetCurrentDirectoryA(MAX_PATH, base_path); - std::string path = std::string(base_path) + "\\data\\splash.jpg"; - //auto hbitmap = (HBITMAP)LoadImageA(NULL, path.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); - Image img; - img.load_file(path); - img.flip(); - auto hbitmap = image_to_hbitmap(img.resize(512 * z, 200 * z)); - SendMessage(GetDlgItem(hWndDlg, IDC_STATIC_IMAGE), STM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hbitmap); - SetDlgItemText(hWndDlg, IDC_STATIC_VERSION, g_version_number_w); - - RECT r; - GetClientRect(hWndDlg, &r); - MoveWindow(GetDlgItem(hWndDlg, IDC_STATIC_IMAGE), 0, 0, 512 * z, 200 * z, TRUE); - SetWindowPos(hWndDlg, HWND_TOP, 0, 0, 512 * z, 200 * z, SWP_NOMOVE); - - HWND hWndVersion = GetDlgItem(hWndDlg, IDC_STATIC_VERSION); - RECT rv; - GetClientRect(hWndVersion, &rv); - MoveWindow(hWndVersion, 0, 200 * z - (rv.bottom - rv.top), r.right - r.left, rv.bottom - rv.top, TRUE); - - return TRUE; - } - case WM_USER_CLOSE: - PostQuitMessage(0); - return TRUE; - } - - return DefWindowProc(hWndDlg, Msg, wParam, lParam);; -} - -void splash_thread_loop() -{ - auto& state = retained_state(); - BT_SetTerminate(); - state.splash_dialog = CreateDialog(state.hInst, MAKEINTRESOURCE(IDD_SPLASH), NULL, reinterpret_cast(splash_proc)); - - MSG msg; - while (GetMessage(&msg, 0, 0, 0) > 0) - { - if (IsDialogMessage(state.splash_dialog, &msg)) - { - DispatchMessage(&msg); - TranslateMessage(&msg); - } - } - - DestroyWindow(state.splash_dialog); -} - BOOL UnadjustWindowRectEx(LPRECT prc, DWORD dwStyle, BOOL fMenu, DWORD dwExStyle) { RECT rc; @@ -811,7 +733,7 @@ int main(int argc, char** argv) LOG("data files ok"); } - std::jthread dialog_thread(splash_thread_loop); + pp::platform::windows::SplashScreen splash(GetModuleHandle(NULL)); init_vk_map(); @@ -1022,9 +944,7 @@ int main(int argc, char** argv) SetForegroundWindow(state.hWnd); //ShowWindow(hWnd, show_cmd); - SendMessage(state.splash_dialog, WM_USER_CLOSE, 0, 0); - if (dialog_thread.joinable()) - dialog_thread.join(); + splash.dismiss(); MSG msg; LOG("start main loop"); diff --git a/src/platform_windows/windows_splash.cpp b/src/platform_windows/windows_splash.cpp new file mode 100644 index 00000000..3db396de --- /dev/null +++ b/src/platform_windows/windows_splash.cpp @@ -0,0 +1,124 @@ +#include "pch.h" +#include "platform_windows/windows_splash.h" + +#include "app.h" +#include "image.h" +#include "log.h" +#include "version.h" + +#include "../resource.h" + +#include +#include +#include +#include + +extern HRESULT(*GetDpiForMonitor_fn)(HMONITOR hmonitor, MONITOR_DPI_TYPE dpiType, UINT* dpiX, UINT* dpiY); + +namespace { + +constexpr UINT kSplashCloseMessage = WM_USER + 1; + +HBITMAP image_to_hbitmap(const Image& img) +{ + BITMAPINFOHEADER bmih; + memset(&bmih, 0, sizeof(BITMAPINFOHEADER)); + bmih.biWidth = img.width; + bmih.biHeight = img.height; + bmih.biBitCount = 32; + bmih.biCompression = BI_RGB; + bmih.biSize = sizeof(BITMAPINFOHEADER); + bmih.biPlanes = 1; + BITMAPINFO* bmi = (BITMAPINFO*)&bmih; + return CreateDIBitmap(GetDC(NULL), &bmih, CBM_INIT, img.data(), bmi, DIB_RGB_COLORS); +} + +LRESULT CALLBACK splash_proc(HWND hWndDlg, UINT Msg, WPARAM wParam, LPARAM lParam) +{ + switch (Msg) + { + case WM_INITDIALOG: + { + auto monitor = MonitorFromWindow(0, MONITOR_DEFAULTTOPRIMARY); + auto x = unsigned{ 96 }; + auto y = unsigned{ 96 }; + if (GetDpiForMonitor_fn) + GetDpiForMonitor_fn(monitor, MDT_EFFECTIVE_DPI, &x, &y); + float z = (float)x / 96.f; + + static char base_path[MAX_PATH]; + GetCurrentDirectoryA(MAX_PATH, base_path); + std::string path = std::string(base_path) + "\\data\\splash.jpg"; + Image img; + img.load_file(path); + img.flip(); + auto hbitmap = image_to_hbitmap(img.resize(512 * z, 200 * z)); + SendMessage(GetDlgItem(hWndDlg, IDC_STATIC_IMAGE), STM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hbitmap); + SetDlgItemText(hWndDlg, IDC_STATIC_VERSION, g_version_number_w); + + RECT r; + GetClientRect(hWndDlg, &r); + MoveWindow(GetDlgItem(hWndDlg, IDC_STATIC_IMAGE), 0, 0, 512 * z, 200 * z, TRUE); + SetWindowPos(hWndDlg, HWND_TOP, 0, 0, 512 * z, 200 * z, SWP_NOMOVE); + + HWND hWndVersion = GetDlgItem(hWndDlg, IDC_STATIC_VERSION); + RECT rv; + GetClientRect(hWndVersion, &rv); + MoveWindow(hWndVersion, 0, 200 * z - (rv.bottom - rv.top), r.right - r.left, rv.bottom - rv.top, TRUE); + + return TRUE; + } + case kSplashCloseMessage: + PostQuitMessage(0); + return TRUE; + } + + return DefWindowProc(hWndDlg, Msg, wParam, lParam); +} + +} + +namespace pp::platform::windows { + +SplashScreen::SplashScreen(HINSTANCE hInst) + : hInst_(hInst) + , thread_([this](std::stop_token) { thread_main(); }) +{ +} + +SplashScreen::~SplashScreen() +{ + dismiss(); +} + +void SplashScreen::dismiss() +{ + if (HWND dialog = dialog_.load(std::memory_order_acquire)) + SendMessage(dialog, kSplashCloseMessage, 0, 0); + if (thread_.joinable()) + thread_.join(); +} + +void SplashScreen::thread_main() +{ + BT_SetTerminate(); + HWND dialog = CreateDialog(hInst_, MAKEINTRESOURCE(IDD_SPLASH), NULL, reinterpret_cast(splash_proc)); + dialog_.store(dialog, std::memory_order_release); + if (!dialog) + return; + + MSG msg; + while (GetMessage(&msg, 0, 0, 0) > 0) + { + if (IsDialogMessage(dialog, &msg)) + { + DispatchMessage(&msg); + TranslateMessage(&msg); + } + } + + if (HWND dialog = dialog_.exchange(nullptr)) + DestroyWindow(dialog); +} + +} diff --git a/src/platform_windows/windows_splash.h b/src/platform_windows/windows_splash.h new file mode 100644 index 00000000..d4c2802a --- /dev/null +++ b/src/platform_windows/windows_splash.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include +#include + +namespace pp::platform::windows { + +class SplashScreen final +{ +public: + explicit SplashScreen(HINSTANCE hInst); + ~SplashScreen(); + + SplashScreen(const SplashScreen&) = delete; + SplashScreen& operator=(const SplashScreen&) = delete; + SplashScreen(SplashScreen&&) = delete; + SplashScreen& operator=(SplashScreen&&) = delete; + + void dismiss(); + +private: + void thread_main(); + + HINSTANCE hInst_{}; + std::atomic dialog_{}; + std::jthread thread_; +}; + +}