diff options
Diffstat (limited to 'platform/macos')
| -rw-r--r-- | platform/macos/display_server_macos.h | 6 | ||||
| -rw-r--r-- | platform/macos/display_server_macos.mm | 286 | ||||
| -rw-r--r-- | platform/macos/doc_classes/EditorExportPlatformMacOS.xml | 3 | ||||
| -rw-r--r-- | platform/macos/export/export_plugin.cpp | 17 | ||||
| -rw-r--r-- | platform/macos/export/run_icon.svg | 2 | ||||
| -rw-r--r-- | platform/macos/os_macos.h | 4 | ||||
| -rw-r--r-- | platform/macos/os_macos.mm | 61 |
7 files changed, 340 insertions, 39 deletions
diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h index 93fa93b259..e5e0e53bfb 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; @@ -326,6 +328,9 @@ public: virtual void clipboard_set(const String &p_text) override; virtual String clipboard_get() const override; + virtual Ref<Image> clipboard_get_image() const override; + virtual bool clipboard_has() const override; + virtual bool clipboard_has_image() const override; virtual int get_screen_count() const override; virtual int get_primary_screen() const override; @@ -433,6 +438,7 @@ public: virtual String keyboard_get_layout_language(int p_index) const override; virtual String keyboard_get_layout_name(int p_index) const override; virtual Key keyboard_get_keycode_from_physical(Key p_keycode) const override; + virtual Key keyboard_get_label_from_physical(Key p_keycode) const override; virtual void process_events() override; virtual void force_process_and_drop_events() override; diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm index 5ccef68e7f..d64bb5211e 100644 --- a/platform/macos/display_server_macos.mm +++ b/platform/macos/display_server_macos.mm @@ -44,8 +44,10 @@ #include "core/io/marshalls.h" #include "core/math/geometry_2d.h" #include "core/os/keyboard.h" +#include "drivers/png/png_driver_common.h" #include "main/main.h" -#include "scene/resources/texture.h" +#include "scene/resources/atlas_texture.h" +#include "scene/resources/image_texture.h" #if defined(GLES3_ENABLED) #include "drivers/gles3/rasterizer_gles3.h" @@ -1847,6 +1849,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_ @@ -2100,6 +2272,37 @@ String DisplayServerMacOS::clipboard_get() const { return ret; } +Ref<Image> DisplayServerMacOS::clipboard_get_image() const { + Ref<Image> image; + NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; + NSString *result = [pasteboard availableTypeFromArray:[NSArray arrayWithObjects:NSPasteboardTypeTIFF, NSPasteboardTypePNG, nil]]; + if (!result) { + return image; + } + NSData *data = [pasteboard dataForType:result]; + if (!data) { + return image; + } + NSBitmapImageRep *bitmap = [NSBitmapImageRep imageRepWithData:data]; + NSData *pngData = [bitmap representationUsingType:NSPNGFileType properties:@{}]; + image.instantiate(); + PNGDriverCommon::png_to_image((const uint8_t *)pngData.bytes, pngData.length, false, image); + return image; +} + +bool DisplayServerMacOS::clipboard_has() const { + NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; + NSArray *classArray = [NSArray arrayWithObject:[NSString class]]; + NSDictionary *options = [NSDictionary dictionary]; + return [pasteboard canReadObjectForClasses:classArray options:options]; +} + +bool DisplayServerMacOS::clipboard_has_image() const { + NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; + NSString *result = [pasteboard availableTypeFromArray:[NSArray arrayWithObjects:NSPasteboardTypeTIFF, NSPasteboardTypePNG, nil]]; + return result; +} + int DisplayServerMacOS::get_screen_count() const { _THREAD_SAFE_METHOD_ @@ -3085,14 +3288,14 @@ bool DisplayServerMacOS::window_is_focused(WindowID p_window) const { } bool DisplayServerMacOS::window_can_draw(WindowID p_window) const { - return window_get_mode(p_window) != WINDOW_MODE_MINIMIZED; + return (window_get_mode(p_window) != WINDOW_MODE_MINIMIZED) && [windows[p_window].window_object isOnActiveSpace]; } bool DisplayServerMacOS::can_any_window_draw() const { _THREAD_SAFE_METHOD_ for (const KeyValue<WindowID, WindowData> &E : windows) { - if (window_get_mode(E.key) != WINDOW_MODE_MINIMIZED) { + if ((window_get_mode(E.key) != WINDOW_MODE_MINIMIZED) && [E.value.window_object isOnActiveSpace]) { return true; } } @@ -3499,6 +3702,17 @@ Key DisplayServerMacOS::keyboard_get_keycode_from_physical(Key p_keycode) const return (Key)(KeyMappingMacOS::remap_key(macos_keycode, 0, false) | modifiers); } +Key DisplayServerMacOS::keyboard_get_label_from_physical(Key p_keycode) const { + if (p_keycode == Key::PAUSE || p_keycode == Key::NONE) { + return p_keycode; + } + + Key modifiers = p_keycode & KeyModifierMask::MODIFIER_MASK; + Key keycode_no_mod = p_keycode & KeyModifierMask::CODE_MASK; + unsigned int macos_keycode = KeyMappingMacOS::unmap_key(keycode_no_mod); + return (Key)(KeyMappingMacOS::remap_key(macos_keycode, 0, true) | modifiers); +} + void DisplayServerMacOS::process_events() { _THREAD_SAFE_METHOD_ @@ -3609,40 +3823,46 @@ void DisplayServerMacOS::set_native_icon(const String &p_filename) { void DisplayServerMacOS::set_icon(const Ref<Image> &p_icon) { _THREAD_SAFE_METHOD_ - Ref<Image> img = p_icon; - img = img->duplicate(); - img->convert(Image::FORMAT_RGBA8); - NSBitmapImageRep *imgrep = [[NSBitmapImageRep alloc] - initWithBitmapDataPlanes:nullptr - pixelsWide:img->get_width() - pixelsHigh:img->get_height() - bitsPerSample:8 - samplesPerPixel:4 - hasAlpha:YES - isPlanar:NO - colorSpaceName:NSDeviceRGBColorSpace - bytesPerRow:img->get_width() * 4 - bitsPerPixel:32]; - ERR_FAIL_COND(imgrep == nil); - uint8_t *pixels = [imgrep bitmapData]; + if (p_icon.is_valid()) { + ERR_FAIL_COND(p_icon->get_width() <= 0 || p_icon->get_height() <= 0); - int len = img->get_width() * img->get_height(); - const uint8_t *r = img->get_data().ptr(); + Ref<Image> img = p_icon->duplicate(); + img->convert(Image::FORMAT_RGBA8); - /* Premultiply the alpha channel */ - for (int i = 0; i < len; i++) { - uint8_t alpha = r[i * 4 + 3]; - pixels[i * 4 + 0] = (uint8_t)(((uint16_t)r[i * 4 + 0] * alpha) / 255); - pixels[i * 4 + 1] = (uint8_t)(((uint16_t)r[i * 4 + 1] * alpha) / 255); - pixels[i * 4 + 2] = (uint8_t)(((uint16_t)r[i * 4 + 2] * alpha) / 255); - pixels[i * 4 + 3] = alpha; - } + NSBitmapImageRep *imgrep = [[NSBitmapImageRep alloc] + initWithBitmapDataPlanes:nullptr + pixelsWide:img->get_width() + pixelsHigh:img->get_height() + bitsPerSample:8 + samplesPerPixel:4 + hasAlpha:YES + isPlanar:NO + colorSpaceName:NSDeviceRGBColorSpace + bytesPerRow:img->get_width() * 4 + bitsPerPixel:32]; + ERR_FAIL_COND(imgrep == nil); + uint8_t *pixels = [imgrep bitmapData]; - NSImage *nsimg = [[NSImage alloc] initWithSize:NSMakeSize(img->get_width(), img->get_height())]; - ERR_FAIL_COND(nsimg == nil); + int len = img->get_width() * img->get_height(); + const uint8_t *r = img->get_data().ptr(); - [nsimg addRepresentation:imgrep]; - [NSApp setApplicationIconImage:nsimg]; + /* Premultiply the alpha channel */ + for (int i = 0; i < len; i++) { + uint8_t alpha = r[i * 4 + 3]; + pixels[i * 4 + 0] = (uint8_t)(((uint16_t)r[i * 4 + 0] * alpha) / 255); + pixels[i * 4 + 1] = (uint8_t)(((uint16_t)r[i * 4 + 1] * alpha) / 255); + pixels[i * 4 + 2] = (uint8_t)(((uint16_t)r[i * 4 + 2] * alpha) / 255); + pixels[i * 4 + 3] = alpha; + } + + NSImage *nsimg = [[NSImage alloc] initWithSize:NSMakeSize(img->get_width(), img->get_height())]; + ERR_FAIL_COND(nsimg == nil); + + [nsimg addRepresentation:imgrep]; + [NSApp setApplicationIconImage:nsimg]; + } else { + [NSApp setApplicationIconImage:nil]; + } } DisplayServer *DisplayServerMacOS::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Error &r_error) { 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 2d185db812..81f9707f6b 100644 --- a/platform/macos/export/export_plugin.cpp +++ b/platform/macos/export/export_plugin.cpp @@ -41,6 +41,7 @@ #include "editor/editor_node.h" #include "editor/editor_paths.h" #include "editor/editor_scale.h" +#include "scene/resources/image_texture.h" #include "modules/modules_enabled.gen.h" // For svg and regex. #ifdef MODULE_SVG_ENABLED @@ -425,6 +426,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())); @@ -1359,7 +1361,7 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p String src_pkg_name; - EditorProgress ep("export", "Exporting for macOS", 3, true); + EditorProgress ep("export", TTR("Exporting for macOS"), 3, true); if (p_debug) { src_pkg_name = p_preset->get("custom_template/debug"); @@ -1922,6 +1924,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>"); @@ -2442,11 +2452,10 @@ EditorExportPlatformMacOS::EditorExportPlatformMacOS() { Ref<Image> img = memnew(Image); const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE); - ImageLoaderSVG img_loader; - img_loader.create_image_from_string(img, _macos_logo_svg, EDSCALE, upsample, false); + ImageLoaderSVG::create_image_from_string(img, _macos_logo_svg, EDSCALE, upsample, false); logo = ImageTexture::create_from_image(img); - img_loader.create_image_from_string(img, _macos_run_icon_svg, EDSCALE, upsample, false); + ImageLoaderSVG::create_image_from_string(img, _macos_run_icon_svg, EDSCALE, upsample, false); run_icon = ImageTexture::create_from_image(img); #endif diff --git a/platform/macos/export/run_icon.svg b/platform/macos/export/run_icon.svg index c7067bb4b6..647270ce22 100644 --- a/platform/macos/export/run_icon.svg +++ b/platform/macos/export/run_icon.svg @@ -1 +1 @@ -<svg height="16" width="16" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path style="color:#000;fill:#e0e0e0;fill-opacity:1;stroke-width:.93168;-inkscape-stroke:none" d="M4.418 1.055c-1.82 0-3.365 1.537-3.365 3.363v7.164c0 1.829 1.548 3.363 3.365 3.363h7.164c1.828 0 3.363-1.544 3.363-3.363V4.418c0-1.822-1.537-3.363-3.363-3.363H7.729Zm3.875 1.164h3.291c1.149 0 2.2 1.053 2.2 2.199v7.164c0 1.14-1.052 2.2-2.2 2.2h-2.15a8.884 8.884 0 0 1-.358-1.598c-.135.02-.487.082-.693.117a3.947 3.947 0 0 1-.049.004l-.008-.002a7.345 7.345 0 0 1-1.205-.004 7.114 7.114 0 0 1-.926-.139 6.057 6.057 0 0 1-.867-.271 3.843 3.843 0 0 1-.988-.566 3.214 3.214 0 0 1-.397-.378 2.8 2.8 0 0 1-.318-.441.558.558 0 0 1-.059-.424.564.564 0 0 1 .881-.299.56.56 0 0 1 .145.164c.083.138.188.26.312.362.096.082.2.158.307.224.12.075.243.142.371.201.285.139.583.247.89.319a5.35 5.35 0 0 0 1.282.158c.065 0 .129-.005.184-.006.056 0 .102-.005.148-.008l.096-.006c.114-.009.228-.02.31-.032.083-.013.11-.021.143-.028.099-.022.204-.058.327-.089a28.438 28.438 0 0 1-.06-1.929V8.53H6.887c.048-1.963.746-4.357 1.181-5.677.1-.293.184-.527.225-.633ZM4.973 5.03h.002a.562.562 0 0 1 .558.559v.805a.556.556 0 0 1-.558.558.56.56 0 0 1-.397-.162.565.565 0 0 1-.164-.396V5.59a.561.561 0 0 1 .559-.559Z"/><path style="color:#000;fill:#e0e0e0;fill-opacity:1;stroke-linecap:round;-inkscape-stroke:none" d="M26.117 11.467c.008.11.014.225.022.328.022.283.052.565.088.846l.012.053c1.238-.252 2.448-.829 3.011-1.803a.6.6 0 1 0-1.039-.6c-.28.486-1.161.936-2.094 1.176z" transform="translate(-15.37 .357) scale(.93168)"/><g style="fill:#e0e0e0;fill-opacity:1;stroke:none;stroke-opacity:1"><path style="color:#000;fill:#e0e0e0;fill-opacity:1;stroke:none;stroke-width:1.2;stroke-linecap:round;stroke-opacity:1;-inkscape-stroke:none" d="M27.836 5.585v.862" transform="translate(-15.37 .357) scale(.93168)"/><path style="color:#000;fill:#e0e0e0;fill-opacity:1;stroke:none;stroke-linecap:round;stroke-opacity:1;-inkscape-stroke:none" d="M27.836 4.984a.6.6 0 0 0-.6.6v.863a.6.6 0 0 0 .6.6.6.6 0 0 0 .6-.6v-.863a.6.6 0 0 0-.6-.6Z" transform="translate(-15.37 .357) scale(.93168)"/></g></svg> +<svg height="16" width="16" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path fill="#e0e0e0" d="M4.418 1.055a3.364 3.364 0 0 0-3.364 3.364v7.164a3.364 3.364 0 0 0 3.364 3.364h7.164a3.364 3.364 0 0 0 3.364-3.364V4.418a3.364 3.364 0 0 0-3.364-3.364H7.729Zm3.875 1.164h3.291a2.2 2.2 0 0 1 2.2 2.2v7.164a2.2 2.2 0 0 1-2.2 2.2h-2.15a8.884 8.884 0 0 1-.358-1.598c-.135.02-.487.082-.693.117a7.345 7.345 0 0 1-1.254 0 7.114 7.114 0 0 1-.926-.139 6.057 6.057 0 0 1-.867-.271 3.843 3.843 0 0 1-.988-.566 3.214 3.214 0 0 1-.397-.378 2.8 2.8 0 0 1-.318-.441.56.56 0 0 1 .968-.56c.083.138.188.26.312.362.096.082.2.158.307.224.12.075.243.142.371.201.285.139.583.247.89.319a5.35 5.35 0 0 0 1.282.158c.065 0 .129-.005.184-.006.056 0 .102-.005.148-.008l.096-.006c.114-.009.228-.02.31-.032.083-.013.11-.021.143-.028.099-.022.204-.058.327-.089a28.438 28.438 0 0 1-.06-1.929V8.53H6.887c.048-1.963.746-4.357 1.181-5.677.1-.293.184-.527.225-.633ZM5.531 6.394a.56.56 0 0 1-1.118 0v-.9a.56.56 0 0 1 1.118 0Zm3.432 4.646.02.306c.02.264.049.527.082.788l.011.05c1.154-.235 2.281-.773 2.806-1.68a.56.56 0 1 0-.968-.56c-.261.454-1.082.873-1.951 1.097zM10 6.364a.56.56 0 0 0 1.118 0v-.9a.56.56 0 0 0-1.118 0z"/></svg> 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 fe6d8d9fb0..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 @@ -189,7 +231,7 @@ Error OS_MacOS::open_dynamic_library(const String p_path, void *&p_library_handl } p_library_handle = dlopen(path.utf8().get_data(), RTLD_NOW); - ERR_FAIL_COND_V_MSG(!p_library_handle, ERR_CANT_OPEN, "Can't open dynamic library: " + p_path + ", error: " + dlerror() + "."); + ERR_FAIL_COND_V_MSG(!p_library_handle, ERR_CANT_OPEN, vformat("Can't open dynamic library: %s. Error: %s.", p_path, dlerror())); if (r_resolved_path != nullptr) { *r_resolved_path = path; @@ -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; |
