#include "pch.h" #include "platform_apple/apple_platform_services.h" #include "platform_legacy/legacy_platform_state.h" #include #if defined(__APPLE__) #include #include "objc_utils.h" #if defined(__OBJC__) && defined(__IOS__) #import "PanoPainter/AppDelegate.h" #import "PanoPainter/GameViewController.h" #elif defined(__OBJC__) && defined(__OSX__) #import "PanoPainter-OSX/main.h" #endif #endif namespace pp::platform::apple { namespace { #if defined(__IOS__) || defined(__OSX__) struct RetainedLegacyAppleDocumentPlatformState final { std::unique_ptr services; }; [[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]] RetainedLegacyAppleDocumentPlatformState& retained_legacy_apple_document_platform_state() { static RetainedLegacyAppleDocumentPlatformState state; return state; } #ifdef __IOS__ [[nodiscard]] AppleDocumentPickerBridge make_legacy_apple_document_picker_bridge() { const auto& apple_state = active_legacy_apple_state(); auto* const ios_view = apple_state.ios_view; auto* const ios_app = apple_state.ios_app; AppleDocumentPickerBridge bridge; bridge.clipboard_text = [ios_view] { return [ios_view clipboard_get_string]; }; bridge.set_clipboard_text = [ios_view](std::string_view text) { const std::string value(text); return [ios_view clipboard_set_string:value]; }; bridge.set_virtual_keyboard_visible = [ios_view](bool visible) { dispatch_async(dispatch_get_main_queue(), ^{ if (visible) [ios_view show_keyboard]; else [ios_view hide_keyboard]; }); }; bridge.trigger_crash_test = [ios_view] { [ios_view crash]; }; bridge.start_sonarpen = [ios_app] { [ios_app sonarpen_start]; }; bridge.acquire_render_context = [ios_view] { [ios_view async_lock]; }; bridge.release_render_context = [ios_view] { [ios_view async_unlock]; }; bridge.present_render_context = [ios_view] { [ios_view async_swap]; }; bridge.bind_main_render_target = [ios_view] { [ios_view->glview bindDrawable]; }; bridge.display_file = [ios_view](std::string path) { dispatch_async(dispatch_get_main_queue(), ^{ [ios_view display_file:path]; }); }; bridge.share_file = [ios_view](std::string path) { dispatch_async(dispatch_get_main_queue(), ^{ [ios_view share_file:[NSString stringWithUTF8String:path.c_str()]]; }); }; bridge.pick_image = [ios_view](PickedPathCallback callback) { dispatch_async(dispatch_get_main_queue(), ^{ [ios_view pick_photo:callback]; }); }; bridge.pick_file = [ios_view]( std::vector file_types, PickedPathCallback callback) { dispatch_async(dispatch_get_main_queue(), ^{ [ios_view pick_file:apple_file_types_array(file_types) then:callback]; }); }; bridge.save_prepared_file = [ios_view]( std::string path, std::string suggested_name, PreparedFileCallback callback) { (void)suggested_name; dispatch_async(dispatch_get_main_queue(), ^{ [ios_view pick_file_save:path]; }); callback(path, true); }; return bridge; } #elif defined(__OSX__) [[nodiscard]] AppleDocumentPickerBridge make_legacy_apple_document_picker_bridge() { const auto& apple_state = active_legacy_apple_state(); auto* const osx_view = apple_state.osx_view; auto* const osx_app = apple_state.osx_app; AppleDocumentPickerBridge bridge; bridge.clipboard_text = [osx_view] { return [osx_view clipboard_get_string]; }; bridge.set_clipboard_text = [osx_view](std::string_view text) { const std::string value(text); return [osx_view clipboard_set_string:value]; }; bridge.share_file = [osx_view](std::string path) { dispatch_async(dispatch_get_main_queue(), ^{ [osx_view share_file:[NSString stringWithUTF8String:path.c_str()]]; }); }; bridge.trigger_crash_test = [osx_view] { [osx_view hockeyapp_crash]; }; bridge.request_app_close = [osx_view] { dispatch_async(dispatch_get_main_queue(), ^{ [osx_view close]; }); }; bridge.acquire_render_context = [osx_view] { [osx_view async_lock]; }; bridge.release_render_context = [osx_view] { [osx_view async_unlock]; }; bridge.present_render_context = [osx_view] { [osx_view async_swap]; }; bridge.set_cursor_visible = [osx_view](bool visible) { [osx_view show_cursor:visible]; }; bridge.save_ui_state = [osx_app] { [osx_app save_ui_state]; }; bridge.pick_file = [osx_view]( std::vector file_types, PickedPathCallback callback) { dispatch_async(dispatch_get_main_queue(), ^{ const std::string path = [osx_view pick_file:apple_file_types_array(file_types)]; callback(path); }); }; bridge.pick_save_file = [osx_view]( std::vector file_types, PickedPathCallback callback) { dispatch_async(dispatch_get_main_queue(), ^{ const std::string path = [osx_view pick_file_save:apple_file_types_array(file_types)]; callback(path); }); }; bridge.pick_directory = [osx_view](PickedPathCallback callback) { dispatch_async(dispatch_get_main_queue(), ^{ const std::string path = [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; } #endif #endif } #if defined(__IOS__) || defined(__OSX__) [[nodiscard]] PlatformFamily legacy_apple_platform_family() { #ifdef __IOS__ return PlatformFamily::ios; #else return PlatformFamily::macos; #endif } [[nodiscard]] RetainedLegacyAppleState& active_legacy_apple_state() { static RetainedLegacyAppleState state; return state; } [[nodiscard]] AppleDocumentPlatformServices& active_legacy_apple_document_platform_services() { auto& retained = retained_legacy_apple_document_platform_state(); if (!retained.services) { retained.services = std::make_unique( legacy_apple_platform_family(), make_legacy_apple_document_picker_bridge()); } return *retained.services; } [[nodiscard]] PlatformStoragePaths prepare_legacy_apple_storage_paths() { const auto& apple_state = active_legacy_apple_state(); #ifdef __IOS__ [apple_state.ios_view init_dirs]; #elif defined(__OSX__) [apple_state.osx_app init_dirs]; #endif return apple_state.storage_paths; } void set_legacy_apple_storage_paths(pp::platform::PlatformStoragePaths paths) { active_legacy_apple_state().storage_paths = std::move(paths); } void set_legacy_apple_state( #ifdef __IOS__ GameViewController* ios_view, AppDelegate* ios_app #elif defined(__OSX__) View* osx_view, AppOSX* osx_app #endif ) { auto& retained = active_legacy_apple_state(); bool changed = false; #ifdef __IOS__ if (ios_view) { changed = changed || retained.ios_view != ios_view; retained.ios_view = ios_view; } if (ios_app) { changed = changed || retained.ios_app != ios_app; retained.ios_app = ios_app; } #elif defined(__OSX__) if (osx_view) { changed = changed || retained.osx_view != osx_view; retained.osx_view = osx_view; } if (osx_app) { changed = changed || retained.osx_app != osx_app; retained.osx_app = osx_app; } #endif if (changed) retained_legacy_apple_document_platform_state().services.reset(); } #endif }