Skip to content

Commit bb9d6d0

Browse files
committed
Merge pull request godotengine#107113 from mihe/macos-open-in-program
Add `OS::open_with_program` for opening files/directories with a specific program on macOS
2 parents 1dc3988 + f610c81 commit bb9d6d0

File tree

7 files changed

+83
-3
lines changed

7 files changed

+83
-3
lines changed

core/core_bind.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,14 @@ int OS::create_instance(const Vector<String> &p_arguments) {
437437
return pid;
438438
}
439439

440+
Error OS::open_with_program(const String &p_program_path, const Vector<String> &p_paths) {
441+
List<String> paths;
442+
for (const String &path : p_paths) {
443+
paths.push_back(path);
444+
}
445+
return ::OS::get_singleton()->open_with_program(p_program_path, paths);
446+
}
447+
440448
int OS::create_process(const String &p_path, const Vector<String> &p_arguments, bool p_open_console) {
441449
List<String> args;
442450
for (const String &arg : p_arguments) {
@@ -755,6 +763,7 @@ void OS::_bind_methods() {
755763
ClassDB::bind_method(D_METHOD("execute_with_pipe", "path", "arguments", "blocking"), &OS::execute_with_pipe, DEFVAL(true));
756764
ClassDB::bind_method(D_METHOD("create_process", "path", "arguments", "open_console"), &OS::create_process, DEFVAL(false));
757765
ClassDB::bind_method(D_METHOD("create_instance", "arguments"), &OS::create_instance);
766+
ClassDB::bind_method(D_METHOD("open_with_program", "program_path", "paths"), &OS::open_with_program);
758767
ClassDB::bind_method(D_METHOD("kill", "pid"), &OS::kill);
759768
ClassDB::bind_method(D_METHOD("shell_open", "uri"), &OS::shell_open);
760769
ClassDB::bind_method(D_METHOD("shell_show_in_file_manager", "file_or_dir_path", "open_folder"), &OS::shell_show_in_file_manager, DEFVAL(true));

core/core_bind.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ class OS : public Object {
221221
Dictionary execute_with_pipe(const String &p_path, const Vector<String> &p_arguments, bool p_blocking = true);
222222
int create_process(const String &p_path, const Vector<String> &p_arguments, bool p_open_console = false);
223223
int create_instance(const Vector<String> &p_arguments);
224+
Error open_with_program(const String &p_program_path, const Vector<String> &p_paths);
224225
Error kill(int p_pid);
225226
Error shell_open(const String &p_uri);
226227
Error shell_show_in_file_manager(const String &p_path, bool p_open_folder = true);

core/os/os.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ class OS {
191191
virtual Dictionary execute_with_pipe(const String &p_path, const List<String> &p_arguments, bool p_blocking = true) { return Dictionary(); }
192192
virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) = 0;
193193
virtual Error create_instance(const List<String> &p_arguments, ProcessID *r_child_id = nullptr) { return create_process(get_executable_path(), p_arguments, r_child_id); }
194+
virtual Error open_with_program(const String &p_program_path, const List<String> &p_paths) { return create_process(p_program_path, p_paths); }
194195
virtual Error kill(const ProcessID &p_pid) = 0;
195196
virtual int get_process_id() const;
196197
virtual bool is_process_running(const ProcessID &p_pid) const = 0;

doc/classes/OS.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -730,6 +730,16 @@
730730
[b]Note:[/b] On the Web platform, using MIDI input requires a browser permission to be granted first. This permission request is performed when calling [method open_midi_inputs]. The browser will refrain from processing MIDI input until the user accepts the permission request.
731731
</description>
732732
</method>
733+
<method name="open_with_program">
734+
<return type="int" enum="Error" />
735+
<param index="0" name="program_path" type="String" />
736+
<param index="1" name="paths" type="PackedStringArray" />
737+
<description>
738+
Opens one or more files/directories with the specified application. The [param program_path] specifies the path to the application to use for opening the files, and [param paths] contains an array of file/directory paths to open.
739+
[b]Note:[/b] This method is mostly only relevant for macOS, where opening files using [method create_process] might fail. On other platforms, this falls back to using [method create_process].
740+
[b]Note:[/b] On macOS, [param program_path] should ideally be the path to an [code].app[/code] bundle.
741+
</description>
742+
</method>
733743
<method name="read_buffer_from_stdin">
734744
<return type="PackedByteArray" />
735745
<param index="0" name="buffer_size" type="int" default="1024" />

editor/filesystem_dock.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2204,9 +2204,9 @@ void FileSystemDock::_file_option(int p_option, const Vector<String> &p_selected
22042204
if (external_program.is_empty()) {
22052205
OS::get_singleton()->shell_open(file);
22062206
} else {
2207-
List<String> args;
2208-
args.push_back(file);
2209-
OS::get_singleton()->create_process(external_program, args);
2207+
List<String> paths;
2208+
paths.push_back(file);
2209+
OS::get_singleton()->open_with_program(external_program, paths);
22102210
}
22112211
} break;
22122212

platform/macos/os_macos.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ class OS_MacOS : public OS_Unix {
115115
virtual String get_executable_path() const override;
116116
virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) override;
117117
virtual Error create_instance(const List<String> &p_arguments, ProcessID *r_child_id = nullptr) override;
118+
virtual Error open_with_program(const String &p_program_path, const List<String> &p_paths) override;
118119
virtual bool is_process_running(const ProcessID &p_pid) const override;
119120

120121
virtual String get_unique_id() const override;

platform/macos/os_macos.mm

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -831,6 +831,64 @@
831831
}
832832
}
833833

834+
Error OS_MacOS::open_with_program(const String &p_program_path, const List<String> &p_paths) {
835+
NSURL *app_url = [NSURL fileURLWithPath:@(p_program_path.utf8().get_data())];
836+
if (!app_url) {
837+
return ERR_INVALID_PARAMETER;
838+
}
839+
840+
NSBundle *bundle = [NSBundle bundleWithURL:app_url];
841+
if (!bundle) {
842+
return OS_Unix::create_process(p_program_path, p_paths);
843+
}
844+
845+
NSMutableArray *urls_to_open = [[NSMutableArray alloc] init];
846+
for (const String &path : p_paths) {
847+
NSURL *file_url = [NSURL fileURLWithPath:@(path.utf8().get_data())];
848+
if (file_url) {
849+
[urls_to_open addObject:file_url];
850+
}
851+
}
852+
853+
if ([urls_to_open count] == 0) {
854+
return ERR_INVALID_PARAMETER;
855+
}
856+
857+
#if defined(__x86_64__)
858+
if (@available(macOS 10.15, *)) {
859+
#endif
860+
NSWorkspaceOpenConfiguration *configuration = [[NSWorkspaceOpenConfiguration alloc] init];
861+
[configuration setCreatesNewApplicationInstance:NO];
862+
__block dispatch_semaphore_t lock = dispatch_semaphore_create(0);
863+
__block Error err = ERR_TIMEOUT;
864+
865+
[[NSWorkspace sharedWorkspace] openURLs:urls_to_open
866+
withApplicationAtURL:app_url
867+
configuration:configuration
868+
completionHandler:^(NSRunningApplication *app, NSError *error) {
869+
if (error) {
870+
err = ERR_CANT_FORK;
871+
NSLog(@"Failed to open paths: %@", error.localizedDescription);
872+
} else {
873+
err = OK;
874+
}
875+
dispatch_semaphore_signal(lock);
876+
}];
877+
dispatch_semaphore_wait(lock, dispatch_time(DISPATCH_TIME_NOW, 20000000000)); // 20 sec timeout, wait for app to launch.
878+
879+
return err;
880+
#if defined(__x86_64__)
881+
} else {
882+
NSError *error = nullptr;
883+
[[NSWorkspace sharedWorkspace] openURLs:urls_to_open withApplicationAtURL:app_url options:NSWorkspaceLaunchDefault configuration:@{} error:&error];
884+
if (error) {
885+
return ERR_CANT_FORK;
886+
}
887+
return OK;
888+
}
889+
#endif
890+
}
891+
834892
bool OS_MacOS::is_process_running(const ProcessID &p_pid) const {
835893
NSRunningApplication *app = [NSRunningApplication runningApplicationWithProcessIdentifier:(pid_t)p_pid];
836894
if (!app) {

0 commit comments

Comments
 (0)