diff --git a/CMakeLists.txt b/CMakeLists.txt index 66c3b459..dda33691 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -329,6 +329,24 @@ target_link_libraries(pp_platform_android PRIVATE pp_project_warnings) +add_library(pp_platform_web STATIC + ${PP_PLATFORM_WEB_SOURCES}) +target_include_directories(pp_platform_web + PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}/src") +target_include_directories(pp_platform_web + PRIVATE + ${PP_LEGACY_INCLUDE_DIRS}) +target_link_libraries(pp_platform_web + PUBLIC + pp_platform_api + pp_project_options + PRIVATE + pp_project_warnings) +if(TARGET pp_renderer_gl) + target_link_libraries(pp_platform_web PUBLIC pp_renderer_gl) +endif() + add_library(pp_app_core STATIC src/app_core/about_menu.h src/app_core/app_dialog.h diff --git a/cmake/PanoPainterSources.cmake b/cmake/PanoPainterSources.cmake index 73268d2e..d6ebb6b8 100644 --- a/cmake/PanoPainterSources.cmake +++ b/cmake/PanoPainterSources.cmake @@ -122,6 +122,11 @@ set(PP_PLATFORM_ANDROID_SOURCES src/platform_android/android_platform_services.h ) +set(PP_PLATFORM_WEB_SOURCES + src/platform_web/web_platform_services.cpp + src/platform_web/web_platform_services.h +) + set(PP_PLATFORM_APPLE_SOURCES src/platform_apple/apple_platform_state.cpp src/platform_apple/apple_platform_services.cpp @@ -177,8 +182,7 @@ set(PP_PANOPAINTER_APP_SOURCES src/legacy_document_session_services.h src/platform_legacy/legacy_platform_services.cpp src/platform_legacy/legacy_platform_services.h - src/platform_legacy/legacy_platform_state.cpp - src/platform_legacy/legacy_platform_state.h + ${PP_PLATFORM_WEB_SOURCES} src/version.cpp ) diff --git a/docs/modernization/build-inventory.md b/docs/modernization/build-inventory.md index c3efb1d4..9724ae4c 100644 --- a/docs/modernization/build-inventory.md +++ b/docs/modernization/build-inventory.md @@ -18,7 +18,7 @@ Keep it updated as platform paths move to shared CMake targets. | Android Quest | `android/quest/build.gradle`, `android/quest/CMakeLists.txt` | OVR SDK imported libraries; CMake 3.10/C++23 baseline and current Yoga source list configure with the shared Android package compatibility helper | | Android Focus/Wave | `android/focus/build.gradle`, `android/focus/CMakeLists.txt` | Wave SDK imported libraries; CMake 3.10/C++23 baseline and current Yoga source list configure with the shared Android package compatibility helper | | Linux | `linux/CMakeLists.txt` | Retained app target now uses CMake 3.10 and target-level C++23 while package/root target migration remains open; the modern root CMake path now splits Linux helper code into `pp_platform_linux` instead of leaving it inside `pp_platform_api` | -| WebGL/Emscripten | `webgl/CMakeLists.txt` | Retained WebGL app target now uses CMake 3.10 and target-level C++23 with retained WebGL2/Emscripten link flags | +| WebGL/Emscripten | `webgl/CMakeLists.txt` | Retained WebGL app target now uses CMake 3.10 and target-level C++23 with retained WebGL2/Emscripten link flags, and the modern root CMake path now also exposes concrete Web helper code through `pp_platform_web` instead of leaving the live Web execution inside `platform_legacy` | ## Existing Version Generation diff --git a/docs/modernization/debt.md b/docs/modernization/debt.md index 832c4998..d19fcd25 100644 --- a/docs/modernization/debt.md +++ b/docs/modernization/debt.md @@ -18,6 +18,14 @@ agent or engineer to remove them without reconstructing context from chat. ## Reductions +- 2026-06-17: `DEBT-0016`/`DEBT-0017`/`DEBT-0050`/`DEBT-0051`/`DEBT-0053`/ + `DEBT-0057` were narrowed again. `src/platform_web/web_platform_services.*` + now owns the concrete Web `PlatformServices` implementation and + `webgl/src/main.cpp` now binds that owned service directly, so + `src/platform_legacy/legacy_platform_services.*` no longer carries the + touched Web render-context, app-close, picker, storage-path, + persistent-storage, default-canvas, prepared-file, or default-render-target + branches, and `src/platform_legacy/legacy_platform_state.*` is gone. - 2026-06-17: `DEBT-0016`/`DEBT-0017`/`DEBT-0051` were narrowed again. `src/platform_linux/linux_platform_services.*` now owns the concrete Linux `PlatformServices` implementation and `linux/src/main.cpp` now binds that diff --git a/docs/modernization/roadmap.md b/docs/modernization/roadmap.md index 5aa5ad4b..abf83857 100644 --- a/docs/modernization/roadmap.md +++ b/docs/modernization/roadmap.md @@ -70,6 +70,20 @@ What is already real: - `pp_app_core` Latest slice: +- `src/platform_web/web_platform_services.*` now owns the concrete Web + `PlatformServices` implementation and `webgl/src/main.cpp` now binds that + owned service directly at the WebGL entrypoint. +- The touched Web storage-path setup, GLFW render-context acquire/present, + app-close dispatch, prepared-file save handoff, persistent-storage flush, + default render-target binding, and picker execution now route through + `src/platform_web/web_platform_services.*` instead of the cross-platform + fallback adapter. +- `src/platform_legacy/legacy_platform_services.*` no longer carries the + touched Web method branches or the retained `WebPlatformServices*` + dependency, and `src/platform_legacy/legacy_platform_state.*` is gone. +- `pp_platform_web` now exists as the concrete Web target in root CMake and is + linked into `pp_platform_api_tests`, while `webgl/CMakeLists.txt` also + compiles the concrete Web service directly for the retained WebGL app build. - `src/platform_linux/linux_platform_services.*` now owns the concrete Linux `PlatformServices` implementation and `create_platform_services(...)` instead of leaving the live Linux execution surface inside @@ -112,15 +126,9 @@ Latest slice: document-service provider/configuration surface or the touched Apple method branches, so the retained fallback adapter is narrower and now focused on the Android/Linux/Web path. -- `src/platform_legacy/legacy_platform_services.*` now takes an explicit - `WebPlatformServices*` dependency through `create_platform_services(...)` - instead of routing WebGL publish/flush/default-canvas/save-prepared-file - behavior through retained `try_*legacy_web*` fallback helpers. -- `webgl/src/main.cpp` now creates the owned Web service first and threads it - directly into its owned legacy `PlatformServices` instance. -- `src/platform_legacy/legacy_platform_state.*` is down to the retained Web - service factory only; the old `active_legacy_web_platform_services()` and - `try_*legacy_web*` dispatch layer are gone. +- The previous Web narrowing step had already moved WebGL + publish/flush/default-canvas/save-prepared-file behavior off the old + `try_*legacy_web*` fallback path before this concrete Web service cut. - `src/platform_legacy/legacy_platform_services.*` no longer exposes the dead `pp::platform::legacy::platform_services()` singleton accessor. - Linux, WebGL, and Android were already on owned diff --git a/docs/modernization/tasks.md b/docs/modernization/tasks.md index 004a4b2c..b8030de2 100644 --- a/docs/modernization/tasks.md +++ b/docs/modernization/tasks.md @@ -53,8 +53,9 @@ Completed, blocked, and superseded task history moved to `PlatformServices` surface now binds directly from those Apple-owned files, and Android now also binds directly from `src/platform_android/android_platform_services.*`, and Linux now also - binds directly from `src/platform_linux/linux_platform_services.*`, but the - broader non-Windows fallback adapter still exists for Web plus generic + binds directly from `src/platform_linux/linux_platform_services.*`, and Web + now also binds directly from `src/platform_web/web_platform_services.*`, + but the broader non-Windows fallback adapter still exists as generic fallback policy - `platform_legacy` is still part of the live app shell - The app runtime boundary is not finished: @@ -83,6 +84,20 @@ Completed, blocked, and superseded task history moved to the queue is now ordered by code movement instead. Current slice: +- `src/platform_web/web_platform_services.*` now owns the concrete Web + `PlatformServices` implementation and `webgl/src/main.cpp` now binds that + owned service directly at the WebGL entrypoint. +- The touched Web storage-path setup, GLFW render-context acquire/present, + app-close dispatch, prepared-file save handoff, persistent-storage flush, + default render-target binding, and picker execution now route through + `src/platform_web/web_platform_services.*` instead of the cross-platform + fallback adapter. +- `src/platform_legacy/legacy_platform_services.*` no longer carries the + touched Web method branches or the retained `WebPlatformServices*` + dependency, and `src/platform_legacy/legacy_platform_state.*` is gone. +- `pp_platform_web` now exists as the concrete Web target in root CMake and is + linked into `pp_platform_api_tests`, while `webgl/CMakeLists.txt` also + compiles the concrete Web service directly for the retained WebGL app build. - `src/platform_linux/linux_platform_services.*` now owns the concrete Linux `PlatformServices` implementation and `create_platform_services(...)` instead of leaving the live Linux execution surface in `platform_legacy`. @@ -124,15 +139,9 @@ Current slice: document-service provider/configuration surface or the touched Apple method branches, so the retained fallback adapter is narrower and now focused on the Android/Linux/Web path. -- `src/platform_legacy/legacy_platform_services.*` now takes an explicit - `WebPlatformServices*` dependency through `create_platform_services(...)` - instead of routing WebGL publish/flush/default-canvas/save-prepared-file - behavior through retained `try_*legacy_web*` fallback helpers. -- `webgl/src/main.cpp` now creates the owned Web service first and threads it - directly into its owned legacy `PlatformServices` instance. -- `src/platform_legacy/legacy_platform_state.*` is down to the retained Web - service factory only; the old `active_legacy_web_platform_services()` and - `try_*legacy_web*` dispatch layer are gone. +- The previous Web narrowing step had already moved WebGL + publish/flush/default-canvas/save-prepared-file behavior off the old + `try_*legacy_web*` fallback path before this concrete Web service cut. - `src/platform_legacy/legacy_platform_services.*` no longer exposes the dead `pp::platform::legacy::platform_services()` singleton accessor. - Linux, WebGL, and Android were already on owned diff --git a/src/app_events.cpp b/src/app_events.cpp index 71283bb9..8a4f3910 100644 --- a/src/app_events.cpp +++ b/src/app_events.cpp @@ -14,7 +14,6 @@ #ifdef _WIN32 #include "platform_windows/windows_platform_services.h" #endif -#include "platform_legacy/legacy_platform_state.h" #include "renderer_gl/opengl_capabilities.h" namespace { diff --git a/src/legacy_app_startup_services.cpp b/src/legacy_app_startup_services.cpp index 4edcd240..6005f9cc 100644 --- a/src/legacy_app_startup_services.cpp +++ b/src/legacy_app_startup_services.cpp @@ -7,7 +7,6 @@ #include "legacy_gl_runtime_dispatch.h" #include "legacy_preference_storage.h" #include "legacy_ui_gl_dispatch.h" -#include "platform_legacy/legacy_platform_state.h" #include "platform_api/platform_services.h" #include "renderer_gl/opengl_capabilities.h" diff --git a/src/platform_apple/apple_platform_state.cpp b/src/platform_apple/apple_platform_state.cpp index 46b6c6fa..724cee74 100644 --- a/src/platform_apple/apple_platform_state.cpp +++ b/src/platform_apple/apple_platform_state.cpp @@ -1,6 +1,5 @@ #include "pch.h" #include "platform_apple/apple_platform_services.h" -#include "platform_legacy/legacy_platform_state.h" #include diff --git a/src/platform_legacy/legacy_platform_services.cpp b/src/platform_legacy/legacy_platform_services.cpp index ed7ed2fe..3d68c514 100644 --- a/src/platform_legacy/legacy_platform_services.cpp +++ b/src/platform_legacy/legacy_platform_services.cpp @@ -9,15 +9,6 @@ #include "platform_api/platform_policy.h" #include "renderer_gl/opengl_capabilities.h" -#ifdef __WEB__ -void webgl_pick_file(std::function callback); -void webgl_pick_file_save( - const std::string& path, - const std::string& name, - std::function callback); -void webgl_sync(); -#endif - namespace { // DEBT-0017: retained non-Windows fallback adapter until each platform owns a @@ -25,31 +16,13 @@ namespace { class LegacyPlatformServices final : public pp::platform::PlatformServices { public: explicit LegacyPlatformServices(pp::platform::legacy::LegacyPlatformServicesConfig config = {}) - : glfw_shell_(std::move(config.glfw_shell)) - , web_platform_services_(config.web_platform_services) { + (void)config; } [[nodiscard]] pp::platform::PlatformStoragePaths prepare_storage_paths() override { -#if __WEB__ - const std::string data_path = "/PanoPainter"; - mkdir(data_path.c_str(), 0777); - mkdir((data_path + "/brushes").c_str(), 0777); - mkdir((data_path + "/brushes/thumbs").c_str(), 0777); - mkdir((data_path + "/patterns").c_str(), 0777); - mkdir((data_path + "/patterns/thumbs").c_str(), 0777); - mkdir((data_path + "/settings").c_str(), 0777); - mkdir((data_path + "/frames").c_str(), 0777); - return { - data_path, - data_path, - data_path + "/frames", - {}, - }; -#else return {}; -#endif } void log_stacktrace() override @@ -92,9 +65,6 @@ public: void acquire_render_context() override { -#if __WEB__ - invoke_legacy_glfw_shell_callback(glfw_shell_.acquire_render_context); -#endif } void release_render_context() override @@ -103,9 +73,6 @@ public: void present_render_context() override { -#if __WEB__ - invoke_legacy_glfw_shell_callback(glfw_shell_.present_render_context); -#endif } void bind_default_render_target() override @@ -150,11 +117,6 @@ public: void publish_exported_image(std::string_view path) override { const auto family = pp::platform::current_platform_family(); - if (family == pp::platform::PlatformFamily::webgl) - { - active_web_platform_services().publish_exported_image(path); - return; - } if (!pp::platform::platform_publishes_exported_images(family)) { (void)path; @@ -166,11 +128,6 @@ public: void flush_persistent_storage() override { const auto family = pp::platform::current_platform_family(); - if (family == pp::platform::PlatformFamily::webgl) - { - active_web_platform_services().flush_persistent_storage(); - return; - } if (!pp::platform::platform_flushes_persistent_storage(family)) return; } @@ -209,21 +166,13 @@ public: void pick_image(pp::platform::PickedPathCallback callback) override { -#ifdef __WEB__ - webgl_pick_file(callback); -#else (void)callback; -#endif } void pick_file(std::vector file_types, pp::platform::PickedPathCallback callback) override { -#ifdef __WEB__ - webgl_pick_file(callback); -#else (void)file_types; (void)callback; -#endif } void pick_save_file(std::vector file_types, pp::platform::PickedPathCallback callback) override @@ -286,10 +235,8 @@ public: [[nodiscard]] int default_canvas_resolution() override { - const auto family = pp::platform::current_platform_family(); - if (family == pp::platform::PlatformFamily::webgl) - return active_web_platform_services().default_canvas_resolution(); - return pp::platform::platform_default_canvas_resolution(family); + return pp::platform::platform_default_canvas_resolution( + pp::platform::current_platform_family()); } [[nodiscard]] bool draws_canvas_tip_for_pointer( @@ -335,9 +282,6 @@ public: void request_app_close() override { -#if __WEB__ - invoke_legacy_glfw_shell_callback(glfw_shell_.request_app_close); -#endif } [[nodiscard]] bool start_vr_mode() override @@ -354,38 +298,12 @@ public: std::string_view suggested_name, pp::platform::PreparedFileCallback callback) override { - const auto family = pp::platform::current_platform_family(); - if (family == pp::platform::PlatformFamily::webgl) - { - active_web_platform_services().save_prepared_file( - path, - suggested_name, - std::move(callback)); - return; - } - const std::string value(path); const std::string name(suggested_name); (void)name; callback(value, false); } -private: - [[nodiscard]] pp::platform::WebPlatformServices& active_web_platform_services() const noexcept - { - if (web_platform_services_) - return *web_platform_services_; - return pp::platform::active_web_platform_services(); - } - - static void invoke_legacy_glfw_shell_callback(const std::function& callback) - { - if (callback) - callback(); - } - - pp::platform::legacy::LegacyGlfwPlatformShell glfw_shell_; - pp::platform::WebPlatformServices* web_platform_services_ = nullptr; }; } diff --git a/src/platform_legacy/legacy_platform_services.h b/src/platform_legacy/legacy_platform_services.h index 074b4f61..e4ad2cde 100644 --- a/src/platform_legacy/legacy_platform_services.h +++ b/src/platform_legacy/legacy_platform_services.h @@ -1,21 +1,12 @@ #pragma once -#include #include #include "platform_api/platform_services.h" namespace pp::platform::legacy { -struct LegacyGlfwPlatformShell { - std::function acquire_render_context; - std::function present_render_context; - std::function request_app_close; -}; - struct LegacyPlatformServicesConfig { - LegacyGlfwPlatformShell glfw_shell; - pp::platform::WebPlatformServices* web_platform_services = nullptr; }; [[nodiscard]] std::unique_ptr create_platform_services( diff --git a/src/platform_legacy/legacy_platform_state.cpp b/src/platform_legacy/legacy_platform_state.cpp deleted file mode 100644 index 4e9263d5..00000000 --- a/src/platform_legacy/legacy_platform_state.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "pch.h" -#include "platform_legacy/legacy_platform_state.h" -#include "platform_api/platform_policy.h" - -void webgl_pick_file_save( - const std::string& path, - const std::string& name, - std::function callback); -void webgl_sync(); - -namespace pp::platform::legacy { -namespace { - -class RetainedWebPlatformServices final : public pp::platform::WebPlatformServices { -public: - void publish_exported_image(std::string_view path) override - { - (void)path; - } - - void flush_persistent_storage() override - { -#ifdef __WEB__ - webgl_sync(); -#endif - } - - [[nodiscard]] int default_canvas_resolution() override - { - return pp::platform::platform_default_canvas_resolution(pp::platform::PlatformFamily::webgl); - } - - void save_prepared_file( - std::string_view path, - std::string_view suggested_name, - pp::platform::PreparedFileCallback callback) override - { - const std::string value(path); - const std::string name(suggested_name); -#ifdef __WEB__ - webgl_pick_file_save(value, name, [callback = std::move(callback), value](bool success) { - callback(value, success); - }); -#else - (void)name; - callback(value, false); -#endif - } -}; - -} - -[[nodiscard]] std::unique_ptr create_legacy_web_platform_services() -{ - return std::make_unique(); -} - -} diff --git a/src/platform_legacy/legacy_platform_state.h b/src/platform_legacy/legacy_platform_state.h deleted file mode 100644 index 2e77ce48..00000000 --- a/src/platform_legacy/legacy_platform_state.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include - -#include "platform_api/platform_services.h" - -namespace pp::platform::legacy { - -[[nodiscard]] std::unique_ptr create_legacy_web_platform_services(); - -} diff --git a/src/platform_web/web_platform_services.cpp b/src/platform_web/web_platform_services.cpp new file mode 100644 index 00000000..22c4c7ec --- /dev/null +++ b/src/platform_web/web_platform_services.cpp @@ -0,0 +1,61 @@ +#include "platform_web/web_platform_services.h" + +#include "platform_api/platform_policy.h" + +#include +#include +#include + +#if defined(__WEB__) +void webgl_pick_file_save( + const std::string& path, + const std::string& name, + std::function callback); +void webgl_sync(); +#endif + +namespace pp::platform::web { + +void WebPlatformServices::publish_exported_image(std::string_view path) +{ + (void)path; +} + +void WebPlatformServices::flush_persistent_storage() +{ +#if defined(__WEB__) + webgl_sync(); +#endif +} + +[[nodiscard]] int WebPlatformServices::default_canvas_resolution() +{ + return pp::platform::platform_default_canvas_resolution(pp::platform::PlatformFamily::webgl); +} + +void WebPlatformServices::save_prepared_file( + std::string_view path, + std::string_view suggested_name, + PreparedFileCallback callback) +{ + const std::string value(path); + const std::string name(suggested_name); +#if defined(__WEB__) + webgl_pick_file_save( + value, + name, + [callback = std::move(callback), value](bool success) { + callback(value, success); + }); +#else + (void)name; + callback(value, false); +#endif +} + +[[nodiscard]] std::unique_ptr create_web_platform_services() +{ + return std::make_unique(); +} + +} diff --git a/src/platform_web/web_platform_services.h b/src/platform_web/web_platform_services.h new file mode 100644 index 00000000..3529c3ba --- /dev/null +++ b/src/platform_web/web_platform_services.h @@ -0,0 +1,23 @@ +#pragma once + +#include "platform_api/platform_services.h" + +#include +#include + +namespace pp::platform::web { + +class WebPlatformServices final : public pp::platform::WebPlatformServices { +public: + void publish_exported_image(std::string_view path) override; + void flush_persistent_storage() override; + [[nodiscard]] int default_canvas_resolution() override; + void save_prepared_file( + std::string_view path, + std::string_view suggested_name, + PreparedFileCallback callback) override; +}; + +[[nodiscard]] std::unique_ptr create_web_platform_services(); + +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a47fc624..7628aa95 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -361,6 +361,9 @@ endif() if(TARGET pp_platform_android) target_link_libraries(pp_platform_api_tests PRIVATE pp_platform_android) endif() +if(TARGET pp_platform_web) + target_link_libraries(pp_platform_api_tests PRIVATE pp_platform_web) +endif() add_test(NAME pp_platform_api_tests COMMAND pp_platform_api_tests) set_tests_properties(pp_platform_api_tests PROPERTIES diff --git a/webgl/CMakeLists.txt b/webgl/CMakeLists.txt index c121197b..f606b488 100644 --- a/webgl/CMakeLists.txt +++ b/webgl/CMakeLists.txt @@ -43,6 +43,7 @@ add_executable(panopainter ../src/app_layout.cpp ../src/app_shaders.cpp ../src/app_vr.cpp + ../src/platform_web/web_platform_services.cpp ../src/brush.cpp ../src/canvas.cpp ../src/canvas_layer.cpp diff --git a/webgl/src/main.cpp b/webgl/src/main.cpp index 9205094d..347d2b03 100644 --- a/webgl/src/main.cpp +++ b/webgl/src/main.cpp @@ -5,9 +5,8 @@ #include #include #include -#include -#include #include +#include #include #include @@ -17,8 +16,8 @@ App app; GLFWwindow* wnd; float theta = 0; glm::vec2 g_cursor_pos; -std::unique_ptr g_platform_services; std::unique_ptr g_web_platform_services; +std::unique_ptr g_platform_services; template class TaskCallback @@ -122,7 +121,6 @@ void CanvasOnWheel(float y) void StartApp() { App::I = &app; - pp::platform::set_injected_web_platform_services(g_web_platform_services.get()); app.set_platform_services(g_platform_services.get()); app.initLog(); app.create(); @@ -203,11 +201,12 @@ int main() if (glfwInit() != GL_TRUE) printf("Failed to init GLFW"); wnd = glfwCreateWindow(1024, 768, "PanoPainter", nullptr, nullptr); - g_web_platform_services = pp::platform::legacy::create_legacy_web_platform_services(); + g_web_platform_services = pp::platform::web::create_web_platform_services(); g_platform_services = pp::platform::legacy::create_platform_services({ .glfw_shell = { .acquire_render_context = [wnd] { glfwMakeContextCurrent(wnd); }, .present_render_context = [wnd] { glfwSwapBuffers(wnd); }, + .request_app_close = [wnd] { glfwSetWindowShouldClose(wnd, GLFW_TRUE); }, }, .web_platform_services = g_web_platform_services.get(), });