diff --git a/docs/modernization/debt.md b/docs/modernization/debt.md index 393bb332..53e2aefd 100644 --- a/docs/modernization/debt.md +++ b/docs/modernization/debt.md @@ -18,6 +18,12 @@ agent or engineer to remove them without reconstructing context from chat. ## Reductions +- 2026-06-17: `DEBT-0017`/`DEBT-0050`/`DEBT-0053`/`DEBT-0057` were narrowed + again. `webgl/src/main.cpp` now binds a concrete + `src/platform_web/web_platform_services.*` `PlatformServices` instance + directly, so the live WebGL entrypoint no longer depends on + `src/platform_legacy/legacy_platform_services.*` for render-context, + prepared-file, picker, app-close, or default-canvas behavior. - 2026-06-17: `DEBT-0003` was narrowed again. `src/platform_windows/windows_runtime_shell.cpp` now binds a local `MainWindowSession` object for the live Win32 session, and the window handle, diff --git a/docs/modernization/roadmap.md b/docs/modernization/roadmap.md index 4c1e816e..e19f4285 100644 --- a/docs/modernization/roadmap.md +++ b/docs/modernization/roadmap.md @@ -70,6 +70,22 @@ What is already real: - `pp_app_core` Latest slice: +- `src/platform_web/web_platform_services.*` now owns the concrete WebGL + `PlatformServices` implementation as well as the narrower + `WebPlatformServices` helper surface. +- `webgl/src/main.cpp` now binds that owned concrete Web `PlatformServices` + instance directly instead of constructing the cross-platform + `platform_legacy` adapter around Web-specific injections. +- The touched Web render-context acquire/present, app-close dispatch, + file/image picker routing, prepared-file handoff, default render-target + binding, and default canvas / prepared-file policy path now route through + `src/platform_web/web_platform_services.*` instead of the fallback adapter. +- `pp_platform_api_tests` now compiles and exercises the concrete Web platform + service surface on the Windows host build, so the Web platform target is no + longer only indirectly covered through the narrower helper interface. +- `src/platform_legacy/legacy_platform_services.*` no longer has a live + platform-entrypoint consumer; it remains only as generic fallback scaffolding + still linked into the retained app target graph. - `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. diff --git a/docs/modernization/tasks.md b/docs/modernization/tasks.md index dad4089e..a78b06f9 100644 --- a/docs/modernization/tasks.md +++ b/docs/modernization/tasks.md @@ -139,6 +139,16 @@ 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_web/web_platform_services.*` now owns the concrete WebGL + `PlatformServices` surface in addition to the narrower helper interface used + by `pp_platform_api` tests. +- `webgl/src/main.cpp` now binds that owned Web `PlatformServices` instance + directly instead of constructing the retained `platform_legacy` adapter. +- The touched Web render-context acquire/present, app-close dispatch, + file/image picker routing, prepared-file handoff, and default render-target + binding now route through `src/platform_web/web_platform_services.*`. +- `src/platform_legacy/legacy_platform_services.*` no longer has a live + platform-entrypoint consumer and is now down to generic fallback scaffolding. - 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. diff --git a/src/platform_legacy/legacy_platform_fallback_behavior.h b/src/platform_legacy/legacy_platform_fallback_behavior.h new file mode 100644 index 00000000..c6f435b6 --- /dev/null +++ b/src/platform_legacy/legacy_platform_fallback_behavior.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include + +#include "legacy_ui_gl_dispatch.h" +#include "platform_api/platform_services.h" +#include "renderer_gl/opengl_capabilities.h" + +namespace pp::platform::legacy { + +inline void bind_default_opengl_render_target() +{ + pp::legacy::ui_gl::bind_opengl_framebuffer( + pp::renderer::gl::framebuffer_target(), + pp::renderer::gl::default_framebuffer_id()); +} + +inline void complete_prepared_file_without_save( + std::string_view path, + const pp::platform::PreparedFileCallback& callback) +{ + if (!callback) + return; + + callback(std::string(path), false); +} + +} diff --git a/src/platform_legacy/legacy_platform_services.cpp b/src/platform_legacy/legacy_platform_services.cpp index 3d68c514..a6c625a0 100644 --- a/src/platform_legacy/legacy_platform_services.cpp +++ b/src/platform_legacy/legacy_platform_services.cpp @@ -3,11 +3,10 @@ #include -#include "legacy_ui_gl_dispatch.h" #include "log.h" +#include "platform_legacy/legacy_platform_fallback_behavior.h" #include "platform_api/network_tls_policy.h" #include "platform_api/platform_policy.h" -#include "renderer_gl/opengl_capabilities.h" namespace { @@ -77,9 +76,7 @@ public: void bind_default_render_target() override { - pp::legacy::ui_gl::bind_opengl_framebuffer( - pp::renderer::gl::framebuffer_target(), - pp::renderer::gl::default_framebuffer_id()); + pp::platform::legacy::bind_default_opengl_render_target(); } void bind_main_render_target() override @@ -298,10 +295,8 @@ public: std::string_view suggested_name, pp::platform::PreparedFileCallback callback) override { - const std::string value(path); - const std::string name(suggested_name); - (void)name; - callback(value, false); + (void)suggested_name; + pp::platform::legacy::complete_prepared_file_without_save(path, callback); } }; diff --git a/src/platform_web/web_platform_services.cpp b/src/platform_web/web_platform_services.cpp index 22c4c7ec..4de603ee 100644 --- a/src/platform_web/web_platform_services.cpp +++ b/src/platform_web/web_platform_services.cpp @@ -1,12 +1,18 @@ +#include "pch.h" + #include "platform_web/web_platform_services.h" +#include "legacy_ui_gl_dispatch.h" +#include "platform_api/network_tls_policy.h" #include "platform_api/platform_policy.h" +#include "renderer_gl/opengl_capabilities.h" #include #include #include #if defined(__WEB__) +void webgl_pick_file(std::function callback); void webgl_pick_file_save( const std::string& path, const std::string& name, @@ -15,6 +21,115 @@ void webgl_sync(); #endif namespace pp::platform::web { +namespace { + +constexpr auto kPlatformFamily = pp::platform::PlatformFamily::webgl; + +} + +WebPlatformServices::WebPlatformServices(WebPlatformServicesConfig config) + : config_(std::move(config)) +{ +} + +PlatformStoragePaths WebPlatformServices::prepare_storage_paths() +{ + return {}; +} + +void WebPlatformServices::log_stacktrace() +{ +} + +void WebPlatformServices::trigger_crash_test() +{ +} + +std::string WebPlatformServices::clipboard_text() +{ + return {}; +} + +bool WebPlatformServices::set_clipboard_text(std::string_view text) +{ + const std::string value(text); + (void)value; + return false; +} + +void WebPlatformServices::set_cursor_visible(bool visible) +{ + (void)visible; +} + +void WebPlatformServices::set_virtual_keyboard_visible(bool visible) +{ + (void)visible; +} + +void WebPlatformServices::attach_ui_thread() +{ +} + +void WebPlatformServices::detach_ui_thread() +{ +} + +void WebPlatformServices::acquire_render_context() +{ + if (config_.shell.acquire_render_context) + config_.shell.acquire_render_context(); +} + +void WebPlatformServices::release_render_context() +{ +} + +void WebPlatformServices::present_render_context() +{ + if (config_.shell.present_render_context) + config_.shell.present_render_context(); +} + +void WebPlatformServices::bind_default_render_target() +{ +#if defined(__WEB__) + pp::legacy::ui_gl::bind_opengl_framebuffer( + pp::renderer::gl::framebuffer_target(), + pp::renderer::gl::default_framebuffer_id()); +#endif +} + +void WebPlatformServices::bind_main_render_target() +{ + bind_default_render_target(); +} + +void WebPlatformServices::apply_render_platform_hints() +{ +} + +void WebPlatformServices::install_render_debug_callback() +{ +} + +void WebPlatformServices::begin_render_capture_frame() +{ +} + +void WebPlatformServices::end_render_capture_frame() +{ +} + +bool WebPlatformServices::deletes_recorded_files_on_clear() +{ + return pp::platform::platform_deletes_recorded_files_on_clear(kPlatformFamily); +} + +void WebPlatformServices::clear_recorded_files(std::string_view recording_path) +{ + (void)recording_path; +} void WebPlatformServices::publish_exported_image(std::string_view path) { @@ -28,9 +143,169 @@ void WebPlatformServices::flush_persistent_storage() #endif } -[[nodiscard]] int WebPlatformServices::default_canvas_resolution() +std::vector WebPlatformServices::document_browse_roots( + std::string_view work_path, + std::string_view data_path) { - return pp::platform::platform_default_canvas_resolution(pp::platform::PlatformFamily::webgl); + return pp::platform::platform_document_browse_roots( + kPlatformFamily, + work_path, + data_path); +} + +void WebPlatformServices::save_ui_state() +{ + if (!pp::platform::platform_saves_native_ui_state(kPlatformFamily)) + return; +} + +bool WebPlatformServices::enables_live_asset_reloading() +{ + return pp::platform::platform_enables_live_asset_reloading(kPlatformFamily); +} + +void WebPlatformServices::update_platform_frame(float delta_time_seconds) +{ + (void)delta_time_seconds; +} + +void WebPlatformServices::report_rendered_frames(int frames) +{ + (void)frames; +} + +void WebPlatformServices::display_file(std::string_view path) +{ + (void)path; +} + +void WebPlatformServices::share_file(std::string_view path) +{ + (void)path; +} + +void WebPlatformServices::request_app_close() +{ + if (config_.shell.request_app_close) + config_.shell.request_app_close(); +} + +bool WebPlatformServices::start_vr_mode() +{ + return false; +} + +void WebPlatformServices::stop_vr_mode() +{ +} + +void WebPlatformServices::pick_image(PickedPathCallback callback) +{ +#if defined(__WEB__) + webgl_pick_file(std::move(callback)); +#else + (void)callback; +#endif +} + +void WebPlatformServices::pick_file( + std::vector file_types, + PickedPathCallback callback) +{ + (void)file_types; +#if defined(__WEB__) + webgl_pick_file(std::move(callback)); +#else + (void)callback; +#endif +} + +void WebPlatformServices::pick_save_file( + std::vector file_types, + PickedPathCallback callback) +{ + (void)file_types; + (void)callback; +} + +void WebPlatformServices::pick_directory(PickedPathCallback callback) +{ + (void)callback; +} + +bool WebPlatformServices::supports_working_directory_picker() +{ + return pp::platform::platform_supports_working_directory_picker(kPlatformFamily); +} + +std::string WebPlatformServices::format_working_directory_path(std::string_view path) +{ + return std::string(path); +} + +bool WebPlatformServices::uses_prepared_file_writes() +{ + return pp::platform::platform_uses_prepared_file_writes(kPlatformFamily); +} + +bool WebPlatformServices::uses_work_directory_document_export_collections() +{ + return pp::platform::platform_uses_work_directory_document_export_collections(kPlatformFamily); +} + +bool WebPlatformServices::disables_network_tls_verification() +{ + return pp::platform::default_disables_network_tls_verification(); +} + +bool WebPlatformServices::uses_ppbr_export_data_directory_override() +{ + return pp::platform::platform_uses_ppbr_export_data_directory_override(kPlatformFamily); +} + +bool WebPlatformServices::supports_sonarpen() +{ + return pp::platform::platform_supports_sonarpen(kPlatformFamily); +} + +void WebPlatformServices::start_sonarpen() +{ +} + +int WebPlatformServices::default_canvas_resolution() +{ + return pp::platform::platform_default_canvas_resolution(kPlatformFamily); +} + +bool WebPlatformServices::draws_canvas_tip_for_pointer( + bool is_mouse, + bool is_stylus, + bool is_left_button_release) +{ + return pp::platform::platform_draws_canvas_tip_for_pointer( + kPlatformFamily, + is_mouse, + is_stylus, + is_left_button_release); +} + +float WebPlatformServices::adjust_canvas_input_pressure(float pressure) +{ + return pressure; +} + +PreparedFileTarget WebPlatformServices::prepare_writable_file( + std::string_view type, + std::string_view default_name, + std::string_view data_path, + std::string_view temporary_path) +{ + return pp::platform::plan_platform_writable_file( + kPlatformFamily, + type, + default_name, + data_path, + temporary_path); } void WebPlatformServices::save_prepared_file( @@ -53,7 +328,13 @@ void WebPlatformServices::save_prepared_file( #endif } -[[nodiscard]] std::unique_ptr create_web_platform_services() +std::unique_ptr create_platform_services( + WebPlatformServicesConfig config) +{ + return std::make_unique(std::move(config)); +} + +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 index 3529c3ba..63d1354c 100644 --- a/src/platform_web/web_platform_services.h +++ b/src/platform_web/web_platform_services.h @@ -2,22 +2,96 @@ #include "platform_api/platform_services.h" +#include #include #include namespace pp::platform::web { -class WebPlatformServices final : public pp::platform::WebPlatformServices { +struct WebPlatformShell { + std::function acquire_render_context; + std::function present_render_context; + std::function request_app_close; +}; + +struct WebPlatformServicesConfig { + WebPlatformShell shell; +}; + +class WebPlatformServices final + : public pp::platform::PlatformServices + , public pp::platform::WebPlatformServices { public: + explicit WebPlatformServices(WebPlatformServicesConfig config = {}); + + [[nodiscard]] pp::platform::PlatformStoragePaths prepare_storage_paths() override; + void log_stacktrace() override; + void trigger_crash_test() override; + [[nodiscard]] std::string clipboard_text() override; + [[nodiscard]] bool set_clipboard_text(std::string_view text) override; + void set_cursor_visible(bool visible) override; + void set_virtual_keyboard_visible(bool visible) override; + void attach_ui_thread() override; + void detach_ui_thread() override; + void acquire_render_context() override; + void release_render_context() override; + void present_render_context() override; + void bind_default_render_target() override; + void bind_main_render_target() override; + void apply_render_platform_hints() override; + void install_render_debug_callback() override; + void begin_render_capture_frame() override; + void end_render_capture_frame() override; + [[nodiscard]] bool deletes_recorded_files_on_clear() override; + void clear_recorded_files(std::string_view recording_path) override; void publish_exported_image(std::string_view path) override; void flush_persistent_storage() override; + [[nodiscard]] std::vector document_browse_roots( + std::string_view work_path, + std::string_view data_path) override; + void save_ui_state() override; + [[nodiscard]] bool enables_live_asset_reloading() override; + void update_platform_frame(float delta_time_seconds) override; + void report_rendered_frames(int frames) override; + void display_file(std::string_view path) override; + void share_file(std::string_view path) override; + void request_app_close() override; + [[nodiscard]] bool start_vr_mode() override; + void stop_vr_mode() override; + void pick_image(PickedPathCallback callback) override; + void pick_file(std::vector file_types, PickedPathCallback callback) override; + void pick_save_file(std::vector file_types, PickedPathCallback callback) override; + void pick_directory(PickedPathCallback callback) override; + [[nodiscard]] bool supports_working_directory_picker() override; + [[nodiscard]] std::string format_working_directory_path(std::string_view path) override; + [[nodiscard]] bool uses_prepared_file_writes() override; + [[nodiscard]] bool uses_work_directory_document_export_collections() override; + [[nodiscard]] bool disables_network_tls_verification() override; + [[nodiscard]] bool uses_ppbr_export_data_directory_override() override; + [[nodiscard]] bool supports_sonarpen() override; + void start_sonarpen() override; [[nodiscard]] int default_canvas_resolution() override; + [[nodiscard]] bool draws_canvas_tip_for_pointer( + bool is_mouse, + bool is_stylus, + bool is_left_button_release) override; + [[nodiscard]] float adjust_canvas_input_pressure(float pressure) override; + [[nodiscard]] PreparedFileTarget prepare_writable_file( + std::string_view type, + std::string_view default_name, + std::string_view data_path, + std::string_view temporary_path) override; void save_prepared_file( std::string_view path, std::string_view suggested_name, PreparedFileCallback callback) override; + +private: + WebPlatformServicesConfig config_; }; +[[nodiscard]] std::unique_ptr create_platform_services( + WebPlatformServicesConfig config = {}); [[nodiscard]] std::unique_ptr create_web_platform_services(); } diff --git a/tests/platform_api/platform_services_tests.cpp b/tests/platform_api/platform_services_tests.cpp index d15abeb5..20e1e532 100644 --- a/tests/platform_api/platform_services_tests.cpp +++ b/tests/platform_api/platform_services_tests.cpp @@ -5,6 +5,7 @@ #include "platform_api/platform_policy.h" #include "platform_api/platform_services.h" #include "platform_apple/apple_platform_services.h" +#include "platform_web/web_platform_services.h" #include #include @@ -1000,6 +1001,37 @@ void web_platform_services_resolve_injected_services(pp::tests::Harness& harness PP_EXPECT(harness, pp::platform::injected_web_platform_services() == nullptr); } +void web_platform_services_dispatch_full_platform_surface(pp::tests::Harness& harness) +{ + auto services = pp::platform::web::create_platform_services(); + std::string saved_path; + bool saved = true; + + PP_EXPECT(harness, services->uses_prepared_file_writes()); + PP_EXPECT(harness, !services->supports_working_directory_picker()); + PP_EXPECT(harness, services->default_canvas_resolution() == 512); + PP_EXPECT(harness, services->document_browse_roots("D:/Paint/work", "D:/Paint").size() == 1); + PP_EXPECT(harness, services->document_browse_roots("D:/Paint/work", "D:/Paint")[0] == "D:/Paint/work"); + PP_EXPECT(harness, services->format_working_directory_path("D:/Paint/.") == "D:/Paint/."); + PP_EXPECT(harness, services->draws_canvas_tip_for_pointer(true, false, false)); + + const auto target = services->prepare_writable_file("png", "export", "/PanoPainter", "/tmp"); + PP_EXPECT(harness, target.path == "/PanoPainter/export.png"); + PP_EXPECT(harness, target.suggested_name == "export.png"); + PP_EXPECT(harness, !target.write_on_background_thread); + + services->save_prepared_file( + "/PanoPainter/export.png", + "export.png", + [&](std::string path, bool success) { + saved_path = std::move(path); + saved = success; + }); + + PP_EXPECT(harness, saved_path == "/PanoPainter/export.png"); + PP_EXPECT(harness, !saved); +} + void platform_services_dispatch_writable_file_target(pp::tests::Harness& harness) { FakePlatformServices fake("unused"); @@ -1385,6 +1417,9 @@ int main() apple_document_platform_services_preserve_working_directory_picker_policy); harness.run("web platform services preserve default web policy", web_platform_services_preserve_default_web_policy); harness.run("web platform services resolve injected services", web_platform_services_resolve_injected_services); + harness.run( + "web platform services dispatch full platform surface", + web_platform_services_dispatch_full_platform_surface); harness.run("platform services dispatch writable file target", platform_services_dispatch_writable_file_target); harness.run( "platform services dispatch document export collection policy", diff --git a/webgl/src/main.cpp b/webgl/src/main.cpp index 347d2b03..1590e60e 100644 --- a/webgl/src/main.cpp +++ b/webgl/src/main.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include @@ -16,7 +15,6 @@ App app; GLFWwindow* wnd; float theta = 0; glm::vec2 g_cursor_pos; -std::unique_ptr g_web_platform_services; std::unique_ptr g_platform_services; template @@ -201,14 +199,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::web::create_web_platform_services(); - g_platform_services = pp::platform::legacy::create_platform_services({ - .glfw_shell = { + g_platform_services = pp::platform::web::create_platform_services({ + .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(), }); glfwMakeContextCurrent(wnd);