Browse Source

Merge pull request #62808 from bruvzg/macos_file_url_handle

Rémi Verschelde 3 years ago
parent
commit
95ddc8cccc

+ 1 - 0
core/os/os.h

@@ -160,6 +160,7 @@ public:
 
 	virtual String get_name() const = 0;
 	virtual List<String> get_cmdline_args() const { return _cmdline; }
+	virtual List<String> get_cmdline_platform_args() const { return List<String>(); }
 	virtual String get_model_name() const;
 
 	bool is_layered_allowed() const { return _allow_layered; }

+ 7 - 0
main/main.cpp

@@ -621,11 +621,18 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
 	/* argument parsing and main creation */
 	List<String> args;
 	List<String> main_args;
+	List<String> platform_args = OS::get_singleton()->get_cmdline_platform_args();
 
+	// Add command line arguments.
 	for (int i = 0; i < argc; i++) {
 		args.push_back(String::utf8(argv[i]));
 	}
 
+	// Add arguments received from macOS LaunchService (URL schemas, file associations).
+	for (const String &arg : platform_args) {
+		args.push_back(arg);
+	}
+
 	List<String>::Element *I = args.front();
 
 	while (I) {

+ 1 - 0
platform/osx/godot_application_delegate.h

@@ -40,6 +40,7 @@
 - (void)forceUnbundledWindowActivationHackStep1;
 - (void)forceUnbundledWindowActivationHackStep2;
 - (void)forceUnbundledWindowActivationHackStep3;
+- (void)handleAppleEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent;
 @end
 
 #endif // GODOT_APPLICATION_DELEGATE_H

+ 46 - 19
platform/osx/godot_application_delegate.mm

@@ -67,6 +67,52 @@
 	}
 }
 
+- (id)init {
+	self = [super init];
+
+	NSAppleEventManager *aem = [NSAppleEventManager sharedAppleEventManager];
+	[aem setEventHandler:self andSelector:@selector(handleAppleEvent:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL];
+	[aem setEventHandler:self andSelector:@selector(handleAppleEvent:withReplyEvent:) forEventClass:kCoreEventClass andEventID:kAEOpenDocuments];
+
+	return self;
+}
+
+- (void)handleAppleEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent {
+	OS_OSX *os = (OS_OSX *)OS::get_singleton();
+	if (!event || !os) {
+		return;
+	}
+
+	List<String> args;
+	if (([event eventClass] == kInternetEventClass) && ([event eventID] == kAEGetURL)) {
+		// Opening URL scheme.
+		NSString *url = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
+		args.push_back(vformat("--uri=\"%s\"", String::utf8([url UTF8String])));
+	}
+
+	if (([event eventClass] == kCoreEventClass) && ([event eventID] == kAEOpenDocuments)) {
+		// Opening file association.
+		NSAppleEventDescriptor *files = [event paramDescriptorForKeyword:keyDirectObject];
+		if (files) {
+			NSInteger count = [files numberOfItems];
+			for (NSInteger i = 1; i <= count; i++) {
+				NSURL *url = [NSURL URLWithString:[[files descriptorAtIndex:i] stringValue]];
+				args.push_back(String::utf8([url.path UTF8String]));
+			}
+		}
+	}
+
+	if (!args.is_empty()) {
+		if (os->get_main_loop()) {
+			// Application is already running, open a new instance with the URL/files as command line arguments.
+			os->create_instance(args);
+		} else {
+			// Application is just started, add to the list of command line arguments and continue.
+			os->set_cmdline_platform_args(args);
+		}
+	}
+}
+
 - (void)applicationDidResignActive:(NSNotification *)notification {
 	DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
 	if (ds) {
@@ -99,25 +145,6 @@
 	}
 }
 
-- (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename {
-	// Note: may be called called before main loop init!
-	OS_OSX *os = (OS_OSX *)OS::get_singleton();
-	if (os) {
-		os->set_open_with_filename(String::utf8([filename UTF8String]));
-	}
-
-#ifdef TOOLS_ENABLED
-	// Open new instance.
-	if (os && os->get_main_loop()) {
-		List<String> args;
-		args.push_back(os->get_open_with_filename());
-		String exec = os->get_executable_path();
-		os->create_process(exec, args);
-	}
-#endif
-	return YES;
-}
-
 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
 	DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
 	if (ds) {

+ 1 - 8
platform/osx/godot_main_osx.mm

@@ -74,14 +74,7 @@ int main(int argc, char **argv) {
 	// We must override main when testing is enabled.
 	TEST_MAIN_OVERRIDE
 
-	if (os.get_open_with_filename() != "") {
-		char *argv_c = (char *)malloc(os.get_open_with_filename().utf8().size());
-		memcpy(argv_c, os.get_open_with_filename().utf8().get_data(), os.get_open_with_filename().utf8().size());
-		err = Main::setup(argv[0], 1, &argv_c);
-		free(argv_c);
-	} else {
-		err = Main::setup(argv[0], argc - first_arg, &argv[first_arg]);
-	}
+	err = Main::setup(argv[0], argc - first_arg, &argv[first_arg]);
 
 	if (err == ERR_HELP) { // Returned by --help and --version, so success.
 		return 0;

+ 3 - 3
platform/osx/os_osx.h

@@ -57,7 +57,7 @@ class OS_OSX : public OS_Unix {
 
 	MainLoop *main_loop = nullptr;
 
-	String open_with_filename;
+	List<String> launch_service_args;
 
 	static _FORCE_INLINE_ String get_framework_executable(const String &p_path);
 	static void pre_wait_observer_cb(CFRunLoopObserverRef p_observer, CFRunLoopActivity p_activiy, void *p_context);
@@ -73,8 +73,8 @@ protected:
 	virtual void delete_main_loop() override;
 
 public:
-	String get_open_with_filename() const;
-	void set_open_with_filename(const String &p_path);
+	virtual void set_cmdline_platform_args(const List<String> &p_args);
+	virtual List<String> get_cmdline_platform_args() const override;
 
 	virtual String get_name() const override;
 

+ 4 - 4
platform/osx/os_osx.mm

@@ -121,12 +121,12 @@ void OS_OSX::delete_main_loop() {
 	main_loop = nullptr;
 }
 
-String OS_OSX::get_open_with_filename() const {
-	return open_with_filename;
+void OS_OSX::set_cmdline_platform_args(const List<String> &p_args) {
+	launch_service_args = p_args;
 }
 
-void OS_OSX::set_open_with_filename(const String &p_path) {
-	open_with_filename = p_path;
+List<String> OS_OSX::get_cmdline_platform_args() const {
+	return launch_service_args;
 }
 
 String OS_OSX::get_name() const {