diff options
Diffstat (limited to 'platform/macos')
| -rw-r--r-- | platform/macos/SCsub | 1 | ||||
| -rw-r--r-- | platform/macos/crash_handler_macos.mm | 10 | ||||
| -rw-r--r-- | platform/macos/detect.py | 20 | ||||
| -rw-r--r-- | platform/macos/display_server_macos.h | 24 | ||||
| -rw-r--r-- | platform/macos/display_server_macos.mm | 784 | ||||
| -rw-r--r-- | platform/macos/doc_classes/EditorExportPlatformMacOS.xml | 15 | ||||
| -rw-r--r-- | platform/macos/export/export_plugin.cpp | 32 | ||||
| -rw-r--r-- | platform/macos/gl_manager_macos_angle.h | 63 | ||||
| -rw-r--r-- | platform/macos/gl_manager_macos_angle.mm | 70 | ||||
| -rw-r--r-- | platform/macos/gl_manager_macos_legacy.h | 25 | ||||
| -rw-r--r-- | platform/macos/gl_manager_macos_legacy.mm | 77 | ||||
| -rw-r--r-- | platform/macos/godot_content_view.h | 2 | ||||
| -rw-r--r-- | platform/macos/godot_content_view.mm | 22 | ||||
| -rw-r--r-- | platform/macos/godot_menu_delegate.mm | 30 | ||||
| -rw-r--r-- | platform/macos/godot_menu_item.h | 1 | ||||
| -rw-r--r-- | platform/macos/godot_window_delegate.mm | 14 | ||||
| -rw-r--r-- | platform/macos/os_macos.mm | 4 | ||||
| -rw-r--r-- | platform/macos/platform_config.h | 1 | ||||
| -rw-r--r-- | platform/macos/platform_gl.h | 52 |
19 files changed, 881 insertions, 366 deletions
diff --git a/platform/macos/SCsub b/platform/macos/SCsub index 7ffb80f70b..30202e5de7 100644 --- a/platform/macos/SCsub +++ b/platform/macos/SCsub @@ -24,6 +24,7 @@ files = [ "tts_macos.mm", "joypad_macos.cpp", "vulkan_context_macos.mm", + "gl_manager_macos_angle.mm", "gl_manager_macos_legacy.mm", ] diff --git a/platform/macos/crash_handler_macos.mm b/platform/macos/crash_handler_macos.mm index 7f9a88121e..7c0cab0210 100644 --- a/platform/macos/crash_handler_macos.mm +++ b/platform/macos/crash_handler_macos.mm @@ -72,6 +72,10 @@ static uint64_t load_address() { } static void handle_crash(int sig) { + signal(SIGSEGV, SIG_DFL); + signal(SIGFPE, SIG_DFL); + signal(SIGILL, SIG_DFL); + if (OS::get_singleton() == nullptr) { abort(); } @@ -186,9 +190,9 @@ void CrashHandler::disable() { } #ifdef CRASH_HANDLER_ENABLED - signal(SIGSEGV, nullptr); - signal(SIGFPE, nullptr); - signal(SIGILL, nullptr); + signal(SIGSEGV, SIG_DFL); + signal(SIGFPE, SIG_DFL); + signal(SIGILL, SIG_DFL); #endif disabled = true; diff --git a/platform/macos/detect.py b/platform/macos/detect.py index 21e824b2d3..b41d2141fb 100644 --- a/platform/macos/detect.py +++ b/platform/macos/detect.py @@ -1,6 +1,6 @@ import os import sys -from methods import detect_darwin_sdk_path +from methods import detect_darwin_sdk_path, get_compiler_version, is_vanilla_clang from platform_methods import detect_arch from typing import TYPE_CHECKING @@ -32,6 +32,7 @@ def get_opts(): BoolVariable("use_asan", "Use LLVM/GCC compiler address sanitizer (ASAN)", False), BoolVariable("use_tsan", "Use LLVM/GCC compiler thread sanitizer (TSAN)", False), BoolVariable("use_coverage", "Use instrumentation codes in the binary (e.g. for code coverage)", False), + ("angle_libs", "Path to the ANGLE static libraries", ""), ] @@ -119,6 +120,15 @@ def configure(env: "Environment"): env.Append(CCFLAGS=["-arch", "x86_64", "-mmacosx-version-min=10.13"]) env.Append(LINKFLAGS=["-arch", "x86_64", "-mmacosx-version-min=10.13"]) + cc_version = get_compiler_version(env) + cc_version_major = cc_version["major"] + cc_version_minor = cc_version["minor"] + vanilla = is_vanilla_clang(env) + + # Workaround for Xcode 15 linker bug. + if not vanilla and cc_version_major == 15 and cc_version_minor == 0: + env.Prepend(LINKFLAGS=["-ld_classic"]) + env.Append(CCFLAGS=["-fobjc-arc"]) if not "osxcross" in env: # regular native build @@ -239,7 +249,13 @@ def configure(env: "Environment"): if env["opengl3"]: env.Append(CPPDEFINES=["GLES3_ENABLED"]) - env.Append(LINKFLAGS=["-framework", "OpenGL"]) + if env["angle_libs"] != "": + env.AppendUnique(CPPDEFINES=["EGL_STATIC"]) + env.Append(LINKFLAGS=["-L" + env["angle_libs"]]) + env.Append(LINKFLAGS=["-lANGLE.macos." + env["arch"]]) + env.Append(LINKFLAGS=["-lEGL.macos." + env["arch"]]) + env.Append(LINKFLAGS=["-lGLES.macos." + env["arch"]]) + env.Prepend(CPPPATH=["#thirdparty/angle/include"]) env.Append(LINKFLAGS=["-rpath", "@executable_path/../Frameworks", "-rpath", "@executable_path"]) diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h index 69f6008043..2ca9e493b7 100644 --- a/platform/macos/display_server_macos.h +++ b/platform/macos/display_server_macos.h @@ -35,6 +35,7 @@ #include "servers/display_server.h" #if defined(GLES3_ENABLED) +#include "gl_manager_macos_angle.h" #include "gl_manager_macos_legacy.h" #endif // GLES3_ENABLED @@ -127,7 +128,8 @@ public: private: #if defined(GLES3_ENABLED) - GLManager_MacOS *gl_manager = nullptr; + GLManagerLegacy_MacOS *gl_manager_legacy = nullptr; + GLManagerANGLE_MacOS *gl_manager_angle = nullptr; #endif #if defined(VULKAN_ENABLED) VulkanContextMacOS *context_vulkan = nullptr; @@ -137,7 +139,14 @@ private: NSMenu *apple_menu = nullptr; NSMenu *dock_menu = nullptr; - HashMap<String, NSMenu *> submenu; + struct MenuData { + Callable open; + Callable close; + NSMenu *menu = nullptr; + bool is_open = false; + }; + HashMap<String, MenuData> submenu; + HashMap<NSMenu *, String> submenu_inv; struct WarpEvent { NSTimeInterval timestamp; @@ -195,6 +204,7 @@ private: const NSMenu *_get_menu_root(const String &p_menu_root) const; NSMenu *_get_menu_root(const String &p_menu_root); + bool _is_menu_opened(NSMenu *p_menu) const; WindowID _create_window(WindowMode p_mode, VSyncMode p_vsync_mode, const Rect2i &p_rect); void _update_window_style(WindowData p_wd); @@ -221,6 +231,8 @@ private: public: NSMenu *get_dock_menu() const; void menu_callback(id p_sender); + void menu_open(NSMenu *p_menu); + void menu_close(NSMenu *p_menu); bool has_window(WindowID p_window) const; WindowData &get_window(WindowID p_window); @@ -243,8 +255,8 @@ public: WindowID _get_focused_window_or_popup() const; void mouse_enter_window(WindowID p_window); void mouse_exit_window(WindowID p_window); + void update_presentation_mode(); - void window_update(WindowID p_window); void window_destroy(WindowID p_window); void window_resize(WindowID p_window, int p_width, int p_height); void window_set_custom_window_buttons(WindowData &p_wd, bool p_enabled); @@ -252,6 +264,8 @@ public: virtual bool has_feature(Feature p_feature) const override; virtual String get_name() const override; + virtual void global_menu_set_popup_callbacks(const String &p_menu_root, const Callable &p_open_callback = Callable(), const Callable &p_close_callback = Callable()) override; + virtual int global_menu_add_submenu_item(const String &p_menu_root, const String &p_label, const String &p_submenu, int p_index = -1) override; virtual int global_menu_add_item(const String &p_menu_root, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; virtual int global_menu_add_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; @@ -275,6 +289,7 @@ public: virtual String global_menu_get_item_submenu(const String &p_menu_root, int p_idx) const override; virtual Key global_menu_get_item_accelerator(const String &p_menu_root, int p_idx) const override; virtual bool global_menu_is_item_disabled(const String &p_menu_root, int p_idx) const override; + virtual bool global_menu_is_item_hidden(const String &p_menu_root, int p_idx) const override; virtual String global_menu_get_item_tooltip(const String &p_menu_root, int p_idx) const override; virtual int global_menu_get_item_state(const String &p_menu_root, int p_idx) const override; virtual int global_menu_get_item_max_states(const String &p_menu_root, int p_idx) const override; @@ -286,11 +301,13 @@ public: virtual void global_menu_set_item_radio_checkable(const String &p_menu_root, int p_idx, bool p_checkable) override; virtual void global_menu_set_item_callback(const String &p_menu_root, int p_idx, const Callable &p_callback) override; virtual void global_menu_set_item_key_callback(const String &p_menu_root, int p_idx, const Callable &p_key_callback) override; + virtual void global_menu_set_item_hover_callbacks(const String &p_menu_root, int p_idx, const Callable &p_callback) override; virtual void global_menu_set_item_tag(const String &p_menu_root, int p_idx, const Variant &p_tag) override; virtual void global_menu_set_item_text(const String &p_menu_root, int p_idx, const String &p_text) override; virtual void global_menu_set_item_submenu(const String &p_menu_root, int p_idx, const String &p_submenu) override; virtual void global_menu_set_item_accelerator(const String &p_menu_root, int p_idx, Key p_keycode) override; virtual void global_menu_set_item_disabled(const String &p_menu_root, int p_idx, bool p_disabled) override; + virtual void global_menu_set_item_hidden(const String &p_menu_root, int p_idx, bool p_hidden) override; virtual void global_menu_set_item_tooltip(const String &p_menu_root, int p_idx, const String &p_tooltip) override; virtual void global_menu_set_item_state(const String &p_menu_root, int p_idx, int p_state) override; virtual void global_menu_set_item_max_states(const String &p_menu_root, int p_idx, int p_max_states) override; @@ -367,6 +384,7 @@ public: virtual void window_set_drop_files_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override; virtual void window_set_title(const String &p_title, WindowID p_window = MAIN_WINDOW_ID) override; + virtual Size2i window_get_title_size(const String &p_title, WindowID p_window) const override; virtual void window_set_mouse_passthrough(const Vector<Vector2> &p_region, WindowID p_window = MAIN_WINDOW_ID) override; virtual int window_get_current_screen(WindowID p_window = MAIN_WINDOW_ID) const override; diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm index e79d6acc3f..b314f2fd44 100644 --- a/platform/macos/display_server_macos.mm +++ b/platform/macos/display_server_macos.mm @@ -75,7 +75,7 @@ const NSMenu *DisplayServerMacOS::_get_menu_root(const String &p_menu_root) cons } else { // Submenu. if (submenu.has(p_menu_root)) { - menu = submenu[p_menu_root]; + menu = submenu[p_menu_root].menu; } } if (menu == apple_menu) { @@ -99,9 +99,10 @@ NSMenu *DisplayServerMacOS::_get_menu_root(const String &p_menu_root) { NSMenu *n_menu = [[NSMenu alloc] initWithTitle:[NSString stringWithUTF8String:p_menu_root.utf8().get_data()]]; [n_menu setAutoenablesItems:NO]; [n_menu setDelegate:menu_delegate]; - submenu[p_menu_root] = n_menu; + submenu[p_menu_root].menu = n_menu; + submenu_inv[n_menu] = p_menu_root; } - menu = submenu[p_menu_root]; + menu = submenu[p_menu_root].menu; } if (menu == apple_menu) { // Do not allow to change Apple menu. @@ -117,7 +118,7 @@ DisplayServerMacOS::WindowID DisplayServerMacOS::_create_window(WindowMode p_mod WindowData wd; wd.window_delegate = [[GodotWindowDelegate alloc] init]; - ERR_FAIL_COND_V_MSG(wd.window_delegate == nil, INVALID_WINDOW_ID, "Can't create a window delegate"); + ERR_FAIL_NULL_V_MSG(wd.window_delegate, INVALID_WINDOW_ID, "Can't create a window delegate"); [wd.window_delegate setWindowID:window_id_counter]; int rq_screen = get_screen_from_rect(p_rect); @@ -139,15 +140,15 @@ DisplayServerMacOS::WindowID DisplayServerMacOS::_create_window(WindowMode p_mod // initWithContentRect uses bottom-left corner of the window’s frame as origin. wd.window_object = [[GodotWindow alloc] - initWithContentRect:NSMakeRect(100, 100, p_rect.size.width / scale, p_rect.size.height / scale) + initWithContentRect:NSMakeRect(100, 100, MAX(1, p_rect.size.width / scale), MAX(1, p_rect.size.height / scale)) styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable backing:NSBackingStoreBuffered defer:NO]; - ERR_FAIL_COND_V_MSG(wd.window_object == nil, INVALID_WINDOW_ID, "Can't create a window"); + ERR_FAIL_NULL_V_MSG(wd.window_object, INVALID_WINDOW_ID, "Can't create a window"); [wd.window_object setWindowID:window_id_counter]; wd.window_view = [[GodotContentView alloc] init]; - ERR_FAIL_COND_V_MSG(wd.window_view == nil, INVALID_WINDOW_ID, "Can't create a window view"); + ERR_FAIL_NULL_V_MSG(wd.window_view, INVALID_WINDOW_ID, "Can't create a window view"); [wd.window_view setWindowID:window_id_counter]; [wd.window_view setWantsLayer:TRUE]; @@ -185,9 +186,14 @@ DisplayServerMacOS::WindowID DisplayServerMacOS::_create_window(WindowMode p_mod } #endif #if defined(GLES3_ENABLED) - if (gl_manager) { - Error err = gl_manager->window_create(window_id_counter, wd.window_view, p_rect.size.width, p_rect.size.height); - ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Can't create an OpenGL context"); + if (gl_manager_legacy) { + Error err = gl_manager_legacy->window_create(window_id_counter, wd.window_view, p_rect.size.width, p_rect.size.height); + ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Can't create an OpenGL context."); + } + if (gl_manager_angle) { + CALayer *layer = [(NSView *)wd.window_view layer]; + Error err = gl_manager_angle->window_create(window_id_counter, nullptr, (__bridge void *)layer, p_rect.size.width, p_rect.size.height); + ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Can't create an OpenGL context."); } window_set_vsync_mode(p_vsync_mode, window_id_counter); #endif @@ -219,8 +225,11 @@ DisplayServerMacOS::WindowID DisplayServerMacOS::_create_window(WindowMode p_mod } #if defined(GLES3_ENABLED) - if (gl_manager) { - gl_manager->window_resize(id, wd.size.width, wd.size.height); + if (gl_manager_legacy) { + gl_manager_legacy->window_resize(id, wd.size.width, wd.size.height); + } + if (gl_manager_angle) { + gl_manager_angle->window_resize(id, wd.size.width, wd.size.height); } #endif #if defined(VULKAN_ENABLED) @@ -279,8 +288,8 @@ void DisplayServerMacOS::_set_window_per_pixel_transparency_enabled(bool p_enabl [layer setOpaque:NO]; } #if defined(GLES3_ENABLED) - if (gl_manager) { - gl_manager->window_set_per_pixel_transparency_enabled(p_window, true); + if (gl_manager_legacy) { + gl_manager_legacy->window_set_per_pixel_transparency_enabled(p_window, true); } #endif wd.layered_window = true; @@ -299,8 +308,8 @@ void DisplayServerMacOS::_set_window_per_pixel_transparency_enabled(bool p_enabl [layer setOpaque:YES]; } #if defined(GLES3_ENABLED) - if (gl_manager) { - gl_manager->window_set_per_pixel_transparency_enabled(p_window, false); + if (gl_manager_legacy) { + gl_manager_legacy->window_set_per_pixel_transparency_enabled(p_window, false); } #endif wd.layered_window = false; @@ -394,11 +403,6 @@ void DisplayServerMacOS::_dispatch_input_event(const Ref<InputEvent> &p_event) { if (!in_dispatch_input_event) { in_dispatch_input_event = true; - Variant ev = p_event; - Variant *evp = &ev; - Variant ret; - Callable::CallError ce; - { List<WindowID>::Element *E = popup_list.back(); if (E && Object::cast_to<InputEventKey>(*p_event)) { @@ -406,7 +410,7 @@ void DisplayServerMacOS::_dispatch_input_event(const Ref<InputEvent> &p_event) { if (windows.has(E->get())) { Callable callable = windows[E->get()].input_event_callback; if (callable.is_valid()) { - callable.callp((const Variant **)&evp, 1, ret, ce); + callable.call(p_event); } } in_dispatch_input_event = false; @@ -420,7 +424,7 @@ void DisplayServerMacOS::_dispatch_input_event(const Ref<InputEvent> &p_event) { if (windows.has(event_from_window->get_window_id())) { Callable callable = windows[event_from_window->get_window_id()].input_event_callback; if (callable.is_valid()) { - callable.callp((const Variant **)&evp, 1, ret, ce); + callable.call(p_event); } } } else { @@ -428,7 +432,7 @@ void DisplayServerMacOS::_dispatch_input_event(const Ref<InputEvent> &p_event) { for (KeyValue<WindowID, WindowData> &E : windows) { Callable callable = E.value.input_event_callback; if (callable.is_valid()) { - callable.callp((const Variant **)&evp, 1, ret, ce); + callable.call(p_event); } } } @@ -563,7 +567,7 @@ NSImage *DisplayServerMacOS::_convert_to_nsimg(Ref<Image> &p_image) const { colorSpaceName:NSDeviceRGBColorSpace bytesPerRow:int(p_image->get_width()) * 4 bitsPerPixel:32]; - ERR_FAIL_COND_V(imgrep == nil, nil); + ERR_FAIL_NULL_V(imgrep, nil); uint8_t *pixels = [imgrep bitmapData]; int len = p_image->get_width() * p_image->get_height(); @@ -579,7 +583,7 @@ NSImage *DisplayServerMacOS::_convert_to_nsimg(Ref<Image> &p_image) const { } NSImage *nsimg = [[NSImage alloc] initWithSize:NSMakeSize(p_image->get_width(), p_image->get_height())]; - ERR_FAIL_COND_V(nsimg == nil, nil); + ERR_FAIL_NULL_V(nsimg, nil); [nsimg addRepresentation:imgrep]; return nsimg; } @@ -602,6 +606,26 @@ NSMenu *DisplayServerMacOS::get_dock_menu() const { return dock_menu; } +void DisplayServerMacOS::menu_open(NSMenu *p_menu) { + if (submenu_inv.has(p_menu)) { + MenuData &md = submenu[submenu_inv[p_menu]]; + md.is_open = true; + if (md.open.is_valid()) { + md.open.call(); + } + } +} + +void DisplayServerMacOS::menu_close(NSMenu *p_menu) { + if (submenu_inv.has(p_menu)) { + MenuData &md = submenu[submenu_inv[p_menu]]; + md.is_open = false; + if (md.close.is_valid()) { + md.close.call(); + } + } +} + void DisplayServerMacOS::menu_callback(id p_sender) { if (![p_sender representedObject]) { return; @@ -667,12 +691,9 @@ void DisplayServerMacOS::send_event(NSEvent *p_event) { void DisplayServerMacOS::send_window_event(const WindowData &wd, WindowEvent p_event) { _THREAD_SAFE_METHOD_ - if (!wd.event_callback.is_null()) { + if (wd.event_callback.is_valid()) { Variant event = int(p_event); - Variant *eventp = &event; - Variant ret; - Callable::CallError ce; - wd.event_callback.callp((const Variant **)&eventp, 1, ret, ce); + wd.event_callback.call(event); } } @@ -730,18 +751,10 @@ bool DisplayServerMacOS::get_is_resizing() const { return is_resizing; } -void DisplayServerMacOS::window_update(WindowID p_window) { -#if defined(GLES3_ENABLED) - if (gl_manager) { - gl_manager->window_update(p_window); - } -#endif -} - void DisplayServerMacOS::window_destroy(WindowID p_window) { #if defined(GLES3_ENABLED) - if (gl_manager) { - gl_manager->window_destroy(p_window); + if (gl_manager_legacy) { + gl_manager_legacy->window_destroy(p_window); } #endif #ifdef VULKAN_ENABLED @@ -750,12 +763,16 @@ void DisplayServerMacOS::window_destroy(WindowID p_window) { } #endif windows.erase(p_window); + update_presentation_mode(); } void DisplayServerMacOS::window_resize(WindowID p_window, int p_width, int p_height) { #if defined(GLES3_ENABLED) - if (gl_manager) { - gl_manager->window_resize(p_window, p_width, p_height); + if (gl_manager_legacy) { + gl_manager_legacy->window_resize(p_window, p_width, p_height); + } + if (gl_manager_angle) { + gl_manager_angle->window_resize(p_window, p_width, p_height); } #endif #if defined(VULKAN_ENABLED) @@ -813,6 +830,24 @@ bool DisplayServerMacOS::_has_help_menu() const { } } +bool DisplayServerMacOS::_is_menu_opened(NSMenu *p_menu) const { + if (submenu_inv.has(p_menu)) { + const MenuData &md = submenu[submenu_inv[p_menu]]; + if (md.is_open) { + return true; + } + } + for (NSInteger i = (p_menu == [NSApp mainMenu]) ? 1 : 0; i < [p_menu numberOfItems]; i++) { + const NSMenuItem *menu_item = [p_menu itemAtIndex:i]; + if ([menu_item submenu]) { + if (_is_menu_opened([menu_item submenu])) { + return true; + } + } + } + return false; +} + NSMenuItem *DisplayServerMacOS::_menu_add_item(const String &p_menu_root, const String &p_label, Key p_accel, int p_index, int *r_out) { NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { @@ -996,6 +1031,23 @@ int DisplayServerMacOS::global_menu_add_multistate_item(const String &p_menu_roo return out; } +void DisplayServerMacOS::global_menu_set_popup_callbacks(const String &p_menu_root, const Callable &p_open_callback, const Callable &p_close_callback) { + _THREAD_SAFE_METHOD_ + + if (p_menu_root != "" && p_menu_root.to_lower() != "_main" && p_menu_root.to_lower() != "_dock") { + // Submenu. + if (!submenu.has(p_menu_root)) { + NSMenu *n_menu = [[NSMenu alloc] initWithTitle:[NSString stringWithUTF8String:p_menu_root.utf8().get_data()]]; + [n_menu setAutoenablesItems:NO]; + [n_menu setDelegate:menu_delegate]; + submenu[p_menu_root].menu = n_menu; + submenu_inv[n_menu] = p_menu_root; + } + submenu[p_menu_root].open = p_open_callback; + submenu[p_menu_root].close = p_close_callback; + } +} + int DisplayServerMacOS::global_menu_add_submenu_item(const String &p_menu_root, const String &p_label, const String &p_submenu, int p_index) { _THREAD_SAFE_METHOD_ @@ -1249,13 +1301,9 @@ String DisplayServerMacOS::global_menu_get_item_submenu(const String &p_menu_roo ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], String()); const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { - const NSMenu *sub_menu = [menu_item submenu]; - if (sub_menu) { - for (const KeyValue<String, NSMenu *> &E : submenu) { - if (E.value == sub_menu) { - return E.key; - } - } + NSMenu *sub_menu = [menu_item submenu]; + if (sub_menu && submenu_inv.has(sub_menu)) { + return submenu_inv[sub_menu]; } } } @@ -1316,6 +1364,24 @@ bool DisplayServerMacOS::global_menu_is_item_disabled(const String &p_menu_root, return false; } +bool DisplayServerMacOS::global_menu_is_item_hidden(const String &p_menu_root, int p_idx) const { + _THREAD_SAFE_METHOD_ + + const NSMenu *menu = _get_menu_root(p_menu_root); + if (menu) { + ERR_FAIL_COND_V(p_idx < 0, false); + if (menu == [NSApp mainMenu]) { // Skip Apple menu. + p_idx++; + } + ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], false); + const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; + if (menu_item) { + return [menu_item isHidden]; + } + } + return false; +} + String DisplayServerMacOS::global_menu_get_item_tooltip(const String &p_menu_root, int p_idx) const { _THREAD_SAFE_METHOD_ @@ -1451,7 +1517,7 @@ void DisplayServerMacOS::global_menu_set_item_checkable(const String &p_menu_roo NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; - ERR_FAIL_COND(!obj); + ERR_FAIL_NULL(obj); obj->checkable_type = (p_checkable) ? CHECKABLE_TYPE_CHECK_BOX : CHECKABLE_TYPE_NONE; } } @@ -1470,7 +1536,7 @@ void DisplayServerMacOS::global_menu_set_item_radio_checkable(const String &p_me NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; - ERR_FAIL_COND(!obj); + ERR_FAIL_NULL(obj); obj->checkable_type = (p_checkable) ? CHECKABLE_TYPE_RADIO_BUTTON : CHECKABLE_TYPE_NONE; } } @@ -1489,12 +1555,31 @@ void DisplayServerMacOS::global_menu_set_item_callback(const String &p_menu_root NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; - ERR_FAIL_COND(!obj); + ERR_FAIL_NULL(obj); obj->callback = p_callback; } } } +void DisplayServerMacOS::global_menu_set_item_hover_callbacks(const String &p_menu_root, int p_idx, const Callable &p_callback) { + _THREAD_SAFE_METHOD_ + + NSMenu *menu = _get_menu_root(p_menu_root); + if (menu) { + ERR_FAIL_COND(p_idx < 0); + if (menu == [NSApp mainMenu]) { // Skip Apple menu. + p_idx++; + } + ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; + if (menu_item) { + GodotMenuItem *obj = [menu_item representedObject]; + ERR_FAIL_NULL(obj); + obj->hover_callback = p_callback; + } + } +} + void DisplayServerMacOS::global_menu_set_item_key_callback(const String &p_menu_root, int p_idx, const Callable &p_key_callback) { _THREAD_SAFE_METHOD_ @@ -1508,7 +1593,7 @@ void DisplayServerMacOS::global_menu_set_item_key_callback(const String &p_menu_ NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; - ERR_FAIL_COND(!obj); + ERR_FAIL_NULL(obj); obj->key_callback = p_key_callback; } } @@ -1527,7 +1612,7 @@ void DisplayServerMacOS::global_menu_set_item_tag(const String &p_menu_root, int NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; - ERR_FAIL_COND(!obj); + ERR_FAIL_NULL(obj); obj->meta = p_tag; } } @@ -1554,6 +1639,23 @@ void DisplayServerMacOS::global_menu_set_item_submenu(const String &p_menu_root, _THREAD_SAFE_METHOD_ NSMenu *menu = _get_menu_root(p_menu_root); + if (menu && p_submenu.is_empty()) { + ERR_FAIL_COND(p_idx < 0); + if (menu == [NSApp mainMenu]) { // Skip Apple menu. + p_idx++; + } + ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; + if (menu_item) { + if ([menu_item submenu] && _is_menu_opened([menu_item submenu])) { + ERR_PRINT("Can't remove open menu!"); + return; + } + [menu setSubmenu:nil forItem:menu_item]; + } + return; + } + NSMenu *sub_menu = _get_menu_root(p_submenu); if (menu && sub_menu) { if (sub_menu == menu) { @@ -1588,9 +1690,13 @@ void DisplayServerMacOS::global_menu_set_item_accelerator(const String &p_menu_r ERR_FAIL_COND(p_idx >= [menu numberOfItems]); NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { - [menu_item setKeyEquivalentModifierMask:KeyMappingMacOS::keycode_get_native_mask(p_keycode)]; - String keycode = KeyMappingMacOS::keycode_get_native_string(p_keycode & KeyModifierMask::CODE_MASK); - [menu_item setKeyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()]]; + if (p_keycode == Key::NONE) { + [menu_item setKeyEquivalent:@""]; + } else { + [menu_item setKeyEquivalentModifierMask:KeyMappingMacOS::keycode_get_native_mask(p_keycode)]; + String keycode = KeyMappingMacOS::keycode_get_native_string(p_keycode & KeyModifierMask::CODE_MASK); + [menu_item setKeyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()]]; + } } } } @@ -1612,6 +1718,23 @@ void DisplayServerMacOS::global_menu_set_item_disabled(const String &p_menu_root } } +void DisplayServerMacOS::global_menu_set_item_hidden(const String &p_menu_root, int p_idx, bool p_hidden) { + _THREAD_SAFE_METHOD_ + + NSMenu *menu = _get_menu_root(p_menu_root); + if (menu) { + ERR_FAIL_COND(p_idx < 0); + if (menu == [NSApp mainMenu]) { // Skip Apple menu. + p_idx++; + } + ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; + if (menu_item) { + [menu_item setHidden:p_hidden]; + } + } +} + void DisplayServerMacOS::global_menu_set_item_tooltip(const String &p_menu_root, int p_idx, const String &p_tooltip) { _THREAD_SAFE_METHOD_ @@ -1642,7 +1765,7 @@ void DisplayServerMacOS::global_menu_set_item_state(const String &p_menu_root, i NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; - ERR_FAIL_COND(!obj); + ERR_FAIL_NULL(obj); obj->state = p_state; } } @@ -1661,7 +1784,7 @@ void DisplayServerMacOS::global_menu_set_item_max_states(const String &p_menu_ro NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; - ERR_FAIL_COND(!obj); + ERR_FAIL_NULL(obj); obj->max_states = p_max_states; } } @@ -1680,7 +1803,7 @@ void DisplayServerMacOS::global_menu_set_item_icon(const String &p_menu_root, in NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; - ERR_FAIL_COND(!obj); + ERR_FAIL_NULL(obj); if (p_icon.is_valid()) { obj->img = p_icon->get_image(); obj->img = obj->img->duplicate(); @@ -1739,6 +1862,11 @@ void DisplayServerMacOS::global_menu_remove_item(const String &p_menu_root, int p_idx++; } ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; + if ([menu_item submenu] && _is_menu_opened([menu_item submenu])) { + ERR_PRINT("Can't remove open menu!"); + return; + } [menu removeItemAtIndex:p_idx]; } } @@ -1748,6 +1876,10 @@ void DisplayServerMacOS::global_menu_clear(const String &p_menu_root) { NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { + if (_is_menu_opened(menu)) { + ERR_PRINT("Can't remove open menu!"); + return; + } [menu removeAllItems]; // Restore Apple menu. if (menu == [NSApp mainMenu]) { @@ -1755,43 +1887,44 @@ void DisplayServerMacOS::global_menu_clear(const String &p_menu_root) { [menu setSubmenu:apple_menu forItem:menu_item]; } if (submenu.has(p_menu_root)) { + submenu_inv.erase(submenu[p_menu_root].menu); submenu.erase(p_menu_root); } } } bool DisplayServerMacOS::tts_is_speaking() const { - ERR_FAIL_COND_V_MSG(!tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); + ERR_FAIL_NULL_V_MSG(tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); return [tts isSpeaking]; } bool DisplayServerMacOS::tts_is_paused() const { - ERR_FAIL_COND_V_MSG(!tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); + ERR_FAIL_NULL_V_MSG(tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); return [tts isPaused]; } TypedArray<Dictionary> DisplayServerMacOS::tts_get_voices() const { - ERR_FAIL_COND_V_MSG(!tts, Array(), "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); + ERR_FAIL_NULL_V_MSG(tts, Array(), "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); return [tts getVoices]; } void DisplayServerMacOS::tts_speak(const String &p_text, const String &p_voice, int p_volume, float p_pitch, float p_rate, int p_utterance_id, bool p_interrupt) { - ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); + ERR_FAIL_NULL_MSG(tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); [tts speak:p_text voice:p_voice volume:p_volume pitch:p_pitch rate:p_rate utterance_id:p_utterance_id interrupt:p_interrupt]; } void DisplayServerMacOS::tts_pause() { - ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); + ERR_FAIL_NULL_MSG(tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); [tts pauseSpeaking]; } void DisplayServerMacOS::tts_resume() { - ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); + ERR_FAIL_NULL_MSG(tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); [tts resumeSpeaking]; } void DisplayServerMacOS::tts_stop() { - ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); + ERR_FAIL_NULL_MSG(tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); [tts stopSpeaking]; } @@ -1858,181 +1991,257 @@ Error DisplayServerMacOS::dialog_show(String p_title, String p_description, Vect } if (!p_callback.is_null()) { - Variant button = button_pressed; - Variant *buttonp = &button; - Variant fun_ret; - Callable::CallError ce; - p_callback.callp((const Variant **)&buttonp, 1, fun_ret, ce); + p_callback.call(button_pressed); } 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_ +@interface FileDialogDropdown : NSObject { + NSSavePanel *dialog; + NSMutableArray *allowed_types; + int cur_index; +} + +- (instancetype)initWithDialog:(NSSavePanel *)p_dialog fileTypes:(NSMutableArray *)p_allowed_types; +- (void)popupAction:(id)sender; +- (int)getIndex; + +@end + +@implementation FileDialogDropdown + +- (int)getIndex { + return cur_index; +} + +- (instancetype)initWithDialog:(NSSavePanel *)p_dialog fileTypes:(NSMutableArray *)p_allowed_types { + if ((self = [super init])) { + dialog = p_dialog; + allowed_types = p_allowed_types; + cur_index = 0; + } + return self; +} + +- (void)popupAction:(id)sender { + NSUInteger index = [sender indexOfSelectedItem]; + if (index < [allowed_types count]) { + [dialog setAllowedFileTypes:[allowed_types objectAtIndex:index]]; + cur_index = index; + } else { + [dialog setAllowedFileTypes:@[]]; + cur_index = -1; + } +} + +@end + +FileDialogDropdown *_make_accessory_view(NSSavePanel *p_panel, const Vector<String> &p_filters) { + NSView *group = [[NSView alloc] initWithFrame:NSZeroRect]; + group.translatesAutoresizingMaskIntoConstraints = NO; + + NSTextField *label = [NSTextField labelWithString:[NSString stringWithUTF8String:RTR("Format").utf8().get_data()]]; + label.translatesAutoresizingMaskIntoConstraints = NO; + if (@available(macOS 10.14, *)) { + label.textColor = NSColor.secondaryLabelColor; + } + if (@available(macOS 11.10, *)) { + label.font = [NSFont systemFontOfSize:[NSFont smallSystemFontSize]]; + } + [group addSubview:label]; + + NSPopUpButton *popup = [[NSPopUpButton alloc] initWithFrame:NSZeroRect pullsDown:NO]; + popup.translatesAutoresizingMaskIntoConstraints = NO; - 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()]]; + if (tokens.size() >= 1) { + String flt = tokens[0].strip_edges(); + int filter_slice_count = flt.get_slice_count(","); + + NSMutableArray *type_filters = [[NSMutableArray alloc] init]; + for (int j = 0; j < filter_slice_count; j++) { + String str = (flt.get_slice(",", j).strip_edges()); + if (str.strip_edges() == "*.*" || str.strip_edges() == "*") { + allow_other = true; + } else if (!str.is_empty()) { + [type_filters addObject:[NSString stringWithUTF8String:str.replace("*.", "").strip_edges().utf8().get_data()]]; + } + } + + if ([type_filters count] > 0) { + NSString *name_str = [NSString stringWithUTF8String:((tokens.size() == 1) ? tokens[0] : vformat("%s (%s)", tokens[1], tokens[0])).strip_edges().utf8().get_data()]; + [allowed_types addObject:type_filters]; + [popup addItemWithTitle:name_str]; } } } + FileDialogDropdown *handler = [[FileDialogDropdown alloc] initWithDialog:p_panel fileTypes:allowed_types]; + popup.target = handler; + popup.action = @selector(popupAction:); - Callable callback = p_callback; // Make a copy for async completion handler. - switch (p_mode) { - case FILE_DIALOG_MODE_SAVE_FILE: { - NSSavePanel *panel = [NSSavePanel savePanel]; + [group addSubview:popup]; - [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]; - } + NSView *view = [[NSView alloc] initWithFrame:NSZeroRect]; + view.translatesAutoresizingMaskIntoConstraints = NO; + [view addSubview:group]; + + NSMutableArray *constraints = [NSMutableArray array]; + [constraints addObject:[popup.topAnchor constraintEqualToAnchor:group.topAnchor constant:10]]; + [constraints addObject:[label.leadingAnchor constraintEqualToAnchor:group.leadingAnchor constant:10]]; + [constraints addObject:[popup.leadingAnchor constraintEqualToAnchor:label.trailingAnchor constant:10]]; + [constraints addObject:[popup.firstBaselineAnchor constraintEqualToAnchor:label.firstBaselineAnchor]]; + [constraints addObject:[group.trailingAnchor constraintEqualToAnchor:popup.trailingAnchor constant:10]]; + [constraints addObject:[group.bottomAnchor constraintEqualToAnchor:popup.bottomAnchor constant:10]]; + [constraints addObject:[group.topAnchor constraintEqualToAnchor:view.topAnchor]]; + [constraints addObject:[group.centerXAnchor constraintEqualToAnchor:view.centerXAnchor]]; + [constraints addObject:[view.bottomAnchor constraintEqualToAnchor:group.bottomAnchor]]; + [NSLayoutConstraint activateConstraints:constraints]; + + [p_panel setAllowsOtherFileTypes:allow_other]; + if ([allowed_types count] > 0) { + [p_panel setAccessoryView:view]; + [p_panel setAllowedFileTypes:[allowed_types objectAtIndex:0]]; + } + + return handler; +} + +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_ - [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"]; + ERR_FAIL_INDEX_V(int(p_mode), FILE_DIALOG_MODE_SAVE_MAX, FAILED); + + NSString *url = [NSString stringWithUTF8String:p_current_directory.utf8().get_data()]; + FileDialogDropdown *handler = nullptr; + + WindowID prev_focus = last_focused_window; + + Callable callback = p_callback; // Make a copy for async completion handler. + if (p_mode == FILE_DIALOG_MODE_SAVE_FILE) { + NSSavePanel *panel = [NSSavePanel savePanel]; + + [panel setDirectoryURL:[NSURL fileURLWithPath:url]]; + handler = _make_accessory_view(panel, p_filters); + [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()) { + callback.call(true, files, [handler getIndex]); + } + } else { + if (!callback.is_null()) { + callback.call(false, Vector<String>(), [handler getIndex]); + } + } + if (prev_focus != INVALID_WINDOW_ID) { + callable_mp(DisplayServer::get_singleton(), &DisplayServer::window_move_to_foreground).call_deferred(prev_focus); + } + }]; + } else { + NSOpenPanel *panel = [NSOpenPanel openPanel]; + + [panel setDirectoryURL:[NSURL fileURLWithPath:url]]; + handler = _make_accessory_view(panel, p_filters); + [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:[[panel directoryURL] path]] == NSOrderedSame)) { + if (!error && !isStale && ([[exurl path] compare:[[urls objectAtIndex:i] path]] == NSOrderedSame)) { skip = true; break; } } if (!skip) { NSError *error = nil; - NSData *bookmark = [[panel directoryURL] bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope includingResourceValuesForKeys:nil relativeToURL:nil error:&error]; + NSData *bookmark = [[urls objectAtIndex:i] bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope includingResourceValuesForKeys:nil relativeToURL:nil error:&error]; if (!error) { - NSArray *new_bookmarks = [bookmarks arrayByAddingObject:bookmark]; - [[NSUserDefaults standardUserDefaults] setObject:new_bookmarks forKey:@"sec_bookmarks"]; + [new_bookmarks addObject:bookmark]; } } } - // Callback. - Vector<String> files; + [[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([[[panel URL] path] UTF8String]); + 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; - 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); - } + if (!callback.is_null()) { + callback.call(true, files, [handler getIndex]); } - }]; - } break; + } else { + if (!callback.is_null()) { + callback.call(false, Vector<String>(), [handler getIndex]); + } + } + if (prev_focus != INVALID_WINDOW_ID) { + callable_mp(DisplayServer::get_singleton(), &DisplayServer::window_move_to_foreground).call_deferred(prev_focus); + } + }]; } return OK; @@ -2060,11 +2269,7 @@ Error DisplayServerMacOS::dialog_input_text(String p_title, String p_description ret.parse_utf8([[input stringValue] UTF8String]); if (!p_callback.is_null()) { - Variant text = ret; - Variant *textp = &text; - Variant fun_ret; - Callable::CallError ce; - p_callback.callp((const Variant **)&textp, 1, fun_ret, ce); + p_callback.call(ret); } return OK; @@ -2624,6 +2829,47 @@ void DisplayServerMacOS::window_set_title(const String &p_title, WindowID p_wind [wd.window_object setTitle:[NSString stringWithUTF8String:p_title.utf8().get_data()]]; } +Size2i DisplayServerMacOS::window_get_title_size(const String &p_title, WindowID p_window) const { + _THREAD_SAFE_METHOD_ + + Size2i size; + ERR_FAIL_COND_V(!windows.has(p_window), size); + + const WindowData &wd = windows[p_window]; + if (wd.fullscreen || wd.borderless) { + return size; + } + if ([wd.window_object respondsToSelector:@selector(isMiniaturized)]) { + if ([wd.window_object isMiniaturized]) { + return size; + } + } + + float scale = screen_get_max_scale(); + + if (wd.window_button_view) { + size.x = ([wd.window_button_view getOffset].x + [wd.window_button_view frame].size.width); + size.y = ([wd.window_button_view getOffset].y + [wd.window_button_view frame].size.height); + } else { + NSButton *cb = [wd.window_object standardWindowButton:NSWindowCloseButton]; + NSButton *mb = [wd.window_object standardWindowButton:NSWindowMiniaturizeButton]; + float cb_frame = NSMinX([cb frame]); + float mb_frame = NSMinX([mb frame]); + bool is_rtl = ([wd.window_object windowTitlebarLayoutDirection] == NSUserInterfaceLayoutDirectionRightToLeft); + + float window_buttons_spacing = (is_rtl) ? (cb_frame - mb_frame) : (mb_frame - cb_frame); + size.x = window_buttons_spacing * 4; + size.y = [cb frame].origin.y + [cb frame].size.height; + } + + NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:[NSFont titleBarFontOfSize:0], NSFontAttributeName, nil]; + NSSize text_size = [[[NSAttributedString alloc] initWithString:[NSString stringWithUTF8String:p_title.utf8().get_data()] attributes:attributes] size]; + size.x += text_size.width; + size.y = MAX(size.y, text_size.height); + + return size * scale; +} + void DisplayServerMacOS::window_set_mouse_passthrough(const Vector<Vector2> &p_region, WindowID p_window) { _THREAD_SAFE_METHOD_ @@ -2866,6 +3112,15 @@ Size2i DisplayServerMacOS::window_get_max_size(WindowID p_window) const { return wd.max_size; } +void DisplayServerMacOS::update_presentation_mode() { + for (const KeyValue<WindowID, WindowData> &wd : windows) { + if (wd.value.fullscreen && wd.value.exclusive_fullscreen) { + return; + } + } + [NSApp setPresentationOptions:NSApplicationPresentationDefault]; +} + void DisplayServerMacOS::window_set_min_size(const Size2i p_size, WindowID p_window) { _THREAD_SAFE_METHOD_ @@ -2912,7 +3167,7 @@ void DisplayServerMacOS::window_set_size(const Size2i p_size, WindowID p_window) top_left.x = old_frame.origin.x; top_left.y = NSMaxY(old_frame); - NSRect new_frame = NSMakeRect(0, 0, size.x, size.y); + NSRect new_frame = NSMakeRect(0, 0, MAX(1, size.x), MAX(1, size.y)); new_frame = [wd.window_object frameRectForContentRect:new_frame]; new_frame.origin.x = top_left.x; @@ -2976,7 +3231,7 @@ void DisplayServerMacOS::window_set_mode(WindowMode p_mode, WindowID p_window) { [wd.window_object toggleFullScreen:nil]; if (old_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) { - [NSApp setPresentationOptions:NSApplicationPresentationDefault]; + update_presentation_mode(); } wd.fullscreen = false; @@ -3174,7 +3429,9 @@ void DisplayServerMacOS::window_set_flag(WindowFlags p_flag, bool p_enabled, Win } break; case WINDOW_FLAG_BORDERLESS: { // OrderOut prevents a lose focus bug with the window. + bool was_visible = false; if ([wd.window_object isVisible]) { + was_visible = true; [wd.window_object orderOut:nil]; } wd.borderless = p_enabled; @@ -3189,7 +3446,7 @@ void DisplayServerMacOS::window_set_flag(WindowFlags p_flag, bool p_enabled, Win [wd.window_object setFrame:frameRect display:NO]; } _update_window_style(wd); - if ([wd.window_object isVisible]) { + if (was_visible || [wd.window_object isVisible]) { if ([wd.window_object isMiniaturized]) { return; } else if (wd.no_focus) { @@ -3370,8 +3627,11 @@ int64_t DisplayServerMacOS::window_get_native_handle(HandleType p_handle_type, W } #ifdef GLES3_ENABLED case OPENGL_CONTEXT: { - if (gl_manager) { - return (int64_t)gl_manager->get_context(p_window); + if (gl_manager_legacy) { + return (int64_t)gl_manager_legacy->get_context(p_window); + } + if (gl_manager_angle) { + return (int64_t)gl_manager_angle->get_context(p_window); } return 0; } @@ -3398,8 +3658,11 @@ ObjectID DisplayServerMacOS::window_get_attached_instance_id(WindowID p_window) void DisplayServerMacOS::gl_window_make_current(DisplayServer::WindowID p_window_id) { #if defined(GLES3_ENABLED) - if (gl_manager) { - gl_manager->window_make_current(p_window_id); + if (gl_manager_legacy) { + gl_manager_legacy->window_make_current(p_window_id); + } + if (gl_manager_angle) { + gl_manager_angle->window_make_current(p_window_id); } #endif } @@ -3407,8 +3670,11 @@ void DisplayServerMacOS::gl_window_make_current(DisplayServer::WindowID p_window void DisplayServerMacOS::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) { _THREAD_SAFE_METHOD_ #if defined(GLES3_ENABLED) - if (gl_manager) { - gl_manager->set_use_vsync(p_vsync_mode != DisplayServer::VSYNC_DISABLED); + if (gl_manager_angle) { + gl_manager_angle->set_use_vsync(p_vsync_mode != DisplayServer::VSYNC_DISABLED); + } + if (gl_manager_legacy) { + gl_manager_legacy->set_use_vsync(p_vsync_mode != DisplayServer::VSYNC_DISABLED); } #endif #if defined(VULKAN_ENABLED) @@ -3421,8 +3687,11 @@ void DisplayServerMacOS::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_ DisplayServer::VSyncMode DisplayServerMacOS::window_get_vsync_mode(WindowID p_window) const { _THREAD_SAFE_METHOD_ #if defined(GLES3_ENABLED) - if (gl_manager) { - return (gl_manager->is_using_vsync() ? DisplayServer::VSyncMode::VSYNC_ENABLED : DisplayServer::VSyncMode::VSYNC_DISABLED); + if (gl_manager_angle) { + return (gl_manager_angle->is_using_vsync() ? DisplayServer::VSyncMode::VSYNC_ENABLED : DisplayServer::VSyncMode::VSYNC_DISABLED); + } + if (gl_manager_legacy) { + return (gl_manager_legacy->is_using_vsync() ? DisplayServer::VSyncMode::VSYNC_ENABLED : DisplayServer::VSyncMode::VSYNC_DISABLED); } #endif #if defined(VULKAN_ENABLED) @@ -3589,7 +3858,7 @@ void DisplayServerMacOS::cursor_set_custom_image(const Ref<Resource> &p_cursor, bytesPerRow:int(texture_size.width) * 4 bitsPerPixel:32]; - ERR_FAIL_COND(imgrep == nil); + ERR_FAIL_NULL(imgrep); uint8_t *pixels = [imgrep bitmapData]; int len = int(texture_size.width * texture_size.height); @@ -3751,12 +4020,7 @@ void DisplayServerMacOS::process_events() { while (List<MenuCall>::Element *call_p = deferred_menu_calls.front()) { MenuCall call = call_p->get(); deferred_menu_calls.pop_front(); // Remove before call to avoid infinite loop in case callback is using `process_events` (e.g. EditorProgress). - - Variant tag = call.tag; - Variant *tagp = &tag; - Variant ret; - Callable::CallError ce; - call.callback.callp((const Variant **)&tagp, 1, ret, ce); + call.callback.call(call.tag); } if (!drop_events) { @@ -3805,8 +4069,11 @@ void DisplayServerMacOS::make_rendering_thread() { void DisplayServerMacOS::swap_buffers() { #if defined(GLES3_ENABLED) - if (gl_manager) { - gl_manager->swap_buffers(); + if (gl_manager_angle) { + gl_manager_angle->swap_buffers(); + } + if (gl_manager_legacy) { + gl_manager_legacy->swap_buffers(); } #endif } @@ -3826,10 +4093,10 @@ void DisplayServerMacOS::set_native_icon(const String &p_filename) { @try { NSData *icon_data = [[NSData alloc] initWithBytes:&data.write[0] length:len]; - ERR_FAIL_COND_MSG(!icon_data, "Error reading icon data."); + ERR_FAIL_NULL_MSG(icon_data, "Error reading icon data."); NSImage *icon = [[NSImage alloc] initWithData:icon_data]; - ERR_FAIL_COND_MSG(!icon, "Error loading icon."); + ERR_FAIL_NULL_MSG(icon, "Error loading icon."); [NSApp setApplicationIconImage:icon]; } @catch (NSException *exception) { @@ -3857,7 +4124,7 @@ void DisplayServerMacOS::set_icon(const Ref<Image> &p_icon) { colorSpaceName:NSDeviceRGBColorSpace bytesPerRow:img->get_width() * 4 bitsPerPixel:32]; - ERR_FAIL_COND(imgrep == nil); + ERR_FAIL_NULL(imgrep); uint8_t *pixels = [imgrep bitmapData]; int len = img->get_width() * img->get_height(); @@ -3873,7 +4140,7 @@ void DisplayServerMacOS::set_icon(const Ref<Image> &p_icon) { } NSImage *nsimg = [[NSImage alloc] initWithSize:NSMakeSize(img->get_width(), img->get_height())]; - ERR_FAIL_COND(nsimg == nil); + ERR_FAIL_NULL(nsimg); [nsimg addRepresentation:imgrep]; [NSApp setApplicationIconImage:nsimg]; @@ -3917,6 +4184,7 @@ Vector<String> DisplayServerMacOS::get_rendering_drivers_func() { #endif #if defined(GLES3_ENABLED) drivers.push_back("opengl3"); + drivers.push_back("opengl3_angle"); #endif return drivers; @@ -4109,15 +4377,19 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM nsappname = [[NSProcessInfo processInfo] processName]; } + menu_delegate = [[GodotMenuDelegate alloc] init]; + // Setup Dock menu. dock_menu = [[NSMenu alloc] initWithTitle:@"_dock"]; [dock_menu setAutoenablesItems:NO]; + [dock_menu setDelegate:menu_delegate]; // Setup Apple menu. apple_menu = [[NSMenu alloc] initWithTitle:@""]; title = [NSString stringWithFormat:NSLocalizedString(@"About %@", nil), nsappname]; [apple_menu addItemWithTitle:title action:@selector(showAbout:) keyEquivalent:@""]; [apple_menu setAutoenablesItems:NO]; + [apple_menu setDelegate:menu_delegate]; [apple_menu addItem:[NSMenuItem separatorItem]]; @@ -4147,21 +4419,28 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM [main_menu setSubmenu:apple_menu forItem:menu_item]; [main_menu setAutoenablesItems:NO]; - menu_delegate = [[GodotMenuDelegate alloc] init]; - //!!!!!!!!!!!!!!!!!!!!!!!!!! //TODO - do Vulkan and OpenGL support checks, driver selection and fallback rendering_driver = p_rendering_driver; #if defined(GLES3_ENABLED) if (rendering_driver == "opengl3") { - GLManager_MacOS::ContextType opengl_api_type = GLManager_MacOS::GLES_3_0_COMPATIBLE; - gl_manager = memnew(GLManager_MacOS(opengl_api_type)); - if (gl_manager->initialize() != OK) { - memdelete(gl_manager); - gl_manager = nullptr; + gl_manager_legacy = memnew(GLManagerLegacy_MacOS); + if (gl_manager_legacy->initialize() != OK) { + memdelete(gl_manager_legacy); + gl_manager_legacy = nullptr; r_error = ERR_UNAVAILABLE; - ERR_FAIL_MSG("Could not initialize OpenGL"); + ERR_FAIL_MSG("Could not initialize OpenGL."); + return; + } + } + if (rendering_driver == "opengl3_angle") { + gl_manager_angle = memnew(GLManagerANGLE_MacOS); + if (gl_manager_angle->initialize() != OK) { + memdelete(gl_manager_angle); + gl_manager_angle = nullptr; + r_error = ERR_UNAVAILABLE; + ERR_FAIL_MSG("Could not initialize OpenGL."); } } #endif @@ -4199,7 +4478,10 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM #if defined(GLES3_ENABLED) if (rendering_driver == "opengl3") { - RasterizerGLES3::make_current(); + RasterizerGLES3::make_current(true); + } + if (rendering_driver == "opengl3_angle") { + RasterizerGLES3::make_current(false); } #endif #if defined(VULKAN_ENABLED) @@ -4230,9 +4512,13 @@ DisplayServerMacOS::~DisplayServerMacOS() { // Destroy drivers. #if defined(GLES3_ENABLED) - if (gl_manager) { - memdelete(gl_manager); - gl_manager = nullptr; + if (gl_manager_legacy) { + memdelete(gl_manager_legacy); + gl_manager_legacy = nullptr; + } + if (gl_manager_angle) { + memdelete(gl_manager_angle); + gl_manager_angle = nullptr; } #endif #if defined(VULKAN_ENABLED) diff --git a/platform/macos/doc_classes/EditorExportPlatformMacOS.xml b/platform/macos/doc_classes/EditorExportPlatformMacOS.xml index 7ba1379fd0..85fefe65c0 100644 --- a/platform/macos/doc_classes/EditorExportPlatformMacOS.xml +++ b/platform/macos/doc_classes/EditorExportPlatformMacOS.xml @@ -22,6 +22,9 @@ <member name="application/copyright_localized" type="Dictionary" setter="" getter=""> Copyright notice for the bundle visible to the user (localized). </member> + <member name="application/export_angle" type="int" setter="" getter=""> + If set to [code]1[/code], ANGLE libraries are exported with the exported application. If set to [code]0[/code], ANGLE libraries are exported only if [member ProjectSettings.rendering/gl_compatibility/driver] is set to [code]"opengl3_angle"[/code]. + </member> <member name="application/icon" type="String" setter="" getter=""> Application icon file. If left empty, it will fallback to [member ProjectSettings.application/config/macos_native_icon], and then to [member ProjectSettings.application/config/icon]. </member> @@ -63,7 +66,7 @@ Array of the additional command line arguments passed to the code signing tool. </member> <member name="codesign/entitlements/address_book" type="bool" setter="" getter=""> - Enable to allow access to contacts in the user's address book, if it's enabled you should also provide usage message in the [code]privacy/address_book_usage_description[/code] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_personal-information_addressbook]com.apple.security.personal-information.addressbook[/url]. + Enable to allow access to contacts in the user's address book, if it's enabled you should also provide usage message in the [member privacy/address_book_usage_description] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_personal-information_addressbook]com.apple.security.personal-information.addressbook[/url]. </member> <member name="codesign/entitlements/allow_dyld_environment_variables" type="bool" setter="" getter=""> Allows app to use dynamic linker environment variables to inject code. If you are using add-ons with dynamic or self-modifying native code, enable them according to the add-on documentation. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_cs_allow-dyld-environment-variables]com.apple.security.cs.allow-dyld-environment-variables[/url]. @@ -112,13 +115,13 @@ Enable to allow app to send Apple events to other apps. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_automation_apple-events]com.apple.security.automation.apple-events[/url]. </member> <member name="codesign/entitlements/audio_input" type="bool" setter="" getter=""> - Enable if you need to use the microphone or other audio input sources, if it's enabled you should also provide usage message in the [code]privacy/microphone_usage_description[/code] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_device_audio-input]com.apple.security.device.audio-input[/url]. + Enable if you need to use the microphone or other audio input sources, if it's enabled you should also provide usage message in the [member privacy/microphone_usage_description] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_device_audio-input]com.apple.security.device.audio-input[/url]. </member> <member name="codesign/entitlements/calendars" type="bool" setter="" getter=""> - Enable to allow access to the user's calendar, if it's enabled you should also provide usage message in the [code]privacy/calendar_usage_description[/code] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_personal-information_calendars]com.apple.security.personal-information.calendars[/url]. + Enable to allow access to the user's calendar, if it's enabled you should also provide usage message in the [member privacy/calendar_usage_description] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_personal-information_calendars]com.apple.security.personal-information.calendars[/url]. </member> <member name="codesign/entitlements/camera" type="bool" setter="" getter=""> - Enable if you need to use the camera, if it's enabled you should also provide usage message in the [code]privacy/camera_usage_description[/code] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_device_camera]com.apple.security.device.camera[/url]. + Enable if you need to use the camera, if it's enabled you should also provide usage message in the [member privacy/camera_usage_description] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_device_camera]com.apple.security.device.camera[/url]. </member> <member name="codesign/entitlements/custom_file" type="String" setter="" getter=""> Custom entitlements [code].plist[/code] file, if specified the rest of entitlements in the export config are ignored. @@ -130,10 +133,10 @@ Allows app to load arbitrary libraries and frameworks (not signed with the same Team ID as the main executable or by Apple). Enable it if you are using GDExtension add-ons or ad-hoc signing, or want to support user-provided external add-ons. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_cs_disable-library-validation]com.apple.security.cs.disable-library-validation[/url]. </member> <member name="codesign/entitlements/location" type="bool" setter="" getter=""> - Enable if you need to use location information from Location Services, if it's enabled you should also provide usage message in the [code]privacy/location_usage_description[/code] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_personal-information_location]com.apple.security.personal-information.location[/url]. + Enable if you need to use location information from Location Services, if it's enabled you should also provide usage message in the [member privacy/location_usage_description] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_personal-information_location]com.apple.security.personal-information.location[/url]. </member> <member name="codesign/entitlements/photos_library" type="bool" setter="" getter=""> - Enable to allow access to the user's Photos library, if it's enabled you should also provide usage message in the [code]privacy/photos_library_usage_description[/code] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_personal-information_photos-library]com.apple.security.personal-information.photos-library[/url]. + Enable to allow access to the user's Photos library, if it's enabled you should also provide usage message in the [member privacy/photos_library_usage_description] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_personal-information_photos-library]com.apple.security.personal-information.photos-library[/url]. </member> <member name="codesign/identity" type="String" setter="" getter=""> The "Full Name", "Common Name" or SHA-1 hash of the signing identity used to sign [code].app[/code] bundle. diff --git a/platform/macos/export/export_plugin.cpp b/platform/macos/export/export_plugin.cpp index 6586fe7f82..804028053d 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 "editor/editor_string_names.h" #include "editor/import/resource_importer_texture_settings.h" #include "scene/resources/image_texture.h" @@ -383,6 +384,7 @@ void EditorExportPlatformMacOS::get_export_options(List<ExportOption> *r_options r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::DICTIONARY, "application/copyright_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary())); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/min_macos_version"), "10.12")); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/export_angle", PROPERTY_HINT_ENUM, "Auto,Yes,No"), 0, true)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "display/high_res"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "xcode/platform_build"), "14C18")); @@ -1045,6 +1047,8 @@ Error EditorExportPlatformMacOS::_code_sign(const Ref<EditorExportPreset> &p_pre args.push_back("--p12-password"); args.push_back(certificate_pass); } + args.push_back("--code-signature-flags"); + args.push_back("runtime"); args.push_back("-v"); /* provide some more feedback */ @@ -1137,7 +1141,6 @@ Error EditorExportPlatformMacOS::_code_sign(const Ref<EditorExportPreset> &p_pre Error EditorExportPlatformMacOS::_code_sign_directory(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path, bool p_should_error_on_non_code) { -#ifdef MACOS_ENABLED static Vector<String> extensions_to_sign; if (extensions_to_sign.is_empty()) { @@ -1184,7 +1187,6 @@ Error EditorExportPlatformMacOS::_code_sign_directory(const Ref<EditorExportPres current_file = dir_access->get_next(); } -#endif return OK; } @@ -1223,7 +1225,7 @@ Error EditorExportPlatformMacOS::_copy_and_sign_files(Ref<DirAccess> &dir_access if (extensions_to_sign.find(p_in_app_path.get_extension()) > -1) { err = _code_sign(p_preset, p_in_app_path, p_ent_path, false); } - if (is_executable(p_in_app_path)) { + if (dir_access->file_exists(p_in_app_path) && is_executable(p_in_app_path)) { // chmod with 0755 if the file is executable. FileAccess::set_unix_permissions(p_in_app_path, 0755); } @@ -1617,6 +1619,14 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p // Now process our template. bool found_binary = false; + int export_angle = p_preset->get("application/export_angle"); + bool include_angle_libs = false; + if (export_angle == 0) { + include_angle_libs = String(GLOBAL_GET("rendering/gl_compatibility/driver.macos")) == "opengl3_angle"; + } else if (export_angle == 1) { + include_angle_libs = true; + } + while (ret == UNZ_OK && err == OK) { // Get filename. unz_file_info info; @@ -1664,6 +1674,20 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p continue; // next } + if (file == "Contents/Frameworks/libEGL.dylib") { + if (!include_angle_libs) { + ret = unzGoToNextFile(src_pkg_zip); + continue; // skip + } + } + + if (file == "Contents/Frameworks/libGLESv2.dylib") { + if (!include_angle_libs) { + ret = unzGoToNextFile(src_pkg_zip); + continue; // skip + } + } + if (file == "Contents/Info.plist") { _fix_plist(p_preset, data, pkg_name); } @@ -2460,7 +2484,7 @@ EditorExportPlatformMacOS::EditorExportPlatformMacOS() { Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme(); if (theme.is_valid()) { - stop_icon = theme->get_icon(SNAME("Stop"), SNAME("EditorIcons")); + stop_icon = theme->get_icon(SNAME("Stop"), EditorStringName(EditorIcons)); } else { stop_icon.instantiate(); } diff --git a/platform/macos/gl_manager_macos_angle.h b/platform/macos/gl_manager_macos_angle.h new file mode 100644 index 0000000000..919b8ec9c8 --- /dev/null +++ b/platform/macos/gl_manager_macos_angle.h @@ -0,0 +1,63 @@ +/**************************************************************************/ +/* gl_manager_macos_angle.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef GL_MANAGER_MACOS_ANGLE_H +#define GL_MANAGER_MACOS_ANGLE_H + +#if defined(MACOS_ENABLED) && defined(GLES3_ENABLED) + +#include "core/error/error_list.h" +#include "core/os/os.h" +#include "core/templates/local_vector.h" +#include "drivers/egl/egl_manager.h" +#include "servers/display_server.h" + +#include <AppKit/AppKit.h> +#include <ApplicationServices/ApplicationServices.h> +#include <CoreVideo/CoreVideo.h> + +class GLManagerANGLE_MacOS : public EGLManager { +private: + virtual const char *_get_platform_extension_name() const override; + virtual EGLenum _get_platform_extension_enum() const override; + virtual EGLenum _get_platform_api_enum() const override; + virtual Vector<EGLAttrib> _get_platform_display_attributes() const override; + virtual Vector<EGLint> _get_platform_context_attribs() const override; + +public: + void window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height) {} + + GLManagerANGLE_MacOS() {} + ~GLManagerANGLE_MacOS() {} +}; + +#endif // MACOS_ENABLED && GLES3_ENABLED + +#endif // GL_MANAGER_MACOS_ANGLE_H diff --git a/platform/macos/gl_manager_macos_angle.mm b/platform/macos/gl_manager_macos_angle.mm new file mode 100644 index 0000000000..ec0ca3e1f3 --- /dev/null +++ b/platform/macos/gl_manager_macos_angle.mm @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* gl_manager_macos_angle.mm */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "gl_manager_macos_angle.h" + +#if defined(MACOS_ENABLED) && defined(GLES3_ENABLED) + +#include <stdio.h> +#include <stdlib.h> + +#include <EGL/eglext_angle.h> + +const char *GLManagerANGLE_MacOS::_get_platform_extension_name() const { + return "EGL_ANGLE_platform_angle"; +} + +EGLenum GLManagerANGLE_MacOS::_get_platform_extension_enum() const { + return EGL_PLATFORM_ANGLE_ANGLE; +} + +Vector<EGLAttrib> GLManagerANGLE_MacOS::_get_platform_display_attributes() const { + Vector<EGLAttrib> ret; + ret.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE); + ret.push_back(EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE); + ret.push_back(EGL_NONE); + + return ret; +} + +EGLenum GLManagerANGLE_MacOS::_get_platform_api_enum() const { + return EGL_OPENGL_ES_API; +} + +Vector<EGLint> GLManagerANGLE_MacOS::_get_platform_context_attribs() const { + Vector<EGLint> ret; + ret.push_back(EGL_CONTEXT_CLIENT_VERSION); + ret.push_back(3); + ret.push_back(EGL_NONE); + + return ret; +} + +#endif // MACOS_ENABLED && GLES3_ENABLED diff --git a/platform/macos/gl_manager_macos_legacy.h b/platform/macos/gl_manager_macos_legacy.h index 94d966f4ed..bafe825efb 100644 --- a/platform/macos/gl_manager_macos_legacy.h +++ b/platform/macos/gl_manager_macos_legacy.h @@ -45,17 +45,12 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" // OpenGL is deprecated in macOS 10.14 -class GLManager_MacOS { -public: - enum ContextType { - GLES_3_0_COMPATIBLE, - }; +typedef CGLError (*CGLEnablePtr)(CGLContextObj ctx, CGLContextEnable pname); +typedef CGLError (*CGLSetParameterPtr)(CGLContextObj ctx, CGLContextParameter pname, const GLint *params); +typedef CGLContextObj (*CGLGetCurrentContextPtr)(void); -private: +class GLManagerLegacy_MacOS { struct GLWindow { - int width = 0; - int height = 0; - id window_view = nullptr; NSOpenGLContext *context = nullptr; }; @@ -68,23 +63,21 @@ private: Error create_context(GLWindow &win); bool use_vsync = false; - ContextType context_type; + CGLEnablePtr CGLEnable = nullptr; + CGLSetParameterPtr CGLSetParameter = nullptr; + CGLGetCurrentContextPtr CGLGetCurrentContext = nullptr; public: Error window_create(DisplayServer::WindowID p_window_id, id p_view, int p_width, int p_height); void window_destroy(DisplayServer::WindowID p_window_id); void window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height); - int window_get_width(DisplayServer::WindowID p_window_id = 0); - int window_get_height(DisplayServer::WindowID p_window_id = 0); - void release_current(); void make_current(); void swap_buffers(); void window_make_current(DisplayServer::WindowID p_window_id); - void window_update(DisplayServer::WindowID p_window_id); void window_set_per_pixel_transparency_enabled(DisplayServer::WindowID p_window_id, bool p_enabled); Error initialize(); @@ -94,8 +87,8 @@ public: NSOpenGLContext *get_context(DisplayServer::WindowID p_window_id); - GLManager_MacOS(ContextType p_context_type); - ~GLManager_MacOS(); + GLManagerLegacy_MacOS(); + ~GLManagerLegacy_MacOS(); }; #pragma clang diagnostic push diff --git a/platform/macos/gl_manager_macos_legacy.mm b/platform/macos/gl_manager_macos_legacy.mm index 550e2d5c59..701de6df78 100644 --- a/platform/macos/gl_manager_macos_legacy.mm +++ b/platform/macos/gl_manager_macos_legacy.mm @@ -38,7 +38,7 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" // OpenGL is deprecated in macOS 10.14 -Error GLManager_MacOS::create_context(GLWindow &win) { +Error GLManagerLegacy_MacOS::create_context(GLWindow &win) { NSOpenGLPixelFormatAttribute attributes[] = { NSOpenGLPFADoubleBuffer, NSOpenGLPFAClosestPolicy, @@ -50,10 +50,10 @@ Error GLManager_MacOS::create_context(GLWindow &win) { }; NSOpenGLPixelFormat *pixel_format = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes]; - ERR_FAIL_COND_V(pixel_format == nil, ERR_CANT_CREATE); + ERR_FAIL_NULL_V(pixel_format, ERR_CANT_CREATE); win.context = [[NSOpenGLContext alloc] initWithFormat:pixel_format shareContext:shared_context]; - ERR_FAIL_COND_V(win.context == nil, ERR_CANT_CREATE); + ERR_FAIL_NULL_V(win.context, ERR_CANT_CREATE); if (shared_context == nullptr) { shared_context = win.context; } @@ -64,10 +64,8 @@ Error GLManager_MacOS::create_context(GLWindow &win) { return OK; } -Error GLManager_MacOS::window_create(DisplayServer::WindowID p_window_id, id p_view, int p_width, int p_height) { +Error GLManagerLegacy_MacOS::window_create(DisplayServer::WindowID p_window_id, id p_view, int p_width, int p_height) { GLWindow win; - win.width = p_width; - win.height = p_height; win.window_view = p_view; if (create_context(win) != OK) { @@ -80,16 +78,13 @@ Error GLManager_MacOS::window_create(DisplayServer::WindowID p_window_id, id p_v return OK; } -void GLManager_MacOS::window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height) { +void GLManagerLegacy_MacOS::window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height) { if (!windows.has(p_window_id)) { return; } GLWindow &win = windows[p_window_id]; - win.width = p_width; - win.height = p_height; - GLint dim[2]; dim[0] = p_width; dim[1] = p_height; @@ -104,25 +99,7 @@ void GLManager_MacOS::window_resize(DisplayServer::WindowID p_window_id, int p_w [win.context update]; } -int GLManager_MacOS::window_get_width(DisplayServer::WindowID p_window_id) { - if (!windows.has(p_window_id)) { - return 0; - } - - GLWindow &win = windows[p_window_id]; - return win.width; -} - -int GLManager_MacOS::window_get_height(DisplayServer::WindowID p_window_id) { - if (!windows.has(p_window_id)) { - return 0; - } - - GLWindow &win = windows[p_window_id]; - return win.height; -} - -void GLManager_MacOS::window_destroy(DisplayServer::WindowID p_window_id) { +void GLManagerLegacy_MacOS::window_destroy(DisplayServer::WindowID p_window_id) { if (!windows.has(p_window_id)) { return; } @@ -134,7 +111,7 @@ void GLManager_MacOS::window_destroy(DisplayServer::WindowID p_window_id) { windows.erase(p_window_id); } -void GLManager_MacOS::release_current() { +void GLManagerLegacy_MacOS::release_current() { if (current_window == DisplayServer::INVALID_WINDOW_ID) { return; } @@ -142,7 +119,7 @@ void GLManager_MacOS::release_current() { [NSOpenGLContext clearCurrentContext]; } -void GLManager_MacOS::window_make_current(DisplayServer::WindowID p_window_id) { +void GLManagerLegacy_MacOS::window_make_current(DisplayServer::WindowID p_window_id) { if (current_window == p_window_id) { return; } @@ -156,7 +133,7 @@ void GLManager_MacOS::window_make_current(DisplayServer::WindowID p_window_id) { current_window = p_window_id; } -void GLManager_MacOS::make_current() { +void GLManagerLegacy_MacOS::make_current() { if (current_window == DisplayServer::INVALID_WINDOW_ID) { return; } @@ -168,21 +145,12 @@ void GLManager_MacOS::make_current() { [win.context makeCurrentContext]; } -void GLManager_MacOS::swap_buffers() { +void GLManagerLegacy_MacOS::swap_buffers() { GLWindow &win = windows[current_window]; [win.context flushBuffer]; } -void GLManager_MacOS::window_update(DisplayServer::WindowID p_window_id) { - if (!windows.has(p_window_id)) { - return; - } - - GLWindow &win = windows[p_window_id]; - [win.context update]; -} - -void GLManager_MacOS::window_set_per_pixel_transparency_enabled(DisplayServer::WindowID p_window_id, bool p_enabled) { +void GLManagerLegacy_MacOS::window_set_per_pixel_transparency_enabled(DisplayServer::WindowID p_window_id, bool p_enabled) { if (!windows.has(p_window_id)) { return; } @@ -198,26 +166,28 @@ void GLManager_MacOS::window_set_per_pixel_transparency_enabled(DisplayServer::W [win.context update]; } -Error GLManager_MacOS::initialize() { +Error GLManagerLegacy_MacOS::initialize() { return OK; } -void GLManager_MacOS::set_use_vsync(bool p_use) { +void GLManagerLegacy_MacOS::set_use_vsync(bool p_use) { use_vsync = p_use; CGLContextObj ctx = CGLGetCurrentContext(); if (ctx) { GLint swapInterval = p_use ? 1 : 0; - CGLSetParameter(ctx, kCGLCPSwapInterval, &swapInterval); + if (CGLSetParameter(ctx, kCGLCPSwapInterval, &swapInterval) != kCGLNoError) { + WARN_PRINT("Could not set V-Sync mode."); + } use_vsync = p_use; } } -bool GLManager_MacOS::is_using_vsync() const { +bool GLManagerLegacy_MacOS::is_using_vsync() const { return use_vsync; } -NSOpenGLContext *GLManager_MacOS::get_context(DisplayServer::WindowID p_window_id) { +NSOpenGLContext *GLManagerLegacy_MacOS::get_context(DisplayServer::WindowID p_window_id) { if (!windows.has(p_window_id)) { return nullptr; } @@ -226,11 +196,16 @@ NSOpenGLContext *GLManager_MacOS::get_context(DisplayServer::WindowID p_window_i return win.context; } -GLManager_MacOS::GLManager_MacOS(ContextType p_context_type) { - context_type = p_context_type; +GLManagerLegacy_MacOS::GLManagerLegacy_MacOS() { + CFBundleRef framework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl")); + CFBundleLoadExecutable(framework); + + CGLEnable = (CGLEnablePtr)CFBundleGetFunctionPointerForName(framework, CFSTR("CGLEnable")); + CGLSetParameter = (CGLSetParameterPtr)CFBundleGetFunctionPointerForName(framework, CFSTR("CGLSetParameter")); + CGLGetCurrentContext = (CGLGetCurrentContextPtr)CFBundleGetFunctionPointerForName(framework, CFSTR("CGLGetCurrentContext")); } -GLManager_MacOS::~GLManager_MacOS() { +GLManagerLegacy_MacOS::~GLManagerLegacy_MacOS() { release_current(); } diff --git a/platform/macos/godot_content_view.h b/platform/macos/godot_content_view.h index 0d18ac742a..c6060c96c6 100644 --- a/platform/macos/godot_content_view.h +++ b/platform/macos/godot_content_view.h @@ -47,9 +47,11 @@ @interface GodotContentLayerDelegate : NSObject <CALayerDelegate> { DisplayServer::WindowID window_id; + bool need_redraw; } - (void)setWindowID:(DisplayServer::WindowID)wid; +- (void)setNeedRedraw:(bool)redraw; @end diff --git a/platform/macos/godot_content_view.mm b/platform/macos/godot_content_view.mm index 231be83a03..139411249c 100644 --- a/platform/macos/godot_content_view.mm +++ b/platform/macos/godot_content_view.mm @@ -40,6 +40,7 @@ - (id)init { self = [super init]; window_id = DisplayServer::INVALID_WINDOW_ID; + need_redraw = false; return self; } @@ -47,13 +48,18 @@ window_id = wid; } +- (void)setNeedRedraw:(bool)redraw { + need_redraw = redraw; +} + - (void)displayLayer:(CALayer *)layer { DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); - if (OS::get_singleton()->get_main_loop() && ds->get_is_resizing()) { + if (OS::get_singleton()->get_main_loop() && ds->get_is_resizing() && need_redraw) { Main::force_redraw(); if (!Main::is_iterating()) { // Avoid cyclic loop. Main::iteration(); } + need_redraw = false; } } @@ -93,6 +99,7 @@ } [super setFrameSize:newSize]; + [layer_delegate setNeedRedraw:true]; [self.layer setNeedsDisplay]; // Force "drawRect" call. } @@ -132,12 +139,6 @@ return [[CAMetalLayer class] layer]; } -- (void)updateLayer { - DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); - ds->window_update(window_id); - [super updateLayer]; -} - - (BOOL)wantsUpdateLayer { return YES; } @@ -322,12 +323,7 @@ NSString *file = [NSURL URLWithString:url].path; files.push_back(String::utf8([file UTF8String])); } - - Variant v = files; - Variant *vp = &v; - Variant ret; - Callable::CallError ce; - wd.drop_files_callback.callp((const Variant **)&vp, 1, ret, ce); + wd.drop_files_callback.call(files); } return NO; diff --git a/platform/macos/godot_menu_delegate.mm b/platform/macos/godot_menu_delegate.mm index ebfe8b1f6d..e393a87134 100644 --- a/platform/macos/godot_menu_delegate.mm +++ b/platform/macos/godot_menu_delegate.mm @@ -39,6 +39,30 @@ - (void)doNothing:(id)sender { } +- (void)menuNeedsUpdate:(NSMenu *)menu { + if (DisplayServer::get_singleton()) { + DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); + ds->menu_open(menu); + } +} + +- (void)menuDidClose:(NSMenu *)menu { + if (DisplayServer::get_singleton()) { + DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); + ds->menu_close(menu); + } +} + +- (void)menu:(NSMenu *)menu willHighlightItem:(NSMenuItem *)item { + if (item) { + GodotMenuItem *value = [item representedObject]; + if (value && value->hover_callback != Callable()) { + // If custom callback is set, use it. + value->hover_callback.call(value->meta); + } + } +} + - (BOOL)menuHasKeyEquivalent:(NSMenu *)menu forEvent:(NSEvent *)event target:(id *)target action:(SEL *)action { NSString *ev_key = [[event charactersIgnoringModifiers] lowercaseString]; NSUInteger ev_modifiers = [event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask; @@ -51,11 +75,7 @@ GodotMenuItem *value = [menu_item representedObject]; if (value->key_callback != Callable()) { // If custom callback is set, use it. - Variant tag = value->meta; - Variant *tagp = &tag; - Variant ret; - Callable::CallError ce; - value->key_callback.callp((const Variant **)&tagp, 1, ret, ce); + value->key_callback.call(value->meta); } else { // Otherwise redirect event to the engine. if (DisplayServer::get_singleton()) { diff --git a/platform/macos/godot_menu_item.h b/platform/macos/godot_menu_item.h index 8876ceae6a..b816ea1b3a 100644 --- a/platform/macos/godot_menu_item.h +++ b/platform/macos/godot_menu_item.h @@ -46,6 +46,7 @@ enum GlobalMenuCheckType { @public Callable callback; Callable key_callback; + Callable hover_callback; Variant meta; GlobalMenuCheckType checkable_type; int max_states; diff --git a/platform/macos/godot_window_delegate.mm b/platform/macos/godot_window_delegate.mm index 46355b4ae8..1756f2d676 100644 --- a/platform/macos/godot_window_delegate.mm +++ b/platform/macos/godot_window_delegate.mm @@ -157,7 +157,7 @@ DisplayServerMacOS::WindowData &wd = ds->get_window(window_id); if (wd.exclusive_fullscreen) { - [NSApp setPresentationOptions:NSApplicationPresentationDefault]; + ds->update_presentation_mode(); } wd.fullscreen = false; @@ -256,11 +256,7 @@ ds->window_resize(window_id, wd.size.width, wd.size.height); if (!wd.rect_changed_callback.is_null()) { - Variant size = Rect2i(ds->window_get_position(window_id), ds->window_get_size(window_id)); - Variant *sizep = &size; - Variant ret; - Callable::CallError ce; - wd.rect_changed_callback.callp((const Variant **)&sizep, 1, ret, ce); + wd.rect_changed_callback.call(Rect2i(ds->window_get_position(window_id), ds->window_get_size(window_id))); } } @@ -283,11 +279,7 @@ ds->release_pressed_events(); if (!wd.rect_changed_callback.is_null()) { - Variant size = Rect2i(ds->window_get_position(window_id), ds->window_get_size(window_id)); - Variant *sizep = &size; - Variant ret; - Callable::CallError ce; - wd.rect_changed_callback.callp((const Variant **)&sizep, 1, ret, ce); + wd.rect_changed_callback.call(Rect2i(ds->window_get_position(window_id), ds->window_get_size(window_id))); } } diff --git a/platform/macos/os_macos.mm b/platform/macos/os_macos.mm index c17ea95f4f..29dff683d5 100644 --- a/platform/macos/os_macos.mm +++ b/platform/macos/os_macos.mm @@ -231,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, vformat("Can't open dynamic library: %s. Error: %s.", p_path, dlerror())); + ERR_FAIL_NULL_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; @@ -819,7 +819,7 @@ OS_MacOS::OS_MacOS() { [NSApp finishLaunching]; id delegate = [[GodotApplicationDelegate alloc] init]; - ERR_FAIL_COND(!delegate); + ERR_FAIL_NULL(delegate); [NSApp setDelegate:delegate]; pre_wait_observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopBeforeWaiting, true, 0, &pre_wait_observer_cb, nullptr); diff --git a/platform/macos/platform_config.h b/platform/macos/platform_config.h index 65a898dcc1..1a571b689a 100644 --- a/platform/macos/platform_config.h +++ b/platform/macos/platform_config.h @@ -30,5 +30,4 @@ #include <alloca.h> -#define OPENGL_INCLUDE_H "thirdparty/glad/glad/gl.h" #define PTHREAD_RENAME_SELF diff --git a/platform/macos/platform_gl.h b/platform/macos/platform_gl.h new file mode 100644 index 0000000000..3bad9d5a5f --- /dev/null +++ b/platform/macos/platform_gl.h @@ -0,0 +1,52 @@ +/**************************************************************************/ +/* platform_gl.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef PLATFORM_GL_H +#define PLATFORM_GL_H + +#ifndef GL_API_ENABLED +#define GL_API_ENABLED // Allow using desktop GL. +#endif + +#ifndef GLES_API_ENABLED +#define GLES_API_ENABLED // Allow using GLES (ANGLE). +#endif + +#ifdef EGL_STATIC +#define KHRONOS_STATIC 1 +#include "thirdparty/angle/include/EGL/egl.h" +#include "thirdparty/angle/include/EGL/eglext.h" +#undef KHRONOS_STATIC +#else +#include "thirdparty/glad/glad/egl.h" +#endif +#include "thirdparty/glad/glad/gl.h" + +#endif // PLATFORM_GL_H |
