#include "pch.h" #include "platform_legacy/legacy_platform_services.h" #include "app.h" #include "app_core/document_platform_io.h" #include "platform_api/network_tls_policy.h" #include "renderer_gl/opengl_capabilities.h" #ifdef __ANDROID__ 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__ 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 std::string linux_home_path(); int mkpath(const std::string& dir, mode_t mode = DEFFILEMODE); void linux_update_fps(int frames); #elif __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 { void invoke_picked_path_if_selected( const std::string& path, const std::function& callback) { if (pp::app::plan_picked_path(path) == pp::app::PickedPathAction::invoke_callback) callback(path); } // 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 __IOS__ return [App::I->ios_view clipboard_get_string]; #elif __OSX__ return [App::I->osx_view clipboard_get_string]; #elif __ANDROID__ return android_get_clipboard(); #else return {}; #endif } [[nodiscard]] bool set_clipboard_text(std::string_view text) override { const std::string value(text); #if __IOS__ return [App::I->ios_view clipboard_set_string:value]; #elif __OSX__ return [App::I->osx_view clipboard_set_string:value]; #elif __ANDROID__ return android_set_clipboard(value); #else return false; #endif } void set_cursor_visible(bool visible) override { #ifdef __OSX__ [App::I->osx_view show_cursor: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 { glBindFramebuffer( static_cast(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__) glEnable(static_cast(pp::renderer::gl::program_point_size_state())); glEnable(static_cast(pp::renderer::gl::line_smooth_state())); #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 { #if defined(__IOS__) || defined(__OSX__) return true; #else return false; #endif } 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 { #ifdef __IOS__ save_image_library(std::string(path)); #else (void)path; #endif } void flush_persistent_storage() override { #ifdef __WEB__ webgl_sync(); #endif } [[nodiscard]] std::vector document_browse_roots( 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 } void save_ui_state() override { #ifdef __OSX__ [App::I->osx_app save_ui_state]; #endif } [[nodiscard]] bool enables_live_asset_reloading() override { #if defined(__OSX__) return true; #else return false; #endif } void update_platform_frame(float delta_time_seconds) override { (void)delta_time_seconds; } void report_rendered_frames(int frames) override { #ifdef __LINUX__ linux_update_fps(frames); #else (void)frames; #endif } void pick_image(pp::platform::PickedPathCallback callback) override { #ifdef __IOS__ dispatch_async(dispatch_get_main_queue(), ^{ [App::I->ios_view pick_photo:callback]; }); #elif __OSX__ dispatch_async(dispatch_get_main_queue(), ^{ NSArray* fileTypes = [NSArray arrayWithObjects:@"png", @"PNG", @"jpg", @"JPG", @"jpeg", nil]; std::string path = [App::I->osx_view pick_file:fileTypes]; invoke_picked_path_if_selected(path, 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__ dispatch_async(dispatch_get_main_queue(), ^{ NSMutableArray* fileTypes = [NSMutableArray arrayWithCapacity:file_types.size()]; for (const auto& t : file_types) [fileTypes addObject:[NSString stringWithCString:t.c_str() encoding:NSUTF8StringEncoding]]; [App::I->ios_view pick_file:fileTypes then:callback]; }); #elif __OSX__ dispatch_async(dispatch_get_main_queue(), ^{ NSMutableArray* fileTypes = [NSMutableArray arrayWithCapacity:file_types.size()]; for (const auto& t : file_types) [fileTypes addObject:[NSString stringWithCString:t.c_str() encoding:NSUTF8StringEncoding]]; std::string path = [App::I->osx_view pick_file:fileTypes]; invoke_picked_path_if_selected(path, 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__ dispatch_async(dispatch_get_main_queue(), ^{ NSMutableArray* fileTypes = [NSMutableArray arrayWithCapacity:file_types.size()]; for (const auto& t : file_types) [fileTypes addObject:[NSString stringWithCString:t.c_str() encoding:NSUTF8StringEncoding]]; std::string path = [App::I->osx_view pick_file_save:fileTypes]; invoke_picked_path_if_selected(path, 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__ dispatch_async(dispatch_get_main_queue(), ^{ std::string path = [App::I->osx_view pick_dir]; invoke_picked_path_if_selected(path, callback); }); #elif __ANDROID__ (void)callback; #else (void)callback; #endif } [[nodiscard]] bool supports_working_directory_picker() override { #if defined(__OSX__) return true; #else return false; #endif } [[nodiscard]] std::string format_working_directory_path(std::string_view path) override { #if defined(__OSX__) char path_buffer[4096] = {}; if (realpath(std::string(path).c_str(), path_buffer)) return path_buffer; #endif return std::string(path); } [[nodiscard]] bool uses_prepared_file_writes() override { #if __IOS__ || __WEB__ return true; #else return false; #endif } [[nodiscard]] bool uses_work_directory_document_export_collections() override { #if __IOS__ return true; #else return false; #endif } [[nodiscard]] bool disables_network_tls_verification() override { return pp::platform::default_disables_network_tls_verification(); } [[nodiscard]] bool uses_ppbr_export_data_directory_override() override { #if defined(__OSX__) return true; #else return false; #endif } [[nodiscard]] bool supports_sonarpen() override { #if __IOS__ return true; #else return false; #endif } void start_sonarpen() override { #if __IOS__ [App::I->ios_app sonarpen_start]; #endif } [[nodiscard]] bool draws_canvas_tip_for_pointer( bool is_mouse, 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 } [[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 { 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 } void display_file(std::string_view path) override { const std::string value(path); #ifdef __IOS__ dispatch_async(dispatch_get_main_queue(), ^{ [App::I->ios_view display_file:value]; }); #elif __OSX__ [[NSWorkspace sharedWorkspace] openFile:[NSString stringWithUTF8String:value.c_str()]]; #else (void)value; #endif } void share_file(std::string_view path) override { const std::string value(path); #ifdef __IOS__ dispatch_async(dispatch_get_main_queue(), ^{ [App::I->ios_view share_file:[NSString stringWithUTF8String:value.c_str()]]; }); #elif __OSX__ dispatch_async(dispatch_get_main_queue(), ^{ [App::I->osx_view share_file:[NSString stringWithUTF8String:value.c_str()]]; }); #else (void)value; #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 } 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 __IOS__ (void)name; dispatch_async(dispatch_get_main_queue(), ^{ [App::I->ios_view pick_file_save:value]; }); callback(value, true); #elif __WEB__ webgl_pick_file_save(value, name, [callback = std::move(callback), value](bool success) { callback(value, success); }); #else (void)name; callback(value, false); #endif } }; } namespace pp::platform::legacy { PlatformServices& platform_services() { static LegacyPlatformServices services; return services; } }