summaryrefslogtreecommitdiff
path: root/platform/osx/os_osx.mm
diff options
context:
space:
mode:
Diffstat (limited to 'platform/osx/os_osx.mm')
-rw-r--r--platform/osx/os_osx.mm123
1 files changed, 112 insertions, 11 deletions
diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm
index c6e35fee83..39608bdea8 100644
--- a/platform/osx/os_osx.mm
+++ b/platform/osx/os_osx.mm
@@ -178,7 +178,7 @@
class OSXTerminalLogger : public StdLogger {
public:
- virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type = ERR_ERROR) {
+ virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify = false, ErrorType p_type = ERR_ERROR) {
if (!should_log(true)) {
return;
}
@@ -314,17 +314,27 @@ String OS_OSX::get_name() const {
return "macOS";
}
+_FORCE_INLINE_ String _get_framework_executable(const String p_path) {
+ // Append framework executable name, or return as is if p_path is not a framework.
+ DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ if (da->dir_exists(p_path) && da->file_exists(p_path.plus_file(p_path.get_file().get_basename()))) {
+ return p_path.plus_file(p_path.get_file().get_basename());
+ } else {
+ return p_path;
+ }
+}
+
Error OS_OSX::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path) {
- String path = p_path;
+ String path = _get_framework_executable(p_path);
if (!FileAccess::exists(path)) {
- //this code exists so gdnative can load .dylib files from within the executable path
- path = get_executable_path().get_base_dir().plus_file(p_path.get_file());
+ // This code exists so gdnative can load .dylib files from within the executable path.
+ path = _get_framework_executable(get_executable_path().get_base_dir().plus_file(p_path.get_file()));
}
if (!FileAccess::exists(path)) {
- //this code exists so gdnative can load .dylib files from a standard macOS location
- path = get_executable_path().get_base_dir().plus_file("../Frameworks").plus_file(p_path.get_file());
+ // This code exists so gdnative can load .dylib files from a standard macOS location.
+ path = _get_framework_executable(get_executable_path().get_base_dir().plus_file("../Frameworks").plus_file(p_path.get_file()));
}
p_library_handle = dlopen(path.utf8().get_data(), RTLD_NOW);
@@ -379,14 +389,26 @@ String OS_OSX::get_cache_path() const {
}
String OS_OSX::get_bundle_resource_dir() const {
+ String ret;
+
NSBundle *main = [NSBundle mainBundle];
- NSString *resourcePath = [main resourcePath];
+ if (main) {
+ NSString *resourcePath = [main resourcePath];
+ ret.parse_utf8([resourcePath UTF8String]);
+ }
+ return ret;
+}
- char *utfs = strdup([resourcePath UTF8String]);
+String OS_OSX::get_bundle_icon_path() const {
String ret;
- ret.parse_utf8(utfs);
- free(utfs);
+ NSBundle *main = [NSBundle mainBundle];
+ if (main) {
+ NSString *iconPath = [[main infoDictionary] objectForKey:@"CFBundleIconFile"];
+ if (iconPath) {
+ ret.parse_utf8([iconPath UTF8String]);
+ }
+ }
return ret;
}
@@ -469,14 +491,89 @@ String OS_OSX::get_executable_path() const {
}
}
+Error OS_OSX::create_instance(const List<String> &p_arguments, ProcessID *r_child_id) {
+ // If executable is bundled, always execute editor instances as an app bundle to ensure app window is registered and activated correctly.
+ NSString *nsappname = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];
+ if (nsappname != nil) {
+ String path;
+ path.parse_utf8([[[NSBundle mainBundle] bundlePath] UTF8String]);
+ return create_process(path, p_arguments, r_child_id);
+ } else {
+ return create_process(get_executable_path(), p_arguments, r_child_id);
+ }
+}
+
+Error OS_OSX::create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id) {
+ if (@available(macOS 10.15, *)) {
+ // Use NSWorkspace if path is an .app bundle.
+ NSURL *url = [NSURL fileURLWithPath:@(p_path.utf8().get_data())];
+ NSBundle *bundle = [NSBundle bundleWithURL:url];
+ if (bundle) {
+ NSMutableArray *arguments = [[NSMutableArray alloc] init];
+ for (const List<String>::Element *E = p_arguments.front(); E; E = E->next()) {
+ [arguments addObject:[NSString stringWithUTF8String:E->get().utf8().get_data()]];
+ }
+ NSWorkspaceOpenConfiguration *configuration = [[NSWorkspaceOpenConfiguration alloc] init];
+ [configuration setArguments:arguments];
+ [configuration setCreatesNewApplicationInstance:YES];
+ __block dispatch_semaphore_t lock = dispatch_semaphore_create(0);
+ __block Error err = ERR_TIMEOUT;
+ __block pid_t pid = 0;
+ [[NSWorkspace sharedWorkspace] openApplicationAtURL:url
+ configuration:configuration
+ completionHandler:^(NSRunningApplication *app, NSError *error) {
+ if (error) {
+ err = ERR_CANT_FORK;
+ NSLog(@"Failed to execute: %@", error.localizedDescription);
+ } else {
+ pid = [app processIdentifier];
+ err = OK;
+ }
+ dispatch_semaphore_signal(lock);
+ }];
+ dispatch_semaphore_wait(lock, dispatch_time(DISPATCH_TIME_NOW, 20000000000)); // 20 sec timeout, wait for app to launch.
+ dispatch_release(lock);
+
+ if (err == OK) {
+ if (r_child_id) {
+ *r_child_id = (ProcessID)pid;
+ }
+ }
+
+ return err;
+ } else {
+ return OS_Unix::create_process(p_path, p_arguments, r_child_id);
+ }
+ } else {
+ return OS_Unix::create_process(p_path, p_arguments, r_child_id);
+ }
+}
+
+void OS_OSX::pre_wait_observer_cb(CFRunLoopObserverRef p_observer, CFRunLoopActivity p_activiy, void *p_context) {
+ // Prevent main loop from sleeping and redraw window during resize / modal popups.
+
+ if (get_singleton()->get_main_loop()) {
+ Main::force_redraw();
+ if (!Main::is_iterating()) { // Avoid cyclic loop.
+ Main::iteration();
+ }
+ }
+
+ CFRunLoopWakeUp(CFRunLoopGetCurrent()); // Prevent main loop from sleeping.
+}
+
void OS_OSX::run() {
force_quit = false;
- if (!main_loop)
+ if (!main_loop) {
return;
+ }
main_loop->initialize();
+ CFRunLoopObserverRef pre_wait_observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopBeforeWaiting, true, 0, &pre_wait_observer_cb, nullptr);
+ CFRunLoopAddObserver(CFRunLoopGetCurrent(), pre_wait_observer, kCFRunLoopCommonModes);
+
bool quit = false;
while (!force_quit && !quit) {
@try {
@@ -492,6 +589,10 @@ void OS_OSX::run() {
ERR_PRINT("NSException: " + String([exception reason].UTF8String));
}
};
+
+ CFRunLoopRemoveObserver(CFRunLoopGetCurrent(), pre_wait_observer, kCFRunLoopCommonModes);
+ CFRelease(pre_wait_observer);
+
main_loop->finalize();
}