diff --git a/cmake/PanoPainterSources.cmake b/cmake/PanoPainterSources.cmake index e81bbe1c..c485ee93 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_about_layer_menu.cpp src/app_layout_file_menu.cpp src/app_layout_tools_menu.cpp src/app_shaders.cpp @@ -164,6 +165,8 @@ set(PP_WINDOWS_PLATFORM_SOURCES src/platform_windows/windows_platform_services.h src/platform_windows/windows_splash.cpp src/platform_windows/windows_splash.h + src/platform_windows/windows_stylus_input.cpp + src/platform_windows/windows_stylus_input.h ) set(PP_WINDOWS_APP_SOURCES diff --git a/docs/modernization/roadmap.md b/docs/modernization/roadmap.md index 57c9c088..77cba2b0 100644 --- a/docs/modernization/roadmap.md +++ b/docs/modernization/roadmap.md @@ -80,13 +80,13 @@ What is still carrying too much live ownership: Current hotspot files: - `src/canvas.cpp`: 2645 lines -- `src/app_layout.cpp`: 1360 lines +- `src/app_layout.cpp`: 1249 lines - `src/canvas_modes.cpp`: 1798 lines - `src/node.cpp`: 1551 lines -- `src/main.cpp`: 1181 lines +- `src/main.cpp`: 1218 lines - `src/node_panel_brush.cpp`: 1197 lines - `src/node_stroke_preview.cpp`: 933 lines -- `src/node_canvas.cpp`: 888 lines +- `src/node_canvas.cpp`: 968 lines - `src/app.cpp`: 950 lines - `src/app_dialogs.cpp`: 908 lines @@ -122,14 +122,19 @@ Current architecture mismatches that must be treated as real blockers: current-mode, and grid-mode callback setup now routed through the same retained helper family, while post-draw callback assembly and the remaining per-layer render-path orchestration now also route through retained - draw-merge helpers even though the broader node draw loop is still inline. + draw-merge helpers even though the broader node draw loop is still inline, + with the non-`draw_merged` outer layer/plane traversal now also routing + through `execute_legacy_canvas_draw_layer_traversal(...)` while the heavier + per-layer GL setup and draw lambdas still live in `src/node_canvas.cpp`. - `app_layout.cpp` and `app_dialogs.cpp` are still mixed shell/controller files 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, 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. + thin call-through, while about-menu and layer-menu wiring now also live in + `src/app_layout_about_layer_menu.cpp` and `App::init_menu_about()` plus + `App::init_menu_layer()` are now thin call-throughs. - `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 @@ -153,7 +158,9 @@ Current architecture mismatches that must be treated as real blockers: live under `AppRuntime` instead of retained static app-events/canvas 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`, + `src/main.cpp`, while Win32 pointer API loading, stylus/ink timer decay, + Wintab packet reset, and `WM_POINTERUPDATE` pen/touch handling now also live + in `src/platform_windows/windows_stylus_input.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 diff --git a/docs/modernization/tasks.md b/docs/modernization/tasks.md index 227f8818..3f2069af 100644 --- a/docs/modernization/tasks.md +++ b/docs/modernization/tasks.md @@ -193,6 +193,10 @@ Current slice: remaining per-layer render-path orchestration now also routes through `execute_legacy_canvas_draw_merge_layer_path(...)`, but the node still owns broader draw-loop and renderer-state shell sequencing. +- `NodeCanvas` outer non-`draw_merged` layer/plane traversal, onion-range + failure handling, and visit payload setup now also route through + `execute_legacy_canvas_draw_layer_traversal(...)`, but the node still owns + the heavier per-layer GL setup and draw lambdas. Write scope: - `src/node_stroke_preview.cpp` @@ -271,7 +275,7 @@ targets look like helpers under one old monolith. Status: In Progress Why now: -`src/app_layout.cpp` is still a 1360-line mixed file that builds menus, +`src/app_layout.cpp` is still a 1249-line mixed file that builds menus, attaches callbacks, computes planner inputs, and mutates UI state directly. Current slice: @@ -283,6 +287,10 @@ Current slice: `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`. +- About-menu and layer-menu wiring now also live in + `src/app_layout_about_layer_menu.cpp`, and `App::init_menu_about()` plus + `App::init_menu_layer()` are now thin call-throughs, but edit/sidebar and + broader layout composition are still inline in `src/app_layout.cpp`. Write scope: - `src/app_layout.cpp` @@ -405,6 +413,10 @@ Current slice: pair during initialization and context recreation - `main.cpp` main-thread queued task state now sits behind a narrow retained helper instead of `RetainedState.main_tasklist` / `main_task_mutex` directly +- Win32 pointer API loading, stylus/ink timer ownership and decay, `WT_PACKET` + reset, and `WM_POINTERUPDATE` pen/touch handling now live in + `src/platform_windows/windows_stylus_input.cpp` instead of `src/main.cpp`, + but broader retained Win32 shell state is still open - prepared-file background work now runs through an `AppRuntime`-owned worker queue instead of a retained static worker in `src/app_events.cpp` - canvas async import/export/save/open background work now also runs through an diff --git a/src/app_layout.cpp b/src/app_layout.cpp index 0dfee304..4edb945e 100644 --- a/src/app_layout.cpp +++ b/src/app_layout.cpp @@ -33,6 +33,8 @@ namespace pp::panopainter { void bind_legacy_file_menu(App& app); +void bind_legacy_about_menu(App& app); +void bind_legacy_layer_menu(App& app); void bind_legacy_tools_menu(App& app); } @@ -93,39 +95,6 @@ void close_legacy_overlay_handles_if_open( } } -pp::app::DocumentLayerMenuPlan make_layer_menu_plan( - pp::app::DocumentLayerMenuCommand command, - App& app) -{ - const bool has_current_layer = app.layers && app.layers->m_current_layer; - const int current_index = app.canvas && app.canvas->m_canvas - ? app.canvas->m_canvas->m_current_layer_idx - : 0; - const int animation_duration = Canvas::I - ? Canvas::I->anim_duration() - : 0; - const std::string current_name = has_current_layer - ? app.layers->m_current_layer->m_label_text - : std::string {}; - std::string lower_name; - if (app.canvas && app.canvas->m_canvas && current_index > 0 - && current_index - 1 < static_cast(app.canvas->m_canvas->m_layers.size())) - { - lower_name = app.canvas->m_canvas->m_layers[current_index - 1]->m_name; - } - - const auto plan = pp::app::plan_document_layer_menu( - command, - has_current_layer, - current_index, - animation_duration, - current_name, - lower_name); - if (plan) - return plan.value(); - return {}; -} - [[nodiscard]] bool should_open_tools_panel(const pp::app::ToolsPanelPlan& plan) noexcept { return plan.action == pp::app::ToolsPanelAction::open_floating_panel; @@ -154,23 +123,11 @@ void execute_main_toolbar_plan(App& app, const pp::app::MainToolbarPlan& plan) pp::panopainter::execute_legacy_main_toolbar_plan(app, plan); } -void execute_about_menu_plan(App& app, const pp::app::AboutMenuPlan& plan) -{ - pp::panopainter::execute_legacy_about_menu_plan(app, plan); -} - void execute_tools_menu_plan(App& app, const pp::app::ToolsMenuPlan& plan) { pp::panopainter::execute_legacy_tools_menu_plan(app, plan); } -void execute_document_layer_menu_plan(App& app, const pp::app::DocumentLayerMenuPlan& plan) -{ - const auto status = pp::panopainter::execute_legacy_document_layer_menu_plan(app, plan); - if (!status.ok()) - LOG("Layer menu action failed: %s", status.message); -} - void execute_document_layer_merge_plan(App& app, const pp::app::DocumentLayerMergePlan& plan) { const auto status = pp::panopainter::execute_legacy_document_layer_merge_plan(app, plan); @@ -764,113 +721,7 @@ void App::init_menu_tools() void App::init_menu_about() { - if (auto* menu_file = layout[main_id]->find("menu-about")) - { - menu_file->on_click = [=](Node*) { - auto* popup_root = layout[main_id]; - if (!popup_root) { - return; - } - glm::vec2 pos = menu_file->m_pos + glm::vec2(0, menu_file->m_size.y); - auto popup = add_menu_popup(*this, "about-menu", pos, menu_file->m_size.x); - 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; - } - auto popup_handle = popup_overlay.value(); - - popup->find("about-app")->on_click = [this, popup_root, popup_handle](Node*) { - const auto plan = pp::app::plan_about_menu_command( - pp::app::AboutMenuCommand::about_app, - g_version_major, - g_version_minor, - g_version_fix); - execute_about_menu_plan(*this, plan); - if (plan.closes_root_popup) - { - close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); - } - }; - - popup->find("about-doc")->on_click = [this, popup_root, popup_handle](Node*) { -// auto path = Asset::absolute("data/doc/test.pdf"); -// display_file(path); - const auto plan = pp::app::plan_about_menu_command( - pp::app::AboutMenuCommand::help_guide, - g_version_major, - g_version_minor, - g_version_fix); - execute_about_menu_plan(*this, plan); - if (plan.closes_root_popup) - { - close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); - } - }; - - if (auto item = popup->find("about-news")) - { - if (auto text = item->find("menu-label")) - { - const auto plan = pp::app::plan_about_menu_command( - pp::app::AboutMenuCommand::whats_new, - g_version_major, - g_version_minor, - g_version_fix); - text->set_text(plan.label.c_str()); - } - item->on_click = [this, popup_root, popup_handle](Node*) { - const auto plan = pp::app::plan_about_menu_command( - pp::app::AboutMenuCommand::whats_new, - g_version_major, - g_version_minor, - g_version_fix); - execute_about_menu_plan(*this, plan); - if (plan.closes_root_popup) - { - close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); - } - }; - } - - if (auto b = popup->find("about-crash")) - { - b->on_click = [this, popup_root, popup_handle](Node*) { - const auto plan = pp::app::plan_about_menu_command( - pp::app::AboutMenuCommand::induce_crash, - g_version_major, - g_version_minor, - g_version_fix); - execute_about_menu_plan(*this, plan); - if (plan.closes_root_popup) - { - close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); - } - }; - } - - if (auto b = popup->find("about-perf")) - { - b->on_click = [this, popup_root, popup_handle](Node*) { - const auto plan = pp::app::plan_about_menu_command( - pp::app::AboutMenuCommand::performance_test, - g_version_major, - g_version_minor, - g_version_fix, - true, - Canvas::I != nullptr); - execute_about_menu_plan(*this, plan); - if (plan.closes_root_popup) - { - close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); - } - }; - } - }; - } + pp::panopainter::bind_legacy_about_menu(*this); } void App::brush_update(bool update_color, bool update_brush) @@ -933,63 +784,7 @@ void App::brush_update(bool update_color, bool update_brush) void App::init_menu_layer() { - if (auto* menu_file = layout[main_id]->find("menu-layers")) - { - menu_file->on_click = [=](Node*) { - auto* popup_root = layout[main_id]; - if (!popup_root) { - return; - } - glm::vec2 pos = menu_file->m_pos + glm::vec2(0, menu_file->m_size.y); - auto popup = add_menu_popup(*this, "layers-menu", pos, menu_file->m_size.x); - 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; - } - auto popup_handle = popup_overlay.value(); - - popup->find("layer-clear")->on_click = [this, popup_root, popup_handle](Node*) { - const auto plan = make_layer_menu_plan(pp::app::DocumentLayerMenuCommand::clear, *this); - execute_document_layer_menu_plan(*this, plan); - close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); - }; - { - const auto plan = make_layer_menu_plan(pp::app::DocumentLayerMenuCommand::clear, *this); - popup->find("layer-clear")-> - find("menu-label")-> - set_text(plan.label.c_str()); - } - - popup->find("layer-rename")->on_click = [this, popup_root, popup_handle](Node*) { - const auto plan = make_layer_menu_plan(pp::app::DocumentLayerMenuCommand::rename, *this); - execute_document_layer_menu_plan(*this, plan); - close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); - }; - { - const auto plan = make_layer_menu_plan(pp::app::DocumentLayerMenuCommand::rename, *this); - popup->find("layer-rename")-> - find("menu-label")-> - set_text(plan.label.c_str()); - } - - popup->find("layer-merge")->on_click = [this, popup_root, popup_handle](Node*) { - const auto plan = make_layer_menu_plan(pp::app::DocumentLayerMenuCommand::merge_down, *this); - execute_document_layer_menu_plan(*this, plan); - close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); - }; - { - const auto plan = make_layer_menu_plan(pp::app::DocumentLayerMenuCommand::merge_down, *this); - popup->find("layer-merge")-> - find("menu-label")-> - set_text(plan.label.c_str()); - } - - }; - } + pp::panopainter::bind_legacy_layer_menu(*this); } void App::initLayout() diff --git a/src/app_layout_about_layer_menu.cpp b/src/app_layout_about_layer_menu.cpp new file mode 100644 index 00000000..8f9c5609 --- /dev/null +++ b/src/app_layout_about_layer_menu.cpp @@ -0,0 +1,264 @@ +#include "pch.h" +#include "app.h" +#include "app_core/about_menu.h" +#include "app_core/document_layer.h" +#include "legacy_app_shell_services.h" +#include "legacy_document_layer_services.h" +#include "legacy_ui_overlay_services.h" +#include "node_button_custom.h" +#include "node_popup_menu.h" +#include "node_text.h" + +namespace { + +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); +} + +pp::app::DocumentLayerMenuPlan make_layer_menu_plan( + pp::app::DocumentLayerMenuCommand command, + App& app) +{ + const bool has_current_layer = app.layers && app.layers->m_current_layer; + const int current_index = app.canvas && app.canvas->m_canvas + ? app.canvas->m_canvas->m_current_layer_idx + : 0; + const int animation_duration = Canvas::I + ? Canvas::I->anim_duration() + : 0; + const std::string current_name = has_current_layer + ? app.layers->m_current_layer->m_label_text + : std::string {}; + std::string lower_name; + if (app.canvas && app.canvas->m_canvas && current_index > 0 + && current_index - 1 < static_cast(app.canvas->m_canvas->m_layers.size())) + { + lower_name = app.canvas->m_canvas->m_layers[current_index - 1]->m_name; + } + + const auto plan = pp::app::plan_document_layer_menu( + command, + has_current_layer, + current_index, + animation_duration, + current_name, + lower_name); + if (plan) + return plan.value(); + return {}; +} + +void execute_about_menu_plan(App& app, const pp::app::AboutMenuPlan& plan) +{ + pp::panopainter::execute_legacy_about_menu_plan(app, plan); +} + +void execute_document_layer_menu_plan(App& app, const pp::app::DocumentLayerMenuPlan& plan) +{ + const auto status = pp::panopainter::execute_legacy_document_layer_menu_plan(app, plan); + if (!status.ok()) + LOG("Layer menu action failed: %s", status.message); +} + +} // namespace + +namespace pp::panopainter { + +void bind_legacy_about_menu(App& app) +{ + auto main = app.layout[app.main_id]; + + if (auto* menu_file = main->find("menu-about")) + { + menu_file->on_click = [&app, menu_file](Node*) { + auto* popup_root = app.layout[app.main_id]; + if (!popup_root) { + return; + } + glm::vec2 pos = menu_file->m_pos + glm::vec2(0, menu_file->m_size.y); + auto popup = add_menu_popup(app, "about-menu", pos, menu_file->m_size.x); + 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; + } + auto popup_handle = popup_overlay.value(); + + popup->find("about-app")->on_click = [&app, popup_root, popup_handle](Node*) { + const auto plan = pp::app::plan_about_menu_command( + pp::app::AboutMenuCommand::about_app, + g_version_major, + g_version_minor, + g_version_fix); + execute_about_menu_plan(app, plan); + if (plan.closes_root_popup) + { + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); + } + }; + + popup->find("about-doc")->on_click = [&app, popup_root, popup_handle](Node*) { +// auto path = Asset::absolute("data/doc/test.pdf"); +// display_file(path); + const auto plan = pp::app::plan_about_menu_command( + pp::app::AboutMenuCommand::help_guide, + g_version_major, + g_version_minor, + g_version_fix); + execute_about_menu_plan(app, plan); + if (plan.closes_root_popup) + { + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); + } + }; + + if (auto item = popup->find("about-news")) + { + if (auto text = item->find("menu-label")) + { + const auto plan = pp::app::plan_about_menu_command( + pp::app::AboutMenuCommand::whats_new, + g_version_major, + g_version_minor, + g_version_fix); + text->set_text(plan.label.c_str()); + } + item->on_click = [&app, popup_root, popup_handle](Node*) { + const auto plan = pp::app::plan_about_menu_command( + pp::app::AboutMenuCommand::whats_new, + g_version_major, + g_version_minor, + g_version_fix); + execute_about_menu_plan(app, plan); + if (plan.closes_root_popup) + { + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); + } + }; + } + + if (auto b = popup->find("about-crash")) + { + b->on_click = [&app, popup_root, popup_handle](Node*) { + const auto plan = pp::app::plan_about_menu_command( + pp::app::AboutMenuCommand::induce_crash, + g_version_major, + g_version_minor, + g_version_fix); + execute_about_menu_plan(app, plan); + if (plan.closes_root_popup) + { + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); + } + }; + } + + if (auto b = popup->find("about-perf")) + { + b->on_click = [&app, popup_root, popup_handle](Node*) { + const auto plan = pp::app::plan_about_menu_command( + pp::app::AboutMenuCommand::performance_test, + g_version_major, + g_version_minor, + g_version_fix, + true, + Canvas::I != nullptr); + execute_about_menu_plan(app, plan); + if (plan.closes_root_popup) + { + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); + } + }; + } + }; + } +} + +void bind_legacy_layer_menu(App& app) +{ + auto main = app.layout[app.main_id]; + + if (auto* menu_file = main->find("menu-layers")) + { + menu_file->on_click = [&app, menu_file](Node*) { + auto* popup_root = app.layout[app.main_id]; + if (!popup_root) { + return; + } + glm::vec2 pos = menu_file->m_pos + glm::vec2(0, menu_file->m_size.y); + auto popup = add_menu_popup(app, "layers-menu", pos, menu_file->m_size.x); + 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; + } + auto popup_handle = popup_overlay.value(); + + popup->find("layer-clear")->on_click = [&app, popup_root, popup_handle](Node*) { + const auto plan = make_layer_menu_plan(pp::app::DocumentLayerMenuCommand::clear, app); + execute_document_layer_menu_plan(app, plan); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); + }; + { + const auto plan = make_layer_menu_plan(pp::app::DocumentLayerMenuCommand::clear, app); + popup->find("layer-clear")-> + find("menu-label")-> + set_text(plan.label.c_str()); + } + + popup->find("layer-rename")->on_click = [&app, popup_root, popup_handle](Node*) { + const auto plan = make_layer_menu_plan(pp::app::DocumentLayerMenuCommand::rename, app); + execute_document_layer_menu_plan(app, plan); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); + }; + { + const auto plan = make_layer_menu_plan(pp::app::DocumentLayerMenuCommand::rename, app); + popup->find("layer-rename")-> + find("menu-label")-> + set_text(plan.label.c_str()); + } + + popup->find("layer-merge")->on_click = [&app, popup_root, popup_handle](Node*) { + const auto plan = make_layer_menu_plan(pp::app::DocumentLayerMenuCommand::merge_down, app); + execute_document_layer_menu_plan(app, plan); + close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle); + }; + { + const auto plan = make_layer_menu_plan(pp::app::DocumentLayerMenuCommand::merge_down, app); + popup->find("layer-merge")-> + find("menu-label")-> + set_text(plan.label.c_str()); + } + }; + } +} + +} // namespace pp::panopainter diff --git a/src/legacy_canvas_draw_merge_services.h b/src/legacy_canvas_draw_merge_services.h index 26e26a3d..8d8a879c 100644 --- a/src/legacy_canvas_draw_merge_services.h +++ b/src/legacy_canvas_draw_merge_services.h @@ -130,6 +130,14 @@ struct LegacyCanvasDrawMergeLayerPathExecution { std::function draw_frame; }; +struct LegacyCanvasDrawLayerVisit { + size_t layer_index = 0; + int plane_index = 0; + int z = 0; + int first_frame = 0; + int last_frame = 0; +}; + struct LegacyCanvasDrawMergeTemporaryCompositeExecution { std::function setup; std::function bind_samplers; @@ -671,6 +679,39 @@ inline void execute_legacy_canvas_draw_merge_layer_path( execution.draw_debug_outline(); } +template +inline void execute_legacy_canvas_draw_layer_traversal( + size_t layer_count, + PlanOnionRange&& plan_onion_range, + ShouldDrawPlane&& should_draw_plane, + VisitLayerPlane&& visit_layer_plane, + LogOnionRangeFailure&& log_onion_range_failure) +{ + for (size_t i = 0; i < layer_count; ++i) { + const auto layer_index = i; + const auto onion_range_result = plan_onion_range(layer_index); + if (!onion_range_result) { + log_onion_range_failure(onion_range_result.status().message); + continue; + } + + const auto onion_range = onion_range_result.value(); + for (int plane_index = 0; plane_index < 6; ++plane_index) { + if (!should_draw_plane(layer_index, plane_index, onion_range.first_frame, onion_range.last_frame)) { + continue; + } + + visit_layer_plane(LegacyCanvasDrawLayerVisit { + .layer_index = layer_index, + .plane_index = plane_index, + .z = static_cast(layer_count - i), + .first_frame = onion_range.first_frame, + .last_frame = onion_range.last_frame, + }, onion_range); + } + } +} + inline void execute_legacy_canvas_draw_merge_temporary_composite( const LegacyCanvasDrawMergeTemporaryCompositeExecution& execution) { diff --git a/src/main.cpp b/src/main.cpp index faa11d17..4a2bfe88 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -13,6 +13,7 @@ #include "renderer_gl/opengl_capabilities.h" #include "platform_windows/windows_platform_services.h" #include "platform_windows/windows_splash.h" +#include "platform_windows/windows_stylus_input.h" #include "../resource.h" #include @@ -55,9 +56,6 @@ struct RetainedState std::map vkey_map; wchar_t window_title[512]{}; std::jthread hmd_renderer; - float timer_stylus = 0; - float timer_ink_touch = 0; - float timer_ink_pen = 0; bool sandboxed = false; std::mutex hmd_render_mutex; std::condition_variable hmd_render_cv; @@ -176,25 +174,6 @@ void init_shcore_API() SetProcessDpiAwareness_fn = (decltype(SetProcessDpiAwareness_fn))GetProcAddress(dll, "SetProcessDpiAwareness"); } -BOOL(*GetPointerInfo_fn)(UINT32 pointerId, POINTER_INFO* pointerInfo); -BOOL(*GetPointerType_fn)(UINT32 pointerId, POINTER_INPUT_TYPE* pointerType); -BOOL(*GetPointerTouchInfo_fn)(UINT32 pointerId, POINTER_TOUCH_INFO* touchInfo); -BOOL(*GetPointerPenInfo_fn)(UINT32 pointerId, POINTER_PEN_INFO* penInfo); -void init_ink_API() -{ - HMODULE dll = LoadLibrary(L"User32.dll"); - if (!dll) - { - LOG("cannot load User32.dll"); - return; - } - LOG("loaded User32.dll"); - GetPointerInfo_fn = (decltype(GetPointerInfo_fn))GetProcAddress(dll, "GetPointerInfo"); - GetPointerType_fn = (decltype(GetPointerType_fn))GetProcAddress(dll, "GetPointerType"); - GetPointerTouchInfo_fn = (decltype(GetPointerTouchInfo_fn))GetProcAddress(dll, "GetPointerTouchInfo"); - GetPointerPenInfo_fn = (decltype(GetPointerPenInfo_fn))GetProcAddress(dll, "GetPointerPenInfo"); -} - //Returns the last Win32 error, in string format. Returns an empty string if there is no error. std::string GetLastErrorAsString() { @@ -245,27 +224,7 @@ void async_unlock() void win32_update_stylus(float dt) { - auto& state = retained_state(); - state.timer_stylus += dt; - state.timer_ink_touch += dt; - state.timer_ink_pen += dt; - - if (state.timer_stylus > 0.1 && (WacomTablet::I.m_stylus || WacomTablet::I.m_eraser)) - { - WacomTablet::I.m_stylus = false; - WacomTablet::I.m_eraser = false; - App::I->redraw = true; - } - if (state.timer_ink_pen > 0.1 && WacomTablet::I.m_ink_pen) - { - WacomTablet::I.m_ink_pen = false; - App::I->redraw = true; - } - if (state.timer_ink_touch > 0.1 && WacomTablet::I.m_ink_touch) - { - WacomTablet::I.m_ink_touch = false; - App::I->redraw = true; - } + pp::platform::windows::update_stylus_state(dt); } void win32_update_fps(int frames) @@ -710,7 +669,7 @@ int main(int argc, char** argv) App::I->initLog(); init_shcore_API(); - init_ink_API(); + pp::platform::windows::initialize_stylus_input(); if(SetProcessDpiAwareness_fn) SetProcessDpiAwareness_fn(PROCESS_PER_MONITOR_DPI_AWARE); @@ -1064,8 +1023,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) // } case WT_PACKET: { - App::I->set_stylus(); - state.timer_stylus = 0; + pp::platform::windows::note_wintab_packet(); App::I->ui_task_async([=] { WacomTablet::I.handle_message(hWnd, msg, wp, lp); }); @@ -1229,79 +1187,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) } case WM_POINTERUPDATE: { - POINTER_TOUCH_INFO touchInfo; - POINTER_PEN_INFO penInfo; - POINTER_INFO pointerInfo; - UINT32 pointerId = GET_POINTERID_WPARAM(wp); - POINTER_INPUT_TYPE pointerType = PT_POINTER; - - if(!GetPointerInfo_fn) - break; - - // Retrieve common pointer information - if (!GetPointerInfo_fn(pointerId, &pointerInfo)) - { - // failure, call GetLastError() - } - else - { - // success, process pointerInfo - if (!GetPointerType_fn(pointerId, &pointerType)) - { - // failure, call GetLastError() - // set PT_POINTER to fall to default case below - pointerType = PT_POINTER; - } - switch (pointerType) - { - case PT_TOUCH: - // Retrieve touch information - if (!GetPointerTouchInfo_fn(pointerId, &touchInfo)) - { - // failure, call GetLastError() - } - else - { - // success, process touchInfo - // mark as handled to skip call to DefWindowProc - //fHandled = TRUE; - state.timer_ink_touch = 0; - WacomTablet::I.m_ink_touch = true; - WacomTablet::I.m_pen_pres = 1; - } - break; - case PT_PEN: - // Retrieve pen information - if (!GetPointerPenInfo_fn(pointerId, &penInfo)) - { - // failure, call GetLastError() - } - else - { - // success, process penInfo - // mark as handled to skip call to DefWindowProc - //fHandled = TRUE; - //penInfo.pressure - state.timer_ink_pen = 0; - WacomTablet::I.m_ink_pen = true; - WacomTablet::I.m_pen_pres = (float)penInfo.pressure / 1024.f; - App::I->set_stylus(); - } - break; - default: - if (!GetPointerInfo_fn(pointerId, &pointerInfo)) - { - // failure. - } - else - { - // success, proceed with pointerInfo. - //fHandled = HandleGenericPointerInfo(&pointerInfo); - } - break; - } - } - + pp::platform::windows::handle_pointer_update_message(wp); break; } } diff --git a/src/node_canvas.cpp b/src/node_canvas.cpp index 156c0fe7..21e4d252 100644 --- a/src/node_canvas.cpp +++ b/src/node_canvas.cpp @@ -439,34 +439,33 @@ void NodeCanvas::draw() apply_node_canvas_capability(pp::renderer::gl::depth_test_state(), false); const auto& b = m_canvas->m_current_stroke->m_brush; - - for (size_t i = 0; i < m_canvas->m_layers.size(); i++) - { - auto layer_index = i; - for (int plane_index = 0; plane_index < 6; plane_index++) - { - const auto onion_range_result = pp::app::plan_animation_onion_frame_range( + pp::panopainter::execute_legacy_canvas_draw_layer_traversal( + m_canvas->m_layers.size(), + [&](size_t layer_index) { + return pp::app::plan_animation_onion_frame_range( m_canvas->m_layers[layer_index]->frames_count(), m_canvas->m_layers[layer_index]->m_frame_index, App::I->animation->get_onion_size()); - if (!onion_range_result) { - LOG("NodeCanvas onion frame range failed: %s", onion_range_result.status().message); - continue; - } - const auto onion_range = onion_range_result.value(); + }, + [&](size_t layer_index, int plane_index, int first_frame, int last_frame) { bool faces = false; - for (int frame = onion_range.first_frame; frame <= onion_range.last_frame; frame++) + for (int frame = first_frame; frame <= last_frame; ++frame) faces |= m_canvas->m_layers[layer_index]->face(plane_index, frame); - if (!(m_canvas->m_show_tmp && m_canvas->m_current_layer_idx == layer_index) && - (!m_canvas->m_layers[layer_index]->m_visible || - m_canvas->m_layers[layer_index]->m_opacity == .0f || !faces)) - continue; - int z = (int)(m_canvas->m_layers.size() - i); - auto plane_mvp_z = proj * camera * - glm::scale(glm::vec3(z + 1)) * + if (m_canvas->m_show_tmp && m_canvas->m_current_layer_idx == layer_index) + return true; + + return m_canvas->m_layers[layer_index]->m_visible && + m_canvas->m_layers[layer_index]->m_opacity != .0f && + faces; + }, + [&](const pp::panopainter::LegacyCanvasDrawLayerVisit& visit, const auto& onion_range) { + const auto layer_index = visit.layer_index; + const auto plane_index = visit.plane_index; + const auto plane_mvp_z = proj * camera * + glm::scale(glm::vec3(visit.z + 1)) * glm::eulerAngleYXZ(yaw, pitch, roll) * - m_canvas->m_plane_transform[plane_index] * + m_canvas->m_plane_transform[plane_index] * glm::translate(glm::vec3(0, 0, -1)); const auto draw_layer_frame = pp::panopainter::make_legacy_canvas_draw_merge_layer_frame_draw( @@ -480,8 +479,8 @@ void NodeCanvas::draw() m_canvas->m_current_stroke && m_canvas->m_current_mode == kCanvasMode::Erase && m_canvas->m_show_tmp && m_canvas->m_current_layer_idx == layer_index, m_canvas->m_current_stroke && m_canvas->m_show_tmp && m_canvas->m_current_layer_idx == layer_index, use_blend, - onion_range.first_frame, - onion_range.last_frame, + visit.first_frame, + visit.last_frame, [&](int frame) { return pp::app::animation_onion_frame_alpha(onion_range, frame); }, @@ -649,9 +648,10 @@ void NodeCanvas::draw() #endif .draw_frame = draw_layer_frame, }); - - } - } + }, + [&](const char* message) { + LOG("NodeCanvas onion frame range failed: %s", message); + }); if (use_blend) { m_cache_rtt.unbindFramebuffer(); diff --git a/src/platform_windows/windows_stylus_input.cpp b/src/platform_windows/windows_stylus_input.cpp new file mode 100644 index 00000000..8fdcaae1 --- /dev/null +++ b/src/platform_windows/windows_stylus_input.cpp @@ -0,0 +1,129 @@ +#include "pch.h" + +#include "platform_windows/windows_stylus_input.h" + +#include "app.h" +#include "log.h" +#include "wacom.h" + +namespace pp::platform::windows { +namespace { + +struct StylusInputState final +{ + float timer_stylus = 0.0f; + float timer_ink_touch = 0.0f; + float timer_ink_pen = 0.0f; + + BOOL (*get_pointer_info)(UINT32, POINTER_INFO*) = nullptr; + BOOL (*get_pointer_type)(UINT32, POINTER_INPUT_TYPE*) = nullptr; + BOOL (*get_pointer_touch_info)(UINT32, POINTER_TOUCH_INFO*) = nullptr; + BOOL (*get_pointer_pen_info)(UINT32, POINTER_PEN_INFO*) = nullptr; +}; + +StylusInputState& stylus_input_state() +{ + static StylusInputState state; + return state; +} + +} + +void initialize_stylus_input() +{ + auto& state = stylus_input_state(); + HMODULE dll = LoadLibrary(L"User32.dll"); + if (!dll) + { + LOG("cannot load User32.dll"); + return; + } + + LOG("loaded User32.dll"); + state.get_pointer_info = + reinterpret_cast(GetProcAddress(dll, "GetPointerInfo")); + state.get_pointer_type = + reinterpret_cast(GetProcAddress(dll, "GetPointerType")); + state.get_pointer_touch_info = + reinterpret_cast(GetProcAddress(dll, "GetPointerTouchInfo")); + state.get_pointer_pen_info = + reinterpret_cast(GetProcAddress(dll, "GetPointerPenInfo")); +} + +void update_stylus_state(float dt) +{ + auto& state = stylus_input_state(); + state.timer_stylus += dt; + state.timer_ink_touch += dt; + state.timer_ink_pen += dt; + + if (state.timer_stylus > 0.1f && (WacomTablet::I.m_stylus || WacomTablet::I.m_eraser)) + { + WacomTablet::I.m_stylus = false; + WacomTablet::I.m_eraser = false; + App::I->redraw = true; + } + if (state.timer_ink_pen > 0.1f && WacomTablet::I.m_ink_pen) + { + WacomTablet::I.m_ink_pen = false; + App::I->redraw = true; + } + if (state.timer_ink_touch > 0.1f && WacomTablet::I.m_ink_touch) + { + WacomTablet::I.m_ink_touch = false; + App::I->redraw = true; + } +} + +void note_wintab_packet() +{ + auto& state = stylus_input_state(); + App::I->set_stylus(); + state.timer_stylus = 0.0f; +} + +void handle_pointer_update_message(WPARAM wp) +{ + auto& state = stylus_input_state(); + if (!state.get_pointer_info) + return; + + POINTER_TOUCH_INFO touch_info {}; + POINTER_PEN_INFO pen_info {}; + POINTER_INFO pointer_info {}; + const UINT32 pointer_id = GET_POINTERID_WPARAM(wp); + POINTER_INPUT_TYPE pointer_type = PT_POINTER; + + if (!state.get_pointer_info(pointer_id, &pointer_info)) + return; + + if (!state.get_pointer_type(pointer_id, &pointer_type)) + pointer_type = PT_POINTER; + + switch (pointer_type) + { + case PT_TOUCH: + if (!state.get_pointer_touch_info(pointer_id, &touch_info)) + return; + + state.timer_ink_touch = 0.0f; + WacomTablet::I.m_ink_touch = true; + WacomTablet::I.m_pen_pres = 1.0f; + return; + + case PT_PEN: + if (!state.get_pointer_pen_info(pointer_id, &pen_info)) + return; + + state.timer_ink_pen = 0.0f; + WacomTablet::I.m_ink_pen = true; + WacomTablet::I.m_pen_pres = static_cast(pen_info.pressure) / 1024.0f; + App::I->set_stylus(); + return; + + default: + return; + } +} + +} diff --git a/src/platform_windows/windows_stylus_input.h b/src/platform_windows/windows_stylus_input.h new file mode 100644 index 00000000..9c9d1d9d --- /dev/null +++ b/src/platform_windows/windows_stylus_input.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace pp::platform::windows { + +void initialize_stylus_input(); +void update_stylus_state(float dt); +void note_wintab_packet(); +void handle_pointer_update_message(WPARAM wp); + +}