diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h index e340d56d8f9..fd317b6df60 100644 --- a/platform/osx/os_osx.h +++ b/platform/osx/os_osx.h @@ -249,6 +249,7 @@ public: virtual void get_fullscreen_mode_list(List *p_list, int p_screen = 0) const; virtual String get_executable_path() const; + virtual Error execute(const String &p_path, const List &p_arguments, bool p_blocking = true, ProcessID *r_child_id = nullptr, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr); virtual LatinKeyboardVariant get_latin_keyboard_variant() const; virtual int keyboard_get_layout_count() const; diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm index 6b90e77b7de..6517549283a 100644 --- a/platform/osx/os_osx.mm +++ b/platform/osx/os_osx.mm @@ -2955,6 +2955,60 @@ String OS_OSX::get_executable_path() const { } } +Error OS_OSX::execute(const String &p_path, const List &p_arguments, bool p_blocking, ProcessID *r_child_id, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex) { + if (@available(macOS 10.15, *)) { + NSString *nsappname = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"]; + // If executable is bundled, always execute editor instances using NSWorkspace to ensure app window is registered and activated correctly. + if (p_path == get_executable_path() && nsappname != nil) { + if (p_blocking && r_pipe) { + ERR_FAIL_V_MSG(ERR_CANT_OPEN, "Cannot pipe stream from process running as app bundle."); + } + NSMutableArray *arguments = [[NSMutableArray alloc] init]; + for (const List::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:[[NSBundle mainBundle] bundleURL] + 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 (p_blocking) { + int status; + waitpid(pid, &status, 0); + if (r_exitcode) { + *r_exitcode = WIFEXITED(status) ? WEXITSTATUS(status) : status; + } + } else if (r_child_id) { + *r_child_id = (ProcessID)pid; + } + } + + return err; + } else { + return OS_Unix::execute(p_path, p_arguments, p_blocking, r_child_id, r_pipe, r_exitcode, read_stderr, p_pipe_mutex); + } + } else { + return OS_Unix::execute(p_path, p_arguments, p_blocking, r_child_id, r_pipe, r_exitcode, read_stderr, p_pipe_mutex); + } +} + // Returns string representation of keys, if they are printable. // static NSString *createStringForKeys(const CGKeyCode *keyCode, int length) {