summaryrefslogtreecommitdiff
path: root/platform/osx
diff options
context:
space:
mode:
Diffstat (limited to 'platform/osx')
-rw-r--r--platform/osx/crash_handler_osx.mm10
-rw-r--r--platform/osx/detect.py39
-rw-r--r--platform/osx/dir_access_osx.h2
-rw-r--r--platform/osx/display_server_osx.h23
-rw-r--r--platform/osx/display_server_osx.mm454
-rw-r--r--platform/osx/export/export.cpp1074
-rw-r--r--platform/osx/export/export_plugin.cpp1085
-rw-r--r--platform/osx/export/export_plugin.h128
-rw-r--r--platform/osx/joypad_osx.cpp45
-rw-r--r--platform/osx/joypad_osx.h1
-rw-r--r--platform/osx/os_osx.h4
-rw-r--r--platform/osx/os_osx.mm228
-rw-r--r--platform/osx/vulkan_context_osx.h2
-rw-r--r--platform/osx/vulkan_context_osx.mm14
14 files changed, 1678 insertions, 1431 deletions
diff --git a/platform/osx/crash_handler_osx.mm b/platform/osx/crash_handler_osx.mm
index 147ce26779..0f128d504f 100644
--- a/platform/osx/crash_handler_osx.mm
+++ b/platform/osx/crash_handler_osx.mm
@@ -70,7 +70,7 @@ static uint64_t load_address() {
}
static void handle_crash(int sig) {
- if (OS::get_singleton() == NULL) {
+ if (OS::get_singleton() == nullptr) {
abort();
}
@@ -105,7 +105,7 @@ static void handle_crash(int sig) {
if (dladdr(bt_buffer[i], &info) && info.dli_sname) {
if (info.dli_sname[0] == '_') {
int status;
- char *demangled = abi::__cxa_demangle(info.dli_sname, NULL, 0, &status);
+ char *demangled = abi::__cxa_demangle(info.dli_sname, nullptr, 0, &status);
if (status == 0 && demangled) {
snprintf(fname, 1024, "%s", demangled);
@@ -167,9 +167,9 @@ void CrashHandler::disable() {
return;
#ifdef CRASH_HANDLER_ENABLED
- signal(SIGSEGV, NULL);
- signal(SIGFPE, NULL);
- signal(SIGILL, NULL);
+ signal(SIGSEGV, nullptr);
+ signal(SIGFPE, nullptr);
+ signal(SIGILL, nullptr);
#endif
disabled = true;
diff --git a/platform/osx/detect.py b/platform/osx/detect.py
index 5b320da82f..6b25daf05d 100644
--- a/platform/osx/detect.py
+++ b/platform/osx/detect.py
@@ -24,19 +24,13 @@ def get_opts():
return [
("osxcross_sdk", "OSXCross SDK version", "darwin16"),
("MACOS_SDK_PATH", "Path to the macOS SDK", ""),
- BoolVariable(
- "use_static_mvk",
- "Link MoltenVK statically as Level-0 driver (better portability) or use Vulkan ICD loader (enables"
- " validation layers)",
- False,
- ),
+ ("VULKAN_SDK_PATH", "Path to the Vulkan SDK", ""),
EnumVariable("macports_clang", "Build using Clang from MacPorts", "no", ("no", "5.0", "devel")),
BoolVariable("debug_symbols", "Add debugging symbols to release/release_debug builds", True),
BoolVariable("separate_debug_symbols", "Create a separate file containing debugging symbols", False),
BoolVariable("use_ubsan", "Use LLVM/GCC compiler undefined behavior sanitizer (UBSAN)", False),
- BoolVariable("use_asan", "Use LLVM/GCC compiler address sanitizer (ASAN))", False),
- BoolVariable("use_lsan", "Use LLVM/GCC compiler leak sanitizer (LSAN))", False),
- BoolVariable("use_tsan", "Use LLVM/GCC compiler thread sanitizer (TSAN))", False),
+ BoolVariable("use_asan", "Use LLVM/GCC compiler address sanitizer (ASAN)", False),
+ BoolVariable("use_tsan", "Use LLVM/GCC compiler thread sanitizer (TSAN)", False),
]
@@ -132,21 +126,22 @@ def configure(env):
env["AS"] = basecmd + "as"
env.Append(CPPDEFINES=["__MACPORTS__"]) # hack to fix libvpx MM256_BROADCASTSI128_SI256 define
- if env["use_ubsan"] or env["use_asan"] or env["use_lsan"] or env["use_tsan"]:
+ if env["use_ubsan"] or env["use_asan"] or env["use_tsan"]:
env.extra_suffix += "s"
if env["use_ubsan"]:
- env.Append(CCFLAGS=["-fsanitize=undefined"])
+ env.Append(
+ CCFLAGS=[
+ "-fsanitize=undefined,shift,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,null,return,signed-integer-overflow,bounds,float-divide-by-zero,float-cast-overflow,nonnull-attribute,returns-nonnull-attribute,bool,enum,vptr,pointer-overflow,builtin"
+ ]
+ )
env.Append(LINKFLAGS=["-fsanitize=undefined"])
+ env.Append(CCFLAGS=["-fsanitize=nullability-return,nullability-arg,function,nullability-assign"])
if env["use_asan"]:
- env.Append(CCFLAGS=["-fsanitize=address"])
+ env.Append(CCFLAGS=["-fsanitize=address,pointer-subtract,pointer-compare"])
env.Append(LINKFLAGS=["-fsanitize=address"])
- if env["use_lsan"]:
- env.Append(CCFLAGS=["-fsanitize=leak"])
- env.Append(LINKFLAGS=["-fsanitize=leak"])
-
if env["use_tsan"]:
env.Append(CCFLAGS=["-fsanitize=thread"])
env.Append(LINKFLAGS=["-fsanitize=thread"])
@@ -187,12 +182,10 @@ def configure(env):
)
env.Append(LIBS=["pthread", "z"])
- env.Append(CPPDEFINES=["VULKAN_ENABLED"])
- env.Append(LINKFLAGS=["-framework", "Metal", "-framework", "QuartzCore", "-framework", "IOSurface"])
- if env["use_static_mvk"]:
- env.Append(LINKFLAGS=["-framework", "MoltenVK"])
- env["builtin_vulkan"] = False
- elif not env["builtin_vulkan"]:
- env.Append(LIBS=["vulkan"])
+ if env["vulkan"]:
+ env.Append(CPPDEFINES=["VULKAN_ENABLED"])
+ env.Append(LINKFLAGS=["-framework", "Metal", "-framework", "QuartzCore", "-framework", "IOSurface"])
+ if not env["use_volk"]:
+ env.Append(LINKFLAGS=["-L$VULKAN_SDK_PATH/MoltenVK/MoltenVK.xcframework/macos-arm64_x86_64/", "-lMoltenVK"])
# env.Append(CPPDEFINES=['GLES_ENABLED', 'OPENGL_ENABLED'])
diff --git a/platform/osx/dir_access_osx.h b/platform/osx/dir_access_osx.h
index f61581979f..a894723e64 100644
--- a/platform/osx/dir_access_osx.h
+++ b/platform/osx/dir_access_osx.h
@@ -38,7 +38,7 @@
#include <sys/types.h>
#include <unistd.h>
-#include "core/os/dir_access.h"
+#include "core/io/dir_access.h"
#include "drivers/unix/dir_access_unix.h"
class DirAccessOSX : public DirAccessUnix {
diff --git a/platform/osx/display_server_osx.h b/platform/osx/display_server_osx.h
index 9fac99810b..06b14f1c14 100644
--- a/platform/osx/display_server_osx.h
+++ b/platform/osx/display_server_osx.h
@@ -60,6 +60,10 @@ class DisplayServerOSX : public DisplayServer {
_THREAD_SAFE_CLASS_
public:
+ void _send_event(NSEvent *p_event);
+ NSMenu *_get_dock_menu() const;
+ void _menu_callback(id p_sender);
+
#if defined(OPENGL_ENABLED)
ContextGL_OSX *context_gles2;
#endif
@@ -81,7 +85,7 @@ public:
bool pressed = false;
bool echo = false;
bool raw = false;
- uint32_t keycode = 0;
+ Key keycode = KEY_NONE;
uint32_t physical_keycode = 0;
uint32_t unicode = 0;
};
@@ -145,7 +149,7 @@ public:
WindowID window_id_counter = MAIN_WINDOW_ID;
- WindowID _create_window(WindowMode p_mode, const Rect2i &p_rect);
+ WindowID _create_window(WindowMode p_mode, VSyncMode p_vsync_mode, const Rect2i &p_rect);
void _update_window(WindowData p_wd);
void _send_window_event(const WindowData &wd, WindowEvent p_event);
static void _dispatch_input_events(const Ref<InputEvent> &p_event);
@@ -163,7 +167,6 @@ public:
String rendering_driver;
- id delegate;
id autoreleasePool;
CGEventSourceRef eventSource;
@@ -173,7 +176,7 @@ public:
MouseMode mouse_mode;
Point2i last_mouse_pos;
- uint32_t last_button_state;
+ MouseButton last_button_state = MOUSE_BUTTON_NONE;
bool window_focused;
bool drop_events;
@@ -207,7 +210,6 @@ public:
virtual void global_menu_remove_item(const String &p_menu_root, int p_idx) override;
virtual void global_menu_clear(const String &p_menu_root) override;
- virtual void alert(const String &p_alert, const String &p_title = "ALERT!") override;
virtual Error dialog_show(String p_title, String p_description, Vector<String> p_buttons, const Callable &p_callback) override;
virtual Error dialog_input_text(String p_title, String p_description, String p_partial, const Callable &p_callback) override;
@@ -217,7 +219,7 @@ public:
virtual void mouse_warp_to_position(const Point2i &p_to) override;
virtual Point2i mouse_get_position() const override;
virtual Point2i mouse_get_absolute_position() const override;
- virtual int mouse_get_button_state() const override;
+ virtual MouseButton mouse_get_button_state() const override;
virtual void clipboard_set(const String &p_text) override;
virtual String clipboard_get() const override;
@@ -232,7 +234,7 @@ public:
virtual Vector<int> get_window_list() const override;
- virtual WindowID create_sub_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect = Rect2i()) override;
+ virtual WindowID create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect = Rect2i()) override;
virtual void show_window(WindowID p_id) override;
virtual void delete_sub_window(WindowID p_id) override;
@@ -286,6 +288,9 @@ public:
virtual void window_attach_instance_id(ObjectID p_instance, WindowID p_window = MAIN_WINDOW_ID) override;
virtual ObjectID window_get_attached_instance_id(WindowID p_window = MAIN_WINDOW_ID) const override;
+ virtual void window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual DisplayServer::VSyncMode window_get_vsync_mode(WindowID p_vsync_mode) const override;
+
virtual Point2i ime_get_selection() const override;
virtual String ime_get_text() const override;
@@ -314,12 +319,12 @@ public:
virtual void console_set_visible(bool p_enabled) override;
virtual bool is_console_visible() const override;
- static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
+ static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
static Vector<String> get_rendering_drivers_func();
static void register_osx_driver();
- DisplayServerOSX(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
+ DisplayServerOSX(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
~DisplayServerOSX();
};
diff --git a/platform/osx/display_server_osx.mm b/platform/osx/display_server_osx.mm
index 412dd59f56..43b7d7c1e0 100644
--- a/platform/osx/display_server_osx.mm
+++ b/platform/osx/display_server_osx.mm
@@ -66,10 +66,10 @@
static bool ignore_momentum_scroll = false;
static void _get_key_modifier_state(unsigned int p_osx_state, Ref<InputEventWithModifiers> r_state) {
- r_state->set_shift((p_osx_state & NSEventModifierFlagShift));
- r_state->set_control((p_osx_state & NSEventModifierFlagControl));
- r_state->set_alt((p_osx_state & NSEventModifierFlagOption));
- r_state->set_metakey((p_osx_state & NSEventModifierFlagCommand));
+ r_state->set_shift_pressed((p_osx_state & NSEventModifierFlagShift));
+ r_state->set_ctrl_pressed((p_osx_state & NSEventModifierFlagControl));
+ r_state->set_alt_pressed((p_osx_state & NSEventModifierFlagOption));
+ r_state->set_meta_pressed((p_osx_state & NSEventModifierFlagCommand));
}
static Vector2i _get_mouse_pos(DisplayServerOSX::WindowData &p_wd, NSPoint p_locationInWindow) {
@@ -105,46 +105,6 @@ static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) {
}
/*************************************************************************/
-/* GodotApplication */
-/*************************************************************************/
-
-@interface GodotApplication : NSApplication
-@end
-
-@implementation GodotApplication
-
-- (void)sendEvent:(NSEvent *)event {
- // special case handling of command-period, which is traditionally a special
- // shortcut in macOS and doesn't arrive at our regular keyDown handler.
- if ([event type] == NSEventTypeKeyDown) {
- if (([event modifierFlags] & NSEventModifierFlagCommand) && [event keyCode] == 0x2f) {
- Ref<InputEventKey> k;
- k.instance();
-
- _get_key_modifier_state([event modifierFlags], k);
- k->set_window_id(DisplayServerOSX::INVALID_WINDOW_ID);
- k->set_pressed(true);
- k->set_keycode(KEY_PERIOD);
- k->set_physical_keycode(KEY_PERIOD);
- k->set_echo([event isARepeat]);
-
- Input::get_singleton()->accumulate_input_event(k);
- }
- }
-
- // From http://cocoadev.com/index.pl?GameKeyboardHandlingAlmost
- // This works around an AppKit bug, where key up events while holding
- // down the command key don't get sent to the key window.
- if ([event type] == NSEventTypeKeyUp && ([event modifierFlags] & NSEventModifierFlagCommand)) {
- [[self keyWindow] sendEvent:event];
- } else {
- [super sendEvent:event];
- }
-}
-
-@end
-
-/*************************************************************************/
/* GlobalMenuItem */
/*************************************************************************/
@@ -161,121 +121,6 @@ static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) {
@end
/*************************************************************************/
-/* GodotApplicationDelegate */
-/*************************************************************************/
-
-@interface GodotApplicationDelegate : NSObject
-- (void)forceUnbundledWindowActivationHackStep1;
-- (void)forceUnbundledWindowActivationHackStep2;
-- (void)forceUnbundledWindowActivationHackStep3;
-@end
-
-@implementation GodotApplicationDelegate
-
-- (void)forceUnbundledWindowActivationHackStep1 {
- // Step1: Switch focus to macOS Dock.
- // Required to perform step 2, TransformProcessType will fail if app is already the in focus.
- for (NSRunningApplication *app in [NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.apple.dock"]) {
- [app activateWithOptions:NSApplicationActivateIgnoringOtherApps];
- break;
- }
- [self performSelector:@selector(forceUnbundledWindowActivationHackStep2) withObject:nil afterDelay:0.02];
-}
-
-- (void)forceUnbundledWindowActivationHackStep2 {
- // Step 2: Register app as foreground process.
- ProcessSerialNumber psn = { 0, kCurrentProcess };
- (void)TransformProcessType(&psn, kProcessTransformToForegroundApplication);
- [self performSelector:@selector(forceUnbundledWindowActivationHackStep3) withObject:nil afterDelay:0.02];
-}
-
-- (void)forceUnbundledWindowActivationHackStep3 {
- // Step 3: Switch focus back to app window.
- [[NSRunningApplication currentApplication] activateWithOptions:NSApplicationActivateIgnoringOtherApps];
-}
-
-- (void)applicationDidFinishLaunching:(NSNotification *)notice {
- NSString *nsappname = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];
- if (nsappname == nil) {
- // If executable is not a bundled, macOS WindowServer won't register and activate app window correctly (menu and title bar are grayed out and input ignored).
- [self performSelector:@selector(forceUnbundledWindowActivationHackStep1) withObject:nil afterDelay:0.02];
- }
-}
-
-- (void)applicationDidResignActive:(NSNotification *)notification {
- if (OS_OSX::get_singleton()->get_main_loop()) {
- OS_OSX::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT);
- }
-}
-
-- (void)applicationDidBecomeActive:(NSNotification *)notification {
- if (OS_OSX::get_singleton()->get_main_loop()) {
- OS_OSX::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_IN);
- }
-}
-
-- (void)globalMenuCallback:(id)sender {
- if (![sender representedObject]) {
- return;
- }
-
- GlobalMenuItem *value = [sender representedObject];
-
- if (value) {
- if (value->checkable) {
- if ([sender state] == NSControlStateValueOff) {
- [sender setState:NSControlStateValueOn];
- } else {
- [sender setState:NSControlStateValueOff];
- }
- }
-
- if (value->callback != Callable()) {
- Variant tag = value->meta;
- Variant *tagp = &tag;
- Variant ret;
- Callable::CallError ce;
- value->callback.call((const Variant **)&tagp, 1, ret, ce);
- }
- }
-}
-
-- (NSMenu *)applicationDockMenu:(NSApplication *)sender {
- return DS_OSX->dock_menu;
-}
-
-- (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename {
- // Note: may be called called before main loop init!
- char *utfs = strdup([filename UTF8String]);
- ((OS_OSX *)(OS_OSX::get_singleton()))->open_with_filename.parse_utf8(utfs);
- free(utfs);
-
-#ifdef TOOLS_ENABLED
- // Open new instance
- if (OS_OSX::get_singleton()->get_main_loop()) {
- List<String> args;
- args.push_back(((OS_OSX *)(OS_OSX::get_singleton()))->open_with_filename);
- String exec = OS::get_singleton()->get_executable_path();
- OS::get_singleton()->create_process(exec, args);
- }
-#endif
- return YES;
-}
-
-- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
- DS_OSX->_send_window_event(DS_OSX->windows[DisplayServerOSX::MAIN_WINDOW_ID], DisplayServerOSX::WINDOW_EVENT_CLOSE_REQUEST);
- return NSTerminateCancel;
-}
-
-- (void)showAbout:(id)sender {
- if (OS_OSX::get_singleton()->get_main_loop()) {
- OS_OSX::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_ABOUT);
- }
-}
-
-@end
-
-/*************************************************************************/
/* GodotWindowDelegate */
/*************************************************************************/
@@ -368,6 +213,10 @@ static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) {
if (wd.resize_disabled) {
[wd.window_object setStyleMask:[wd.window_object styleMask] & ~NSWindowStyleMaskResizable];
}
+
+ if (wd.on_top) {
+ [wd.window_object setLevel:NSFloatingWindowLevel];
+ }
}
- (void)windowDidChangeBackingProperties:(NSNotification *)notification {
@@ -469,8 +318,16 @@ static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) {
}
DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
- _get_mouse_pos(wd, [wd.window_object mouseLocationOutsideOfEventStream]);
- Input::get_singleton()->set_mouse_position(wd.mouse_pos);
+ if (DS_OSX->mouse_mode == DisplayServer::MOUSE_MODE_CAPTURED) {
+ const NSRect contentRect = [wd.window_view frame];
+ NSRect pointInWindowRect = NSMakeRect(contentRect.size.width / 2, contentRect.size.height / 2, 0, 0);
+ NSPoint pointOnScreen = [[wd.window_view window] convertRectToScreen:pointInWindowRect].origin;
+ CGPoint lMouseWarpPos = { pointOnScreen.x, CGDisplayBounds(CGMainDisplayID()).size.height - pointOnScreen.y };
+ CGWarpMouseCursorPosition(lMouseWarpPos);
+ } else {
+ _get_mouse_pos(wd, [wd.window_object mouseLocationOutsideOfEventStream]);
+ Input::get_singleton()->set_mouse_position(wd.mouse_pos);
+ }
DS_OSX->window_focused = true;
DS_OSX->_send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_FOCUS_IN);
@@ -728,7 +585,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
ke.pressed = true;
ke.echo = false;
ke.raw = false; // IME input event
- ke.keycode = 0;
+ ke.keycode = KEY_NONE;
ke.physical_keycode = 0;
ke.unicode = codepoint;
@@ -809,18 +666,18 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
DS_OSX->cursor_set_shape(p_shape);
}
-static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, int index, int mask, bool pressed) {
+static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, MouseButton index, MouseButton mask, bool pressed) {
ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
if (pressed) {
DS_OSX->last_button_state |= mask;
} else {
- DS_OSX->last_button_state &= ~mask;
+ DS_OSX->last_button_state &= (MouseButton)~mask;
}
Ref<InputEventMouseButton> mb;
- mb.instance();
+ mb.instantiate();
mb->set_window_id(window_id);
const Vector2 pos = _get_mouse_pos(wd, [event locationInWindow]);
_get_key_modifier_state([event modifierFlags], mb);
@@ -830,10 +687,10 @@ static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, i
mb->set_global_position(pos);
mb->set_button_mask(DS_OSX->last_button_state);
if (index == MOUSE_BUTTON_LEFT && pressed) {
- mb->set_doubleclick([event clickCount] == 2);
+ mb->set_double_click([event clickCount] == 2);
}
- Input::get_singleton()->accumulate_input_event(mb);
+ Input::get_singleton()->parse_input_event(mb);
}
- (void)mouseDown:(NSEvent *)event {
@@ -880,7 +737,7 @@ static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, i
return;
}
- if (DS_OSX->mouse_mode == DisplayServer::MOUSE_MODE_CONFINED) {
+ if (DS_OSX->mouse_mode == DisplayServer::MOUSE_MODE_CONFINED || DS_OSX->mouse_mode == DisplayServer::MOUSE_MODE_CONFINED_HIDDEN) {
// Discard late events
if (([event timestamp]) < DS_OSX->last_warp) {
return;
@@ -924,7 +781,7 @@ static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, i
}
Ref<InputEventMouseMotion> mm;
- mm.instance();
+ mm.instantiate();
mm->set_window_id(window_id);
mm->set_button_mask(DS_OSX->last_button_state);
@@ -942,7 +799,7 @@ static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, i
_get_key_modifier_state([event modifierFlags], mm);
Input::get_singleton()->set_mouse_position(wd.mouse_pos);
- Input::get_singleton()->accumulate_input_event(mm);
+ Input::get_singleton()->parse_input_event(mm);
}
- (void)rightMouseDown:(NSEvent *)event {
@@ -1012,13 +869,13 @@ static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, i
DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
Ref<InputEventMagnifyGesture> ev;
- ev.instance();
+ ev.instantiate();
ev->set_window_id(window_id);
_get_key_modifier_state([event modifierFlags], ev);
ev->set_position(_get_mouse_pos(wd, [event locationInWindow]));
ev->set_factor([event magnification] + 1.0);
- Input::get_singleton()->accumulate_input_event(ev);
+ Input::get_singleton()->parse_input_event(ev);
}
- (void)viewDidChangeBackingProperties {
@@ -1071,9 +928,9 @@ static bool isNumpadKey(unsigned int key) {
// Translates a OS X keycode to a Godot keycode
//
-static int translateKey(unsigned int key) {
+static Key translateKey(unsigned int key) {
// Keyboard symbol translation table
- static const unsigned int table[128] = {
+ static const Key table[128] = {
/* 00 */ KEY_A,
/* 01 */ KEY_S,
/* 02 */ KEY_D,
@@ -1133,10 +990,10 @@ static int translateKey(unsigned int key) {
/* 38 */ KEY_SHIFT,
/* 39 */ KEY_CAPSLOCK,
/* 3a */ KEY_ALT,
- /* 3b */ KEY_CONTROL,
+ /* 3b */ KEY_CTRL,
/* 3c */ KEY_SHIFT,
/* 3d */ KEY_ALT,
- /* 3e */ KEY_CONTROL,
+ /* 3e */ KEY_CTRL,
/* 3f */ KEY_UNKNOWN, /* Function */
/* 40 */ KEY_UNKNOWN, /* F17 */
/* 41 */ KEY_KP_PERIOD,
@@ -1213,7 +1070,7 @@ static int translateKey(unsigned int key) {
struct _KeyCodeMap {
UniChar kchar;
- int kcode;
+ Key kcode;
};
static const _KeyCodeMap _keycodes[55] = {
@@ -1274,7 +1131,7 @@ static const _KeyCodeMap _keycodes[55] = {
{ '/', KEY_SLASH }
};
-static int remapKey(unsigned int key, unsigned int state) {
+static Key remapKey(unsigned int key, unsigned int state) {
if (isNumpadKey(key)) {
return translateKey(key);
}
@@ -1481,14 +1338,14 @@ static int remapKey(unsigned int key, unsigned int state) {
}
}
-inline void sendScrollEvent(DisplayServer::WindowID window_id, int button, double factor, int modifierFlags) {
+inline void sendScrollEvent(DisplayServer::WindowID window_id, MouseButton button, double factor, int modifierFlags) {
ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
- unsigned int mask = 1 << (button - 1);
+ MouseButton mask = MouseButton(1 << (button - 1));
Ref<InputEventMouseButton> sc;
- sc.instance();
+ sc.instantiate();
sc->set_window_id(window_id);
_get_key_modifier_state(modifierFlags, sc);
@@ -1497,22 +1354,22 @@ inline void sendScrollEvent(DisplayServer::WindowID window_id, int button, doubl
sc->set_pressed(true);
sc->set_position(wd.mouse_pos);
sc->set_global_position(wd.mouse_pos);
- DS_OSX->last_button_state |= mask;
+ DS_OSX->last_button_state |= (MouseButton)mask;
sc->set_button_mask(DS_OSX->last_button_state);
- Input::get_singleton()->accumulate_input_event(sc);
+ Input::get_singleton()->parse_input_event(sc);
- sc.instance();
+ sc.instantiate();
sc->set_window_id(window_id);
sc->set_button_index(button);
sc->set_factor(factor);
sc->set_pressed(false);
sc->set_position(wd.mouse_pos);
sc->set_global_position(wd.mouse_pos);
- DS_OSX->last_button_state &= ~mask;
+ DS_OSX->last_button_state &= (MouseButton)~mask;
sc->set_button_mask(DS_OSX->last_button_state);
- Input::get_singleton()->accumulate_input_event(sc);
+ Input::get_singleton()->parse_input_event(sc);
}
inline void sendPanEvent(DisplayServer::WindowID window_id, double dx, double dy, int modifierFlags) {
@@ -1520,14 +1377,14 @@ inline void sendPanEvent(DisplayServer::WindowID window_id, double dx, double dy
DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
Ref<InputEventPanGesture> pg;
- pg.instance();
+ pg.instantiate();
pg->set_window_id(window_id);
_get_key_modifier_state(modifierFlags, pg);
pg->set_position(wd.mouse_pos);
pg->set_delta(Vector2(-dx, -dy));
- Input::get_singleton()->accumulate_input_event(pg);
+ Input::get_singleton()->parse_input_event(pg);
}
- (void)scrollWheel:(NSEvent *)event {
@@ -1640,7 +1497,7 @@ String DisplayServerOSX::get_name() const {
}
const NSMenu *DisplayServerOSX::_get_menu_root(const String &p_menu_root) const {
- const NSMenu *menu = NULL;
+ const NSMenu *menu = nullptr;
if (p_menu_root == "") {
// Main menu.x
menu = [NSApp mainMenu];
@@ -1655,13 +1512,13 @@ const NSMenu *DisplayServerOSX::_get_menu_root(const String &p_menu_root) const
}
if (menu == apple_menu) {
// Do not allow to change Apple menu.
- return NULL;
+ return nullptr;
}
return menu;
}
NSMenu *DisplayServerOSX::_get_menu_root(const String &p_menu_root) {
- NSMenu *menu = NULL;
+ NSMenu *menu = nullptr;
if (p_menu_root == "") {
// Main menu.
menu = [NSApp mainMenu];
@@ -1678,7 +1535,7 @@ NSMenu *DisplayServerOSX::_get_menu_root(const String &p_menu_root) {
}
if (menu == apple_menu) {
// Do not allow to change Apple menu.
- return NULL;
+ return nullptr;
}
return menu;
}
@@ -1979,26 +1836,6 @@ void DisplayServerOSX::global_menu_clear(const String &p_menu_root) {
}
}
-void DisplayServerOSX::alert(const String &p_alert, const String &p_title) {
- _THREAD_SAFE_METHOD_
-
- NSAlert *window = [[NSAlert alloc] init];
- NSString *ns_title = [NSString stringWithUTF8String:p_title.utf8().get_data()];
- NSString *ns_alert = [NSString stringWithUTF8String:p_alert.utf8().get_data()];
-
- [window addButtonWithTitle:@"OK"];
- [window setMessageText:ns_title];
- [window setInformativeText:ns_alert];
- [window setAlertStyle:NSAlertStyleWarning];
-
- id key_window = [[NSApplication sharedApplication] keyWindow];
- [window runModal];
- [window release];
- if (key_window) {
- [key_window makeKeyAndOrderFront:nil];
- }
-}
-
Error DisplayServerOSX::dialog_show(String p_title, String p_description, Vector<String> p_buttons, const Callable &p_callback) {
_THREAD_SAFE_METHOD_
@@ -2102,7 +1939,12 @@ void DisplayServerOSX::mouse_set_mode(MouseMode p_mode) {
} else if (p_mode == MOUSE_MODE_CONFINED) {
CGDisplayShowCursor(kCGDirectMainDisplay);
CGAssociateMouseAndMouseCursorPosition(false);
- } else {
+ } else if (p_mode == MOUSE_MODE_CONFINED_HIDDEN) {
+ if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) {
+ CGDisplayHideCursor(kCGDirectMainDisplay);
+ }
+ CGAssociateMouseAndMouseCursorPosition(false);
+ } else { // MOUSE_MODE_VISIBLE
CGDisplayShowCursor(kCGDirectMainDisplay);
CGAssociateMouseAndMouseCursorPosition(true);
}
@@ -2111,6 +1953,12 @@ void DisplayServerOSX::mouse_set_mode(MouseMode p_mode) {
ignore_warp = true;
warp_events.clear();
mouse_mode = p_mode;
+
+ if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) {
+ CursorShape p_shape = cursor_shape;
+ cursor_shape = DisplayServer::CURSOR_MAX;
+ cursor_set_shape(p_shape);
+ }
}
DisplayServer::MouseMode DisplayServerOSX::mouse_get_mode() const {
@@ -2139,7 +1987,7 @@ void DisplayServerOSX::mouse_warp_to_position(const Point2i &p_to) {
CGEventSourceSetLocalEventsSuppressionInterval(lEventRef, 0.0);
CGAssociateMouseAndMouseCursorPosition(false);
CGWarpMouseCursorPosition(lMouseWarpPos);
- if (mouse_mode != MOUSE_MODE_CONFINED) {
+ if (mouse_mode != MOUSE_MODE_CONFINED && mouse_mode != MOUSE_MODE_CONFINED_HIDDEN) {
CGAssociateMouseAndMouseCursorPosition(true);
}
}
@@ -2164,7 +2012,7 @@ Point2i DisplayServerOSX::mouse_get_absolute_position() const {
return Vector2i();
}
-int DisplayServerOSX::mouse_get_button_state() const {
+MouseButton DisplayServerOSX::mouse_get_button_state() const {
return last_button_state;
}
@@ -2373,10 +2221,10 @@ Vector<DisplayServer::WindowID> DisplayServerOSX::get_window_list() const {
return ret;
}
-DisplayServer::WindowID DisplayServerOSX::create_sub_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect) {
+DisplayServer::WindowID DisplayServerOSX::create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect) {
_THREAD_SAFE_METHOD_
- WindowID id = _create_window(p_mode, p_rect);
+ WindowID id = _create_window(p_mode, p_vsync_mode, p_rect);
for (int i = 0; i < WINDOW_FLAG_MAX; i++) {
if (p_flags & (1 << i)) {
window_set_flag(WindowFlags(i), true, id);
@@ -2437,7 +2285,7 @@ void DisplayServerOSX::_update_window(WindowData p_wd) {
[p_wd.window_object setHidesOnDeactivate:YES];
} else {
// Reset these when our window is not a borderless window that covers up the screen
- if (p_wd.on_top) {
+ if (p_wd.on_top && !p_wd.fullscreen) {
[p_wd.window_object setLevel:NSFloatingWindowLevel];
} else {
[p_wd.window_object setLevel:NSNormalWindowLevel];
@@ -2786,6 +2634,7 @@ void DisplayServerOSX::window_set_mode(WindowMode p_mode, WindowID p_window) {
[wd.window_object deminiaturize:nil];
} break;
case WINDOW_MODE_FULLSCREEN: {
+ [wd.window_object setLevel:NSNormalWindowLevel];
if (wd.layered_window) {
_set_window_per_pixel_transparency_enabled(true, p_window);
}
@@ -2903,6 +2752,9 @@ void DisplayServerOSX::window_set_flag(WindowFlags p_flag, bool p_enabled, Windo
} break;
case WINDOW_FLAG_ALWAYS_ON_TOP: {
wd.on_top = p_enabled;
+ if (wd.fullscreen) {
+ return;
+ }
if (p_enabled) {
[wd.window_object setLevel:NSFloatingWindowLevel];
} else {
@@ -2940,7 +2792,11 @@ bool DisplayServerOSX::window_get_flag(WindowFlags p_flag, WindowID p_window) co
return [wd.window_object styleMask] == NSWindowStyleMaskBorderless;
} break;
case WINDOW_FLAG_ALWAYS_ON_TOP: {
- return [wd.window_object level] == NSFloatingWindowLevel;
+ if (wd.fullscreen) {
+ return wd.on_top;
+ } else {
+ return [wd.window_object level] == NSFloatingWindowLevel;
+ }
} break;
case WINDOW_FLAG_TRANSPARENT: {
return wd.layered_window;
@@ -3029,7 +2885,7 @@ void DisplayServerOSX::cursor_set_shape(CursorShape p_shape) {
return;
}
- if (cursors[p_shape] != NULL) {
+ if (cursors[p_shape] != nullptr) {
[cursors[p_shape] set];
} else {
switch (p_shape) {
@@ -3117,7 +2973,7 @@ void DisplayServerOSX::cursor_set_custom_image(const RES &p_cursor, CursorShape
Rect2 atlas_rect;
if (texture.is_valid()) {
- image = texture->get_data();
+ image = texture->get_image();
}
if (!image.is_valid() && atlas_texture.is_valid()) {
@@ -3140,7 +2996,7 @@ void DisplayServerOSX::cursor_set_custom_image(const RES &p_cursor, CursorShape
ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256);
ERR_FAIL_COND(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height);
- image = texture->get_data();
+ image = texture->get_image();
ERR_FAIL_COND(!image.is_valid());
@@ -3202,9 +3058,9 @@ void DisplayServerOSX::cursor_set_custom_image(const RES &p_cursor, CursorShape
[nsimage release];
} else {
// Reset to default system cursor
- if (cursors[p_shape] != NULL) {
+ if (cursors[p_shape] != nullptr) {
[cursors[p_shape] release];
- cursors[p_shape] = NULL;
+ cursors[p_shape] = nullptr;
}
CursorShape c = cursor_shape;
@@ -3342,7 +3198,7 @@ String DisplayServerOSX::keyboard_get_layout_name(int p_index) const {
void DisplayServerOSX::_push_input(const Ref<InputEvent> &p_event) {
Ref<InputEvent> ev = p_event;
- Input::get_singleton()->accumulate_input_event(ev);
+ Input::get_singleton()->parse_input_event(ev);
}
void DisplayServerOSX::_release_pressed_events() {
@@ -3352,47 +3208,97 @@ void DisplayServerOSX::_release_pressed_events() {
}
}
+NSMenu *DisplayServerOSX::_get_dock_menu() const {
+ return dock_menu;
+}
+
+void DisplayServerOSX::_menu_callback(id p_sender) {
+ if (![p_sender representedObject]) {
+ return;
+ }
+
+ GlobalMenuItem *value = [p_sender representedObject];
+
+ if (value) {
+ if (value->checkable) {
+ if ([p_sender state] == NSControlStateValueOff) {
+ [p_sender setState:NSControlStateValueOn];
+ } else {
+ [p_sender setState:NSControlStateValueOff];
+ }
+ }
+
+ if (value->callback != Callable()) {
+ Variant tag = value->meta;
+ Variant *tagp = &tag;
+ Variant ret;
+ Callable::CallError ce;
+ value->callback.call((const Variant **)&tagp, 1, ret, ce);
+ }
+ }
+}
+
+void DisplayServerOSX::_send_event(NSEvent *p_event) {
+ // special case handling of command-period, which is traditionally a special
+ // shortcut in macOS and doesn't arrive at our regular keyDown handler.
+ if ([p_event type] == NSEventTypeKeyDown) {
+ if (([p_event modifierFlags] & NSEventModifierFlagCommand) && [p_event keyCode] == 0x2f) {
+ Ref<InputEventKey> k;
+ k.instantiate();
+
+ _get_key_modifier_state([p_event modifierFlags], k);
+ k->set_window_id(DisplayServerOSX::INVALID_WINDOW_ID);
+ k->set_pressed(true);
+ k->set_keycode(KEY_PERIOD);
+ k->set_physical_keycode(KEY_PERIOD);
+ k->set_echo([p_event isARepeat]);
+
+ Input::get_singleton()->parse_input_event(k);
+ }
+ }
+}
+
void DisplayServerOSX::_process_key_events() {
Ref<InputEventKey> k;
for (int i = 0; i < key_event_pos; i++) {
const KeyEvent &ke = key_event_buffer[i];
if (ke.raw) {
// Non IME input - no composite characters, pass events as is
- k.instance();
+ k.instantiate();
k->set_window_id(ke.window_id);
_get_key_modifier_state(ke.osx_state, k);
k->set_pressed(ke.pressed);
k->set_echo(ke.echo);
k->set_keycode(ke.keycode);
- k->set_physical_keycode(ke.physical_keycode);
+ k->set_physical_keycode((Key)ke.physical_keycode);
k->set_unicode(ke.unicode);
_push_input(k);
} else {
// IME input
if ((i == 0 && ke.keycode == 0) || (i > 0 && key_event_buffer[i - 1].keycode == 0)) {
- k.instance();
+ k.instantiate();
k->set_window_id(ke.window_id);
_get_key_modifier_state(ke.osx_state, k);
k->set_pressed(ke.pressed);
k->set_echo(ke.echo);
- k->set_keycode(0);
- k->set_physical_keycode(0);
+ k->set_keycode(KEY_NONE);
+ k->set_physical_keycode(KEY_NONE);
k->set_unicode(ke.unicode);
_push_input(k);
}
if (ke.keycode != 0) {
- k.instance();
+ k.instantiate();
k->set_window_id(ke.window_id);
_get_key_modifier_state(ke.osx_state, k);
k->set_pressed(ke.pressed);
k->set_echo(ke.echo);
k->set_keycode(ke.keycode);
- k->set_physical_keycode(ke.physical_keycode);
+ k->set_physical_keycode((Key)ke.physical_keycode);
if (i + 1 < key_event_pos && key_event_buffer[i + 1].keycode == 0) {
k->set_unicode(key_event_buffer[i + 1].unicode);
@@ -3425,7 +3331,7 @@ void DisplayServerOSX::process_events() {
if (!drop_events) {
_process_key_events();
- Input::get_singleton()->flush_accumulated_events();
+ Input::get_singleton()->flush_buffered_events();
}
for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
@@ -3467,7 +3373,7 @@ void DisplayServerOSX::set_native_icon(const String &p_filename) {
ERR_FAIL_COND(!f);
Vector<uint8_t> data;
- uint32_t len = f->get_len();
+ uint64_t len = f->get_length();
data.resize(len);
f->get_buffer((uint8_t *)&data.write[0], len);
memdelete(f);
@@ -3523,6 +3429,22 @@ void DisplayServerOSX::set_icon(const Ref<Image> &p_icon) {
[nsimg release];
}
+void DisplayServerOSX::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+#if defined(VULKAN_ENABLED)
+ context_vulkan->set_vsync_mode(p_window, p_vsync_mode);
+#endif
+}
+
+DisplayServer::VSyncMode DisplayServerOSX::window_get_vsync_mode(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+#if defined(VULKAN_ENABLED)
+ return context_vulkan->get_vsync_mode(p_window);
+#else
+ return DisplayServer::VSYNC_ENABLED;
+#endif
+}
+
Vector<String> DisplayServerOSX::get_rendering_drivers_func() {
Vector<String> drivers;
@@ -3573,15 +3495,15 @@ ObjectID DisplayServerOSX::window_get_attached_instance_id(WindowID p_window) co
return windows[p_window].instance_id;
}
-DisplayServer *DisplayServerOSX::create_func(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
- DisplayServer *ds = memnew(DisplayServerOSX(p_rendering_driver, p_mode, p_flags, p_resolution, r_error));
+DisplayServer *DisplayServerOSX::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
+ DisplayServer *ds = memnew(DisplayServerOSX(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_resolution, r_error));
if (r_error != OK) {
- ds->alert("Your video card driver does not support any of the supported Metal versions.", "Unable to initialize Video driver");
+ OS::get_singleton()->alert("Your video card driver does not support any of the supported Metal versions.", "Unable to initialize Video driver");
}
return ds;
}
-DisplayServerOSX::WindowID DisplayServerOSX::_create_window(WindowMode p_mode, const Rect2i &p_rect) {
+DisplayServerOSX::WindowID DisplayServerOSX::_create_window(WindowMode p_mode, VSyncMode p_vsync_mode, const Rect2i &p_rect) {
WindowID id;
const float scale = screen_get_max_scale();
{
@@ -3628,7 +3550,7 @@ DisplayServerOSX::WindowID DisplayServerOSX::_create_window(WindowMode p_mode, c
#if defined(VULKAN_ENABLED)
if (rendering_driver == "vulkan") {
if (context_vulkan) {
- Error err = context_vulkan->window_create(window_id_counter, wd.window_view, p_rect.size.width, p_rect.size.height);
+ Error err = context_vulkan->window_create(window_id_counter, p_vsync_mode, wd.window_view, p_rect.size.width, p_rect.size.height);
ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Can't create a Vulkan context");
}
}
@@ -3727,7 +3649,7 @@ bool DisplayServerOSX::is_console_visible() const {
return isatty(STDIN_FILENO);
}
-DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
+DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events);
r_error = OK;
@@ -3738,7 +3660,6 @@ DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode
key_event_pos = 0;
mouse_mode = MOUSE_MODE_VISIBLE;
- last_button_state = 0;
autoreleasePool = [[NSAutoreleasePool alloc] init];
@@ -3747,28 +3668,19 @@ DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode
CGEventSourceSetLocalEventsSuppressionInterval(eventSource, 0.0);
- // Implicitly create shared NSApplication instance
- [GodotApplication sharedApplication];
-
- // In case we are unbundled, make us a proper UI application
- [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
-
keyboard_layout_dirty = true;
displays_arrangement_dirty = true;
displays_scale_dirty = true;
// Register to be notified on keyboard layout changes
CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(),
- NULL, keyboard_layout_changed,
- kTISNotifySelectedKeyboardInputSourceChanged, NULL,
+ nullptr, keyboard_layout_changed,
+ kTISNotifySelectedKeyboardInputSourceChanged, nullptr,
CFNotificationSuspensionBehaviorDeliverImmediately);
// Register to be notified on displays arrangement changes
- CGDisplayRegisterReconfigurationCallback(displays_arrangement_changed, NULL);
+ CGDisplayRegisterReconfigurationCallback(displays_arrangement_changed, nullptr);
- // Menu bar setup must go between sharedApplication above and
- // finishLaunching below, in order to properly emulate the behavior
- // of NSApplicationMain
NSMenuItem *menu_item;
NSString *title;
@@ -3808,32 +3720,10 @@ DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode
title = [NSString stringWithFormat:NSLocalizedString(@"Quit %@", nil), nsappname];
[apple_menu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
- // Setup menu bar
- NSMenu *main_menu = [[[NSMenu alloc] initWithTitle:@""] autorelease];
+ // Add items to the menu bar
+ NSMenu *main_menu = [NSApp mainMenu];
menu_item = [main_menu addItemWithTitle:@"" action:nil keyEquivalent:@""];
[main_menu setSubmenu:apple_menu forItem:menu_item];
- [NSApp setMainMenu:main_menu];
-
- [NSApp finishLaunching];
-
- delegate = [[GodotApplicationDelegate alloc] init];
- ERR_FAIL_COND(!delegate);
- [NSApp setDelegate:delegate];
-
- //process application:openFile: event
- while (true) {
- NSEvent *event = [NSApp
- nextEventMatchingMask:NSEventMaskAny
- untilDate:[NSDate distantPast]
- inMode:NSDefaultRunLoopMode
- dequeue:YES];
-
- if (event == nil) {
- break;
- }
-
- [NSApp sendEvent:event];
- }
//!!!!!!!!!!!!!!!!!!!!!!!!!!
//TODO - do Vulkan and GLES2 support checks, driver selection and fallback
@@ -3854,7 +3744,7 @@ DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode
context_vulkan = memnew(VulkanContextOSX);
if (context_vulkan->initialize() != OK) {
memdelete(context_vulkan);
- context_vulkan = NULL;
+ context_vulkan = nullptr;
r_error = ERR_CANT_CREATE;
ERR_FAIL_MSG("Could not initialize Vulkan");
}
@@ -3864,7 +3754,7 @@ DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode
Point2i window_position(
screen_get_position(0).x + (screen_get_size(0).width - p_resolution.width) / 2,
screen_get_position(0).y + (screen_get_size(0).height - p_resolution.height) / 2);
- WindowID main_window = _create_window(p_mode, Rect2i(window_position, p_resolution));
+ WindowID main_window = _create_window(p_mode, p_vsync_mode, Rect2i(window_position, p_resolution));
ERR_FAIL_COND(main_window == INVALID_WINDOW_ID);
for (int i = 0; i < WINDOW_FLAG_MAX; i++) {
if (p_flags & (1 << i)) {
@@ -3886,8 +3776,6 @@ DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode
RendererCompositorRD::make_current();
}
#endif
-
- [NSApp activateIgnoringOtherApps:YES];
}
DisplayServerOSX::~DisplayServerOSX() {
@@ -3926,8 +3814,8 @@ DisplayServerOSX::~DisplayServerOSX() {
}
#endif
- CFNotificationCenterRemoveObserver(CFNotificationCenterGetDistributedCenter(), NULL, kTISNotifySelectedKeyboardInputSourceChanged, NULL);
- CGDisplayRemoveReconfigurationCallback(displays_arrangement_changed, NULL);
+ CFNotificationCenterRemoveObserver(CFNotificationCenterGetDistributedCenter(), nullptr, kTISNotifySelectedKeyboardInputSourceChanged, nullptr);
+ CGDisplayRemoveReconfigurationCallback(displays_arrangement_changed, nullptr);
cursors_cache.clear();
}
diff --git a/platform/osx/export/export.cpp b/platform/osx/export/export.cpp
index 6ac98cae9c..1164d76580 100644
--- a/platform/osx/export/export.cpp
+++ b/platform/osx/export/export.cpp
@@ -30,1081 +30,11 @@
#include "export.h"
-#include "core/config/project_settings.h"
-#include "core/io/marshalls.h"
-#include "core/io/resource_saver.h"
-#include "core/io/zip_io.h"
-#include "core/os/dir_access.h"
-#include "core/os/file_access.h"
-#include "core/os/os.h"
-#include "core/version.h"
-#include "editor/editor_export.h"
-#include "editor/editor_node.h"
-#include "editor/editor_settings.h"
-#include "platform/osx/logo.gen.h"
-
-#include <sys/stat.h>
-
-class EditorExportPlatformOSX : public EditorExportPlatform {
- GDCLASS(EditorExportPlatformOSX, EditorExportPlatform);
-
- int version_code = 0;
-
- Ref<ImageTexture> logo;
-
- void _fix_plist(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &plist, const String &p_binary);
- void _make_icon(const Ref<Image> &p_icon, Vector<uint8_t> &p_data);
-
- Error _notarize(const Ref<EditorExportPreset> &p_preset, const String &p_path);
- Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path);
- Error _create_dmg(const String &p_dmg_path, const String &p_pkg_name, const String &p_app_path_name);
- void _zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name);
-
-#ifdef OSX_ENABLED
- bool use_codesign() const { return true; }
- bool use_dmg() const { return true; }
-#else
- bool use_codesign() const { return false; }
- bool use_dmg() const { return false; }
-#endif
- bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const {
- String pname = p_package;
-
- if (pname.length() == 0) {
- if (r_error) {
- *r_error = TTR("Identifier is missing.");
- }
- return false;
- }
-
- for (int i = 0; i < pname.length(); i++) {
- char32_t c = pname[i];
- if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c == '.')) {
- if (r_error) {
- *r_error = vformat(TTR("The character '%s' is not allowed in Identifier."), String::chr(c));
- }
- return false;
- }
- }
-
- return true;
- }
-
-protected:
- virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) override;
- virtual void get_export_options(List<ExportOption> *r_options) override;
-
-public:
- virtual String get_name() const override { return "macOS"; }
- virtual String get_os_name() const override { return "macOS"; }
- virtual Ref<Texture2D> get_logo() const override { return logo; }
-
- virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override {
- List<String> list;
- if (use_dmg()) {
- list.push_back("dmg");
- }
- list.push_back("zip");
- return list;
- }
- virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override;
-
- virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override;
-
- virtual void get_platform_features(List<String> *r_features) override {
- r_features->push_back("pc");
- r_features->push_back("s3tc");
- r_features->push_back("macOS");
- }
-
- virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) override {
- }
-
- EditorExportPlatformOSX();
- ~EditorExportPlatformOSX();
-};
-
-void EditorExportPlatformOSX::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) {
- if (p_preset->get("texture_format/s3tc")) {
- r_features->push_back("s3tc");
- }
- if (p_preset->get("texture_format/etc")) {
- r_features->push_back("etc");
- }
- if (p_preset->get("texture_format/etc2")) {
- r_features->push_back("etc2");
- }
-
- r_features->push_back("64");
-}
-
-void EditorExportPlatformOSX::get_export_options(List<ExportOption> *r_options) {
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
-
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/info"), "Made with Godot Engine"));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.png,*.icns"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/bundle_identifier", PROPERTY_HINT_PLACEHOLDER_TEXT, "com.example.game"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/signature"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/short_version"), "1.0"));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version"), "1.0"));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "display/high_res"), false));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/camera_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the camera"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/microphone_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the microphone"), ""));
-
-#ifdef OSX_ENABLED
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/enable"), false));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity", PROPERTY_HINT_PLACEHOLDER_TEXT, "Type: Name (ID)"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/timestamp"), true));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/hardened_runtime"), true));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/replace_existing_signature"), true));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/entitlements/custom_file", PROPERTY_HINT_GLOBAL_FILE, "*.plist"), ""));
-
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_jit_code_execution"), false));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_unsigned_executable_memory"), false));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_dyld_environment_variables"), false));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/disable_library_validation"), false));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/audio_input"), false));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/camera"), false));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/location"), false));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/address_book"), false));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/calendars"), false));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/photos_library"), false));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/apple_events"), false));
-
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/enabled"), false));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/network_server"), false));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/network_client"), false));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/device_usb"), false));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/device_bluetooth"), false));
- r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_downloads", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0));
- r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_pictures", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0));
- r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_music", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0));
- r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_movies", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0));
-
- r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "codesign/custom_options"), PackedStringArray()));
-
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "notarization/enable"), false));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_id_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Apple ID email"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_id_password", PROPERTY_HINT_PLACEHOLDER_TEXT, "Enable two-factor authentication and provide app-specific password"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_team_id", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide team ID if your Apple ID belongs to multiple teams"), ""));
-#endif
-
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/s3tc"), true));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc"), false));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc2"), false));
-}
-
-void _rgba8_to_packbits_encode(int p_ch, int p_size, Vector<uint8_t> &p_source, Vector<uint8_t> &p_dest) {
- int src_len = p_size * p_size;
-
- Vector<uint8_t> result;
- result.resize(src_len * 1.25); //temp vector for rle encoded data, make it 25% larger for worst case scenario
- int res_size = 0;
-
- uint8_t buf[128];
- int buf_size = 0;
-
- int i = 0;
- while (i < src_len) {
- uint8_t cur = p_source.ptr()[i * 4 + p_ch];
-
- if (i < src_len - 2) {
- if ((p_source.ptr()[(i + 1) * 4 + p_ch] == cur) && (p_source.ptr()[(i + 2) * 4 + p_ch] == cur)) {
- if (buf_size > 0) {
- result.write[res_size++] = (uint8_t)(buf_size - 1);
- copymem(&result.write[res_size], &buf, buf_size);
- res_size += buf_size;
- buf_size = 0;
- }
-
- uint8_t lim = i + 130 >= src_len ? src_len - i - 1 : 130;
- bool hit_lim = true;
-
- for (int j = 3; j <= lim; j++) {
- if (p_source.ptr()[(i + j) * 4 + p_ch] != cur) {
- hit_lim = false;
- i = i + j - 1;
- result.write[res_size++] = (uint8_t)(j - 3 + 0x80);
- result.write[res_size++] = cur;
- break;
- }
- }
- if (hit_lim) {
- result.write[res_size++] = (uint8_t)(lim - 3 + 0x80);
- result.write[res_size++] = cur;
- i = i + lim;
- }
- } else {
- buf[buf_size++] = cur;
- if (buf_size == 128) {
- result.write[res_size++] = (uint8_t)(buf_size - 1);
- copymem(&result.write[res_size], &buf, buf_size);
- res_size += buf_size;
- buf_size = 0;
- }
- }
- } else {
- buf[buf_size++] = cur;
- result.write[res_size++] = (uint8_t)(buf_size - 1);
- copymem(&result.write[res_size], &buf, buf_size);
- res_size += buf_size;
- buf_size = 0;
- }
-
- i++;
- }
-
- int ofs = p_dest.size();
- p_dest.resize(p_dest.size() + res_size);
- copymem(&p_dest.write[ofs], result.ptr(), res_size);
-}
-
-void EditorExportPlatformOSX::_make_icon(const Ref<Image> &p_icon, Vector<uint8_t> &p_data) {
- Ref<ImageTexture> it = memnew(ImageTexture);
-
- Vector<uint8_t> data;
-
- data.resize(8);
- data.write[0] = 'i';
- data.write[1] = 'c';
- data.write[2] = 'n';
- data.write[3] = 's';
-
- struct MacOSIconInfo {
- const char *name;
- const char *mask_name;
- bool is_png;
- int size;
- };
-
- static const MacOSIconInfo icon_infos[] = {
- { "ic10", "", true, 1024 }, //1024x1024 32-bit PNG and 512x512@2x 32-bit "retina" PNG
- { "ic09", "", true, 512 }, //512×512 32-bit PNG
- { "ic14", "", true, 512 }, //256x256@2x 32-bit "retina" PNG
- { "ic08", "", true, 256 }, //256×256 32-bit PNG
- { "ic13", "", true, 256 }, //128x128@2x 32-bit "retina" PNG
- { "ic07", "", true, 128 }, //128x128 32-bit PNG
- { "ic12", "", true, 64 }, //32x32@2x 32-bit "retina" PNG
- { "ic11", "", true, 32 }, //16x16@2x 32-bit "retina" PNG
- { "il32", "l8mk", false, 32 }, //32x32 24-bit RLE + 8-bit uncompressed mask
- { "is32", "s8mk", false, 16 } //16x16 24-bit RLE + 8-bit uncompressed mask
- };
-
- for (uint64_t i = 0; i < (sizeof(icon_infos) / sizeof(icon_infos[0])); ++i) {
- Ref<Image> copy = p_icon; // does this make sense? doesn't this just increase the reference count instead of making a copy? Do we even need a copy?
- copy->convert(Image::FORMAT_RGBA8);
- copy->resize(icon_infos[i].size, icon_infos[i].size);
-
- if (icon_infos[i].is_png) {
- // Encode PNG icon.
- it->create_from_image(copy);
- String path = EditorSettings::get_singleton()->get_cache_dir().plus_file("icon.png");
- ResourceSaver::save(path, it);
-
- FileAccess *f = FileAccess::open(path, FileAccess::READ);
- if (!f) {
- // Clean up generated file.
- DirAccess::remove_file_or_error(path);
- ERR_FAIL();
- }
-
- int ofs = data.size();
- uint32_t len = f->get_len();
- data.resize(data.size() + len + 8);
- f->get_buffer(&data.write[ofs + 8], len);
- memdelete(f);
- len += 8;
- len = BSWAP32(len);
- copymem(&data.write[ofs], icon_infos[i].name, 4);
- encode_uint32(len, &data.write[ofs + 4]);
-
- // Clean up generated file.
- DirAccess::remove_file_or_error(path);
-
- } else {
- Vector<uint8_t> src_data = copy->get_data();
-
- //encode 24bit RGB RLE icon
- {
- int ofs = data.size();
- data.resize(data.size() + 8);
-
- _rgba8_to_packbits_encode(0, icon_infos[i].size, src_data, data); // encode R
- _rgba8_to_packbits_encode(1, icon_infos[i].size, src_data, data); // encode G
- _rgba8_to_packbits_encode(2, icon_infos[i].size, src_data, data); // encode B
-
- int len = data.size() - ofs;
- len = BSWAP32(len);
- copymem(&data.write[ofs], icon_infos[i].name, 4);
- encode_uint32(len, &data.write[ofs + 4]);
- }
-
- //encode 8bit mask uncompressed icon
- {
- int ofs = data.size();
- int len = copy->get_width() * copy->get_height();
- data.resize(data.size() + len + 8);
-
- for (int j = 0; j < len; j++) {
- data.write[ofs + 8 + j] = src_data.ptr()[j * 4 + 3];
- }
- len += 8;
- len = BSWAP32(len);
- copymem(&data.write[ofs], icon_infos[i].mask_name, 4);
- encode_uint32(len, &data.write[ofs + 4]);
- }
- }
- }
-
- uint32_t total_len = data.size();
- total_len = BSWAP32(total_len);
- encode_uint32(total_len, &data.write[4]);
-
- p_data = data;
-}
-
-void EditorExportPlatformOSX::_fix_plist(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &plist, const String &p_binary) {
- String str;
- String strnew;
- str.parse_utf8((const char *)plist.ptr(), plist.size());
- Vector<String> lines = str.split("\n");
- for (int i = 0; i < lines.size(); i++) {
- if (lines[i].find("$binary") != -1) {
- strnew += lines[i].replace("$binary", p_binary) + "\n";
- } else if (lines[i].find("$name") != -1) {
- strnew += lines[i].replace("$name", p_binary) + "\n";
- } else if (lines[i].find("$info") != -1) {
- strnew += lines[i].replace("$info", p_preset->get("application/info")) + "\n";
- } else if (lines[i].find("$bundle_identifier") != -1) {
- strnew += lines[i].replace("$bundle_identifier", p_preset->get("application/bundle_identifier")) + "\n";
- } else if (lines[i].find("$short_version") != -1) {
- strnew += lines[i].replace("$short_version", p_preset->get("application/short_version")) + "\n";
- } else if (lines[i].find("$version") != -1) {
- strnew += lines[i].replace("$version", p_preset->get("application/version")) + "\n";
- } else if (lines[i].find("$signature") != -1) {
- strnew += lines[i].replace("$signature", p_preset->get("application/signature")) + "\n";
- } else if (lines[i].find("$copyright") != -1) {
- strnew += lines[i].replace("$copyright", p_preset->get("application/copyright")) + "\n";
- } else if (lines[i].find("$highres") != -1) {
- strnew += lines[i].replace("$highres", p_preset->get("display/high_res") ? "<true/>" : "<false/>") + "\n";
- } else if (lines[i].find("$camera_usage_description") != -1) {
- String description = p_preset->get("privacy/camera_usage_description");
- strnew += lines[i].replace("$camera_usage_description", description) + "\n";
- } else if (lines[i].find("$microphone_usage_description") != -1) {
- String description = p_preset->get("privacy/microphone_usage_description");
- strnew += lines[i].replace("$microphone_usage_description", description) + "\n";
- } else {
- strnew += lines[i] + "\n";
- }
- }
-
- CharString cs = strnew.utf8();
- plist.resize(cs.size() - 1);
- for (int i = 0; i < cs.size() - 1; i++) {
- plist.write[i] = cs[i];
- }
-}
-
-/**
- If we're running the OSX version of the Godot editor we'll:
- - export our application bundle to a temporary folder
- - attempt to code sign it
- - and then wrap it up in a DMG
-**/
-
-Error EditorExportPlatformOSX::_notarize(const Ref<EditorExportPreset> &p_preset, const String &p_path) {
-#ifdef OSX_ENABLED
- List<String> args;
-
- args.push_back("altool");
- args.push_back("--notarize-app");
-
- args.push_back("--primary-bundle-id");
- args.push_back(p_preset->get("application/bundle_identifier"));
-
- args.push_back("--username");
- args.push_back(p_preset->get("notarization/apple_id_name"));
-
- args.push_back("--password");
- args.push_back(p_preset->get("notarization/apple_id_password"));
-
- args.push_back("--type");
- args.push_back("osx");
-
- if (p_preset->get("notarization/apple_team_id")) {
- args.push_back("--asc-provider");
- args.push_back(p_preset->get("notarization/apple_team_id"));
- }
-
- args.push_back("--file");
- args.push_back(p_path);
-
- String str;
- Error err = OS::get_singleton()->execute("xcrun", args, &str, nullptr, true);
- ERR_FAIL_COND_V(err != OK, err);
-
- print_line("altool (" + p_path + "):\n" + str);
- if (str.find("RequestUUID") == -1) {
- EditorNode::add_io_error("altool: " + str);
- return FAILED;
- } else {
- print_line("Note: The notarization process generally takes less than an hour. When the process is completed, you'll receive an email.");
- print_line(" You can check progress manually by opening a Terminal and running the following command:");
- print_line(" \"xcrun altool --notarization-history 0 -u <your email> -p <app-specific pwd>\"");
- }
-
-#endif
-
- return OK;
-}
-
-Error EditorExportPlatformOSX::_code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path) {
-#ifdef OSX_ENABLED
- List<String> args;
-
- if (p_preset->get("codesign/timestamp")) {
- args.push_back("--timestamp");
- }
- if (p_preset->get("codesign/hardened_runtime")) {
- args.push_back("--options");
- args.push_back("runtime");
- }
-
- if (p_path.get_extension() != "dmg") {
- args.push_back("--entitlements");
- args.push_back(p_ent_path);
- }
-
- PackedStringArray user_args = p_preset->get("codesign/custom_options");
- for (int i = 0; i < user_args.size(); i++) {
- String user_arg = user_args[i].strip_edges();
- if (!user_arg.is_empty()) {
- args.push_back(user_arg);
- }
- }
-
- args.push_back("-s");
- args.push_back(p_preset->get("codesign/identity"));
-
- args.push_back("-v"); /* provide some more feedback */
-
- if (p_preset->get("codesign/replace_existing_signature")) {
- args.push_back("-f");
- }
-
- args.push_back(p_path);
-
- String str;
- Error err = OS::get_singleton()->execute("codesign", args, &str, nullptr, true);
- ERR_FAIL_COND_V(err != OK, err);
-
- print_line("codesign (" + p_path + "):\n" + str);
- if (str.find("no identity found") != -1) {
- EditorNode::add_io_error("codesign: no identity found");
- return FAILED;
- }
- if ((str.find("unrecognized blob type") != -1) || (str.find("cannot read entitlement data") != -1)) {
- EditorNode::add_io_error("codesign: invalid entitlements file");
- return FAILED;
- }
-#endif
-
- return OK;
-}
-
-Error EditorExportPlatformOSX::_create_dmg(const String &p_dmg_path, const String &p_pkg_name, const String &p_app_path_name) {
- List<String> args;
-
- if (FileAccess::exists(p_dmg_path)) {
- OS::get_singleton()->move_to_trash(p_dmg_path);
- }
-
- args.push_back("create");
- args.push_back(p_dmg_path);
- args.push_back("-volname");
- args.push_back(p_pkg_name);
- args.push_back("-fs");
- args.push_back("HFS+");
- args.push_back("-srcfolder");
- args.push_back(p_app_path_name);
-
- String str;
- Error err = OS::get_singleton()->execute("hdiutil", args, &str, nullptr, true);
- ERR_FAIL_COND_V(err != OK, err);
-
- print_line("hdiutil returned: " + str);
- if (str.find("create failed") != -1) {
- if (str.find("File exists") != -1) {
- EditorNode::add_io_error("hdiutil: create failed - file exists");
- } else {
- EditorNode::add_io_error("hdiutil: create failed");
- }
- return FAILED;
- }
-
- return OK;
-}
-
-Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
- ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
-
- String src_pkg_name;
-
- EditorProgress ep("export", "Exporting for OSX", 3, true);
-
- if (p_debug) {
- src_pkg_name = p_preset->get("custom_template/debug");
- } else {
- src_pkg_name = p_preset->get("custom_template/release");
- }
-
- if (src_pkg_name == "") {
- String err;
- src_pkg_name = find_export_template("osx.zip", &err);
- if (src_pkg_name == "") {
- EditorNode::add_io_error(err);
- return ERR_FILE_NOT_FOUND;
- }
- }
-
- if (!DirAccess::exists(p_path.get_base_dir())) {
- return ERR_FILE_BAD_PATH;
- }
-
- FileAccess *src_f = nullptr;
- zlib_filefunc_def io = zipio_create_io_from_file(&src_f);
-
- if (ep.step("Creating app", 0)) {
- return ERR_SKIP;
- }
-
- unzFile src_pkg_zip = unzOpen2(src_pkg_name.utf8().get_data(), &io);
- if (!src_pkg_zip) {
- EditorNode::add_io_error("Could not find template app to export:\n" + src_pkg_name);
- return ERR_FILE_NOT_FOUND;
- }
-
- int ret = unzGoToFirstFile(src_pkg_zip);
-
- String binary_to_use = "godot_osx_" + String(p_debug ? "debug" : "release") + ".64";
-
- String pkg_name;
- if (p_preset->get("application/name") != "") {
- pkg_name = p_preset->get("application/name"); // app_name
- } else if (String(ProjectSettings::get_singleton()->get("application/config/name")) != "") {
- pkg_name = String(ProjectSettings::get_singleton()->get("application/config/name"));
- } else {
- pkg_name = "Unnamed";
- }
-
- pkg_name = OS::get_singleton()->get_safe_dir_name(pkg_name);
-
- String export_format = use_dmg() && p_path.ends_with("dmg") ? "dmg" : "zip";
-
- // Create our application bundle.
- String tmp_app_dir_name = pkg_name + ".app";
- String tmp_app_path_name = EditorSettings::get_singleton()->get_cache_dir().plus_file(tmp_app_dir_name);
- print_line("Exporting to " + tmp_app_path_name);
-
- Error err = OK;
-
- DirAccessRef tmp_app_dir = DirAccess::create_for_path(tmp_app_path_name);
- if (!tmp_app_dir) {
- err = ERR_CANT_CREATE;
- }
-
- // Create our folder structure.
- if (err == OK) {
- print_line("Creating " + tmp_app_path_name + "/Contents/MacOS");
- err = tmp_app_dir->make_dir_recursive(tmp_app_path_name + "/Contents/MacOS");
- }
-
- if (err == OK) {
- print_line("Creating " + tmp_app_path_name + "/Contents/Frameworks");
- err = tmp_app_dir->make_dir_recursive(tmp_app_path_name + "/Contents/Frameworks");
- }
-
- if (err == OK) {
- print_line("Creating " + tmp_app_path_name + "/Contents/Resources");
- err = tmp_app_dir->make_dir_recursive(tmp_app_path_name + "/Contents/Resources");
- }
-
- // Now process our template.
- bool found_binary = false;
- int total_size = 0;
- Vector<String> dylibs_found;
-
- while (ret == UNZ_OK && err == OK) {
- bool is_execute = false;
-
- // Get filename.
- unz_file_info info;
- char fname[16384];
- ret = unzGetCurrentFileInfo(src_pkg_zip, &info, fname, 16384, nullptr, 0, nullptr, 0);
-
- String file = fname;
-
- Vector<uint8_t> data;
- data.resize(info.uncompressed_size);
-
- // Read.
- unzOpenCurrentFile(src_pkg_zip);
- unzReadCurrentFile(src_pkg_zip, data.ptrw(), data.size());
- unzCloseCurrentFile(src_pkg_zip);
-
- // Write.
- file = file.replace_first("osx_template.app/", "");
-
- if (file == "Contents/Info.plist") {
- _fix_plist(p_preset, data, pkg_name);
- }
-
- if (file.begins_with("Contents/MacOS/godot_")) {
- if (file != "Contents/MacOS/" + binary_to_use) {
- ret = unzGoToNextFile(src_pkg_zip);
- continue; // skip
- }
- found_binary = true;
- is_execute = true;
- file = "Contents/MacOS/" + pkg_name;
- }
-
- if (file == "Contents/Resources/icon.icns") {
- // See if there is an icon.
- String iconpath;
- if (p_preset->get("application/icon") != "") {
- iconpath = p_preset->get("application/icon");
- } else {
- iconpath = ProjectSettings::get_singleton()->get("application/config/icon");
- }
-
- if (iconpath != "") {
- if (iconpath.get_extension() == "icns") {
- FileAccess *icon = FileAccess::open(iconpath, FileAccess::READ);
- if (icon) {
- data.resize(icon->get_len());
- icon->get_buffer(&data.write[0], icon->get_len());
- icon->close();
- memdelete(icon);
- }
- } else {
- Ref<Image> icon;
- icon.instance();
- icon->load(iconpath);
- if (!icon->is_empty()) {
- _make_icon(icon, data);
- }
- }
- }
- }
-
- if (data.size() > 0) {
- if (file.find("/data.mono.osx.64.release_debug/") != -1) {
- if (!p_debug) {
- ret = unzGoToNextFile(src_pkg_zip);
- continue; // skip
- }
- file = file.replace("/data.mono.osx.64.release_debug/", "/GodotSharp/");
- }
- if (file.find("/data.mono.osx.64.release/") != -1) {
- if (p_debug) {
- ret = unzGoToNextFile(src_pkg_zip);
- continue; // skip
- }
- file = file.replace("/data.mono.osx.64.release/", "/GodotSharp/");
- }
-
- if (file.ends_with(".dylib")) {
- dylibs_found.push_back(file);
- }
-
- print_line("ADDING: " + file + " size: " + itos(data.size()));
- total_size += data.size();
-
- // Write it into our application bundle.
- file = tmp_app_path_name.plus_file(file);
- if (err == OK) {
- err = tmp_app_dir->make_dir_recursive(file.get_base_dir());
- }
- if (err == OK) {
- FileAccess *f = FileAccess::open(file, FileAccess::WRITE);
- if (f) {
- f->store_buffer(data.ptr(), data.size());
- f->close();
- if (is_execute) {
- // chmod with 0755 if the file is executable.
- FileAccess::set_unix_permissions(file, 0755);
- }
- memdelete(f);
- } else {
- err = ERR_CANT_CREATE;
- }
- }
- }
-
- ret = unzGoToNextFile(src_pkg_zip);
- }
-
- // We're done with our source zip.
- unzClose(src_pkg_zip);
-
- if (!found_binary) {
- ERR_PRINT("Requested template binary '" + binary_to_use + "' not found. It might be missing from your template archive.");
- err = ERR_FILE_NOT_FOUND;
- }
-
- if (err == OK) {
- if (ep.step("Making PKG", 1)) {
- return ERR_SKIP;
- }
-
- String pack_path = tmp_app_path_name + "/Contents/Resources/" + pkg_name + ".pck";
- Vector<SharedObject> shared_objects;
- err = save_pack(p_preset, pack_path, &shared_objects);
-
- // See if we can code sign our new package.
- bool sign_enabled = p_preset->get("codesign/enable");
-
- String ent_path = p_preset->get("codesign/entitlements/custom_file");
- if (sign_enabled && (ent_path == "")) {
- ent_path = EditorSettings::get_singleton()->get_cache_dir().plus_file(pkg_name + ".entitlements");
-
- FileAccess *ent_f = FileAccess::open(ent_path, FileAccess::WRITE);
- if (ent_f) {
- ent_f->store_line("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
- ent_f->store_line("<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">");
- ent_f->store_line("<plist version=\"1.0\">");
- ent_f->store_line("<dict>");
- if ((bool)p_preset->get("codesign/entitlements/allow_jit_code_execution")) {
- ent_f->store_line("<key>com.apple.security.cs.allow-jit</key>");
- ent_f->store_line("<true/>");
- }
- if ((bool)p_preset->get("codesign/entitlements/allow_unsigned_executable_memory")) {
- ent_f->store_line("<key>com.apple.security.cs.allow-unsigned-executable-memory</key>");
- ent_f->store_line("<true/>");
- }
- if ((bool)p_preset->get("codesign/entitlements/allow_dyld_environment_variables")) {
- ent_f->store_line("<key>com.apple.security.cs.allow-dyld-environment-variables</key>");
- ent_f->store_line("<true/>");
- }
- if ((bool)p_preset->get("codesign/entitlements/disable_library_validation")) {
- ent_f->store_line("<key>com.apple.security.cs.disable-library-validation</key>");
- ent_f->store_line("<true/>");
- }
- if ((bool)p_preset->get("codesign/entitlements/audio_input")) {
- ent_f->store_line("<key>com.apple.security.device.audio-input</key>");
- ent_f->store_line("<true/>");
- }
- if ((bool)p_preset->get("codesign/entitlements/camera")) {
- ent_f->store_line("<key>com.apple.security.device.camera</key>");
- ent_f->store_line("<true/>");
- }
- if ((bool)p_preset->get("codesign/entitlements/location")) {
- ent_f->store_line("<key>com.apple.security.personal-information.location</key>");
- ent_f->store_line("<true/>");
- }
- if ((bool)p_preset->get("codesign/entitlements/address_book")) {
- ent_f->store_line("<key>com.apple.security.personal-information.addressbook</key>");
- ent_f->store_line("<true/>");
- }
- if ((bool)p_preset->get("codesign/entitlements/calendars")) {
- ent_f->store_line("<key>com.apple.security.personal-information.calendars</key>");
- ent_f->store_line("<true/>");
- }
- if ((bool)p_preset->get("codesign/entitlements/photos_library")) {
- ent_f->store_line("<key>com.apple.security.personal-information.photos-library</key>");
- ent_f->store_line("<true/>");
- }
- if ((bool)p_preset->get("codesign/entitlements/apple_events")) {
- ent_f->store_line("<key>com.apple.security.automation.apple-events</key>");
- ent_f->store_line("<true/>");
- }
-
- if ((bool)p_preset->get("codesign/entitlements/app_sandbox/enabled")) {
- ent_f->store_line("<key>com.apple.security.app-sandbox</key>");
- ent_f->store_line("<true/>");
-
- if ((bool)p_preset->get("codesign/entitlements/app_sandbox/network_server")) {
- ent_f->store_line("<key>com.apple.security.network.server</key>");
- ent_f->store_line("<true/>");
- }
- if ((bool)p_preset->get("codesign/entitlements/app_sandbox/network_client")) {
- ent_f->store_line("<key>com.apple.security.network.client</key>");
- ent_f->store_line("<true/>");
- }
- if ((bool)p_preset->get("codesign/entitlements/app_sandbox/device_usb")) {
- ent_f->store_line("<key>com.apple.security.device.usb</key>");
- ent_f->store_line("<true/>");
- }
- if ((bool)p_preset->get("codesign/entitlements/app_sandbox/device_bluetooth")) {
- ent_f->store_line("<key>com.apple.security.device.bluetooth</key>");
- ent_f->store_line("<true/>");
- }
- if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_downloads") == 1) {
- ent_f->store_line("<key>com.apple.security.files.downloads.read-only</key>");
- ent_f->store_line("<true/>");
- }
- if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_downloads") == 2) {
- ent_f->store_line("<key>com.apple.security.files.downloads.read-write</key>");
- ent_f->store_line("<true/>");
- }
- if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_pictures") == 1) {
- ent_f->store_line("<key>com.apple.security.files.pictures.read-only</key>");
- ent_f->store_line("<true/>");
- }
- if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_pictures") == 2) {
- ent_f->store_line("<key>com.apple.security.files.pictures.read-write</key>");
- ent_f->store_line("<true/>");
- }
- if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_music") == 1) {
- ent_f->store_line("<key>com.apple.security.files.music.read-only</key>");
- ent_f->store_line("<true/>");
- }
- if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_music") == 2) {
- ent_f->store_line("<key>com.apple.security.files.music.read-write</key>");
- ent_f->store_line("<true/>");
- }
- if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_movies") == 1) {
- ent_f->store_line("<key>com.apple.security.files.movies.read-only</key>");
- ent_f->store_line("<true/>");
- }
- if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_movies") == 2) {
- ent_f->store_line("<key>com.apple.security.files.movies.read-write</key>");
- ent_f->store_line("<true/>");
- }
- }
-
- ent_f->store_line("</dict>");
- ent_f->store_line("</plist>");
-
- ent_f->close();
- memdelete(ent_f);
- } else {
- err = ERR_CANT_CREATE;
- }
- }
-
- if (err == OK) {
- DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
- for (int i = 0; i < shared_objects.size(); i++) {
- err = da->copy(shared_objects[i].path, tmp_app_path_name + "/Contents/Frameworks/" + shared_objects[i].path.get_file());
- if (err == OK && sign_enabled) {
- err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Frameworks/" + shared_objects[i].path.get_file(), ent_path);
- }
- }
- memdelete(da);
- }
-
- if (sign_enabled) {
- for (int i = 0; i < dylibs_found.size(); i++) {
- if (err == OK) {
- err = _code_sign(p_preset, tmp_app_path_name + "/" + dylibs_found[i], ent_path);
- }
- }
- }
-
- if (err == OK && sign_enabled) {
- if (ep.step("Code signing bundle", 2)) {
- return ERR_SKIP;
- }
- err = _code_sign(p_preset, tmp_app_path_name + "/Contents/MacOS/" + pkg_name, ent_path);
- }
-
- if (export_format == "dmg") {
- // Create a DMG.
- if (err == OK) {
- if (ep.step("Making DMG", 3)) {
- return ERR_SKIP;
- }
- err = _create_dmg(p_path, pkg_name, tmp_app_path_name);
- }
- // Sign DMG.
- if (err == OK && sign_enabled) {
- if (ep.step("Code signing DMG", 3)) {
- return ERR_SKIP;
- }
- err = _code_sign(p_preset, p_path, ent_path);
- }
- } else {
- // Create ZIP.
- if (err == OK) {
- if (ep.step("Making ZIP", 3)) {
- return ERR_SKIP;
- }
- if (FileAccess::exists(p_path)) {
- OS::get_singleton()->move_to_trash(p_path);
- }
-
- FileAccess *dst_f = nullptr;
- zlib_filefunc_def io_dst = zipio_create_io_from_file(&dst_f);
- zipFile zip = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io_dst);
-
- _zip_folder_recursive(zip, EditorSettings::get_singleton()->get_cache_dir(), pkg_name + ".app", pkg_name);
-
- zipClose(zip, nullptr);
- }
- }
-
- bool noto_enabled = p_preset->get("notarization/enable");
- if (err == OK && noto_enabled) {
- if (ep.step("Sending archive for notarization", 4)) {
- return ERR_SKIP;
- }
- err = _notarize(p_preset, p_path);
- }
-
- // Clean up temporary .app dir.
- tmp_app_dir->change_dir(tmp_app_path_name);
- tmp_app_dir->erase_contents_recursive();
- tmp_app_dir->change_dir("..");
- tmp_app_dir->remove(tmp_app_dir_name);
- }
-
- return err;
-}
-
-void EditorExportPlatformOSX::_zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name) {
- String dir = p_root_path.plus_file(p_folder);
-
- DirAccess *da = DirAccess::open(dir);
- da->list_dir_begin();
- String f;
- while ((f = da->get_next()) != "") {
- if (f == "." || f == "..") {
- continue;
- }
- if (da->current_is_dir()) {
- _zip_folder_recursive(p_zip, p_root_path, p_folder.plus_file(f), p_pkg_name);
- } else {
- bool is_executable = (p_folder.ends_with("MacOS") && (f == p_pkg_name));
-
- OS::Time time = OS::get_singleton()->get_time();
- OS::Date date = OS::get_singleton()->get_date();
-
- zip_fileinfo zipfi;
- zipfi.tmz_date.tm_hour = time.hour;
- zipfi.tmz_date.tm_mday = date.day;
- zipfi.tmz_date.tm_min = time.min;
- zipfi.tmz_date.tm_mon = date.month - 1; // Note: "tm" month range - 0..11, Godot month range - 1..12, http://www.cplusplus.com/reference/ctime/tm/
- zipfi.tmz_date.tm_sec = time.sec;
- zipfi.tmz_date.tm_year = date.year;
- zipfi.dosDate = 0;
- // 0100000: regular file type
- // 0000755: permissions rwxr-xr-x
- // 0000644: permissions rw-r--r--
- uint32_t _mode = (is_executable ? 0100755 : 0100644);
- zipfi.external_fa = (_mode << 16L) | !(_mode & 0200);
- zipfi.internal_fa = 0;
-
- zipOpenNewFileInZip4(p_zip,
- p_folder.plus_file(f).utf8().get_data(),
- &zipfi,
- nullptr,
- 0,
- nullptr,
- 0,
- nullptr,
- Z_DEFLATED,
- Z_DEFAULT_COMPRESSION,
- 0,
- -MAX_WBITS,
- DEF_MEM_LEVEL,
- Z_DEFAULT_STRATEGY,
- nullptr,
- 0,
- 0x0314, // "version made by", 0x03 - Unix, 0x14 - ZIP specification version 2.0, required to store Unix file permissions
- 0);
-
- Vector<uint8_t> array = FileAccess::get_file_as_array(dir.plus_file(f));
- zipWriteInFileInZip(p_zip, array.ptr(), array.size());
- zipCloseFileInZip(p_zip);
- }
- }
- da->list_dir_end();
- memdelete(da);
-}
-
-bool EditorExportPlatformOSX::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
- String err;
- bool valid = false;
-
- // Look for export templates (first official, and if defined custom templates).
-
- bool dvalid = exists_export_template("osx.zip", &err);
- bool rvalid = dvalid; // Both in the same ZIP.
-
- if (p_preset->get("custom_template/debug") != "") {
- dvalid = FileAccess::exists(p_preset->get("custom_template/debug"));
- if (!dvalid) {
- err += TTR("Custom debug template not found.") + "\n";
- }
- }
- if (p_preset->get("custom_template/release") != "") {
- rvalid = FileAccess::exists(p_preset->get("custom_template/release"));
- if (!rvalid) {
- err += TTR("Custom release template not found.") + "\n";
- }
- }
-
- valid = dvalid || rvalid;
- r_missing_templates = !valid;
-
- String identifier = p_preset->get("application/bundle_identifier");
- String pn_err;
- if (!is_package_name_valid(identifier, &pn_err)) {
- err += TTR("Invalid bundle identifier:") + " " + pn_err + "\n";
- valid = false;
- }
-
- bool sign_enabled = p_preset->get("codesign/enable");
- if (sign_enabled) {
- if (p_preset->get("codesign/identity") == "") {
- err += TTR("Codesign: identity not specified.") + "\n";
- valid = false;
- }
- }
- bool noto_enabled = p_preset->get("notarization/enable");
- if (noto_enabled) {
- if (!sign_enabled) {
- err += TTR("Notarization: code signing required.") + "\n";
- valid = false;
- }
- bool hr_enabled = p_preset->get("codesign/hardened_runtime");
- if (!hr_enabled) {
- err += TTR("Notarization: hardened runtime required.") + "\n";
- valid = false;
- }
- if (p_preset->get("notarization/apple_id_name") == "") {
- err += TTR("Notarization: Apple ID name not specified.") + "\n";
- valid = false;
- }
- if (p_preset->get("notarization/apple_id_password") == "") {
- err += TTR("Notarization: Apple ID password not specified.") + "\n";
- valid = false;
- }
- }
-
- if (!err.is_empty()) {
- r_error = err;
- }
- return valid;
-}
-
-EditorExportPlatformOSX::EditorExportPlatformOSX() {
- Ref<Image> img = memnew(Image(_osx_logo));
- logo.instance();
- logo->create_from_image(img);
-}
-
-EditorExportPlatformOSX::~EditorExportPlatformOSX() {
-}
+#include "export_plugin.h"
void register_osx_exporter() {
Ref<EditorExportPlatformOSX> platform;
- platform.instance();
+ platform.instantiate();
EditorExport::get_singleton()->add_export_platform(platform);
}
diff --git a/platform/osx/export/export_plugin.cpp b/platform/osx/export/export_plugin.cpp
new file mode 100644
index 0000000000..54a3104482
--- /dev/null
+++ b/platform/osx/export/export_plugin.cpp
@@ -0,0 +1,1085 @@
+/*************************************************************************/
+/* export_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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 "export_plugin.h"
+
+void EditorExportPlatformOSX::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) {
+ if (p_preset->get("texture_format/s3tc")) {
+ r_features->push_back("s3tc");
+ }
+ if (p_preset->get("texture_format/etc")) {
+ r_features->push_back("etc");
+ }
+ if (p_preset->get("texture_format/etc2")) {
+ r_features->push_back("etc2");
+ }
+
+ r_features->push_back("64");
+}
+
+void EditorExportPlatformOSX::get_export_options(List<ExportOption> *r_options) {
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/info"), "Made with Godot Engine"));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.png,*.icns"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/bundle_identifier", PROPERTY_HINT_PLACEHOLDER_TEXT, "com.example.game"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/signature"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/app_category", PROPERTY_HINT_ENUM, "Business,Developer-tools,Education,Entertainment,Finance,Games,Action-games,Adventure-games,Arcade-games,Board-games,Card-games,Casino-games,Dice-games,Educational-games,Family-games,Kids-games,Music-games,Puzzle-games,Racing-games,Role-playing-games,Simulation-games,Sports-games,Strategy-games,Trivia-games,Word-games,Graphics-design,Healthcare-fitness,Lifestyle,Medical,Music,News,Photography,Productivity,Reference,Social-networking,Sports,Travel,Utilities,Video,Weather"), "Games"));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/short_version"), "1.0"));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version"), "1.0"));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "display/high_res"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/camera_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the camera"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/microphone_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the microphone"), ""));
+
+#ifdef OSX_ENABLED
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/enable"), true));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity", PROPERTY_HINT_PLACEHOLDER_TEXT, "Type: Name (ID)"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/timestamp"), true));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/hardened_runtime"), true));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/replace_existing_signature"), true));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/entitlements/custom_file", PROPERTY_HINT_GLOBAL_FILE, "*.plist"), ""));
+
+ if (!Engine::get_singleton()->has_singleton("GodotSharp")) {
+ // These entitlements are required to run managed code, and are always enabled in Mono builds.
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_jit_code_execution"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_unsigned_executable_memory"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_dyld_environment_variables"), false));
+ }
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/disable_library_validation"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/audio_input"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/camera"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/location"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/address_book"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/calendars"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/photos_library"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/apple_events"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/debugging"), false));
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/enabled"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/network_server"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/network_client"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/device_usb"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/device_bluetooth"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_downloads", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_pictures", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_music", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_movies", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0));
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "codesign/custom_options"), PackedStringArray()));
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "notarization/enable"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_id_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Apple ID email"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_id_password", PROPERTY_HINT_PLACEHOLDER_TEXT, "Enable two-factor authentication and provide app-specific password"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_team_id", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide team ID if your Apple ID belongs to multiple teams"), ""));
+#endif
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/s3tc"), true));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc2"), false));
+}
+
+void _rgba8_to_packbits_encode(int p_ch, int p_size, Vector<uint8_t> &p_source, Vector<uint8_t> &p_dest) {
+ int src_len = p_size * p_size;
+
+ Vector<uint8_t> result;
+ result.resize(src_len * 1.25); //temp vector for rle encoded data, make it 25% larger for worst case scenario
+ int res_size = 0;
+
+ uint8_t buf[128];
+ int buf_size = 0;
+
+ int i = 0;
+ while (i < src_len) {
+ uint8_t cur = p_source.ptr()[i * 4 + p_ch];
+
+ if (i < src_len - 2) {
+ if ((p_source.ptr()[(i + 1) * 4 + p_ch] == cur) && (p_source.ptr()[(i + 2) * 4 + p_ch] == cur)) {
+ if (buf_size > 0) {
+ result.write[res_size++] = (uint8_t)(buf_size - 1);
+ memcpy(&result.write[res_size], &buf, buf_size);
+ res_size += buf_size;
+ buf_size = 0;
+ }
+
+ uint8_t lim = i + 130 >= src_len ? src_len - i - 1 : 130;
+ bool hit_lim = true;
+
+ for (int j = 3; j <= lim; j++) {
+ if (p_source.ptr()[(i + j) * 4 + p_ch] != cur) {
+ hit_lim = false;
+ i = i + j - 1;
+ result.write[res_size++] = (uint8_t)(j - 3 + 0x80);
+ result.write[res_size++] = cur;
+ break;
+ }
+ }
+ if (hit_lim) {
+ result.write[res_size++] = (uint8_t)(lim - 3 + 0x80);
+ result.write[res_size++] = cur;
+ i = i + lim;
+ }
+ } else {
+ buf[buf_size++] = cur;
+ if (buf_size == 128) {
+ result.write[res_size++] = (uint8_t)(buf_size - 1);
+ memcpy(&result.write[res_size], &buf, buf_size);
+ res_size += buf_size;
+ buf_size = 0;
+ }
+ }
+ } else {
+ buf[buf_size++] = cur;
+ result.write[res_size++] = (uint8_t)(buf_size - 1);
+ memcpy(&result.write[res_size], &buf, buf_size);
+ res_size += buf_size;
+ buf_size = 0;
+ }
+
+ i++;
+ }
+
+ int ofs = p_dest.size();
+ p_dest.resize(p_dest.size() + res_size);
+ memcpy(&p_dest.write[ofs], result.ptr(), res_size);
+}
+
+void EditorExportPlatformOSX::_make_icon(const Ref<Image> &p_icon, Vector<uint8_t> &p_data) {
+ Ref<ImageTexture> it = memnew(ImageTexture);
+
+ Vector<uint8_t> data;
+
+ data.resize(8);
+ data.write[0] = 'i';
+ data.write[1] = 'c';
+ data.write[2] = 'n';
+ data.write[3] = 's';
+
+ struct MacOSIconInfo {
+ const char *name;
+ const char *mask_name;
+ bool is_png;
+ int size;
+ };
+
+ static const MacOSIconInfo icon_infos[] = {
+ { "ic10", "", true, 1024 }, //1024×1024 32-bit PNG and 512×512@2x 32-bit "retina" PNG
+ { "ic09", "", true, 512 }, //512×512 32-bit PNG
+ { "ic14", "", true, 512 }, //256×256@2x 32-bit "retina" PNG
+ { "ic08", "", true, 256 }, //256×256 32-bit PNG
+ { "ic13", "", true, 256 }, //128×128@2x 32-bit "retina" PNG
+ { "ic07", "", true, 128 }, //128×128 32-bit PNG
+ { "ic12", "", true, 64 }, //32×32@2× 32-bit "retina" PNG
+ { "ic11", "", true, 32 }, //16×16@2× 32-bit "retina" PNG
+ { "il32", "l8mk", false, 32 }, //32×32 24-bit RLE + 8-bit uncompressed mask
+ { "is32", "s8mk", false, 16 } //16×16 24-bit RLE + 8-bit uncompressed mask
+ };
+
+ for (uint64_t i = 0; i < (sizeof(icon_infos) / sizeof(icon_infos[0])); ++i) {
+ Ref<Image> copy = p_icon; // does this make sense? doesn't this just increase the reference count instead of making a copy? Do we even need a copy?
+ copy->convert(Image::FORMAT_RGBA8);
+ copy->resize(icon_infos[i].size, icon_infos[i].size);
+
+ if (icon_infos[i].is_png) {
+ // Encode PNG icon.
+ it->create_from_image(copy);
+ String path = EditorPaths::get_singleton()->get_cache_dir().plus_file("icon.png");
+ ResourceSaver::save(path, it);
+
+ FileAccess *f = FileAccess::open(path, FileAccess::READ);
+ if (!f) {
+ // Clean up generated file.
+ DirAccess::remove_file_or_error(path);
+ ERR_FAIL();
+ }
+
+ int ofs = data.size();
+ uint64_t len = f->get_length();
+ data.resize(data.size() + len + 8);
+ f->get_buffer(&data.write[ofs + 8], len);
+ memdelete(f);
+ len += 8;
+ len = BSWAP32(len);
+ memcpy(&data.write[ofs], icon_infos[i].name, 4);
+ encode_uint32(len, &data.write[ofs + 4]);
+
+ // Clean up generated file.
+ DirAccess::remove_file_or_error(path);
+
+ } else {
+ Vector<uint8_t> src_data = copy->get_data();
+
+ //encode 24bit RGB RLE icon
+ {
+ int ofs = data.size();
+ data.resize(data.size() + 8);
+
+ _rgba8_to_packbits_encode(0, icon_infos[i].size, src_data, data); // encode R
+ _rgba8_to_packbits_encode(1, icon_infos[i].size, src_data, data); // encode G
+ _rgba8_to_packbits_encode(2, icon_infos[i].size, src_data, data); // encode B
+
+ int len = data.size() - ofs;
+ len = BSWAP32(len);
+ memcpy(&data.write[ofs], icon_infos[i].name, 4);
+ encode_uint32(len, &data.write[ofs + 4]);
+ }
+
+ //encode 8bit mask uncompressed icon
+ {
+ int ofs = data.size();
+ int len = copy->get_width() * copy->get_height();
+ data.resize(data.size() + len + 8);
+
+ for (int j = 0; j < len; j++) {
+ data.write[ofs + 8 + j] = src_data.ptr()[j * 4 + 3];
+ }
+ len += 8;
+ len = BSWAP32(len);
+ memcpy(&data.write[ofs], icon_infos[i].mask_name, 4);
+ encode_uint32(len, &data.write[ofs + 4]);
+ }
+ }
+ }
+
+ uint32_t total_len = data.size();
+ total_len = BSWAP32(total_len);
+ encode_uint32(total_len, &data.write[4]);
+
+ p_data = data;
+}
+
+void EditorExportPlatformOSX::_fix_plist(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &plist, const String &p_binary) {
+ String str;
+ String strnew;
+ str.parse_utf8((const char *)plist.ptr(), plist.size());
+ Vector<String> lines = str.split("\n");
+ for (int i = 0; i < lines.size(); i++) {
+ if (lines[i].find("$binary") != -1) {
+ strnew += lines[i].replace("$binary", p_binary) + "\n";
+ } else if (lines[i].find("$name") != -1) {
+ strnew += lines[i].replace("$name", p_binary) + "\n";
+ } else if (lines[i].find("$info") != -1) {
+ strnew += lines[i].replace("$info", p_preset->get("application/info")) + "\n";
+ } else if (lines[i].find("$bundle_identifier") != -1) {
+ strnew += lines[i].replace("$bundle_identifier", p_preset->get("application/bundle_identifier")) + "\n";
+ } else if (lines[i].find("$short_version") != -1) {
+ strnew += lines[i].replace("$short_version", p_preset->get("application/short_version")) + "\n";
+ } else if (lines[i].find("$version") != -1) {
+ strnew += lines[i].replace("$version", p_preset->get("application/version")) + "\n";
+ } else if (lines[i].find("$signature") != -1) {
+ strnew += lines[i].replace("$signature", p_preset->get("application/signature")) + "\n";
+ } else if (lines[i].find("$app_category") != -1) {
+ String cat = p_preset->get("application/app_category");
+ strnew += lines[i].replace("$app_category", cat.to_lower()) + "\n";
+ } else if (lines[i].find("$copyright") != -1) {
+ strnew += lines[i].replace("$copyright", p_preset->get("application/copyright")) + "\n";
+ } else if (lines[i].find("$highres") != -1) {
+ strnew += lines[i].replace("$highres", p_preset->get("display/high_res") ? "<true/>" : "<false/>") + "\n";
+ } else if (lines[i].find("$camera_usage_description") != -1) {
+ String description = p_preset->get("privacy/camera_usage_description");
+ strnew += lines[i].replace("$camera_usage_description", description) + "\n";
+ } else if (lines[i].find("$microphone_usage_description") != -1) {
+ String description = p_preset->get("privacy/microphone_usage_description");
+ strnew += lines[i].replace("$microphone_usage_description", description) + "\n";
+ } else {
+ strnew += lines[i] + "\n";
+ }
+ }
+
+ CharString cs = strnew.utf8();
+ plist.resize(cs.size() - 1);
+ for (int i = 0; i < cs.size() - 1; i++) {
+ plist.write[i] = cs[i];
+ }
+}
+
+/**
+ If we're running the OSX version of the Godot editor we'll:
+ - export our application bundle to a temporary folder
+ - attempt to code sign it
+ - and then wrap it up in a DMG
+**/
+
+Error EditorExportPlatformOSX::_notarize(const Ref<EditorExportPreset> &p_preset, const String &p_path) {
+#ifdef OSX_ENABLED
+ List<String> args;
+
+ args.push_back("altool");
+ args.push_back("--notarize-app");
+
+ args.push_back("--primary-bundle-id");
+ args.push_back(p_preset->get("application/bundle_identifier"));
+
+ args.push_back("--username");
+ args.push_back(p_preset->get("notarization/apple_id_name"));
+
+ args.push_back("--password");
+ args.push_back(p_preset->get("notarization/apple_id_password"));
+
+ args.push_back("--type");
+ args.push_back("osx");
+
+ if (p_preset->get("notarization/apple_team_id")) {
+ args.push_back("--asc-provider");
+ args.push_back(p_preset->get("notarization/apple_team_id"));
+ }
+
+ args.push_back("--file");
+ args.push_back(p_path);
+
+ String str;
+ Error err = OS::get_singleton()->execute("xcrun", args, &str, nullptr, true);
+ ERR_FAIL_COND_V(err != OK, err);
+
+ print_line("altool (" + p_path + "):\n" + str);
+ if (str.find("RequestUUID") == -1) {
+ EditorNode::add_io_error("altool: " + str);
+ return FAILED;
+ } else {
+ print_line("Note: The notarization process generally takes less than an hour. When the process is completed, you'll receive an email.");
+ print_line(" You can check progress manually by opening a Terminal and running the following command:");
+ print_line(" \"xcrun altool --notarization-history 0 -u <your email> -p <app-specific pwd>\"");
+ }
+
+#endif
+
+ return OK;
+}
+
+Error EditorExportPlatformOSX::_code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path) {
+#ifdef OSX_ENABLED
+ List<String> args;
+
+ if (p_preset->get("codesign/timestamp")) {
+ args.push_back("--timestamp");
+ }
+ if (p_preset->get("codesign/hardened_runtime")) {
+ args.push_back("--options");
+ args.push_back("runtime");
+ }
+
+ if (p_path.get_extension() != "dmg") {
+ args.push_back("--entitlements");
+ args.push_back(p_ent_path);
+ }
+
+ PackedStringArray user_args = p_preset->get("codesign/custom_options");
+ for (int i = 0; i < user_args.size(); i++) {
+ String user_arg = user_args[i].strip_edges();
+ if (!user_arg.is_empty()) {
+ args.push_back(user_arg);
+ }
+ }
+
+ args.push_back("-s");
+ if (p_preset->get("codesign/identity") == "") {
+ args.push_back("-");
+ } else {
+ args.push_back(p_preset->get("codesign/identity"));
+ }
+
+ args.push_back("-v"); /* provide some more feedback */
+
+ if (p_preset->get("codesign/replace_existing_signature")) {
+ args.push_back("-f");
+ }
+
+ args.push_back(p_path);
+
+ String str;
+ Error err = OS::get_singleton()->execute("codesign", args, &str, nullptr, true);
+ ERR_FAIL_COND_V(err != OK, err);
+
+ print_line("codesign (" + p_path + "):\n" + str);
+ if (str.find("no identity found") != -1) {
+ EditorNode::add_io_error("codesign: no identity found");
+ return FAILED;
+ }
+ if ((str.find("unrecognized blob type") != -1) || (str.find("cannot read entitlement data") != -1)) {
+ EditorNode::add_io_error("codesign: invalid entitlements file");
+ return FAILED;
+ }
+#endif
+
+ return OK;
+}
+
+Error EditorExportPlatformOSX::_create_dmg(const String &p_dmg_path, const String &p_pkg_name, const String &p_app_path_name) {
+ List<String> args;
+
+ if (FileAccess::exists(p_dmg_path)) {
+ OS::get_singleton()->move_to_trash(p_dmg_path);
+ }
+
+ args.push_back("create");
+ args.push_back(p_dmg_path);
+ args.push_back("-volname");
+ args.push_back(p_pkg_name);
+ args.push_back("-fs");
+ args.push_back("HFS+");
+ args.push_back("-srcfolder");
+ args.push_back(p_app_path_name);
+
+ String str;
+ Error err = OS::get_singleton()->execute("hdiutil", args, &str, nullptr, true);
+ ERR_FAIL_COND_V(err != OK, err);
+
+ print_line("hdiutil returned: " + str);
+ if (str.find("create failed") != -1) {
+ if (str.find("File exists") != -1) {
+ EditorNode::add_io_error("hdiutil: create failed - file exists");
+ } else {
+ EditorNode::add_io_error("hdiutil: create failed");
+ }
+ return FAILED;
+ }
+
+ return OK;
+}
+
+Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
+ ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
+
+ String src_pkg_name;
+
+ EditorProgress ep("export", "Exporting for OSX", 3, true);
+
+ if (p_debug) {
+ src_pkg_name = p_preset->get("custom_template/debug");
+ } else {
+ src_pkg_name = p_preset->get("custom_template/release");
+ }
+
+ if (src_pkg_name == "") {
+ String err;
+ src_pkg_name = find_export_template("osx.zip", &err);
+ if (src_pkg_name == "") {
+ EditorNode::add_io_error(err);
+ return ERR_FILE_NOT_FOUND;
+ }
+ }
+
+ if (!DirAccess::exists(p_path.get_base_dir())) {
+ return ERR_FILE_BAD_PATH;
+ }
+
+ FileAccess *src_f = nullptr;
+ zlib_filefunc_def io = zipio_create_io_from_file(&src_f);
+
+ if (ep.step("Creating app", 0)) {
+ return ERR_SKIP;
+ }
+
+ unzFile src_pkg_zip = unzOpen2(src_pkg_name.utf8().get_data(), &io);
+ if (!src_pkg_zip) {
+ EditorNode::add_io_error("Could not find template app to export:\n" + src_pkg_name);
+ return ERR_FILE_NOT_FOUND;
+ }
+
+ int ret = unzGoToFirstFile(src_pkg_zip);
+
+ String binary_to_use = "godot_osx_" + String(p_debug ? "debug" : "release") + ".64";
+
+ String pkg_name;
+ if (p_preset->get("application/name") != "") {
+ pkg_name = p_preset->get("application/name"); // app_name
+ } else if (String(ProjectSettings::get_singleton()->get("application/config/name")) != "") {
+ pkg_name = String(ProjectSettings::get_singleton()->get("application/config/name"));
+ } else {
+ pkg_name = "Unnamed";
+ }
+
+ pkg_name = OS::get_singleton()->get_safe_dir_name(pkg_name);
+
+ String export_format = use_dmg() && p_path.ends_with("dmg") ? "dmg" : "zip";
+
+ // Create our application bundle.
+ String tmp_app_dir_name = pkg_name + ".app";
+ String tmp_app_path_name = EditorPaths::get_singleton()->get_cache_dir().plus_file(tmp_app_dir_name);
+ print_line("Exporting to " + tmp_app_path_name);
+
+ Error err = OK;
+
+ DirAccessRef tmp_app_dir = DirAccess::create_for_path(tmp_app_path_name);
+ if (!tmp_app_dir) {
+ err = ERR_CANT_CREATE;
+ }
+
+ // Create our folder structure.
+ if (err == OK) {
+ print_line("Creating " + tmp_app_path_name + "/Contents/MacOS");
+ err = tmp_app_dir->make_dir_recursive(tmp_app_path_name + "/Contents/MacOS");
+ }
+
+ if (err == OK) {
+ print_line("Creating " + tmp_app_path_name + "/Contents/Frameworks");
+ err = tmp_app_dir->make_dir_recursive(tmp_app_path_name + "/Contents/Frameworks");
+ }
+
+ if (err == OK) {
+ print_line("Creating " + tmp_app_path_name + "/Contents/Resources");
+ err = tmp_app_dir->make_dir_recursive(tmp_app_path_name + "/Contents/Resources");
+ }
+
+ // Now process our template.
+ bool found_binary = false;
+ int total_size = 0;
+ Vector<String> dylibs_found;
+
+ while (ret == UNZ_OK && err == OK) {
+ bool is_execute = false;
+
+ // Get filename.
+ unz_file_info info;
+ char fname[16384];
+ ret = unzGetCurrentFileInfo(src_pkg_zip, &info, fname, 16384, nullptr, 0, nullptr, 0);
+
+ String file = fname;
+
+ Vector<uint8_t> data;
+ data.resize(info.uncompressed_size);
+
+ // Read.
+ unzOpenCurrentFile(src_pkg_zip);
+ unzReadCurrentFile(src_pkg_zip, data.ptrw(), data.size());
+ unzCloseCurrentFile(src_pkg_zip);
+
+ // Write.
+ file = file.replace_first("osx_template.app/", "");
+
+ if (file == "Contents/Info.plist") {
+ _fix_plist(p_preset, data, pkg_name);
+ }
+
+ if (file.begins_with("Contents/MacOS/godot_")) {
+ if (file != "Contents/MacOS/" + binary_to_use) {
+ ret = unzGoToNextFile(src_pkg_zip);
+ continue; // skip
+ }
+ found_binary = true;
+ is_execute = true;
+ file = "Contents/MacOS/" + pkg_name;
+ }
+
+ if (file == "Contents/Resources/icon.icns") {
+ // See if there is an icon.
+ String iconpath;
+ if (p_preset->get("application/icon") != "") {
+ iconpath = p_preset->get("application/icon");
+ } else {
+ iconpath = ProjectSettings::get_singleton()->get("application/config/icon");
+ }
+
+ if (iconpath != "") {
+ if (iconpath.get_extension() == "icns") {
+ FileAccess *icon = FileAccess::open(iconpath, FileAccess::READ);
+ if (icon) {
+ data.resize(icon->get_length());
+ icon->get_buffer(&data.write[0], icon->get_length());
+ icon->close();
+ memdelete(icon);
+ }
+ } else {
+ Ref<Image> icon;
+ icon.instantiate();
+ icon->load(iconpath);
+ if (!icon->is_empty()) {
+ _make_icon(icon, data);
+ }
+ }
+ }
+ }
+
+ if (data.size() > 0) {
+ if (file.find("/data.mono.osx.64.release_debug/") != -1) {
+ if (!p_debug) {
+ ret = unzGoToNextFile(src_pkg_zip);
+ continue; // skip
+ }
+ file = file.replace("/data.mono.osx.64.release_debug/", "/GodotSharp/");
+ }
+ if (file.find("/data.mono.osx.64.release/") != -1) {
+ if (p_debug) {
+ ret = unzGoToNextFile(src_pkg_zip);
+ continue; // skip
+ }
+ file = file.replace("/data.mono.osx.64.release/", "/GodotSharp/");
+ }
+
+ if (file.ends_with(".dylib")) {
+ dylibs_found.push_back(file);
+ }
+
+ print_line("ADDING: " + file + " size: " + itos(data.size()));
+ total_size += data.size();
+
+ // Write it into our application bundle.
+ file = tmp_app_path_name.plus_file(file);
+ if (err == OK) {
+ err = tmp_app_dir->make_dir_recursive(file.get_base_dir());
+ }
+ if (err == OK) {
+ FileAccess *f = FileAccess::open(file, FileAccess::WRITE);
+ if (f) {
+ f->store_buffer(data.ptr(), data.size());
+ f->close();
+ if (is_execute) {
+ // chmod with 0755 if the file is executable.
+ FileAccess::set_unix_permissions(file, 0755);
+ }
+ memdelete(f);
+ } else {
+ err = ERR_CANT_CREATE;
+ }
+ }
+ }
+
+ ret = unzGoToNextFile(src_pkg_zip);
+ }
+
+ // We're done with our source zip.
+ unzClose(src_pkg_zip);
+
+ if (!found_binary) {
+ ERR_PRINT("Requested template binary '" + binary_to_use + "' not found. It might be missing from your template archive.");
+ err = ERR_FILE_NOT_FOUND;
+ }
+
+ if (err == OK) {
+ if (ep.step("Making PKG", 1)) {
+ return ERR_SKIP;
+ }
+
+ String pack_path = tmp_app_path_name + "/Contents/Resources/" + pkg_name + ".pck";
+ Vector<SharedObject> shared_objects;
+ err = save_pack(p_preset, pack_path, &shared_objects);
+
+ // See if we can code sign our new package.
+ bool sign_enabled = p_preset->get("codesign/enable");
+
+ String ent_path = p_preset->get("codesign/entitlements/custom_file");
+ if (sign_enabled && (ent_path == "")) {
+ ent_path = EditorPaths::get_singleton()->get_cache_dir().plus_file(pkg_name + ".entitlements");
+
+ FileAccess *ent_f = FileAccess::open(ent_path, FileAccess::WRITE);
+ if (ent_f) {
+ ent_f->store_line("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+ ent_f->store_line("<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">");
+ ent_f->store_line("<plist version=\"1.0\">");
+ ent_f->store_line("<dict>");
+ if (Engine::get_singleton()->has_singleton("GodotSharp")) {
+ // These entitlements are required to run managed code, and are always enabled in Mono builds.
+ ent_f->store_line("<key>com.apple.security.cs.allow-jit</key>");
+ ent_f->store_line("<true/>");
+ ent_f->store_line("<key>com.apple.security.cs.allow-unsigned-executable-memory</key>");
+ ent_f->store_line("<true/>");
+ ent_f->store_line("<key>com.apple.security.cs.allow-dyld-environment-variables</key>");
+ ent_f->store_line("<true/>");
+ } else {
+ if ((bool)p_preset->get("codesign/entitlements/allow_jit_code_execution")) {
+ ent_f->store_line("<key>com.apple.security.cs.allow-jit</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((bool)p_preset->get("codesign/entitlements/allow_unsigned_executable_memory")) {
+ ent_f->store_line("<key>com.apple.security.cs.allow-unsigned-executable-memory</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((bool)p_preset->get("codesign/entitlements/allow_dyld_environment_variables")) {
+ ent_f->store_line("<key>com.apple.security.cs.allow-dyld-environment-variables</key>");
+ ent_f->store_line("<true/>");
+ }
+ }
+
+ if ((bool)p_preset->get("codesign/entitlements/disable_library_validation")) {
+ ent_f->store_line("<key>com.apple.security.cs.disable-library-validation</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((bool)p_preset->get("codesign/entitlements/audio_input")) {
+ ent_f->store_line("<key>com.apple.security.device.audio-input</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((bool)p_preset->get("codesign/entitlements/camera")) {
+ ent_f->store_line("<key>com.apple.security.device.camera</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((bool)p_preset->get("codesign/entitlements/location")) {
+ ent_f->store_line("<key>com.apple.security.personal-information.location</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((bool)p_preset->get("codesign/entitlements/address_book")) {
+ ent_f->store_line("<key>com.apple.security.personal-information.addressbook</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((bool)p_preset->get("codesign/entitlements/calendars")) {
+ ent_f->store_line("<key>com.apple.security.personal-information.calendars</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((bool)p_preset->get("codesign/entitlements/photos_library")) {
+ ent_f->store_line("<key>com.apple.security.personal-information.photos-library</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((bool)p_preset->get("codesign/entitlements/apple_events")) {
+ ent_f->store_line("<key>com.apple.security.automation.apple-events</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((bool)p_preset->get("codesign/entitlements/debugging")) {
+ ent_f->store_line("<key>com.apple.security.get-task-allow</key>");
+ ent_f->store_line("<true/>");
+ }
+
+ if ((bool)p_preset->get("codesign/entitlements/app_sandbox/enabled")) {
+ ent_f->store_line("<key>com.apple.security.app-sandbox</key>");
+ ent_f->store_line("<true/>");
+
+ if ((bool)p_preset->get("codesign/entitlements/app_sandbox/network_server")) {
+ ent_f->store_line("<key>com.apple.security.network.server</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((bool)p_preset->get("codesign/entitlements/app_sandbox/network_client")) {
+ ent_f->store_line("<key>com.apple.security.network.client</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((bool)p_preset->get("codesign/entitlements/app_sandbox/device_usb")) {
+ ent_f->store_line("<key>com.apple.security.device.usb</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((bool)p_preset->get("codesign/entitlements/app_sandbox/device_bluetooth")) {
+ ent_f->store_line("<key>com.apple.security.device.bluetooth</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_downloads") == 1) {
+ ent_f->store_line("<key>com.apple.security.files.downloads.read-only</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_downloads") == 2) {
+ ent_f->store_line("<key>com.apple.security.files.downloads.read-write</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_pictures") == 1) {
+ ent_f->store_line("<key>com.apple.security.files.pictures.read-only</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_pictures") == 2) {
+ ent_f->store_line("<key>com.apple.security.files.pictures.read-write</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_music") == 1) {
+ ent_f->store_line("<key>com.apple.security.files.music.read-only</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_music") == 2) {
+ ent_f->store_line("<key>com.apple.security.files.music.read-write</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_movies") == 1) {
+ ent_f->store_line("<key>com.apple.security.files.movies.read-only</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_movies") == 2) {
+ ent_f->store_line("<key>com.apple.security.files.movies.read-write</key>");
+ ent_f->store_line("<true/>");
+ }
+ }
+
+ ent_f->store_line("</dict>");
+ ent_f->store_line("</plist>");
+
+ ent_f->close();
+ memdelete(ent_f);
+ } else {
+ err = ERR_CANT_CREATE;
+ }
+ }
+
+ if (err == OK) {
+ DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ for (int i = 0; i < shared_objects.size(); i++) {
+ String src_path = ProjectSettings::get_singleton()->globalize_path(shared_objects[i].path);
+ if (da->dir_exists(src_path)) {
+#ifndef UNIX_ENABLED
+ WARN_PRINT("Relative symlinks are not supported, exported " + src_path.get_file() + " might be broken!");
+#endif
+ print_verbose("export framework: " + src_path + " -> " + tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file());
+ err = da->make_dir_recursive(tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file());
+ if (err == OK) {
+ err = da->copy_dir(src_path, tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file(), -1, true);
+ }
+ } else {
+ print_verbose("export dylib: " + src_path + " -> " + tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file());
+ err = da->copy(src_path, tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file());
+ }
+ if (err == OK && sign_enabled) {
+ err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file(), ent_path);
+ }
+ }
+ memdelete(da);
+ }
+
+ if (sign_enabled) {
+ for (int i = 0; i < dylibs_found.size(); i++) {
+ if (err == OK) {
+ err = _code_sign(p_preset, tmp_app_path_name + "/" + dylibs_found[i], ent_path);
+ }
+ }
+ }
+
+ if (err == OK && sign_enabled) {
+ if (ep.step("Code signing bundle", 2)) {
+ return ERR_SKIP;
+ }
+ err = _code_sign(p_preset, tmp_app_path_name + "/Contents/MacOS/" + pkg_name, ent_path);
+ }
+
+ if (export_format == "dmg") {
+ // Create a DMG.
+ if (err == OK) {
+ if (ep.step("Making DMG", 3)) {
+ return ERR_SKIP;
+ }
+ err = _create_dmg(p_path, pkg_name, tmp_app_path_name);
+ }
+ // Sign DMG.
+ if (err == OK && sign_enabled) {
+ if (ep.step("Code signing DMG", 3)) {
+ return ERR_SKIP;
+ }
+ err = _code_sign(p_preset, p_path, ent_path);
+ }
+ } else {
+ // Create ZIP.
+ if (err == OK) {
+ if (ep.step("Making ZIP", 3)) {
+ return ERR_SKIP;
+ }
+ if (FileAccess::exists(p_path)) {
+ OS::get_singleton()->move_to_trash(p_path);
+ }
+
+ FileAccess *dst_f = nullptr;
+ zlib_filefunc_def io_dst = zipio_create_io_from_file(&dst_f);
+ zipFile zip = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io_dst);
+
+ _zip_folder_recursive(zip, EditorPaths::get_singleton()->get_cache_dir(), pkg_name + ".app", pkg_name);
+
+ zipClose(zip, nullptr);
+ }
+ }
+
+ bool noto_enabled = p_preset->get("notarization/enable");
+ if (err == OK && noto_enabled) {
+ if (ep.step("Sending archive for notarization", 4)) {
+ return ERR_SKIP;
+ }
+ err = _notarize(p_preset, p_path);
+ }
+
+ // Clean up temporary .app dir.
+ tmp_app_dir->change_dir(tmp_app_path_name);
+ tmp_app_dir->erase_contents_recursive();
+ tmp_app_dir->change_dir("..");
+ tmp_app_dir->remove(tmp_app_dir_name);
+ }
+
+ return err;
+}
+
+void EditorExportPlatformOSX::_zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name) {
+ String dir = p_root_path.plus_file(p_folder);
+
+ DirAccess *da = DirAccess::open(dir);
+ da->list_dir_begin();
+ String f;
+ while ((f = da->get_next()) != "") {
+ if (f == "." || f == "..") {
+ continue;
+ }
+ if (da->is_link(f)) {
+ OS::Time time = OS::get_singleton()->get_time();
+ OS::Date date = OS::get_singleton()->get_date();
+
+ zip_fileinfo zipfi;
+ zipfi.tmz_date.tm_hour = time.hour;
+ zipfi.tmz_date.tm_mday = date.day;
+ zipfi.tmz_date.tm_min = time.minute;
+ zipfi.tmz_date.tm_mon = date.month - 1; // Note: "tm" month range - 0..11, Godot month range - 1..12, https://www.cplusplus.com/reference/ctime/tm/
+ zipfi.tmz_date.tm_sec = time.second;
+ zipfi.tmz_date.tm_year = date.year;
+ zipfi.dosDate = 0;
+ // 0120000: symbolic link type
+ // 0000755: permissions rwxr-xr-x
+ // 0000644: permissions rw-r--r--
+ uint32_t _mode = 0120644;
+ zipfi.external_fa = (_mode << 16L) | !(_mode & 0200);
+ zipfi.internal_fa = 0;
+
+ zipOpenNewFileInZip4(p_zip,
+ p_folder.plus_file(f).utf8().get_data(),
+ &zipfi,
+ nullptr,
+ 0,
+ nullptr,
+ 0,
+ nullptr,
+ Z_DEFLATED,
+ Z_DEFAULT_COMPRESSION,
+ 0,
+ -MAX_WBITS,
+ DEF_MEM_LEVEL,
+ Z_DEFAULT_STRATEGY,
+ nullptr,
+ 0,
+ 0x0314, // "version made by", 0x03 - Unix, 0x14 - ZIP specification version 2.0, required to store Unix file permissions
+ 0);
+
+ String target = da->read_link(f);
+ zipWriteInFileInZip(p_zip, target.utf8().get_data(), target.utf8().size());
+ zipCloseFileInZip(p_zip);
+ } else if (da->current_is_dir()) {
+ _zip_folder_recursive(p_zip, p_root_path, p_folder.plus_file(f), p_pkg_name);
+ } else {
+ bool is_executable = (p_folder.ends_with("MacOS") && (f == p_pkg_name));
+
+ OS::Time time = OS::get_singleton()->get_time();
+ OS::Date date = OS::get_singleton()->get_date();
+
+ zip_fileinfo zipfi;
+ zipfi.tmz_date.tm_hour = time.hour;
+ zipfi.tmz_date.tm_mday = date.day;
+ zipfi.tmz_date.tm_min = time.minute;
+ zipfi.tmz_date.tm_mon = date.month - 1; // Note: "tm" month range - 0..11, Godot month range - 1..12, https://www.cplusplus.com/reference/ctime/tm/
+ zipfi.tmz_date.tm_sec = time.second;
+ zipfi.tmz_date.tm_year = date.year;
+ zipfi.dosDate = 0;
+ // 0100000: regular file type
+ // 0000755: permissions rwxr-xr-x
+ // 0000644: permissions rw-r--r--
+ uint32_t _mode = (is_executable ? 0100755 : 0100644);
+ zipfi.external_fa = (_mode << 16L) | !(_mode & 0200);
+ zipfi.internal_fa = 0;
+
+ zipOpenNewFileInZip4(p_zip,
+ p_folder.plus_file(f).utf8().get_data(),
+ &zipfi,
+ nullptr,
+ 0,
+ nullptr,
+ 0,
+ nullptr,
+ Z_DEFLATED,
+ Z_DEFAULT_COMPRESSION,
+ 0,
+ -MAX_WBITS,
+ DEF_MEM_LEVEL,
+ Z_DEFAULT_STRATEGY,
+ nullptr,
+ 0,
+ 0x0314, // "version made by", 0x03 - Unix, 0x14 - ZIP specification version 2.0, required to store Unix file permissions
+ 0);
+
+ Vector<uint8_t> array = FileAccess::get_file_as_array(dir.plus_file(f));
+ zipWriteInFileInZip(p_zip, array.ptr(), array.size());
+ zipCloseFileInZip(p_zip);
+ }
+ }
+ da->list_dir_end();
+ memdelete(da);
+}
+
+bool EditorExportPlatformOSX::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
+ String err;
+ bool valid = false;
+
+ // Look for export templates (first official, and if defined custom templates).
+
+ bool dvalid = exists_export_template("osx.zip", &err);
+ bool rvalid = dvalid; // Both in the same ZIP.
+
+ if (p_preset->get("custom_template/debug") != "") {
+ dvalid = FileAccess::exists(p_preset->get("custom_template/debug"));
+ if (!dvalid) {
+ err += TTR("Custom debug template not found.") + "\n";
+ }
+ }
+ if (p_preset->get("custom_template/release") != "") {
+ rvalid = FileAccess::exists(p_preset->get("custom_template/release"));
+ if (!rvalid) {
+ err += TTR("Custom release template not found.") + "\n";
+ }
+ }
+
+ valid = dvalid || rvalid;
+ r_missing_templates = !valid;
+
+ String identifier = p_preset->get("application/bundle_identifier");
+ String pn_err;
+ if (!is_package_name_valid(identifier, &pn_err)) {
+ err += TTR("Invalid bundle identifier:") + " " + pn_err + "\n";
+ valid = false;
+ }
+
+ bool sign_enabled = p_preset->get("codesign/enable");
+ bool noto_enabled = p_preset->get("notarization/enable");
+ if (noto_enabled) {
+ if (!sign_enabled) {
+ err += TTR("Notarization: code signing required.") + "\n";
+ valid = false;
+ }
+ bool hr_enabled = p_preset->get("codesign/hardened_runtime");
+ if (!hr_enabled) {
+ err += TTR("Notarization: hardened runtime required.") + "\n";
+ valid = false;
+ }
+ if (p_preset->get("notarization/apple_id_name") == "") {
+ err += TTR("Notarization: Apple ID name not specified.") + "\n";
+ valid = false;
+ }
+ if (p_preset->get("notarization/apple_id_password") == "") {
+ err += TTR("Notarization: Apple ID password not specified.") + "\n";
+ valid = false;
+ }
+ }
+
+ if (!err.is_empty()) {
+ r_error = err;
+ }
+ return valid;
+}
+
+EditorExportPlatformOSX::EditorExportPlatformOSX() {
+ Ref<Image> img = memnew(Image(_osx_logo));
+ logo.instantiate();
+ logo->create_from_image(img);
+}
+
+EditorExportPlatformOSX::~EditorExportPlatformOSX() {
+}
diff --git a/platform/osx/export/export_plugin.h b/platform/osx/export/export_plugin.h
new file mode 100644
index 0000000000..cd85ce2aad
--- /dev/null
+++ b/platform/osx/export/export_plugin.h
@@ -0,0 +1,128 @@
+/*************************************************************************/
+/* export_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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 OSX_EXPORT_PLUGIN_H
+#define OSX_EXPORT_PLUGIN_H
+
+#include "core/config/project_settings.h"
+#include "core/io/dir_access.h"
+#include "core/io/file_access.h"
+#include "core/io/marshalls.h"
+#include "core/io/resource_saver.h"
+#include "core/io/zip_io.h"
+#include "core/os/os.h"
+#include "core/version.h"
+#include "editor/editor_export.h"
+#include "editor/editor_node.h"
+#include "editor/editor_settings.h"
+#include "platform/osx/logo.gen.h"
+
+#include <sys/stat.h>
+
+class EditorExportPlatformOSX : public EditorExportPlatform {
+ GDCLASS(EditorExportPlatformOSX, EditorExportPlatform);
+
+ int version_code = 0;
+
+ Ref<ImageTexture> logo;
+
+ void _fix_plist(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &plist, const String &p_binary);
+ void _make_icon(const Ref<Image> &p_icon, Vector<uint8_t> &p_data);
+
+ Error _notarize(const Ref<EditorExportPreset> &p_preset, const String &p_path);
+ Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path);
+ Error _create_dmg(const String &p_dmg_path, const String &p_pkg_name, const String &p_app_path_name);
+ void _zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name);
+
+#ifdef OSX_ENABLED
+ bool use_codesign() const { return true; }
+ bool use_dmg() const { return true; }
+#else
+ bool use_codesign() const { return false; }
+ bool use_dmg() const { return false; }
+#endif
+ bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const {
+ String pname = p_package;
+
+ if (pname.length() == 0) {
+ if (r_error) {
+ *r_error = TTR("Identifier is missing.");
+ }
+ return false;
+ }
+
+ for (int i = 0; i < pname.length(); i++) {
+ char32_t c = pname[i];
+ if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c == '.')) {
+ if (r_error) {
+ *r_error = vformat(TTR("The character '%s' is not allowed in Identifier."), String::chr(c));
+ }
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+protected:
+ virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) override;
+ virtual void get_export_options(List<ExportOption> *r_options) override;
+
+public:
+ virtual String get_name() const override { return "macOS"; }
+ virtual String get_os_name() const override { return "macOS"; }
+ virtual Ref<Texture2D> get_logo() const override { return logo; }
+
+ virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override {
+ List<String> list;
+ if (use_dmg()) {
+ list.push_back("dmg");
+ }
+ list.push_back("zip");
+ return list;
+ }
+ virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override;
+
+ virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override;
+
+ virtual void get_platform_features(List<String> *r_features) override {
+ r_features->push_back("pc");
+ r_features->push_back("s3tc");
+ r_features->push_back("macOS");
+ }
+
+ virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) override {
+ }
+
+ EditorExportPlatformOSX();
+ ~EditorExportPlatformOSX();
+};
+
+#endif
diff --git a/platform/osx/joypad_osx.cpp b/platform/osx/joypad_osx.cpp
index b12526915f..e67d2b0e91 100644
--- a/platform/osx/joypad_osx.cpp
+++ b/platform/osx/joypad_osx.cpp
@@ -143,6 +143,8 @@ void joypad::add_hid_element(IOHIDElementRef p_element) {
switch (usage) {
case kHIDUsage_Sim_Rudder:
case kHIDUsage_Sim_Throttle:
+ case kHIDUsage_Sim_Accelerator:
+ case kHIDUsage_Sim_Brake:
if (!has_element(cookie, &axis_elements)) {
list = &axis_elements;
}
@@ -286,8 +288,9 @@ bool JoypadOSX::configure_joypad(IOHIDDeviceRef p_device_ref, joypad *p_joy) {
}
if ((!refCF) || (!CFStringGetCString((CFStringRef)refCF, c_name, sizeof(c_name), kCFStringEncodingUTF8))) {
name = "Unidentified Joypad";
+ } else {
+ name = c_name;
}
- name = c_name;
int id = input->get_unused_joy_id();
ERR_FAIL_COND_V(id == -1, false);
@@ -331,6 +334,13 @@ bool JoypadOSX::configure_joypad(IOHIDDeviceRef p_device_ref, joypad *p_joy) {
p_joy->add_hid_elements(array);
CFRelease(array);
}
+ // Xbox controller hat values start at 1 rather than 0.
+ p_joy->offset_hat = vendor == 0x45e &&
+ (product_id == 0x0b05 ||
+ product_id == 0x02e0 ||
+ product_id == 0x02fd ||
+ product_id == 0x0b13);
+
return true;
}
@@ -387,41 +397,44 @@ bool joypad::check_ff_features() {
return false;
}
-static int process_hat_value(int p_min, int p_max, int p_value) {
+static int process_hat_value(int p_min, int p_max, int p_value, bool p_offset_hat) {
int range = (p_max - p_min + 1);
int value = p_value - p_min;
- int hat_value = Input::HAT_MASK_CENTER;
+ int hat_value = HatMask::HAT_MASK_CENTER;
if (range == 4) {
value *= 2;
}
+ if (p_offset_hat) {
+ value -= 1;
+ }
switch (value) {
case 0:
- hat_value = Input::HAT_MASK_UP;
+ hat_value = (HatMask)HatMask::HAT_MASK_UP;
break;
case 1:
- hat_value = Input::HAT_MASK_UP | Input::HAT_MASK_RIGHT;
+ hat_value = (HatMask)(HatMask::HAT_MASK_UP | HatMask::HAT_MASK_RIGHT);
break;
case 2:
- hat_value = Input::HAT_MASK_RIGHT;
+ hat_value = (HatMask)HatMask::HAT_MASK_RIGHT;
break;
case 3:
- hat_value = Input::HAT_MASK_DOWN | Input::HAT_MASK_RIGHT;
+ hat_value = (HatMask)(HatMask::HAT_MASK_DOWN | HatMask::HAT_MASK_RIGHT);
break;
case 4:
- hat_value = Input::HAT_MASK_DOWN;
+ hat_value = (HatMask)HatMask::HAT_MASK_DOWN;
break;
case 5:
- hat_value = Input::HAT_MASK_DOWN | Input::HAT_MASK_LEFT;
+ hat_value = (HatMask)(HatMask::HAT_MASK_DOWN | HatMask::HAT_MASK_LEFT);
break;
case 6:
- hat_value = Input::HAT_MASK_LEFT;
+ hat_value = (HatMask)HatMask::HAT_MASK_LEFT;
break;
case 7:
- hat_value = Input::HAT_MASK_UP | Input::HAT_MASK_LEFT;
+ hat_value = (HatMask)(HatMask::HAT_MASK_UP | HatMask::HAT_MASK_LEFT);
break;
default:
- hat_value = Input::HAT_MASK_CENTER;
+ hat_value = (HatMask)HatMask::HAT_MASK_CENTER;
break;
}
return hat_value;
@@ -458,17 +471,17 @@ void JoypadOSX::process_joypads() {
for (int j = 0; j < joy.axis_elements.size(); j++) {
rec_element &elem = joy.axis_elements.write[j];
int value = joy.get_hid_element_state(&elem);
- input->joy_axis(joy.id, j, axis_correct(value, elem.min, elem.max));
+ input->joy_axis(joy.id, (JoyAxis)j, axis_correct(value, elem.min, elem.max));
}
for (int j = 0; j < joy.button_elements.size(); j++) {
int value = joy.get_hid_element_state(&joy.button_elements.write[j]);
- input->joy_button(joy.id, j, (value >= 1));
+ input->joy_button(joy.id, (JoyButton)j, (value >= 1));
}
for (int j = 0; j < joy.hat_elements.size(); j++) {
rec_element &elem = joy.hat_elements.write[j];
int value = joy.get_hid_element_state(&elem);
- int hat_value = process_hat_value(elem.min, elem.max, value);
- input->joy_hat(joy.id, hat_value);
+ int hat_value = process_hat_value(elem.min, elem.max, value, joy.offset_hat);
+ input->joy_hat(joy.id, (HatMask)hat_value);
}
if (joy.ffservice) {
diff --git a/platform/osx/joypad_osx.h b/platform/osx/joypad_osx.h
index bf7e8949df..c060c3d523 100644
--- a/platform/osx/joypad_osx.h
+++ b/platform/osx/joypad_osx.h
@@ -64,6 +64,7 @@ struct joypad {
Vector<rec_element> hat_elements;
int id = 0;
+ bool offset_hat = false;
io_service_t ffservice = 0; /* Interface for force feedback, 0 = no ff */
FFCONSTANTFORCE ff_constant_force;
diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h
index d57940775d..df41ccd892 100644
--- a/platform/osx/os_osx.h
+++ b/platform/osx/os_osx.h
@@ -72,6 +72,8 @@ protected:
public:
virtual String get_name() const override;
+ virtual void alert(const String &p_alert, const String &p_title = "ALERT!") override;
+
virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false) override;
virtual MainLoop *get_main_loop() const override;
@@ -82,7 +84,7 @@ public:
virtual String get_bundle_resource_dir() const override;
virtual String get_godot_dir_name() const override;
- virtual String get_system_dir(SystemDir p_dir) const override;
+ virtual String get_system_dir(SystemDir p_dir, bool p_shared_storage = true) const override;
Error shell_open(String p_uri) override;
diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm
index 7b5daf5bfb..c6e35fee83 100644
--- a/platform/osx/os_osx.mm
+++ b/platform/osx/os_osx.mm
@@ -41,6 +41,137 @@
#include <mach-o/dyld.h>
#include <os/log.h>
+#define DS_OSX ((DisplayServerOSX *)(DisplayServerOSX::get_singleton()))
+
+/*************************************************************************/
+/* GodotApplication */
+/*************************************************************************/
+
+@interface GodotApplication : NSApplication
+@end
+
+@implementation GodotApplication
+
+- (void)sendEvent:(NSEvent *)event {
+ if (DS_OSX) {
+ DS_OSX->_send_event(event);
+ }
+
+ // From http://cocoadev.com/index.pl?GameKeyboardHandlingAlmost
+ // This works around an AppKit bug, where key up events while holding
+ // down the command key don't get sent to the key window.
+ if ([event type] == NSEventTypeKeyUp && ([event modifierFlags] & NSEventModifierFlagCommand)) {
+ [[self keyWindow] sendEvent:event];
+ } else {
+ [super sendEvent:event];
+ }
+}
+
+@end
+
+/*************************************************************************/
+/* GodotApplicationDelegate */
+/*************************************************************************/
+
+@interface GodotApplicationDelegate : NSObject
+- (void)forceUnbundledWindowActivationHackStep1;
+- (void)forceUnbundledWindowActivationHackStep2;
+- (void)forceUnbundledWindowActivationHackStep3;
+@end
+
+@implementation GodotApplicationDelegate
+
+- (void)forceUnbundledWindowActivationHackStep1 {
+ // Step1: Switch focus to macOS Dock.
+ // Required to perform step 2, TransformProcessType will fail if app is already the in focus.
+ for (NSRunningApplication *app in [NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.apple.dock"]) {
+ [app activateWithOptions:NSApplicationActivateIgnoringOtherApps];
+ break;
+ }
+ [self performSelector:@selector(forceUnbundledWindowActivationHackStep2)
+ withObject:nil
+ afterDelay:0.02];
+}
+
+- (void)forceUnbundledWindowActivationHackStep2 {
+ // Step 2: Register app as foreground process.
+ ProcessSerialNumber psn = { 0, kCurrentProcess };
+ (void)TransformProcessType(&psn, kProcessTransformToForegroundApplication);
+ [self performSelector:@selector(forceUnbundledWindowActivationHackStep3) withObject:nil afterDelay:0.02];
+}
+
+- (void)forceUnbundledWindowActivationHackStep3 {
+ // Step 3: Switch focus back to app window.
+ [[NSRunningApplication currentApplication] activateWithOptions:NSApplicationActivateIgnoringOtherApps];
+}
+
+- (void)applicationDidFinishLaunching:(NSNotification *)notice {
+ NSString *nsappname = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];
+ if (nsappname == nil) {
+ // If executable is not a bundled, macOS WindowServer won't register and activate app window correctly (menu and title bar are grayed out and input ignored).
+ [self performSelector:@selector(forceUnbundledWindowActivationHackStep1) withObject:nil afterDelay:0.02];
+ }
+}
+
+- (void)applicationDidResignActive:(NSNotification *)notification {
+ if (OS::get_singleton()->get_main_loop()) {
+ OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT);
+ }
+}
+
+- (void)applicationDidBecomeActive:(NSNotification *)notification {
+ if (OS::get_singleton()->get_main_loop()) {
+ OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_IN);
+ }
+}
+
+- (void)globalMenuCallback:(id)sender {
+ if (DS_OSX) {
+ return DS_OSX->_menu_callback(sender);
+ }
+}
+
+- (NSMenu *)applicationDockMenu:(NSApplication *)sender {
+ if (DS_OSX) {
+ return DS_OSX->_get_dock_menu();
+ } else {
+ return nullptr;
+ }
+}
+
+- (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename {
+ // Note: may be called called before main loop init!
+ char *utfs = strdup([filename UTF8String]);
+ ((OS_OSX *)OS_OSX::get_singleton())->open_with_filename.parse_utf8(utfs);
+ free(utfs);
+
+#ifdef TOOLS_ENABLED
+ // Open new instance
+ if (OS_OSX::get_singleton()->get_main_loop()) {
+ List<String> args;
+ args.push_back(((OS_OSX *)OS_OSX::get_singleton())->open_with_filename);
+ String exec = OS_OSX::get_singleton()->get_executable_path();
+ OS_OSX::get_singleton()->create_process(exec, args);
+ }
+#endif
+ return YES;
+}
+
+- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
+ if (DS_OSX) {
+ DS_OSX->_send_window_event(DS_OSX->windows[DisplayServerOSX::MAIN_WINDOW_ID], DisplayServerOSX::WINDOW_EVENT_CLOSE_REQUEST);
+ }
+ return NSTerminateCancel;
+}
+
+- (void)showAbout:(id)sender {
+ if (OS_OSX::get_singleton()->get_main_loop()) {
+ OS_OSX::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_ABOUT);
+ }
+}
+
+@end
+
/*************************************************************************/
/* OSXTerminalLogger */
/*************************************************************************/
@@ -101,7 +232,7 @@ String OS_OSX::get_unique_id() const {
if (serial_number.is_empty()) {
io_service_t platformExpert = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice"));
- CFStringRef serialNumberAsCFString = NULL;
+ CFStringRef serialNumberAsCFString = nullptr;
if (platformExpert) {
serialNumberAsCFString = (CFStringRef)IORegistryEntryCreateCFProperty(platformExpert, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0);
IOObjectRelease(platformExpert);
@@ -119,6 +250,24 @@ String OS_OSX::get_unique_id() const {
return serial_number;
}
+void OS_OSX::alert(const String &p_alert, const String &p_title) {
+ NSAlert *window = [[NSAlert alloc] init];
+ NSString *ns_title = [NSString stringWithUTF8String:p_title.utf8().get_data()];
+ NSString *ns_alert = [NSString stringWithUTF8String:p_alert.utf8().get_data()];
+
+ [window addButtonWithTitle:@"OK"];
+ [window setMessageText:ns_title];
+ [window setInformativeText:ns_alert];
+ [window setAlertStyle:NSAlertStyleWarning];
+
+ id key_window = [[NSApplication sharedApplication] keyWindow];
+ [window runModal];
+ [window release];
+ if (key_window) {
+ [key_window makeKeyAndOrderFront:nil];
+ }
+}
+
void OS_OSX::initialize_core() {
OS_Unix::initialize_core();
@@ -158,7 +307,7 @@ void OS_OSX::delete_main_loop() {
if (!main_loop)
return;
memdelete(main_loop);
- main_loop = NULL;
+ main_loop = nullptr;
}
String OS_OSX::get_name() const {
@@ -188,31 +337,45 @@ MainLoop *OS_OSX::get_main_loop() const {
}
String OS_OSX::get_config_path() const {
+ // The XDG Base Directory specification technically only applies on Linux/*BSD, but it doesn't hurt to support it on macOS as well.
if (has_environment("XDG_CONFIG_HOME")) {
- return get_environment("XDG_CONFIG_HOME");
- } else if (has_environment("HOME")) {
+ if (get_environment("XDG_CONFIG_HOME").is_absolute_path()) {
+ return get_environment("XDG_CONFIG_HOME");
+ } else {
+ WARN_PRINT_ONCE("`XDG_CONFIG_HOME` is a relative path. Ignoring its value and falling back to `$HOME/Library/Application Support` or `.` per the XDG Base Directory specification.");
+ }
+ }
+ if (has_environment("HOME")) {
return get_environment("HOME").plus_file("Library/Application Support");
- } else {
- return ".";
}
+ return ".";
}
String OS_OSX::get_data_path() const {
+ // The XDG Base Directory specification technically only applies on Linux/*BSD, but it doesn't hurt to support it on macOS as well.
if (has_environment("XDG_DATA_HOME")) {
- return get_environment("XDG_DATA_HOME");
- } else {
- return get_config_path();
+ if (get_environment("XDG_DATA_HOME").is_absolute_path()) {
+ return get_environment("XDG_DATA_HOME");
+ } else {
+ WARN_PRINT_ONCE("`XDG_DATA_HOME` is a relative path. Ignoring its value and falling back to `get_config_path()` per the XDG Base Directory specification.");
+ }
}
+ return get_config_path();
}
String OS_OSX::get_cache_path() const {
+ // The XDG Base Directory specification technically only applies on Linux/*BSD, but it doesn't hurt to support it on macOS as well.
if (has_environment("XDG_CACHE_HOME")) {
- return get_environment("XDG_CACHE_HOME");
- } else if (has_environment("HOME")) {
+ if (get_environment("XDG_CACHE_HOME").is_absolute_path()) {
+ return get_environment("XDG_CACHE_HOME");
+ } else {
+ WARN_PRINT_ONCE("`XDG_CACHE_HOME` is a relative path. Ignoring its value and falling back to `$HOME/Library/Caches` or `get_config_path()` per the XDG Base Directory specification.");
+ }
+ }
+ if (has_environment("HOME")) {
return get_environment("HOME").plus_file("Library/Caches");
- } else {
- return get_config_path();
}
+ return get_config_path();
}
String OS_OSX::get_bundle_resource_dir() const {
@@ -232,7 +395,7 @@ String OS_OSX::get_godot_dir_name() const {
return String(VERSION_SHORT_NAME).capitalize();
}
-String OS_OSX::get_system_dir(SystemDir p_dir) const {
+String OS_OSX::get_system_dir(SystemDir p_dir, bool p_shared_storage) const {
NSSearchPathDirectory id;
bool found = true;
@@ -346,7 +509,7 @@ Error OS_OSX::move_to_trash(const String &p_path) {
}
OS_OSX::OS_OSX() {
- main_loop = NULL;
+ main_loop = nullptr;
force_quit = false;
Vector<Logger *> loggers;
@@ -358,6 +521,41 @@ OS_OSX::OS_OSX() {
#endif
DisplayServerOSX::register_osx_driver();
+
+ // Implicitly create shared NSApplication instance
+ [GodotApplication sharedApplication];
+
+ // In case we are unbundled, make us a proper UI application
+ [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
+
+ // Menu bar setup must go between sharedApplication above and
+ // finishLaunching below, in order to properly emulate the behavior
+ // of NSApplicationMain
+
+ NSMenu *main_menu = [[[NSMenu alloc] initWithTitle:@""] autorelease];
+ [NSApp setMainMenu:main_menu];
+ [NSApp finishLaunching];
+
+ id delegate = [[GodotApplicationDelegate alloc] init];
+ ERR_FAIL_COND(!delegate);
+ [NSApp setDelegate:delegate];
+
+ //process application:openFile: event
+ while (true) {
+ NSEvent *event = [NSApp
+ nextEventMatchingMask:NSEventMaskAny
+ untilDate:[NSDate distantPast]
+ inMode:NSDefaultRunLoopMode
+ dequeue:YES];
+
+ if (event == nil) {
+ break;
+ }
+
+ [NSApp sendEvent:event];
+ }
+
+ [NSApp activateIgnoringOtherApps:YES];
}
bool OS_OSX::_check_internal_feature_support(const String &p_feature) {
diff --git a/platform/osx/vulkan_context_osx.h b/platform/osx/vulkan_context_osx.h
index 8b6a75adfb..22d43688a3 100644
--- a/platform/osx/vulkan_context_osx.h
+++ b/platform/osx/vulkan_context_osx.h
@@ -38,7 +38,7 @@ class VulkanContextOSX : public VulkanContext {
virtual const char *_get_platform_surface_extension() const;
public:
- Error window_create(DisplayServer::WindowID p_window_id, id p_window, int p_width, int p_height);
+ Error window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, id p_window, int p_width, int p_height);
VulkanContextOSX();
~VulkanContextOSX();
diff --git a/platform/osx/vulkan_context_osx.mm b/platform/osx/vulkan_context_osx.mm
index 75a4fc990f..36c02c2497 100644
--- a/platform/osx/vulkan_context_osx.mm
+++ b/platform/osx/vulkan_context_osx.mm
@@ -29,23 +29,27 @@
/*************************************************************************/
#include "vulkan_context_osx.h"
-#include <vulkan/vulkan_macos.h>
+#ifdef USE_VOLK
+#include <volk.h>
+#else
+#include <vulkan/vulkan.h>
+#endif
const char *VulkanContextOSX::_get_platform_surface_extension() const {
return VK_MVK_MACOS_SURFACE_EXTENSION_NAME;
}
-Error VulkanContextOSX::window_create(DisplayServer::WindowID p_window_id, id p_window, int p_width, int p_height) {
+Error VulkanContextOSX::window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, id p_window, int p_width, int p_height) {
VkMacOSSurfaceCreateInfoMVK createInfo;
createInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
- createInfo.pNext = NULL;
+ createInfo.pNext = nullptr;
createInfo.flags = 0;
createInfo.pView = p_window;
VkSurfaceKHR surface;
- VkResult err = vkCreateMacOSSurfaceMVK(_get_instance(), &createInfo, NULL, &surface);
+ VkResult err = vkCreateMacOSSurfaceMVK(get_instance(), &createInfo, nullptr, &surface);
ERR_FAIL_COND_V(err, ERR_CANT_CREATE);
- return _window_create(p_window_id, surface, p_width, p_height);
+ return _window_create(p_window_id, p_vsync_mode, surface, p_width, p_height);
}
VulkanContextOSX::VulkanContextOSX() {