#include "pch.h" #include "platform_legacy/legacy_platform_services.h" #include "app.h" #include "legacy_ui_gl_dispatch.h" #include "log.h" #include "platform_apple/apple_platform_services.h" #include "platform_api/network_tls_policy.h" #include "platform_api/platform_policy.h" #include "renderer_gl/opengl_capabilities.h" #ifdef __ANDROID__ #include "main.h" void displayKeyboard(bool pShow); void android_async_lock(); void android_async_swap(); void android_async_unlock(); void android_attach_jni(); void android_detach_jni(); void android_pick_file(std::function callback); void android_pick_file_save(std::function callback); std::string android_get_clipboard(); bool android_set_clipboard(const std::string& s); #elif __APPLE__ #include #include "objc_utils.h" #ifdef __IOS__ #include "AppDelegate.h" #include "GameViewController.h" #elif defined(__OSX__) #include "main.h" #endif void delete_all_files_in_path(const std::string& source_path); #ifdef __IOS__ void save_image_library(const std::string& path); #endif #elif __LINUX__ #include #include #include "platform_linux/linux_platform_services.h" std::string linux_home_path(); int mkpath(const std::string& dir, mode_t mode = DEFFILEMODE); #elif __WEB__ #include 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 { #ifdef __WEB__ class RetainedWebPlatformServices final : public pp::platform::WebPlatformServices { public: void publish_exported_image(std::string_view path) override { (void)path; } void flush_persistent_storage() override { webgl_sync(); } [[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); webgl_pick_file_save(value, name, [callback = std::move(callback), value](bool success) { callback(value, success); }); } }; #endif [[nodiscard]] pp::platform::WebPlatformServices& active_legacy_web_platform_services() { #ifdef __WEB__ static RetainedWebPlatformServices services; return pp::platform::resolve_web_platform_services(services); #else return pp::platform::active_web_platform_services(); #endif } #if defined(__IOS__) || defined(__OSX__) [[nodiscard]] NSMutableArray* apple_file_types_array(const std::vector& file_types) { NSMutableArray* types = [NSMutableArray arrayWithCapacity:file_types.size()]; for (const auto& type : file_types) { [types addObject:[NSString stringWithCString:type.c_str() encoding:NSUTF8StringEncoding]]; } return types; } [[nodiscard]] pp::platform::apple::AppleDocumentPlatformServices& active_apple_document_platform_services() { #ifdef __IOS__ static pp::platform::apple::AppleDocumentPlatformServices services( pp::platform::PlatformFamily::ios, [] { pp::platform::apple::AppleDocumentPickerBridge bridge; bridge.pick_image = [](pp::platform::PickedPathCallback callback) { dispatch_async(dispatch_get_main_queue(), ^{ [App::I->ios_view pick_photo:callback]; }); }; bridge.pick_file = []( std::vector file_types, pp::platform::PickedPathCallback callback) { dispatch_async(dispatch_get_main_queue(), ^{ [App::I->ios_view pick_file:apple_file_types_array(file_types) then:callback]; }); }; return bridge; }()); return services; #else static pp::platform::apple::AppleDocumentPlatformServices services( pp::platform::PlatformFamily::macos, [] { pp::platform::apple::AppleDocumentPickerBridge bridge; bridge.pick_file = []( std::vector file_types, pp::platform::PickedPathCallback callback) { dispatch_async(dispatch_get_main_queue(), ^{ const std::string path = [App::I->osx_view pick_file:apple_file_types_array(file_types)]; callback(path); }); }; bridge.pick_save_file = []( std::vector file_types, pp::platform::PickedPathCallback callback) { dispatch_async(dispatch_get_main_queue(), ^{ const std::string path = [App::I->osx_view pick_file_save:apple_file_types_array(file_types)]; callback(path); }); }; bridge.pick_directory = [](pp::platform::PickedPathCallback callback) { dispatch_async(dispatch_get_main_queue(), ^{ const std::string path = [App::I->osx_view pick_dir]; callback(path); }); }; bridge.format_working_directory_path = [](std::string_view path) { char path_buffer[4096] = {}; if (realpath(std::string(path).c_str(), path_buffer)) return std::string(path_buffer); return std::string(path); }; return bridge; }()); return services; #endif } #endif // DEBT-0017: fallback for platforms that do not inject PlatformServices yet. class LegacyPlatformServices final : public pp::platform::PlatformServices { public: [[nodiscard]] pp::platform::PlatformStoragePaths prepare_storage_paths() override { #if defined(__IOS__) [App::I->ios_view init_dirs]; return { App::I->data_path, App::I->work_path, App::I->rec_path, App::I->tmp_path, }; #elif defined(__OSX__) [App::I->osx_app init_dirs]; return { App::I->data_path, App::I->work_path, App::I->rec_path, App::I->tmp_path, }; #elif __LINUX__ const std::string data_path = linux_home_path() + "/PanoPainter"; mkpath(data_path + "/brushes"); mkpath(data_path + "/brushes/thumbs"); mkpath(data_path + "/patterns"); mkpath(data_path + "/patterns/thumbs"); mkpath(data_path + "/settings"); mkpath(data_path + "/frames"); return { data_path, data_path, data_path + "/frames", {}, }; #elif __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 { App::I->data_path, App::I->work_path, App::I->rec_path, App::I->tmp_path, }; #endif } void log_stacktrace() override { #if defined(__OSX__) NSString* callstack = [[NSThread callStackSymbols] componentsJoinedByString:@"\n"]; LOG("callstack:\n%s", [callstack cStringUsingEncoding:NSUTF8StringEncoding]); #endif } void trigger_crash_test() override { #ifdef __IOS__ [App::I->ios_view crash]; #elif __OSX__ [App::I->osx_view hockeyapp_crash]; #elif defined(__ANDROID__) int *x = nullptr; *x = 42; LOG("%d", *x); #endif } [[nodiscard]] std::string clipboard_text() override { #if defined(__IOS__) || defined(__OSX__) const auto family = pp::platform::current_platform_family(); if (family == pp::platform::PlatformFamily::ios || family == pp::platform::PlatformFamily::macos) return active_apple_document_platform_services().clipboard_text(); #endif #ifdef __ANDROID__ return android_get_clipboard(); #else return {}; #endif } [[nodiscard]] bool set_clipboard_text(std::string_view text) override { #if defined(__IOS__) || defined(__OSX__) const auto family = pp::platform::current_platform_family(); if (family == pp::platform::PlatformFamily::ios || family == pp::platform::PlatformFamily::macos) return active_apple_document_platform_services().set_clipboard_text(text); #endif const std::string value(text); #ifdef __ANDROID__ return android_set_clipboard(value); #else (void)value; return false; #endif } void set_cursor_visible(bool visible) override { #if defined(__IOS__) || defined(__OSX__) active_apple_document_platform_services().set_cursor_visible(visible); #else (void)visible; #endif } void set_virtual_keyboard_visible(bool visible) override { #ifdef __IOS__ dispatch_async(dispatch_get_main_queue(), ^{ if (visible) [App::I->ios_view show_keyboard]; else [App::I->ios_view hide_keyboard]; }); #elif __ANDROID__ displayKeyboard(visible); #else (void)visible; #endif } void attach_ui_thread() override { #ifdef __ANDROID__ android_attach_jni(); #endif } void detach_ui_thread() override { #ifdef __ANDROID__ android_detach_jni(); #endif } void acquire_render_context() override { #if __OSX__ [App::I->osx_view async_lock]; #elif __IOS__ [App::I->ios_view async_lock]; #elif __ANDROID__ android_async_lock(); #elif __LINUX__ || __WEB__ glfwMakeContextCurrent(App::I->glfw_window); #endif } void release_render_context() override { #if __OSX__ [App::I->osx_view async_unlock]; #elif __IOS__ [App::I->ios_view async_unlock]; #elif __ANDROID__ android_async_unlock(); #endif } void present_render_context() override { #if __OSX__ [App::I->osx_view async_swap]; #elif __IOS__ [App::I->ios_view async_swap]; #elif __ANDROID__ android_async_swap(); #elif __LINUX__ || __WEB__ glfwSwapBuffers(App::I->glfw_window); #endif } void bind_default_render_target() override { pp::legacy::ui_gl::bind_opengl_framebuffer( pp::renderer::gl::framebuffer_target(), pp::renderer::gl::default_framebuffer_id()); } void bind_main_render_target() override { #if __IOS__ [App::I->ios_view->glview bindDrawable]; #else bind_default_render_target(); #endif } void apply_render_platform_hints() override { #if defined(__OSX__) const auto status = pp::renderer::gl::apply_opengl_render_platform_hints( pp::renderer::gl::OpenGlRenderPlatformHintDispatch { .enable = pp::legacy::ui_gl::enable_opengl_state, }); if (!status.ok()) LOG("OpenGL legacy render platform hints failed: %s", status.message); #endif } 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 { return pp::platform::platform_deletes_recorded_files_on_clear( pp::platform::current_platform_family()); } void clear_recorded_files(std::string_view recording_path) override { #if defined(__IOS__) || defined(__OSX__) delete_all_files_in_path(std::string(recording_path)); #else (void)recording_path; #endif } void publish_exported_image(std::string_view path) override { const auto family = pp::platform::current_platform_family(); if (family == pp::platform::PlatformFamily::webgl) { active_legacy_web_platform_services().publish_exported_image(path); return; } if (!pp::platform::platform_publishes_exported_images(family)) { (void)path; return; } #ifdef __IOS__ save_image_library(std::string(path)); #else (void)path; #endif } void flush_persistent_storage() override { const auto family = pp::platform::current_platform_family(); if (family == pp::platform::PlatformFamily::webgl) { active_legacy_web_platform_services().flush_persistent_storage(); return; } if (!pp::platform::platform_flushes_persistent_storage(family)) return; } [[nodiscard]] std::vector document_browse_roots( std::string_view work_path, std::string_view data_path) override { #if defined(__IOS__) || defined(__OSX__) return active_apple_document_platform_services().document_browse_roots(work_path, data_path); #else return pp::platform::platform_document_browse_roots( pp::platform::current_platform_family(), work_path, data_path); #endif } void save_ui_state() override { if (!pp::platform::platform_saves_native_ui_state(pp::platform::current_platform_family())) return; #if defined(__IOS__) || defined(__OSX__) active_apple_document_platform_services().save_ui_state(); #endif } [[nodiscard]] bool enables_live_asset_reloading() override { return pp::platform::platform_enables_live_asset_reloading( pp::platform::current_platform_family()); } void update_platform_frame(float delta_time_seconds) override { (void)delta_time_seconds; } void report_rendered_frames(int frames) override { #ifdef __LINUX__ pp::platform::linux::report_rendered_frames(frames); #else (void)frames; #endif } void pick_image(pp::platform::PickedPathCallback callback) override { #ifdef __IOS__ active_apple_document_platform_services().pick_image(std::move(callback)); #elif __OSX__ active_apple_document_platform_services().pick_image(std::move(callback)); #elif __ANDROID__ android_pick_file(callback); #elif __LINUX__ if (auto p = tinyfd_openFileDialog("Open File", "", 0, nullptr, nullptr, false)) invoke_picked_path_if_selected(p, callback); #elif __WEB__ webgl_pick_file(callback); #else (void)callback; #endif } void pick_file(std::vector file_types, pp::platform::PickedPathCallback callback) override { #ifdef __IOS__ active_apple_document_platform_services().pick_file(std::move(file_types), std::move(callback)); #elif __OSX__ active_apple_document_platform_services().pick_file(std::move(file_types), std::move(callback)); #elif __ANDROID__ android_pick_file(callback); #elif __LINUX__ if (auto p = tinyfd_openFileDialog("Open File", "", 0, nullptr, nullptr, false)) invoke_picked_path_if_selected(p, callback); #elif __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 { #if __OSX__ active_apple_document_platform_services().pick_save_file(std::move(file_types), std::move(callback)); #elif __ANDROID__ android_pick_file_save(callback); #else (void)file_types; (void)callback; #endif } void pick_directory(pp::platform::PickedPathCallback callback) override { #ifdef __IOS__ (void)callback; #elif __OSX__ active_apple_document_platform_services().pick_directory(std::move(callback)); #elif __ANDROID__ (void)callback; #else (void)callback; #endif } [[nodiscard]] bool supports_working_directory_picker() override { #if defined(__IOS__) || defined(__OSX__) return active_apple_document_platform_services().supports_working_directory_picker(); #else return pp::platform::platform_supports_working_directory_picker( pp::platform::current_platform_family()); #endif } [[nodiscard]] std::string format_working_directory_path(std::string_view path) override { #if defined(__IOS__) || defined(__OSX__) return active_apple_document_platform_services().format_working_directory_path(path); #endif return std::string(path); } [[nodiscard]] bool uses_prepared_file_writes() override { return pp::platform::platform_uses_prepared_file_writes( pp::platform::current_platform_family()); } [[nodiscard]] bool uses_work_directory_document_export_collections() override { return pp::platform::platform_uses_work_directory_document_export_collections( pp::platform::current_platform_family()); } [[nodiscard]] bool disables_network_tls_verification() override { return pp::platform::default_disables_network_tls_verification(); } [[nodiscard]] bool uses_ppbr_export_data_directory_override() override { return pp::platform::platform_uses_ppbr_export_data_directory_override( pp::platform::current_platform_family()); } [[nodiscard]] bool supports_sonarpen() override { return pp::platform::platform_supports_sonarpen(pp::platform::current_platform_family()); } void start_sonarpen() override { #if __IOS__ [App::I->ios_app sonarpen_start]; #endif } [[nodiscard]] int default_canvas_resolution() override { const auto family = pp::platform::current_platform_family(); if (family == pp::platform::PlatformFamily::webgl) return active_legacy_web_platform_services().default_canvas_resolution(); return pp::platform::platform_default_canvas_resolution(family); } [[nodiscard]] bool draws_canvas_tip_for_pointer( bool is_mouse, bool is_stylus, bool is_left_button_release) override { 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 { return pressure; } [[nodiscard]] pp::platform::PreparedFileTarget prepare_writable_file( std::string_view type, std::string_view default_name, std::string_view data_path, std::string_view temporary_path) override { 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 { #if defined(__IOS__) || defined(__OSX__) active_apple_document_platform_services().display_file(path); #else (void)path; #endif } void share_file(std::string_view path) override { #if defined(__IOS__) || defined(__OSX__) active_apple_document_platform_services().share_file(path); #else (void)path; #endif } void request_app_close() override { #ifdef __OSX__ dispatch_async(dispatch_get_main_queue(), ^{ [App::I->osx_view close]; }); #elif __LINUX__ glfwSetWindowShouldClose(App::I->glfw_window, GLFW_TRUE); #endif } [[nodiscard]] bool start_vr_mode() override { return false; } void stop_vr_mode() override { } void save_prepared_file( std::string_view path, 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_legacy_web_platform_services().save_prepared_file( path, suggested_name, std::move(callback)); return; } const std::string value(path); const std::string name(suggested_name); #ifdef __IOS__ (void)name; dispatch_async(dispatch_get_main_queue(), ^{ [App::I->ios_view pick_file_save:value]; }); callback(value, true); #else (void)name; callback(value, false); #endif } }; } namespace pp::platform::legacy { PlatformServices& platform_services() { static LegacyPlatformServices services; return services; } }