mirror of
https://github.com/Redot-Engine/redot-engine.git
synced 2025-12-06 07:17:42 -05:00
Merge pull request #107113 from mihe/macos-open-in-program
Add `OS::open_with_program` for opening files/directories with a specific program on macOS
This commit is contained in:
@@ -437,6 +437,14 @@ int OS::create_instance(const Vector<String> &p_arguments) {
|
||||
return pid;
|
||||
}
|
||||
|
||||
Error OS::open_with_program(const String &p_program_path, const Vector<String> &p_paths) {
|
||||
List<String> paths;
|
||||
for (const String &path : p_paths) {
|
||||
paths.push_back(path);
|
||||
}
|
||||
return ::OS::get_singleton()->open_with_program(p_program_path, paths);
|
||||
}
|
||||
|
||||
int OS::create_process(const String &p_path, const Vector<String> &p_arguments, bool p_open_console) {
|
||||
List<String> args;
|
||||
for (const String &arg : p_arguments) {
|
||||
@@ -755,6 +763,7 @@ void OS::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("execute_with_pipe", "path", "arguments", "blocking"), &OS::execute_with_pipe, DEFVAL(true));
|
||||
ClassDB::bind_method(D_METHOD("create_process", "path", "arguments", "open_console"), &OS::create_process, DEFVAL(false));
|
||||
ClassDB::bind_method(D_METHOD("create_instance", "arguments"), &OS::create_instance);
|
||||
ClassDB::bind_method(D_METHOD("open_with_program", "program_path", "paths"), &OS::open_with_program);
|
||||
ClassDB::bind_method(D_METHOD("kill", "pid"), &OS::kill);
|
||||
ClassDB::bind_method(D_METHOD("shell_open", "uri"), &OS::shell_open);
|
||||
ClassDB::bind_method(D_METHOD("shell_show_in_file_manager", "file_or_dir_path", "open_folder"), &OS::shell_show_in_file_manager, DEFVAL(true));
|
||||
|
||||
@@ -221,6 +221,7 @@ public:
|
||||
Dictionary execute_with_pipe(const String &p_path, const Vector<String> &p_arguments, bool p_blocking = true);
|
||||
int create_process(const String &p_path, const Vector<String> &p_arguments, bool p_open_console = false);
|
||||
int create_instance(const Vector<String> &p_arguments);
|
||||
Error open_with_program(const String &p_program_path, const Vector<String> &p_paths);
|
||||
Error kill(int p_pid);
|
||||
Error shell_open(const String &p_uri);
|
||||
Error shell_show_in_file_manager(const String &p_path, bool p_open_folder = true);
|
||||
|
||||
@@ -191,6 +191,7 @@ public:
|
||||
virtual Dictionary execute_with_pipe(const String &p_path, const List<String> &p_arguments, bool p_blocking = true) { return Dictionary(); }
|
||||
virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) = 0;
|
||||
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); }
|
||||
virtual Error open_with_program(const String &p_program_path, const List<String> &p_paths) { return create_process(p_program_path, p_paths); }
|
||||
virtual Error kill(const ProcessID &p_pid) = 0;
|
||||
virtual int get_process_id() const;
|
||||
virtual bool is_process_running(const ProcessID &p_pid) const = 0;
|
||||
|
||||
@@ -730,6 +730,16 @@
|
||||
[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.
|
||||
</description>
|
||||
</method>
|
||||
<method name="open_with_program">
|
||||
<return type="int" enum="Error" />
|
||||
<param index="0" name="program_path" type="String" />
|
||||
<param index="1" name="paths" type="PackedStringArray" />
|
||||
<description>
|
||||
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.
|
||||
[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].
|
||||
[b]Note:[/b] On macOS, [param program_path] should ideally be the path to an [code].app[/code] bundle.
|
||||
</description>
|
||||
</method>
|
||||
<method name="read_buffer_from_stdin">
|
||||
<return type="PackedByteArray" />
|
||||
<param index="0" name="buffer_size" type="int" default="1024" />
|
||||
|
||||
@@ -2204,9 +2204,9 @@ void FileSystemDock::_file_option(int p_option, const Vector<String> &p_selected
|
||||
if (external_program.is_empty()) {
|
||||
OS::get_singleton()->shell_open(file);
|
||||
} else {
|
||||
List<String> args;
|
||||
args.push_back(file);
|
||||
OS::get_singleton()->create_process(external_program, args);
|
||||
List<String> paths;
|
||||
paths.push_back(file);
|
||||
OS::get_singleton()->open_with_program(external_program, paths);
|
||||
}
|
||||
} break;
|
||||
|
||||
|
||||
@@ -115,6 +115,7 @@ public:
|
||||
virtual String get_executable_path() const override;
|
||||
virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) override;
|
||||
virtual Error create_instance(const List<String> &p_arguments, ProcessID *r_child_id = nullptr) override;
|
||||
virtual Error open_with_program(const String &p_program_path, const List<String> &p_paths) override;
|
||||
virtual bool is_process_running(const ProcessID &p_pid) const override;
|
||||
|
||||
virtual String get_unique_id() const override;
|
||||
|
||||
@@ -831,6 +831,64 @@ Error OS_MacOS::create_instance(const List<String> &p_arguments, ProcessID *r_ch
|
||||
}
|
||||
}
|
||||
|
||||
Error OS_MacOS::open_with_program(const String &p_program_path, const List<String> &p_paths) {
|
||||
NSURL *app_url = [NSURL fileURLWithPath:@(p_program_path.utf8().get_data())];
|
||||
if (!app_url) {
|
||||
return ERR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
NSBundle *bundle = [NSBundle bundleWithURL:app_url];
|
||||
if (!bundle) {
|
||||
return OS_Unix::create_process(p_program_path, p_paths);
|
||||
}
|
||||
|
||||
NSMutableArray *urls_to_open = [[NSMutableArray alloc] init];
|
||||
for (const String &path : p_paths) {
|
||||
NSURL *file_url = [NSURL fileURLWithPath:@(path.utf8().get_data())];
|
||||
if (file_url) {
|
||||
[urls_to_open addObject:file_url];
|
||||
}
|
||||
}
|
||||
|
||||
if ([urls_to_open count] == 0) {
|
||||
return ERR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
#if defined(__x86_64__)
|
||||
if (@available(macOS 10.15, *)) {
|
||||
#endif
|
||||
NSWorkspaceOpenConfiguration *configuration = [[NSWorkspaceOpenConfiguration alloc] init];
|
||||
[configuration setCreatesNewApplicationInstance:NO];
|
||||
__block dispatch_semaphore_t lock = dispatch_semaphore_create(0);
|
||||
__block Error err = ERR_TIMEOUT;
|
||||
|
||||
[[NSWorkspace sharedWorkspace] openURLs:urls_to_open
|
||||
withApplicationAtURL:app_url
|
||||
configuration:configuration
|
||||
completionHandler:^(NSRunningApplication *app, NSError *error) {
|
||||
if (error) {
|
||||
err = ERR_CANT_FORK;
|
||||
NSLog(@"Failed to open paths: %@", error.localizedDescription);
|
||||
} else {
|
||||
err = OK;
|
||||
}
|
||||
dispatch_semaphore_signal(lock);
|
||||
}];
|
||||
dispatch_semaphore_wait(lock, dispatch_time(DISPATCH_TIME_NOW, 20000000000)); // 20 sec timeout, wait for app to launch.
|
||||
|
||||
return err;
|
||||
#if defined(__x86_64__)
|
||||
} else {
|
||||
NSError *error = nullptr;
|
||||
[[NSWorkspace sharedWorkspace] openURLs:urls_to_open withApplicationAtURL:app_url options:NSWorkspaceLaunchDefault configuration:@{} error:&error];
|
||||
if (error) {
|
||||
return ERR_CANT_FORK;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool OS_MacOS::is_process_running(const ProcessID &p_pid) const {
|
||||
NSRunningApplication *app = [NSRunningApplication runningApplicationWithProcessIdentifier:(pid_t)p_pid];
|
||||
if (!app) {
|
||||
|
||||
Reference in New Issue
Block a user