diff options
Diffstat (limited to 'platform')
-rw-r--r-- | platform/macos/display_server_macos.h | 2 | ||||
-rw-r--r-- | platform/macos/display_server_macos.mm | 170 | ||||
-rw-r--r-- | platform/macos/doc_classes/EditorExportPlatformMacOS.xml | 3 | ||||
-rw-r--r-- | platform/macos/export/export_plugin.cpp | 9 | ||||
-rw-r--r-- | platform/macos/os_macos.h | 4 | ||||
-rw-r--r-- | platform/macos/os_macos.mm | 59 |
6 files changed, 247 insertions, 0 deletions
diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h index 2a7b2ce2a9..aef45e534f 100644 --- a/platform/macos/display_server_macos.h +++ b/platform/macos/display_server_macos.h @@ -315,6 +315,8 @@ public: virtual Error dialog_show(String p_title, String p_description, Vector<String> p_buttons, const Callable &p_callback) override; virtual Error dialog_input_text(String p_title, String p_description, String p_partial, const Callable &p_callback) override; + virtual Error file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) override; + virtual void mouse_set_mode(MouseMode p_mode) override; virtual MouseMode mouse_get_mode() const override; diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm index a4db78b697..9be0c8ec74 100644 --- a/platform/macos/display_server_macos.mm +++ b/platform/macos/display_server_macos.mm @@ -1847,6 +1847,176 @@ Error DisplayServerMacOS::dialog_show(String p_title, String p_description, Vect return OK; } +Error DisplayServerMacOS::file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) { + _THREAD_SAFE_METHOD_ + + NSString *url = [NSString stringWithUTF8String:p_current_directory.utf8().get_data()]; + NSMutableArray *allowed_types = [[NSMutableArray alloc] init]; + bool allow_other = false; + for (int i = 0; i < p_filters.size(); i++) { + Vector<String> tokens = p_filters[i].split(";"); + if (tokens.size() > 0) { + if (tokens[0].strip_edges() == "*.*") { + allow_other = true; + } else { + [allowed_types addObject:[NSString stringWithUTF8String:tokens[0].replace("*.", "").strip_edges().utf8().get_data()]]; + } + } + } + + Callable callback = p_callback; // Make a copy for async completion handler. + switch (p_mode) { + case FILE_DIALOG_MODE_SAVE_FILE: { + NSSavePanel *panel = [NSSavePanel savePanel]; + + [panel setDirectoryURL:[NSURL fileURLWithPath:url]]; + if ([allowed_types count]) { + [panel setAllowedFileTypes:allowed_types]; + } + [panel setAllowsOtherFileTypes:allow_other]; + [panel setExtensionHidden:YES]; + [panel setCanSelectHiddenExtension:YES]; + [panel setCanCreateDirectories:YES]; + [panel setShowsHiddenFiles:p_show_hidden]; + if (p_filename != "") { + NSString *fileurl = [NSString stringWithUTF8String:p_filename.utf8().get_data()]; + [panel setNameFieldStringValue:fileurl]; + } + + [panel beginSheetModalForWindow:[[NSApplication sharedApplication] mainWindow] + completionHandler:^(NSInteger ret) { + if (ret == NSModalResponseOK) { + // Save bookmark for folder. + if (OS::get_singleton()->is_sandboxed()) { + NSArray *bookmarks = [[NSUserDefaults standardUserDefaults] arrayForKey:@"sec_bookmarks"]; + bool skip = false; + for (id bookmark in bookmarks) { + NSError *error = nil; + BOOL isStale = NO; + NSURL *exurl = [NSURL URLByResolvingBookmarkData:bookmark options:NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil bookmarkDataIsStale:&isStale error:&error]; + if (!error && !isStale && ([[exurl path] compare:[[panel directoryURL] path]] == NSOrderedSame)) { + skip = true; + break; + } + } + if (!skip) { + NSError *error = nil; + NSData *bookmark = [[panel directoryURL] bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope includingResourceValuesForKeys:nil relativeToURL:nil error:&error]; + if (!error) { + NSArray *new_bookmarks = [bookmarks arrayByAddingObject:bookmark]; + [[NSUserDefaults standardUserDefaults] setObject:new_bookmarks forKey:@"sec_bookmarks"]; + } + } + } + // Callback. + Vector<String> files; + String url; + url.parse_utf8([[[panel URL] path] UTF8String]); + files.push_back(url); + if (!callback.is_null()) { + Variant v_status = true; + Variant v_files = files; + Variant *v_args[2] = { &v_status, &v_files }; + Variant ret; + Callable::CallError ce; + callback.callp((const Variant **)&v_args, 2, ret, ce); + } + } else { + if (!callback.is_null()) { + Variant v_status = false; + Variant v_files = Vector<String>(); + Variant *v_args[2] = { &v_status, &v_files }; + Variant ret; + Callable::CallError ce; + callback.callp((const Variant **)&v_args, 2, ret, ce); + } + } + }]; + } break; + case FILE_DIALOG_MODE_OPEN_ANY: + case FILE_DIALOG_MODE_OPEN_FILE: + case FILE_DIALOG_MODE_OPEN_FILES: + case FILE_DIALOG_MODE_OPEN_DIR: { + NSOpenPanel *panel = [NSOpenPanel openPanel]; + + [panel setDirectoryURL:[NSURL fileURLWithPath:url]]; + if ([allowed_types count]) { + [panel setAllowedFileTypes:allowed_types]; + } + [panel setAllowsOtherFileTypes:allow_other]; + [panel setExtensionHidden:YES]; + [panel setCanSelectHiddenExtension:YES]; + [panel setCanCreateDirectories:YES]; + [panel setCanChooseFiles:(p_mode != FILE_DIALOG_MODE_OPEN_DIR)]; + [panel setCanChooseDirectories:(p_mode == FILE_DIALOG_MODE_OPEN_DIR || p_mode == FILE_DIALOG_MODE_OPEN_ANY)]; + [panel setShowsHiddenFiles:p_show_hidden]; + if (p_filename != "") { + NSString *fileurl = [NSString stringWithUTF8String:p_filename.utf8().get_data()]; + [panel setNameFieldStringValue:fileurl]; + } + [panel setAllowsMultipleSelection:(p_mode == FILE_DIALOG_MODE_OPEN_FILES)]; + + [panel beginSheetModalForWindow:[[NSApplication sharedApplication] mainWindow] + completionHandler:^(NSInteger ret) { + if (ret == NSModalResponseOK) { + // Save bookmark for folder. + NSArray *urls = [(NSOpenPanel *)panel URLs]; + if (OS::get_singleton()->is_sandboxed()) { + NSArray *bookmarks = [[NSUserDefaults standardUserDefaults] arrayForKey:@"sec_bookmarks"]; + NSMutableArray *new_bookmarks = [bookmarks mutableCopy]; + for (NSUInteger i = 0; i != [urls count]; ++i) { + bool skip = false; + for (id bookmark in bookmarks) { + NSError *error = nil; + BOOL isStale = NO; + NSURL *exurl = [NSURL URLByResolvingBookmarkData:bookmark options:NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil bookmarkDataIsStale:&isStale error:&error]; + if (!error && !isStale && ([[exurl path] compare:[[urls objectAtIndex:i] path]] == NSOrderedSame)) { + skip = true; + break; + } + } + if (!skip) { + NSError *error = nil; + NSData *bookmark = [[urls objectAtIndex:i] bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope includingResourceValuesForKeys:nil relativeToURL:nil error:&error]; + if (!error) { + [new_bookmarks addObject:bookmark]; + } + } + } + [[NSUserDefaults standardUserDefaults] setObject:new_bookmarks forKey:@"sec_bookmarks"]; + } + // Callback. + Vector<String> files; + for (NSUInteger i = 0; i != [urls count]; ++i) { + String url; + url.parse_utf8([[[urls objectAtIndex:i] path] UTF8String]); + files.push_back(url); + } + if (!callback.is_null()) { + Variant v_status = true; + Variant v_files = files; + Variant *v_args[2] = { &v_status, &v_files }; + Variant ret; + Callable::CallError ce; + callback.callp((const Variant **)&v_args, 2, ret, ce); + } + } else { + if (!callback.is_null()) { + Variant v_status = false; + Variant v_files = Vector<String>(); + Variant *v_args[2] = { &v_status, &v_files }; + Variant ret; + Callable::CallError ce; + callback.callp((const Variant **)&v_args, 2, ret, ce); + } + } + }]; + } break; + } + + return OK; +} + Error DisplayServerMacOS::dialog_input_text(String p_title, String p_description, String p_partial, const Callable &p_callback) { _THREAD_SAFE_METHOD_ diff --git a/platform/macos/doc_classes/EditorExportPlatformMacOS.xml b/platform/macos/doc_classes/EditorExportPlatformMacOS.xml index 9199701eb3..6af816989d 100644 --- a/platform/macos/doc_classes/EditorExportPlatformMacOS.xml +++ b/platform/macos/doc_classes/EditorExportPlatformMacOS.xml @@ -96,6 +96,9 @@ <member name="codesign/entitlements/app_sandbox/files_pictures" type="int" setter="" getter=""> Allows read or write access to the user's "Pictures" folder. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_assets_pictures_read-write]com.apple.security.files.pictures.read-write[/url]. </member> + <member name="codesign/entitlements/app_sandbox/files_user_selected" type="int" setter="" getter=""> + Allows read or write access to the locations the user has selected using a native file dialog. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_files_user-selected_read-write]com.apple.security.files.user-selected.read-write[/url]. + </member> <member name="codesign/entitlements/app_sandbox/helper_executables" type="Array" setter="" getter=""> List of helper executables to embedded to the app bundle. Sandboxed app are limited to execute only these executable. See [url=https://developer.apple.com/documentation/xcode/embedding-a-helper-tool-in-a-sandboxed-app]Embedding a command-line tool in a sandboxed app[/url]. </member> diff --git a/platform/macos/export/export_plugin.cpp b/platform/macos/export/export_plugin.cpp index 0dc6d0dcee..a3ba31a5d9 100644 --- a/platform/macos/export/export_plugin.cpp +++ b/platform/macos/export/export_plugin.cpp @@ -425,6 +425,7 @@ void EditorExportPlatformMacOS::get_export_options(List<ExportOption> *r_options r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_pictures", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0)); r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_music", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0)); r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_movies", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0)); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_user_selected", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0)); r_options->push_back(ExportOption(PropertyInfo(Variant::ARRAY, "codesign/entitlements/app_sandbox/helper_executables", PROPERTY_HINT_ARRAY_TYPE, itos(Variant::STRING) + "/" + itos(PROPERTY_HINT_GLOBAL_FILE) + ":"), Array())); r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "codesign/custom_options"), PackedStringArray())); @@ -1922,6 +1923,14 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p ent_f->store_line("<key>com.apple.security.files.movies.read-write</key>"); ent_f->store_line("<true/>"); } + if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_user_selected") == 1) { + ent_f->store_line("<key>com.apple.security.files.user-selected.read-only</key>"); + ent_f->store_line("<true/>"); + } + if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_user_selected") == 2) { + ent_f->store_line("<key>com.apple.security.files.user-selected.read-write</key>"); + ent_f->store_line("<true/>"); + } } ent_f->store_line("</dict>"); diff --git a/platform/macos/os_macos.h b/platform/macos/os_macos.h index ab61649d19..ae94b6296d 100644 --- a/platform/macos/os_macos.h +++ b/platform/macos/os_macos.h @@ -113,6 +113,10 @@ public: virtual String get_unique_id() const override; virtual String get_processor_name() const override; + virtual bool is_sandboxed() const override; + virtual Vector<String> get_granted_permissions() const override; + virtual void revoke_granted_permissions() override; + virtual bool _check_internal_feature_support(const String &p_feature) override; virtual void disable_crash_handler() override; diff --git a/platform/macos/os_macos.mm b/platform/macos/os_macos.mm index 69062c5920..c17ea95f4f 100644 --- a/platform/macos/os_macos.mm +++ b/platform/macos/os_macos.mm @@ -76,6 +76,36 @@ String OS_MacOS::get_processor_name() const { ERR_FAIL_V_MSG("", String("Couldn't get the CPU model name. Returning an empty string.")); } +bool OS_MacOS::is_sandboxed() const { + return has_environment("APP_SANDBOX_CONTAINER_ID"); +} + +Vector<String> OS_MacOS::get_granted_permissions() const { + Vector<String> ret; + + if (is_sandboxed()) { + NSArray *bookmarks = [[NSUserDefaults standardUserDefaults] arrayForKey:@"sec_bookmarks"]; + for (id bookmark in bookmarks) { + NSError *error = nil; + BOOL isStale = NO; + NSURL *url = [NSURL URLByResolvingBookmarkData:bookmark options:NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil bookmarkDataIsStale:&isStale error:&error]; + if (!error && !isStale) { + String url_string; + url_string.parse_utf8([[url path] UTF8String]); + ret.push_back(url_string); + } + } + } + + return ret; +} + +void OS_MacOS::revoke_granted_permissions() { + if (is_sandboxed()) { + [[NSUserDefaults standardUserDefaults] setObject:nil forKey:@"sec_bookmarks"]; + } +} + void OS_MacOS::initialize_core() { OS_Unix::initialize_core(); @@ -85,6 +115,18 @@ void OS_MacOS::initialize_core() { } void OS_MacOS::finalize() { + if (is_sandboxed()) { + NSArray *bookmarks = [[NSUserDefaults standardUserDefaults] arrayForKey:@"sec_bookmarks"]; + for (id bookmark in bookmarks) { + NSError *error = nil; + BOOL isStale = NO; + NSURL *url = [NSURL URLByResolvingBookmarkData:bookmark options:NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil bookmarkDataIsStale:&isStale error:&error]; + if (!error && !isStale) { + [url stopAccessingSecurityScopedResource]; + } + } + } + #ifdef COREMIDI_ENABLED midi_driver.close(); #endif @@ -733,6 +775,23 @@ void OS_MacOS::run() { } OS_MacOS::OS_MacOS() { + if (is_sandboxed()) { + // Load security-scoped bookmarks, request access, remove stale or invalid bookmarks. + NSArray *bookmarks = [[NSUserDefaults standardUserDefaults] arrayForKey:@"sec_bookmarks"]; + NSMutableArray *new_bookmarks = [[NSMutableArray alloc] init]; + for (id bookmark in bookmarks) { + NSError *error = nil; + BOOL isStale = NO; + NSURL *url = [NSURL URLByResolvingBookmarkData:bookmark options:NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil bookmarkDataIsStale:&isStale error:&error]; + if (!error && !isStale) { + if ([url startAccessingSecurityScopedResource]) { + [new_bookmarks addObject:bookmark]; + } + } + } + [[NSUserDefaults standardUserDefaults] setObject:new_bookmarks forKey:@"sec_bookmarks"]; + } + main_loop = nullptr; Vector<Logger *> loggers; |