diff --git a/CMakeLists.txt b/CMakeLists.txt index 257cdd8..ff8ecb8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -240,6 +240,7 @@ target_link_libraries(pp_platform_api add_library(pp_app_core STATIC src/app_core/about_menu.h + src/app_core/app_frame.h src/app_core/app_preferences.h src/app_core/app_status.h src/app_core/app_startup.h diff --git a/docs/modernization/build-inventory.md b/docs/modernization/build-inventory.md index 0ac49f2..ea01c9c 100644 --- a/docs/modernization/build-inventory.md +++ b/docs/modernization/build-inventory.md @@ -241,6 +241,11 @@ Known local toolchain state: retained shader loading, asset initialization, layout creation, title updates, UI render-target creation, recording startup, VR-controller state mutation, settings writes, and license-warning dialogs remain legacy execution. +- `src/app_core/app_frame.h` owns the current initial surface, update-gating, + and draw-pass decisions consumed by `App::create`, `App::update`, + `App::draw`, and `pano_cli plan-app-frame`; retained layout traversal, + toolbar widget writes, canvas stroke drawing, VR UI render-target drawing, + main target binding, and OpenGL/UI drawing remain in the legacy app. - `src/legacy_app_preference_services.*` is the current app-shell bridge for options-menu preference execution. It keeps UI scale, viewport scale, RTL, VR mode, VR-controller, auto-timelapse, and canvas cursor-mode callbacks on @@ -824,6 +829,9 @@ Known local toolchain state: planning, optional auto-timelapse/license/VR-controller decisions, negative and overflow run-counter rejection, stable full startup dispatch ordering, split persistence/runtime dispatch, and malformed startup-plan rejection. +- `pp_app_core_app_frame_tests` covers the legacy initial surface default, + idle/redraw/animation update gating, canvas-stroke draw eligibility, VR UI + visibility, main UI suppression in VR-only mode, and redraw reset planning. - `pp_platform_api_tests` covers service dispatch for clipboard read/write, empty clipboard writes, cursor visibility, virtual-keyboard visibility, external file display, file sharing, VR lifecycle, layout/asset file load diff --git a/docs/modernization/debt.md b/docs/modernization/debt.md index 7af1348..e69d668 100644 --- a/docs/modernization/debt.md +++ b/docs/modernization/debt.md @@ -20,7 +20,7 @@ agent or engineer to remove them without reconstructing context from chat. - 2026-06-04: DEBT-0009 was narrowed. `platform-build.ps1` and `platform-build.sh` now include the current headless component/test matrix, - including brush-package coverage and the app-core startup/file/document/ + including brush-package coverage and the app-core startup/frame/file/document/ brush/canvas/history/grid/toolbar/tools/about/preferences/status automation tests, and `panopainter_platform_build_target_matrix_self_test` now verifies the wrapper defaults against the current CMake test executables. On @@ -91,6 +91,12 @@ agent or engineer to remove them without reconstructing context from chat. the retained OpenGL startup task in place, then delegates startup resources and runtime side effects through the startup bridge. `pano_cli plan-app-startup-resources` exposes the resource path for automation. +- 2026-06-05: DEBT-0003 was narrowed. Initial surface sizing, redraw/animation + update gating, canvas-stroke draw eligibility, VR UI pass selection, main UI + pass selection, and redraw reset are now tested `pp_app_core` frame plans + consumed by `App::create`, `App::update`, `App::draw`, and + `pano_cli plan-app-frame`; retained layout traversal, toolbar widget writes, + and OpenGL/UI drawing remain in the legacy app. - 2026-06-04: DEBT-0036 was narrowed again. Canvas stroke commit, thumbnail, and object-draw history paths now query saved blend state through tested `pp_renderer_gl` capability-state dispatch; CanvasLayer equirect diff --git a/docs/modernization/roadmap.md b/docs/modernization/roadmap.md index 84ba0a8..f071c80 100644 --- a/docs/modernization/roadmap.md +++ b/docs/modernization/roadmap.md @@ -198,6 +198,11 @@ and floating-point render targets; `App::title_update`, `App::update_memory_usage`, `App::update_rec_frames`, resolution helpers, `App::initLayout`, and `pano_cli plan-app-status` consume those contracts while legacy UI nodes still render the strings and status lights. +Frame-level app decisions for the initial surface size, redraw/animation update +gating, canvas-stroke drawing, VR UI drawing, main UI drawing, and redraw reset +now live in `pp_app_core`; `App::create`, `App::update`, `App::draw`, and +`pano_cli plan-app-frame` consume those plans while retained layout traversal +and OpenGL/UI drawing stay in the legacy app. `panopainter_app` is now a real static target that owns app orchestration sources, app version metadata, and version-header generation. `pp_panopainter_ui` now owns app-specific modal, dialog, panel, canvas, @@ -1286,7 +1291,7 @@ standard x64/arm64, Android Quest arm64, Android Focus/Wave arm64, Emscripten/WebGL, macOS, iOS device, and iOS simulator. `platform-build` automation now builds the current headless component matrix, including `pp_platform_api`, `pp_app_core`, platform API tests, brush-package tests, and -the current app-core startup/file/document/brush/canvas/history/grid/toolbar/ +the current app-core startup/frame/file/document/brush/canvas/history/grid/toolbar/ tools/about/preferences/status automation tests. The PowerShell wrapper also normalizes comma-separated `-Presets` and `-Targets` values for reliable machine-driven partial matrix checks. `panopainter_platform_build_target_matrix_self_test` @@ -1643,6 +1648,11 @@ Results: `pano_cli_plan_app_startup_rejects_negative_counter`, with startup resource sequencing also covered by `pano_cli_plan_app_startup_resources_smoke` and `pano_cli_plan_app_startup_resources_rejects_bad_size`. +- `PanoPainter`, `pp_app_core_app_frame_tests`, and `pano_cli` built after + app frame surface/update/draw-pass decisions moved into `pp_app_core`. +- Focused frame CTest coverage passed for `pp_app_core_app_frame_tests`, + `pano_cli_plan_app_frame_vr_smoke`, and + `pano_cli_plan_app_frame_idle_missing_canvas_smoke`. - `PanoPainter`, `pp_app_core_brush_package_export_tests`, and `pano_cli` built after PPBR brush package export request validation and dispatch moved behind app-core brush package services. diff --git a/scripts/automation/platform-build.ps1 b/scripts/automation/platform-build.ps1 index 4c54325..19d5403 100644 --- a/scripts/automation/platform-build.ps1 +++ b/scripts/automation/platform-build.ps1 @@ -42,6 +42,7 @@ param( "pp_ui_core_layout_xml_tests", "pp_app_core_about_menu_tests", "pp_app_core_app_preferences_tests", + "pp_app_core_app_frame_tests", "pp_app_core_app_startup_tests", "pp_app_core_app_status_tests", "pp_app_core_brush_package_export_tests", @@ -49,6 +50,7 @@ param( "pp_app_core_brush_ui_tests", "pp_app_core_canvas_hotkey_tests", "pp_app_core_canvas_tool_ui_tests", + "pp_app_core_canvas_view_tests", "pp_app_core_document_animation_tests", "pp_app_core_document_canvas_tests", "pp_app_core_document_cloud_tests", diff --git a/scripts/automation/platform-build.sh b/scripts/automation/platform-build.sh index 9ada203..fa040db 100644 --- a/scripts/automation/platform-build.sh +++ b/scripts/automation/platform-build.sh @@ -3,7 +3,7 @@ set -u presets="${1:-android-arm64 android-x64 android-quest-arm64 android-focus-arm64}" shift || true -targets="${*:-pp_foundation pp_assets pp_paint pp_document pp_renderer_api pp_renderer_gl pp_paint_renderer pp_ui_core pp_platform_api pp_app_core pano_cli pp_foundation_binary_stream_tests pp_foundation_event_tests pp_foundation_log_tests pp_foundation_parse_tests pp_foundation_task_queue_tests pp_foundation_trace_tests pp_assets_brush_package_tests pp_assets_image_format_tests pp_assets_image_metadata_tests pp_assets_image_pixels_tests pp_assets_ppi_header_tests pp_assets_settings_document_tests pp_paint_brush_tests pp_paint_blend_tests pp_paint_stroke_tests pp_paint_stroke_script_tests pp_document_tests pp_document_ppi_import_tests pp_document_ppi_export_tests pp_renderer_api_tests pp_renderer_gl_capabilities_tests pp_renderer_gl_command_plan_tests pp_paint_renderer_compositor_tests pp_platform_api_tests pp_ui_core_color_tests pp_ui_core_layout_value_tests pp_ui_core_layout_xml_tests pp_app_core_about_menu_tests pp_app_core_app_preferences_tests pp_app_core_app_startup_tests pp_app_core_app_status_tests pp_app_core_brush_package_export_tests pp_app_core_brush_package_import_tests pp_app_core_brush_ui_tests pp_app_core_canvas_hotkey_tests pp_app_core_canvas_tool_ui_tests pp_app_core_document_animation_tests pp_app_core_document_canvas_tests pp_app_core_document_cloud_tests pp_app_core_document_export_tests pp_app_core_document_import_tests pp_app_core_document_layer_tests pp_app_core_document_platform_io_tests pp_app_core_document_recording_tests pp_app_core_document_resize_tests pp_app_core_document_route_tests pp_app_core_document_sharing_tests pp_app_core_document_session_tests pp_app_core_file_menu_tests pp_app_core_grid_ui_tests pp_app_core_history_ui_tests pp_app_core_main_toolbar_tests pp_app_core_quick_ui_tests pp_app_core_tools_menu_tests}" +targets="${*:-pp_foundation pp_assets pp_paint pp_document pp_renderer_api pp_renderer_gl pp_paint_renderer pp_ui_core pp_platform_api pp_app_core pano_cli pp_foundation_binary_stream_tests pp_foundation_event_tests pp_foundation_log_tests pp_foundation_parse_tests pp_foundation_task_queue_tests pp_foundation_trace_tests pp_assets_brush_package_tests pp_assets_image_format_tests pp_assets_image_metadata_tests pp_assets_image_pixels_tests pp_assets_ppi_header_tests pp_assets_settings_document_tests pp_paint_brush_tests pp_paint_blend_tests pp_paint_stroke_tests pp_paint_stroke_script_tests pp_document_tests pp_document_ppi_import_tests pp_document_ppi_export_tests pp_renderer_api_tests pp_renderer_gl_capabilities_tests pp_renderer_gl_command_plan_tests pp_paint_renderer_compositor_tests pp_platform_api_tests pp_ui_core_color_tests pp_ui_core_layout_value_tests pp_ui_core_layout_xml_tests pp_app_core_about_menu_tests pp_app_core_app_preferences_tests pp_app_core_app_frame_tests pp_app_core_app_startup_tests pp_app_core_app_status_tests pp_app_core_brush_package_export_tests pp_app_core_brush_package_import_tests pp_app_core_brush_ui_tests pp_app_core_canvas_hotkey_tests pp_app_core_canvas_tool_ui_tests pp_app_core_canvas_view_tests pp_app_core_document_animation_tests pp_app_core_document_canvas_tests pp_app_core_document_cloud_tests pp_app_core_document_export_tests pp_app_core_document_import_tests pp_app_core_document_layer_tests pp_app_core_document_platform_io_tests pp_app_core_document_recording_tests pp_app_core_document_resize_tests pp_app_core_document_route_tests pp_app_core_document_sharing_tests pp_app_core_document_session_tests pp_app_core_file_menu_tests pp_app_core_grid_ui_tests pp_app_core_history_ui_tests pp_app_core_main_toolbar_tests pp_app_core_quick_ui_tests pp_app_core_tools_menu_tests}" start="$(date +%s)" overall_exit=0 diff --git a/src/app.cpp b/src/app.cpp index 7f5d45e..b3c4174 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -5,6 +5,7 @@ #include "node_dialog_open.h" #include "node_progress_bar.h" #include "mp4enc.h" +#include "app_core/app_frame.h" #include "app_core/app_status.h" #include "app_core/app_startup.h" #include "app_core/canvas_tool_ui.h" @@ -182,8 +183,9 @@ bool App::ui_running = false; void App::create() { - width = 1920/2; - height = 1080/2; + const auto initial_surface = pp::app::plan_app_initial_surface(); + width = initial_surface.width; + height = initial_surface.height; } void App::open_document(std::string path) @@ -540,13 +542,20 @@ bool App::update_ui_observer(Node *n) void App::draw(float dt) { + const auto draw_plan = pp::app::plan_app_frame_draw( + canvas != nullptr, + canvas && canvas->m_canvas, + vr_active, + ui_visible, + vr_only); + // update offscreen stuff - if (canvas && canvas->m_canvas) + if (draw_plan.draw_canvas_stroke) canvas->m_canvas->stroke_draw(); auto observer = std::bind(&App::update_ui_observer, this, std::placeholders::_1); - if (vr_active && ui_visible) + if (draw_plan.draw_vr_ui) { uirtt.bindFramebuffer(); uirtt.clear(); @@ -564,7 +573,7 @@ void App::draw(float dt) uirtt.unbindFramebuffer(); } - if (!vr_only) + if (draw_plan.draw_main_ui) { bind_main_render_target(); apply_app_viewport(pp::renderer::gl::OpenGlViewportRect { @@ -582,7 +591,8 @@ void App::draw(float dt) apply_app_scissor_test(false); } - redraw = false; + if (draw_plan.reset_redraw) + redraw = false; } void App::update(float dt) @@ -592,15 +602,19 @@ void App::update(float dt) // avoid multiple threads to update the scene //std::lock_guard lock(mutex); - if (!(redraw || animate)) + const auto update_plan = pp::app::plan_app_frame_update(redraw, animate); + if (!update_plan.update_frame) return; - if (auto* main = layout[main_id]) + if (auto* main = layout[main_id]; update_plan.update_layouts && main) main->update(width, height, zoom); - if (auto* main = layout_designer[main_id]) + if (auto* main = layout_designer[main_id]; update_plan.update_layouts && main) main->update(width, height, zoom); + if (!update_plan.refresh_canvas_toolbar) + return; + { auto mode = Canvas::I->m_current_mode; diff --git a/src/app_core/app_frame.h b/src/app_core/app_frame.h new file mode 100644 index 0000000..b93cbe4 --- /dev/null +++ b/src/app_core/app_frame.h @@ -0,0 +1,56 @@ +#pragma once + +namespace pp::app { + +struct AppInitialSurfacePlan { + float width = 960.0F; + float height = 540.0F; +}; + +struct AppFrameUpdatePlan { + bool update_frame = false; + bool update_layouts = false; + bool refresh_canvas_toolbar = false; +}; + +struct AppFrameDrawPlan { + bool draw_canvas_stroke = false; + bool draw_vr_ui = false; + bool draw_main_ui = true; + bool reset_redraw = true; +}; + +[[nodiscard]] constexpr AppInitialSurfacePlan plan_app_initial_surface() noexcept +{ + return AppInitialSurfacePlan { + .width = 1920.0F / 2.0F, + .height = 1080.0F / 2.0F, + }; +} + +[[nodiscard]] constexpr AppFrameUpdatePlan plan_app_frame_update(bool redraw, bool animate) noexcept +{ + const bool update_frame = redraw || animate; + return AppFrameUpdatePlan { + .update_frame = update_frame, + .update_layouts = update_frame, + .refresh_canvas_toolbar = update_frame, + }; +} + +[[nodiscard]] constexpr AppFrameDrawPlan plan_app_frame_draw( + bool has_canvas_node, + bool has_canvas_document, + bool vr_active, + bool ui_visible, + bool vr_only) noexcept +{ + return AppFrameDrawPlan { + .draw_canvas_stroke = has_canvas_node && has_canvas_document, + .draw_vr_ui = vr_active && ui_visible, + .draw_main_ui = !vr_only, + .reset_redraw = true, + }; +} + +} // namespace pp::app diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9a7800f..ea5b7e7 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -555,6 +555,16 @@ add_test(NAME pp_app_core_app_startup_tests COMMAND pp_app_core_app_startup_test set_tests_properties(pp_app_core_app_startup_tests PROPERTIES LABELS "app;desktop-fast;fuzz") +add_executable(pp_app_core_app_frame_tests + app_core/app_frame_tests.cpp) +target_link_libraries(pp_app_core_app_frame_tests PRIVATE + pp_app_core + pp_test_harness) + +add_test(NAME pp_app_core_app_frame_tests COMMAND pp_app_core_app_frame_tests) +set_tests_properties(pp_app_core_app_frame_tests PROPERTIES + LABELS "app;desktop-fast;fuzz") + add_executable(pp_app_core_document_sharing_tests app_core/document_sharing_tests.cpp) target_link_libraries(pp_app_core_document_sharing_tests PRIVATE @@ -947,6 +957,18 @@ if(TARGET pano_cli) WILL_FAIL TRUE PASS_REGULAR_EXPRESSION "\"command\":\"plan-app-startup-resources\".*\"message\":\"startup resource dimensions") + add_test(NAME pano_cli_plan_app_frame_vr_smoke + COMMAND pano_cli plan-app-frame --redraw --vr-active) + set_tests_properties(pano_cli_plan_app_frame_vr_smoke PROPERTIES + LABELS "app;integration;desktop-fast" + PASS_REGULAR_EXPRESSION "\"command\":\"plan-app-frame\".*\"surface\":\\{\"width\":960,\"height\":540\\}.*\"updateFrame\":true.*\"updateLayouts\":true.*\"refreshCanvasToolbar\":true.*\"drawCanvasStroke\":true.*\"drawVrUi\":true.*\"drawMainUi\":true.*\"resetRedraw\":true") + + add_test(NAME pano_cli_plan_app_frame_idle_missing_canvas_smoke + COMMAND pano_cli plan-app-frame --no-canvas --ui-hidden --vr-only) + set_tests_properties(pano_cli_plan_app_frame_idle_missing_canvas_smoke PROPERTIES + LABELS "app;integration;desktop-fast;fuzz" + PASS_REGULAR_EXPRESSION "\"command\":\"plan-app-frame\".*\"updateFrame\":false.*\"drawCanvasStroke\":false.*\"drawVrUi\":false.*\"drawMainUi\":false.*\"resetRedraw\":true") + add_test(NAME pano_cli_plan_brush_package_import_ppbr_smoke COMMAND pano_cli plan-brush-package-import --kind ppbr diff --git a/tests/app_core/app_frame_tests.cpp b/tests/app_core/app_frame_tests.cpp new file mode 100644 index 0000000..bf71599 --- /dev/null +++ b/tests/app_core/app_frame_tests.cpp @@ -0,0 +1,78 @@ +#include "app_core/app_frame.h" +#include "test_harness.h" + +namespace { + +void initial_surface_matches_legacy_default(pp::tests::Harness& harness) +{ + const auto plan = pp::app::plan_app_initial_surface(); + + PP_EXPECT(harness, plan.width == 960.0F); + PP_EXPECT(harness, plan.height == 540.0F); +} + +void update_plan_skips_idle_frames(pp::tests::Harness& harness) +{ + const auto plan = pp::app::plan_app_frame_update(false, false); + + PP_EXPECT(harness, !plan.update_frame); + PP_EXPECT(harness, !plan.update_layouts); + PP_EXPECT(harness, !plan.refresh_canvas_toolbar); +} + +void update_plan_refreshes_redraw_or_animated_frames(pp::tests::Harness& harness) +{ + const auto redraw = pp::app::plan_app_frame_update(true, false); + const auto animated = pp::app::plan_app_frame_update(false, true); + + PP_EXPECT(harness, redraw.update_frame); + PP_EXPECT(harness, redraw.update_layouts); + PP_EXPECT(harness, redraw.refresh_canvas_toolbar); + PP_EXPECT(harness, animated.update_frame); + PP_EXPECT(harness, animated.update_layouts); + PP_EXPECT(harness, animated.refresh_canvas_toolbar); +} + +void draw_plan_selects_canvas_and_ui_passes(pp::tests::Harness& harness) +{ + const auto plan = pp::app::plan_app_frame_draw(true, true, true, true, false); + + PP_EXPECT(harness, plan.draw_canvas_stroke); + PP_EXPECT(harness, plan.draw_vr_ui); + PP_EXPECT(harness, plan.draw_main_ui); + PP_EXPECT(harness, plan.reset_redraw); +} + +void draw_plan_skips_missing_canvas_document(pp::tests::Harness& harness) +{ + const auto missing_node = pp::app::plan_app_frame_draw(false, true, false, true, false); + const auto missing_document = pp::app::plan_app_frame_draw(true, false, false, true, false); + + PP_EXPECT(harness, !missing_node.draw_canvas_stroke); + PP_EXPECT(harness, !missing_document.draw_canvas_stroke); +} + +void draw_plan_respects_vr_visibility_modes(pp::tests::Harness& harness) +{ + const auto hidden_vr_ui = pp::app::plan_app_frame_draw(true, true, true, false, false); + const auto vr_only = pp::app::plan_app_frame_draw(true, true, false, true, true); + + PP_EXPECT(harness, !hidden_vr_ui.draw_vr_ui); + PP_EXPECT(harness, hidden_vr_ui.draw_main_ui); + PP_EXPECT(harness, !vr_only.draw_vr_ui); + PP_EXPECT(harness, !vr_only.draw_main_ui); +} + +} // namespace + +int main() +{ + pp::tests::Harness harness; + harness.run("initial surface matches legacy default", initial_surface_matches_legacy_default); + harness.run("update plan skips idle frames", update_plan_skips_idle_frames); + harness.run("update plan refreshes redraw or animated frames", update_plan_refreshes_redraw_or_animated_frames); + harness.run("draw plan selects canvas and UI passes", draw_plan_selects_canvas_and_ui_passes); + harness.run("draw plan skips missing canvas document", draw_plan_skips_missing_canvas_document); + harness.run("draw plan respects VR visibility modes", draw_plan_respects_vr_visibility_modes); + return harness.finish(); +} diff --git a/tools/pano_cli/main.cpp b/tools/pano_cli/main.cpp index ac16f5e..2902f37 100644 --- a/tools/pano_cli/main.cpp +++ b/tools/pano_cli/main.cpp @@ -1,5 +1,6 @@ #include "app_core/about_menu.h" #include "app_core/app_preferences.h" +#include "app_core/app_frame.h" #include "app_core/app_status.h" #include "app_core/app_startup.h" #include "app_core/brush_package_import.h" @@ -245,6 +246,16 @@ struct PlanAppStartupResourcesArgs { bool bad_size = false; }; +struct PlanAppFrameArgs { + bool redraw = false; + bool animate = false; + bool has_canvas_node = true; + bool has_canvas_document = true; + bool vr_active = false; + bool ui_visible = true; + bool vr_only = false; +}; + struct PlanBrushPackageExportArgs { std::string path; std::string author; @@ -2010,6 +2021,7 @@ void print_help() << " plan-app-preferences [--ui-scale N] [--display-density N] [--current-scale N] [--scale-option N] [--viewport-scale N] [--rtl] [--timelapse-disabled] [--recording-running] [--vr-controllers-disabled] [--cursor-mode N]\n" << " plan-app-startup [--run-counter N] [--auto-timelapse-disabled] [--vr-controllers-disabled] [--license-invalid]\n" << " plan-app-startup-resources [--width N] [--height N] [--bad-size]\n" + << " plan-app-frame [--redraw] [--animate] [--no-canvas] [--no-canvas-document] [--vr-active] [--ui-hidden] [--vr-only]\n" << " plan-app-status [--doc-name NAME] [--unsaved] [--resolution N] [--resolution-index N] [--zoom N] [--history-bytes N] [--recording-running] [--encoder-available] [--encoded-frames N] [--framebuffer-fetch] [--float32] [--float32-linear] [--float16]\n" << " plan-brush-package-import --kind abr|ppbr --path FILE\n" << " plan-brush-package-export --path FILE [--author NAME] [--email EMAIL] [--url URL] [--description TEXT] [--dest-path DIR] [--export-data|--no-export-data] [--header-image]\n" @@ -3715,6 +3727,68 @@ int plan_app_startup_resources(int argc, char** argv) return 0; } +pp::foundation::Status parse_plan_app_frame_args( + int argc, + char** argv, + PlanAppFrameArgs& args) +{ + for (int i = 2; i < argc; ++i) { + const std::string_view key(argv[i]); + if (key == "--redraw") { + args.redraw = true; + } else if (key == "--animate") { + args.animate = true; + } else if (key == "--no-canvas") { + args.has_canvas_node = false; + args.has_canvas_document = false; + } else if (key == "--no-canvas-document") { + args.has_canvas_document = false; + } else if (key == "--vr-active") { + args.vr_active = true; + } else if (key == "--ui-hidden") { + args.ui_visible = false; + } else if (key == "--vr-only") { + args.vr_only = true; + } else { + return pp::foundation::Status::invalid_argument("unknown option"); + } + } + + return pp::foundation::Status::success(); +} + +int plan_app_frame(int argc, char** argv) +{ + PlanAppFrameArgs args; + const auto status = parse_plan_app_frame_args(argc, argv, args); + if (!status.ok()) { + print_error("plan-app-frame", status.message); + return 2; + } + + const auto surface = pp::app::plan_app_initial_surface(); + const auto update = pp::app::plan_app_frame_update(args.redraw, args.animate); + const auto draw = pp::app::plan_app_frame_draw( + args.has_canvas_node, + args.has_canvas_document, + args.vr_active, + args.ui_visible, + args.vr_only); + + std::cout << "{\"ok\":true,\"command\":\"plan-app-frame\"" + << ",\"surface\":{\"width\":" << surface.width + << ",\"height\":" << surface.height + << "},\"update\":{\"updateFrame\":" << json_bool(update.update_frame) + << ",\"updateLayouts\":" << json_bool(update.update_layouts) + << ",\"refreshCanvasToolbar\":" << json_bool(update.refresh_canvas_toolbar) + << "},\"draw\":{\"drawCanvasStroke\":" << json_bool(draw.draw_canvas_stroke) + << ",\"drawVrUi\":" << json_bool(draw.draw_vr_ui) + << ",\"drawMainUi\":" << json_bool(draw.draw_main_ui) + << ",\"resetRedraw\":" << json_bool(draw.reset_redraw) + << "}}\n"; + return 0; +} + pp::foundation::Status parse_plan_brush_package_import_args( int argc, char** argv, @@ -9962,6 +10036,10 @@ int main(int argc, char** argv) return plan_app_startup_resources(argc, argv); } + if (command == "plan-app-frame") { + return plan_app_frame(argc, argv); + } + if (command == "plan-brush-package-import") { return plan_brush_package_import(argc, argv); }