diff --git a/CMakeLists.txt b/CMakeLists.txt index 77aa735..8f0e387 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -234,6 +234,8 @@ add_library(pp_platform_api STATIC src/platform_api/asset_file_load_policy.h src/platform_api/network_tls_policy.cpp src/platform_api/network_tls_policy.h + src/platform_api/platform_policy.cpp + src/platform_api/platform_policy.h src/platform_api/platform_services.cpp src/platform_api/platform_services.h) target_include_directories(pp_platform_api diff --git a/docs/modernization/build-inventory.md b/docs/modernization/build-inventory.md index 54ceb8c..4c58a09 100644 --- a/docs/modernization/build-inventory.md +++ b/docs/modernization/build-inventory.md @@ -739,6 +739,14 @@ powershell -ExecutionPolicy Bypass -File scripts\automation\apple-remote-build.p and cloud browse-dialog curl sites now consume the same default platform TLS policy helper in `pp_platform_api` instead of spelling Android branches locally; + retained platform-family decisions for exported-image publishing, + persistent-storage flushing, document browse roots, working-directory picker + availability, prepared-file target planning, work-directory collection export + policy, PPBR data-directory override policy, SonarPen availability, live + asset reload policy, recording cleanup policy, default canvas resolution, and + canvas tip visibility now live in the tested `platform_policy` catalog and + are consumed by both `WindowsPlatformServices` and the retained non-Windows + fallback; Windows live app execution now uses injected `WindowsPlatformServices` from @@ -908,7 +916,8 @@ powershell -ExecutionPolicy Bypass -File scripts\automation\apple-remote-build.p - `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 - policy, and picker callbacks without platform SDK headers or a window. + policy, platform-family export/storage/browse/prepared-file/canvas policies, + and picker callbacks without platform SDK headers or a window. - `pp_app_core_document_cloud_tests` covers cloud upload no-canvas, new-document warning, clean publish prompt, and dirty save-before-upload decisions, plus cloud browse no-canvas/show-browser and selected-download @@ -1025,8 +1034,9 @@ Known warnings after the current CMake app build: should shrink as app painting and document behavior consume `pp_paint` and `pp_document` directly. Shared `canvas.h` no longer owns the platform `CANVAS_RES` macro; default canvas allocation now asks `PlatformServices`, - preserving the WebGL 512 default in the legacy fallback while DEBT-0057 - tracks moving that policy into an injected Web platform service. + preserving the WebGL 512 default through the tested `pp_platform_api` + platform policy while DEBT-0057 tracks moving that policy into an injected + Web platform service. - `pp_legacy_engine` intentionally contains retained legacy runtime shell sources for now, so it concentrates existing legacy tablet, video, HMD, log, and low-level utility warnings until those paths move to cleaner component diff --git a/docs/modernization/debt.md b/docs/modernization/debt.md index 9457437..1b1ed37 100644 --- a/docs/modernization/debt.md +++ b/docs/modernization/debt.md @@ -224,6 +224,16 @@ agent or engineer to remove them without reconstructing context from chat. Retained canvas/video export calls, Web prepared-file handoff, picker execution, export-directory creation, and desktop timelapse worker threading remain open. +- 2026-06-05: DEBT-0050, DEBT-0051, DEBT-0053, and DEBT-0057 were narrowed. + `pp_platform_api` now owns a tested `platform_policy` catalog for retained + platform-family decisions covering iOS exported-image publishing, WebGL + persistent-storage flushing, iOS document browse Inbox roots, Windows/macOS + working-directory picker availability, iOS/Web prepared-file target + selection, iOS collection-export working-directory policy, macOS PPBR data + directory override, SonarPen availability, WebGL default canvas resolution, + live asset reload policy, recording cleanup policy, and canvas tip visibility. + `WindowsPlatformServices` and the non-Windows legacy fallback consume those + helpers while SDK-specific execution remains open in platform shells. - 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 @@ -287,14 +297,14 @@ agent or engineer to remove them without reconstructing context from chat. | DEBT-0047 | Open | Modernization | PPBR brush package export request validation, success-dialog metadata, and execution dispatch now consume pure `pp_app_core` through `App::dialog_ppbr_export`, `pano_cli plan-brush-package-export`, `BrushPackageExportServices`, and `src/legacy_brush_package_export_services.*`; PPBR header/path planning now consumes `pp_assets::brush_package`, and the macOS data-directory override now routes through `PlatformServices`, but the bridge still reads `NodeDialogExportPPBR`, carries the legacy `Image` header object outside the pure request, converts to `NodePanelBrushPreset::PPBRInfo`, calls `NodePanelBrushPreset::export_ppbr`, owns desktop worker-thread dispatch, dialog destruction, and mobile/Web completion directly | Preserve current PPBR export behavior while brush assets, PPBR serialization, picker completion, and UI lifetime move toward asset/storage/UI/platform services | `pp_assets_brush_package_tests`; `pp_app_core_brush_package_export_tests`; `pp_platform_api_tests`; `pano_cli plan-brush-package-export --path D:/Paint/clouds.ppbr --author Artist --dest-path D:/Paint/BrushPreviews --export-data --header-image`; `pano_cli plan-brush-package-export --path D:/Paint/clouds.ppbr`; `pano_cli plan-brush-package-export`; `pano_cli plan-brush-package-export --path clouds`; `pano_cli plan-brush-package-export --path D:/Paint/clouds.ppbr --dest-path D:/Paint/BrushPreviews --no-export-data`; `ctest --preset desktop-fast --build-config Debug`; `cmake --build --preset windows-msvc-default --config Debug --target PanoPainter` | PPBR metadata collection, header-image ownership, serialization, picker-selected path execution, desktop threading, dialog lifetime, and mobile/Web completion are owned by injected brush asset/storage/UI/platform services with `App::dialog_ppbr_export` acting only as a UI adapter | | DEBT-0048 | Open | Modernization | ABR/PPBR brush package import execution now consumes pure `pp_app_core` through document-open confirmation callbacks, `pano_cli plan-brush-package-import`, `BrushPackageImportServices`, and `src/legacy_brush_package_import_services.*`; imported brush tip/pattern target paths now consume `pp_assets::brush_package`, but the bridge still launches detached legacy `NodePanelBrushPreset::import_abr`/`import_ppbr` worker threads and depends on the legacy preset panel as the importer/storage owner | Preserve current brush import behavior while brush package parsing, preset storage, progress/error reporting, and UI refresh move toward asset/paint/UI services | `pp_assets_brush_package_tests`; `pp_app_core_brush_package_import_tests`; `pano_cli plan-brush-package-import --kind ppbr --path D:/Paint/Brushes/clouds.ppbr`; `pano_cli plan-brush-package-import --kind abr --path D:/Paint/Brushes/clouds.abr`; `pano_cli plan-brush-package-import --kind ppbr`; `ctest --preset desktop-fast --build-config Debug`; `cmake --build --preset windows-msvc-default --config Debug --target PanoPainter` | ABR/PPBR parsing, preset creation/storage, import threading/progress, duplicate asset policy, and UI refresh are owned by injected brush asset/paint/UI services with document-open callbacks only confirming user intent | | DEBT-0049 | Open | Modernization | `pp_assets::validate_ppbr_header` intentionally preserves the legacy PPBR version check from `NodePanelBrushPreset::import_ppbr`, which accepts files when either major is `0` or minor is `1` instead of requiring exactly version `0.1` | Avoid rejecting existing brush packages before compatibility fixtures prove the stricter rule is safe | `pp_assets_brush_package_tests`; `pano_cli plan-brush-package-export --path D:/Paint/clouds.ppbr`; `ctest --preset desktop-fast --build-config Debug`; `cmake --build --preset windows-msvc-default --config Debug --target PanoPainter` | Add PPBR compatibility fixtures for accepted/rejected historical package versions, then require canonical `0.1` or an explicit supported-version matrix and update live import accordingly | -| DEBT-0050 | Open | Modernization | iOS exported-image photo-library publishing and WebGL persistent-storage flushing now dispatch through `PlatformServices`, but non-Windows execution still lives in `src/platform_legacy/legacy_platform_services.*` and forwards to retained `save_image_library`/`webgl_sync` bridges | Preserve current iOS/Web export and save behavior while the Apple/Web platform shells are extracted incrementally | `pp_platform_api_tests`; `ctest --preset desktop-fast --build-config Debug`; platform package smoke once Apple/Web root builds exist | Exported-image publishing and persistent-storage flushing are owned by injected Apple/Web `pp_platform_*` services with no legacy adapter branch | -| DEBT-0051 | Open | Modernization | Document browser search roots and Browse dialog working-directory picker visibility/path formatting now dispatch through `PlatformServices`, but iOS `Inbox` inclusion and macOS directory picker/display-path behavior still live in `src/platform_legacy/legacy_platform_services.*` | Preserve current iOS document import/browse and desktop browse picker behavior while Apple platform shells are extracted incrementally | `pp_platform_api_tests`; `ctest --preset desktop-fast --build-config Debug`; Apple package smoke once root Apple builds exist | Document browse roots and browse-directory picker/display formatting are owned by injected Apple and desktop `pp_platform_*` services with no legacy adapter branch | +| DEBT-0050 | Open | Modernization | iOS exported-image photo-library publishing and WebGL persistent-storage flushing now dispatch through `PlatformServices`; the iOS/Web policy decision lives in tested `pp_platform_api::platform_policy`, but non-Windows execution still lives in `src/platform_legacy/legacy_platform_services.*` and forwards to retained `save_image_library`/`webgl_sync` bridges | Preserve current iOS/Web export and save behavior while the Apple/Web platform shells are extracted incrementally | `pp_platform_api_tests`; `ctest --preset desktop-fast --build-config Debug`; platform package smoke once Apple/Web root builds exist | Exported-image publishing and persistent-storage flushing are owned by injected Apple/Web `pp_platform_*` services with no legacy adapter branch | +| DEBT-0051 | Open | Modernization | Document browser search roots and Browse dialog working-directory picker visibility/path formatting now dispatch through `PlatformServices`; iOS Inbox roots and working-directory picker availability live in tested `pp_platform_api::platform_policy`, but macOS directory picker/display-path execution still lives in `src/platform_legacy/legacy_platform_services.*` | Preserve current iOS document import/browse and desktop browse picker behavior while Apple platform shells are extracted incrementally | `pp_platform_api_tests`; `ctest --preset desktop-fast --build-config Debug`; Apple package smoke once root Apple builds exist | Document browse roots and browse-directory picker/display formatting are owned by injected Apple and desktop `pp_platform_*` services with no legacy adapter branch | | DEBT-0052 | Open | Modernization | Native UI/window state saving now dispatches through `PlatformServices`, but macOS execution still lives in `src/platform_legacy/legacy_platform_services.*` and forwards to the retained Objective-C app bridge | Preserve current Windows/macOS UI persistence while platform shells are extracted incrementally | `pp_platform_api_tests`; `ctest --preset desktop-fast --build-config Debug`; Windows app build; Apple package smoke once root Apple builds exist | UI/window state persistence is owned by injected platform services with no legacy adapter branch | -| DEBT-0053 | Open | Modernization | Prepared-file writable target selection and prepared-file export-dialog policy now dispatch through `PlatformServices`, but iOS/Web target selection still lives in `src/platform_legacy/legacy_platform_services.*` | Preserve mobile/Web export handoff behavior while platform shells are extracted incrementally | `pp_platform_api_tests`; `ctest --preset desktop-fast --build-config Debug`; Windows app build; Apple/Web package smoke once root package builds exist | Prepared-file target selection, export-dialog policy, and save/download handoff are owned by injected platform services with no legacy adapter branch | +| DEBT-0053 | Open | Modernization | Prepared-file writable target selection and prepared-file export-dialog policy now dispatch through `PlatformServices`; iOS temporary-file and WebGL data-path target planning live in tested `pp_platform_api::platform_policy`, but retained iOS/Web save/download handoff execution still lives in `src/platform_legacy/legacy_platform_services.*` | Preserve mobile/Web export handoff behavior while platform shells are extracted incrementally | `pp_platform_api_tests`; `ctest --preset desktop-fast --build-config Debug`; Windows app build; Apple/Web package smoke once root package builds exist | Prepared-file target selection, export-dialog policy, and save/download handoff are owned by injected platform services with no legacy adapter branch | | DEBT-0054 | Open | Modernization | Layout XML file read/reload decisions now consume `pp_platform_api::plan_asset_file_load`, but that helper still encodes the retained compile-time platform policy: Windows/macOS use `stat` mtime reload checks, while other platforms treat already-loaded layouts as successful no-op loads | Preserve current layout hot-reload and mobile/Web single-load behavior while removing platform guards from the shared `LayoutManager` parser | `pp_platform_api_tests`; `ctest --preset desktop-fast --build-config Debug -R pp_platform_api_tests`; Windows app build | Layout reload decisions are owned by injected platform storage/file-watch services or an asset manager boundary with platform-specific file watching removed from compile-time helpers | | DEBT-0055 | Open | Modernization | `src/app.h` now forward-declares retained iOS/macOS/Android/Linux/Web platform handles instead of including platform SDK headers, and full SDK includes are isolated in `src/platform_legacy/legacy_platform_services.cpp`, but the `App` singleton still stores those platform handles directly | Reduce central header platform coupling incrementally without rewriting non-Windows platform entrypoints before Phase 6 | Windows app build; Apple/Android/Linux/Web package smoke once platform root builds are active | Platform handles are owned by injected `pp_platform_*` shell state or services, and `App` has no platform SDK handle fields or platform conditional members | | DEBT-0056 | Open | Modernization | `src/asset.h` now forward-declares Android asset-manager types and uses `Asset::set_android_asset_manager` instead of public mutable manager state, but retained `Asset` still stores Android asset handles and `src/asset.cpp` still performs Android `AAssetManager` reads directly; the current `android-arm64` root preset is headless and does not expose `pp_legacy_assets_io` | Reduce legacy asset I/O header coupling without rewriting Android asset loading before the asset manager/storage boundary exists | Windows app build; `cmake --build --preset android-arm64 --target pp_assets`; Android package smoke once package builds consume shared targets | Android asset loading is owned by injected asset storage/platform services or `pp_assets` file providers, with no static Android asset manager on `Asset` | -| DEBT-0057 | Open | Modernization | Default canvas allocation size now dispatches through `PlatformServices::default_canvas_resolution`, removing the `CANVAS_RES` platform macro from `src/canvas.h`, but WebGL's retained 512 default still lives in `src/platform_legacy/legacy_platform_services.cpp` until the Web shell owns injected services | Preserve WebGL memory behavior while moving canvas creation policy out of shared canvas headers and into the platform boundary | `pp_platform_api_tests`; `ctest --preset desktop-fast --build-config Debug -R pp_platform_api_tests`; Windows app build; WebGL package smoke once root Web build exists | Default canvas resolution is owned by injected `pp_platform_*` services for every supported platform, with no WebGL branch in the legacy fallback | +| DEBT-0057 | Open | Modernization | Default canvas allocation size now dispatches through `PlatformServices::default_canvas_resolution`, removing the `CANVAS_RES` platform macro from `src/canvas.h`; WebGL's retained 512 default now lives in tested `pp_platform_api::platform_policy`, but the Web shell still reaches it through the legacy platform fallback until injected Web services own the policy | Preserve WebGL memory behavior while moving canvas creation policy out of shared canvas headers and into the platform boundary | `pp_platform_api_tests`; `ctest --preset desktop-fast --build-config Debug -R pp_platform_api_tests`; Windows app build; WebGL package smoke once root Web build exists | Default canvas resolution is owned by injected `pp_platform_*` services for every supported platform, with no WebGL branch in the legacy fallback | | DEBT-0058 | Open | Modernization | App-level progress/message/input dialog metadata, including message-dialog OK/cancel captions, now consumes pure `pp_app_core` through `App::show_progress`, `App::message_box`, `App::input_box`, `pano_cli plan-app-dialog`, and `pp_app_core_app_dialog_tests`; live execution is centralized in `src/legacy_app_dialog_services.*`, but the bridge still creates retained `NodeProgressBar`, `NodeMessageBox`, and `NodeInputBox` instances and inserts them into the legacy layout tree | Preserve current app-shell dialog behavior while moving shared dialog policy toward UI/app services | `pp_app_core_app_dialog_tests`; `pano_cli plan-app-dialog --kind progress --total -4`; `pano_cli plan-app-dialog --kind message --cancel`; `pano_cli plan-app-dialog --kind input --ok-caption Save`; `ctest --preset desktop-fast --build-config Debug`; Windows app build | Progress/message/input dialog creation, callback wiring, layout insertion, lifetime ownership, and headless automation are owned by injected app/UI services with `App` methods acting only as adapters | | DEBT-0059 | Open | Modernization | iOS root CMake headless builds assign generated bundle identifiers and disable code signing for executable test/tool targets | The current Apple gate is compile validation for shared component targets; signed iOS app/package validation is not migrated to root CMake yet | `powershell -ExecutionPolicy Bypass -File scripts\automation\apple-remote-build.ps1 -Presets macos,ios-simulator,ios-device`; `sh scripts/automation/platform-build.sh "ios-device"` on `panopainter-mac` | Root CMake owns the signed Apple app/package targets, package-smoke validates Apple bundles where signing material is available, and headless iOS test/tool targets are either excluded from signed package builds or use explicit test-runner signing policy | diff --git a/docs/modernization/roadmap.md b/docs/modernization/roadmap.md index 670609f..b3e8416 100644 --- a/docs/modernization/roadmap.md +++ b/docs/modernization/roadmap.md @@ -1399,6 +1399,14 @@ current headless matrix, and builds `macos`, `ios-simulator`, and `ios-device`. The iOS device compile gate assigns generated bundle identifiers and disables code signing for test/tool executables under DEBT-0059; signed Apple app bundle and package-smoke migration remains open under DEBT-0011. +`pp_platform_api` now also owns a tested platform-family policy catalog used by +both `WindowsPlatformServices` and the retained non-Windows fallback for +exported-image publishing, persistent-storage flushing, document browse roots, +working-directory picker availability, prepared-file target planning, +work-directory collection export policy, PPBR data-directory override policy, +SonarPen availability, live asset reload policy, recording cleanup policy, +default canvas resolution, and canvas tip visibility. Platform SDK calls remain +in the platform shells while those decisions are headless-testable. Implementation tasks: diff --git a/src/platform_api/platform_policy.cpp b/src/platform_api/platform_policy.cpp new file mode 100644 index 0000000..17c1159 --- /dev/null +++ b/src/platform_api/platform_policy.cpp @@ -0,0 +1,133 @@ +#include "platform_api/platform_policy.h" + +#include + +namespace pp::platform { + +PlatformFamily current_platform_family() noexcept +{ +#if defined(__IOS__) + return PlatformFamily::ios; +#elif defined(__OSX__) + return PlatformFamily::macos; +#elif defined(__ANDROID__) + return PlatformFamily::android; +#elif defined(__LINUX__) + return PlatformFamily::linux; +#elif defined(__WEB__) + return PlatformFamily::webgl; +#elif defined(_WIN32) + return PlatformFamily::windows; +#else + return PlatformFamily::generic_desktop; +#endif +} + +bool platform_deletes_recorded_files_on_clear(PlatformFamily family) noexcept +{ + return family == PlatformFamily::ios || family == PlatformFamily::macos; +} + +bool platform_publishes_exported_images(PlatformFamily family) noexcept +{ + return family == PlatformFamily::ios; +} + +bool platform_flushes_persistent_storage(PlatformFamily family) noexcept +{ + return family == PlatformFamily::webgl; +} + +bool platform_enables_live_asset_reloading(PlatformFamily family) noexcept +{ + return family == PlatformFamily::windows || family == PlatformFamily::macos; +} + +bool platform_supports_working_directory_picker(PlatformFamily family) noexcept +{ + return family == PlatformFamily::windows || family == PlatformFamily::macos; +} + +bool platform_uses_prepared_file_writes(PlatformFamily family) noexcept +{ + return family == PlatformFamily::ios || family == PlatformFamily::webgl; +} + +bool platform_uses_work_directory_document_export_collections(PlatformFamily family) noexcept +{ + return family == PlatformFamily::ios; +} + +bool platform_uses_ppbr_export_data_directory_override(PlatformFamily family) noexcept +{ + return family == PlatformFamily::macos; +} + +bool platform_supports_sonarpen(PlatformFamily family) noexcept +{ + return family == PlatformFamily::ios; +} + +int platform_default_canvas_resolution(PlatformFamily family) noexcept +{ + return family == PlatformFamily::webgl ? 512 : 1536; +} + +bool platform_draws_canvas_tip_for_pointer( + PlatformFamily family, + bool is_mouse, + bool is_stylus, + bool is_left_button_release) noexcept +{ + if (family == PlatformFamily::ios) + return is_mouse && !is_left_button_release; + return is_mouse || is_stylus; +} + +std::vector platform_document_browse_roots( + PlatformFamily family, + std::string_view work_path, + std::string_view data_path) +{ + if (family == PlatformFamily::ios) + { + return { + std::string(work_path), + std::string(data_path) + "/Inbox", + }; + } + + return { std::string(work_path) }; +} + +PreparedFileTarget plan_platform_writable_file( + PlatformFamily family, + std::string_view type, + std::string_view default_name, + std::string_view data_path, + std::string_view temporary_path) +{ + const std::string name = std::string(default_name) + "." + std::string(type); + + if (family == PlatformFamily::ios) + { + return { + std::string(temporary_path) + "/" + name, + name, + true, + }; + } + + if (family == PlatformFamily::webgl) + { + return { + std::string(data_path) + "/" + name, + name, + false, + }; + } + + return {}; +} + +} diff --git a/src/platform_api/platform_policy.h b/src/platform_api/platform_policy.h new file mode 100644 index 0000000..053cde3 --- /dev/null +++ b/src/platform_api/platform_policy.h @@ -0,0 +1,51 @@ +#pragma once + +#include "platform_api/platform_services.h" + +#include +#include + +namespace pp::platform { + +enum class PlatformFamily { + generic_desktop, + windows, + macos, + ios, + android, + linux, + webgl, +}; + +[[nodiscard]] PlatformFamily current_platform_family() noexcept; + +[[nodiscard]] bool platform_deletes_recorded_files_on_clear(PlatformFamily family) noexcept; +[[nodiscard]] bool platform_publishes_exported_images(PlatformFamily family) noexcept; +[[nodiscard]] bool platform_flushes_persistent_storage(PlatformFamily family) noexcept; +[[nodiscard]] bool platform_enables_live_asset_reloading(PlatformFamily family) noexcept; +[[nodiscard]] bool platform_supports_working_directory_picker(PlatformFamily family) noexcept; +[[nodiscard]] bool platform_uses_prepared_file_writes(PlatformFamily family) noexcept; +[[nodiscard]] bool platform_uses_work_directory_document_export_collections(PlatformFamily family) noexcept; +[[nodiscard]] bool platform_uses_ppbr_export_data_directory_override(PlatformFamily family) noexcept; +[[nodiscard]] bool platform_supports_sonarpen(PlatformFamily family) noexcept; +[[nodiscard]] int platform_default_canvas_resolution(PlatformFamily family) noexcept; + +[[nodiscard]] bool platform_draws_canvas_tip_for_pointer( + PlatformFamily family, + bool is_mouse, + bool is_stylus, + bool is_left_button_release) noexcept; + +[[nodiscard]] std::vector platform_document_browse_roots( + PlatformFamily family, + std::string_view work_path, + std::string_view data_path); + +[[nodiscard]] PreparedFileTarget plan_platform_writable_file( + PlatformFamily family, + std::string_view type, + std::string_view default_name, + std::string_view data_path, + std::string_view temporary_path); + +} diff --git a/src/platform_legacy/legacy_platform_services.cpp b/src/platform_legacy/legacy_platform_services.cpp index db892cb..2928f75 100644 --- a/src/platform_legacy/legacy_platform_services.cpp +++ b/src/platform_legacy/legacy_platform_services.cpp @@ -5,6 +5,7 @@ #include "app_core/document_platform_io.h" #include "log.h" #include "platform_api/network_tls_policy.h" +#include "platform_api/platform_policy.h" #include "renderer_gl/opengl_capabilities.h" #ifdef __ANDROID__ @@ -288,11 +289,8 @@ public: [[nodiscard]] bool deletes_recorded_files_on_clear() override { -#if defined(__IOS__) || defined(__OSX__) - return true; -#else - return false; -#endif + return pp::platform::platform_deletes_recorded_files_on_clear( + pp::platform::current_platform_family()); } void clear_recorded_files(std::string_view recording_path) override @@ -306,6 +304,11 @@ public: void publish_exported_image(std::string_view path) override { + if (!pp::platform::platform_publishes_exported_images(pp::platform::current_platform_family())) + { + (void)path; + return; + } #ifdef __IOS__ save_image_library(std::string(path)); #else @@ -315,6 +318,8 @@ public: void flush_persistent_storage() override { + if (!pp::platform::platform_flushes_persistent_storage(pp::platform::current_platform_family())) + return; #ifdef __WEB__ webgl_sync(); #endif @@ -324,15 +329,10 @@ public: std::string_view work_path, std::string_view data_path) override { -#ifdef __IOS__ - return { - std::string(work_path), - std::string(data_path) + "/Inbox", - }; -#else - (void)data_path; - return { std::string(work_path) }; -#endif + return pp::platform::platform_document_browse_roots( + pp::platform::current_platform_family(), + work_path, + data_path); } void save_ui_state() override @@ -344,11 +344,8 @@ public: [[nodiscard]] bool enables_live_asset_reloading() override { -#if defined(__OSX__) - return true; -#else - return false; -#endif + return pp::platform::platform_enables_live_asset_reloading( + pp::platform::current_platform_family()); } void update_platform_frame(float delta_time_seconds) override @@ -455,11 +452,8 @@ public: [[nodiscard]] bool supports_working_directory_picker() override { -#if defined(__OSX__) - return true; -#else - return false; -#endif + return pp::platform::platform_supports_working_directory_picker( + pp::platform::current_platform_family()); } [[nodiscard]] std::string format_working_directory_path(std::string_view path) override @@ -474,20 +468,14 @@ public: [[nodiscard]] bool uses_prepared_file_writes() override { -#if __IOS__ || __WEB__ - return true; -#else - return false; -#endif + return pp::platform::platform_uses_prepared_file_writes( + pp::platform::current_platform_family()); } [[nodiscard]] bool uses_work_directory_document_export_collections() override { -#if __IOS__ - return true; -#else - return false; -#endif + return pp::platform::platform_uses_work_directory_document_export_collections( + pp::platform::current_platform_family()); } [[nodiscard]] bool disables_network_tls_verification() override @@ -497,20 +485,13 @@ public: [[nodiscard]] bool uses_ppbr_export_data_directory_override() override { -#if defined(__OSX__) - return true; -#else - return false; -#endif + return pp::platform::platform_uses_ppbr_export_data_directory_override( + pp::platform::current_platform_family()); } [[nodiscard]] bool supports_sonarpen() override { -#if __IOS__ - return true; -#else - return false; -#endif + return pp::platform::platform_supports_sonarpen(pp::platform::current_platform_family()); } void start_sonarpen() override @@ -522,11 +503,8 @@ public: [[nodiscard]] int default_canvas_resolution() override { -#if __WEB__ - return 512; -#else - return 1536; -#endif + return pp::platform::platform_default_canvas_resolution( + pp::platform::current_platform_family()); } [[nodiscard]] bool draws_canvas_tip_for_pointer( @@ -534,13 +512,11 @@ public: bool is_stylus, bool is_left_button_release) override { -#if defined(__IOS__) - (void)is_stylus; - return is_mouse && !is_left_button_release; -#else - (void)is_left_button_release; - return is_mouse || is_stylus; -#endif + return pp::platform::platform_draws_canvas_tip_for_pointer( + pp::platform::current_platform_family(), + is_mouse, + is_stylus, + is_left_button_release); } [[nodiscard]] float adjust_canvas_input_pressure(float pressure) override @@ -554,26 +530,12 @@ public: std::string_view data_path, std::string_view temporary_path) override { - const std::string name = std::string(default_name) + "." + std::string(type); -#ifdef __IOS__ - (void)data_path; - return { - std::string(temporary_path) + "/" + name, - name, - true, - }; -#elif __WEB__ - (void)temporary_path; - return { - std::string(data_path) + "/" + name, - name, - false, - }; -#else - (void)data_path; - (void)temporary_path; - return {}; -#endif + return pp::platform::plan_platform_writable_file( + pp::platform::current_platform_family(), + type, + default_name, + data_path, + temporary_path); } void display_file(std::string_view path) override diff --git a/src/platform_windows/windows_platform_services.cpp b/src/platform_windows/windows_platform_services.cpp index 43ba96c..d283eff 100644 --- a/src/platform_windows/windows_platform_services.cpp +++ b/src/platform_windows/windows_platform_services.cpp @@ -3,6 +3,7 @@ #include "log.h" #include "platform_api/network_tls_policy.h" +#include "platform_api/platform_policy.h" #include "renderer_gl/opengl_capabilities.h" #include @@ -368,7 +369,7 @@ public: [[nodiscard]] bool deletes_recorded_files_on_clear() override { - return false; + return pp::platform::platform_deletes_recorded_files_on_clear(pp::platform::PlatformFamily::windows); } void clear_recorded_files(std::string_view recording_path) override @@ -378,19 +379,28 @@ public: void publish_exported_image(std::string_view path) override { + if (!pp::platform::platform_publishes_exported_images(pp::platform::PlatformFamily::windows)) + { + (void)path; + return; + } (void)path; } void flush_persistent_storage() override { + if (!pp::platform::platform_flushes_persistent_storage(pp::platform::PlatformFamily::windows)) + return; } [[nodiscard]] std::vector document_browse_roots( std::string_view work_path, std::string_view data_path) override { - (void)data_path; - return { std::string(work_path) }; + return pp::platform::platform_document_browse_roots( + pp::platform::PlatformFamily::windows, + work_path, + data_path); } void save_ui_state() override @@ -400,7 +410,7 @@ public: [[nodiscard]] bool enables_live_asset_reloading() override { - return true; + return pp::platform::platform_enables_live_asset_reloading(pp::platform::PlatformFamily::windows); } void update_platform_frame(float delta_time_seconds) override @@ -466,7 +476,7 @@ public: [[nodiscard]] bool supports_working_directory_picker() override { - return true; + return pp::platform::platform_supports_working_directory_picker(pp::platform::PlatformFamily::windows); } [[nodiscard]] std::string format_working_directory_path(std::string_view path) override @@ -484,12 +494,13 @@ public: [[nodiscard]] bool uses_prepared_file_writes() override { - return false; + return pp::platform::platform_uses_prepared_file_writes(pp::platform::PlatformFamily::windows); } [[nodiscard]] bool uses_work_directory_document_export_collections() override { - return false; + return pp::platform::platform_uses_work_directory_document_export_collections( + pp::platform::PlatformFamily::windows); } [[nodiscard]] bool disables_network_tls_verification() override @@ -499,12 +510,13 @@ public: [[nodiscard]] bool uses_ppbr_export_data_directory_override() override { - return false; + return pp::platform::platform_uses_ppbr_export_data_directory_override( + pp::platform::PlatformFamily::windows); } [[nodiscard]] bool supports_sonarpen() override { - return false; + return pp::platform::platform_supports_sonarpen(pp::platform::PlatformFamily::windows); } void start_sonarpen() override @@ -513,7 +525,7 @@ public: [[nodiscard]] int default_canvas_resolution() override { - return 1536; + return pp::platform::platform_default_canvas_resolution(pp::platform::PlatformFamily::windows); } [[nodiscard]] bool draws_canvas_tip_for_pointer( @@ -521,8 +533,11 @@ public: bool is_stylus, bool is_left_button_release) override { - (void)is_left_button_release; - return is_mouse || is_stylus; + return pp::platform::platform_draws_canvas_tip_for_pointer( + pp::platform::PlatformFamily::windows, + is_mouse, + is_stylus, + is_left_button_release); } [[nodiscard]] float adjust_canvas_input_pressure(float pressure) override @@ -539,11 +554,12 @@ public: std::string_view data_path, std::string_view temporary_path) override { - (void)type; - (void)default_name; - (void)data_path; - (void)temporary_path; - return {}; + return pp::platform::plan_platform_writable_file( + pp::platform::PlatformFamily::windows, + type, + default_name, + data_path, + temporary_path); } void save_prepared_file( diff --git a/tests/platform_api/platform_services_tests.cpp b/tests/platform_api/platform_services_tests.cpp index b112fa1..ab538f8 100644 --- a/tests/platform_api/platform_services_tests.cpp +++ b/tests/platform_api/platform_services_tests.cpp @@ -2,6 +2,7 @@ #include "platform_api/asset_file_load_policy.h" #include "platform_api/network_tls_policy.h" +#include "platform_api/platform_policy.h" #include "platform_api/platform_services.h" #include @@ -853,6 +854,104 @@ void platform_services_dispatch_canvas_input_policy(pp::tests::Harness& harness) PP_EXPECT(harness, fake.canvas_pressure_adjustments == 1); } +void platform_policy_preserves_recording_and_export_storage_rules(pp::tests::Harness& harness) +{ + PP_EXPECT(harness, pp::platform::platform_deletes_recorded_files_on_clear(pp::platform::PlatformFamily::ios)); + PP_EXPECT(harness, pp::platform::platform_deletes_recorded_files_on_clear(pp::platform::PlatformFamily::macos)); + PP_EXPECT(harness, !pp::platform::platform_deletes_recorded_files_on_clear(pp::platform::PlatformFamily::windows)); + PP_EXPECT(harness, pp::platform::platform_publishes_exported_images(pp::platform::PlatformFamily::ios)); + PP_EXPECT(harness, !pp::platform::platform_publishes_exported_images(pp::platform::PlatformFamily::macos)); + PP_EXPECT(harness, pp::platform::platform_flushes_persistent_storage(pp::platform::PlatformFamily::webgl)); + PP_EXPECT(harness, !pp::platform::platform_flushes_persistent_storage(pp::platform::PlatformFamily::windows)); +} + +void platform_policy_preserves_document_browse_roots(pp::tests::Harness& harness) +{ + const auto ios_roots = pp::platform::platform_document_browse_roots( + pp::platform::PlatformFamily::ios, + "D:/Paint/work", + "D:/Paint"); + const auto desktop_roots = pp::platform::platform_document_browse_roots( + pp::platform::PlatformFamily::windows, + "D:/Paint/work", + "D:/Paint"); + + PP_EXPECT(harness, ios_roots.size() == 2); + PP_EXPECT(harness, ios_roots[0] == "D:/Paint/work"); + PP_EXPECT(harness, ios_roots[1] == "D:/Paint/Inbox"); + PP_EXPECT(harness, desktop_roots.size() == 1); + PP_EXPECT(harness, desktop_roots[0] == "D:/Paint/work"); +} + +void platform_policy_preserves_picker_and_prepared_file_rules(pp::tests::Harness& harness) +{ + PP_EXPECT(harness, pp::platform::platform_supports_working_directory_picker(pp::platform::PlatformFamily::windows)); + PP_EXPECT(harness, pp::platform::platform_supports_working_directory_picker(pp::platform::PlatformFamily::macos)); + PP_EXPECT(harness, !pp::platform::platform_supports_working_directory_picker(pp::platform::PlatformFamily::ios)); + PP_EXPECT(harness, pp::platform::platform_uses_prepared_file_writes(pp::platform::PlatformFamily::ios)); + PP_EXPECT(harness, pp::platform::platform_uses_prepared_file_writes(pp::platform::PlatformFamily::webgl)); + PP_EXPECT(harness, !pp::platform::platform_uses_prepared_file_writes(pp::platform::PlatformFamily::windows)); + PP_EXPECT(harness, pp::platform::platform_uses_work_directory_document_export_collections(pp::platform::PlatformFamily::ios)); + PP_EXPECT(harness, !pp::platform::platform_uses_work_directory_document_export_collections(pp::platform::PlatformFamily::webgl)); + + const auto ios_target = pp::platform::plan_platform_writable_file( + pp::platform::PlatformFamily::ios, + "mp4", + "timelapse", + "/PanoPainter", + "/tmp"); + PP_EXPECT(harness, ios_target.path == "/tmp/timelapse.mp4"); + PP_EXPECT(harness, ios_target.suggested_name == "timelapse.mp4"); + PP_EXPECT(harness, ios_target.write_on_background_thread); + + const auto web_target = pp::platform::plan_platform_writable_file( + pp::platform::PlatformFamily::webgl, + "png", + "export", + "/PanoPainter", + "/tmp"); + PP_EXPECT(harness, web_target.path == "/PanoPainter/export.png"); + PP_EXPECT(harness, web_target.suggested_name == "export.png"); + PP_EXPECT(harness, !web_target.write_on_background_thread); + + const auto desktop_target = pp::platform::plan_platform_writable_file( + pp::platform::PlatformFamily::windows, + "png", + "export", + "/PanoPainter", + "/tmp"); + PP_EXPECT(harness, desktop_target.path.empty()); + PP_EXPECT(harness, desktop_target.suggested_name.empty()); +} + +void platform_policy_preserves_ui_asset_and_input_rules(pp::tests::Harness& harness) +{ + PP_EXPECT(harness, pp::platform::platform_enables_live_asset_reloading(pp::platform::PlatformFamily::windows)); + PP_EXPECT(harness, pp::platform::platform_enables_live_asset_reloading(pp::platform::PlatformFamily::macos)); + PP_EXPECT(harness, !pp::platform::platform_enables_live_asset_reloading(pp::platform::PlatformFamily::ios)); + PP_EXPECT(harness, pp::platform::platform_uses_ppbr_export_data_directory_override(pp::platform::PlatformFamily::macos)); + PP_EXPECT(harness, !pp::platform::platform_uses_ppbr_export_data_directory_override(pp::platform::PlatformFamily::windows)); + PP_EXPECT(harness, pp::platform::platform_supports_sonarpen(pp::platform::PlatformFamily::ios)); + PP_EXPECT(harness, !pp::platform::platform_supports_sonarpen(pp::platform::PlatformFamily::android)); + PP_EXPECT(harness, pp::platform::platform_default_canvas_resolution(pp::platform::PlatformFamily::webgl) == 512); + PP_EXPECT(harness, pp::platform::platform_default_canvas_resolution(pp::platform::PlatformFamily::windows) == 1536); + PP_EXPECT(harness, pp::platform::platform_draws_canvas_tip_for_pointer( + pp::platform::PlatformFamily::ios, + true, + false, + false)); + PP_EXPECT(harness, !pp::platform::platform_draws_canvas_tip_for_pointer( + pp::platform::PlatformFamily::ios, + true, + false, + true)); + PP_EXPECT(harness, pp::platform::platform_draws_canvas_tip_for_pointer( + pp::platform::PlatformFamily::windows, + false, + true, + true)); +} + } int main() @@ -894,5 +993,15 @@ int main() harness.run("platform services dispatch sonarpen policy and start", platform_services_dispatch_sonarpen_policy_and_start); harness.run("platform services dispatch default canvas resolution", platform_services_dispatch_default_canvas_resolution); harness.run("platform services dispatch canvas input policy", platform_services_dispatch_canvas_input_policy); + harness.run( + "platform policy preserves recording and export storage rules", + platform_policy_preserves_recording_and_export_storage_rules); + harness.run("platform policy preserves document browse roots", platform_policy_preserves_document_browse_roots); + harness.run( + "platform policy preserves picker and prepared file rules", + platform_policy_preserves_picker_and_prepared_file_rules); + harness.run( + "platform policy preserves UI asset and input rules", + platform_policy_preserves_ui_asset_and_input_rules); return harness.finish(); }