Skip to content

Commit c3ecb72

Browse files
committed
Merge pull request #104397 from bruvzg/mac_main_loop
[macOS] Replace custom main loop with `[NSApp run]` and `CFRunLoop` observer.
2 parents 350ac5b + a317ce7 commit c3ecb72

File tree

7 files changed

+157
-116
lines changed

7 files changed

+157
-116
lines changed

platform/macos/display_server_macos.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,7 @@ class DisplayServerMacOS : public DisplayServer {
441441
virtual Key keyboard_get_label_from_physical(Key p_keycode) const override;
442442
virtual void show_emoji_and_symbol_picker() const override;
443443

444+
void _process_events(bool p_pump);
444445
virtual void process_events() override;
445446
virtual void force_process_and_drop_events() override;
446447

platform/macos/display_server_macos.mm

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1901,6 +1901,11 @@
19011901
void DisplayServerMacOS::show_window(WindowID p_id) {
19021902
WindowData &wd = windows[p_id];
19031903

1904+
if (p_id == MAIN_WINDOW_ID) {
1905+
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
1906+
static_cast<OS_MacOS *>(OS::get_singleton())->activate();
1907+
}
1908+
19041909
popup_open(p_id);
19051910
if ([wd.window_object isMiniaturized]) {
19061911
return;
@@ -3204,20 +3209,26 @@
32043209
}
32053210

32063211
void DisplayServerMacOS::process_events() {
3212+
_process_events(true);
3213+
}
3214+
3215+
void DisplayServerMacOS::_process_events(bool p_pump) {
32073216
ERR_FAIL_COND(!Thread::is_main_thread());
32083217

3209-
while (true) {
3210-
NSEvent *event = [NSApp
3211-
nextEventMatchingMask:NSEventMaskAny
3212-
untilDate:[NSDate distantPast]
3213-
inMode:NSDefaultRunLoopMode
3214-
dequeue:YES];
3218+
if (p_pump) {
3219+
while (true) {
3220+
NSEvent *event = [NSApp
3221+
nextEventMatchingMask:NSEventMaskAny
3222+
untilDate:[NSDate distantPast]
3223+
inMode:NSDefaultRunLoopMode
3224+
dequeue:YES];
32153225

3216-
if (event == nil) {
3217-
break;
3218-
}
3226+
if (event == nil) {
3227+
break;
3228+
}
32193229

3220-
[NSApp sendEvent:event];
3230+
[NSApp sendEvent:event];
3231+
}
32213232
}
32223233

32233234
// Process "menu_callback"s.

platform/macos/godot_application_delegate.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@
3636
#import <Foundation/Foundation.h>
3737

3838
@interface GodotApplicationDelegate : NSObject <NSUserInterfaceItemSearching, NSApplicationDelegate>
39+
- (void)activate;
3940
- (void)forceUnbundledWindowActivationHackStep1;
4041
- (void)forceUnbundledWindowActivationHackStep2;
4142
- (void)forceUnbundledWindowActivationHackStep3;
42-
- (void)handleAppleEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent;
4343
@end

platform/macos/godot_application_delegate.mm

Lines changed: 47 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,13 @@ - (void)system_theme_changed:(NSNotification *)notification {
124124
}
125125
}
126126

127-
- (void)applicationDidFinishLaunching:(NSNotification *)notice {
127+
- (void)applicationDidFinishLaunching:(NSNotification *)notification {
128+
static_cast<OS_MacOS *>(OS::get_singleton())->start_main();
129+
}
130+
131+
- (void)activate {
132+
[NSApp activateIgnoringOtherApps:YES];
133+
128134
NSString *nsappname = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];
129135
const char *bundled_id = getenv("__CFBundleIdentifier");
130136
NSString *nsbundleid_env = [NSString stringWithUTF8String:(bundled_id != nullptr) ? bundled_id : ""];
@@ -139,11 +145,6 @@ - (void)applicationDidFinishLaunching:(NSNotification *)notice {
139145

140146
- (id)init {
141147
self = [super init];
142-
143-
NSAppleEventManager *aem = [NSAppleEventManager sharedAppleEventManager];
144-
[aem setEventHandler:self andSelector:@selector(handleAppleEvent:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL];
145-
[aem setEventHandler:self andSelector:@selector(handleAppleEvent:withReplyEvent:) forEventClass:kCoreEventClass andEventID:kAEOpenDocuments];
146-
147148
return self;
148149
}
149150

@@ -152,36 +153,45 @@ - (void)dealloc {
152153
[[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:@"AppleColorPreferencesChangedNotification" object:nil];
153154
}
154155

155-
- (void)handleAppleEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent {
156+
- (void)application:(NSApplication *)application openURLs:(NSArray<NSURL *> *)urls {
156157
OS_MacOS *os = (OS_MacOS *)OS::get_singleton();
157-
if (!event || !os) {
158+
if (!os) {
158159
return;
159160
}
160-
161161
List<String> args;
162-
if (([event eventClass] == kInternetEventClass) && ([event eventID] == kAEGetURL)) {
163-
// Opening URL scheme.
164-
NSString *url = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
165-
args.push_back(vformat("--uri=\"%s\"", String::utf8([url UTF8String])));
166-
}
167-
168-
if (([event eventClass] == kCoreEventClass) && ([event eventID] == kAEOpenDocuments)) {
169-
// Opening file association.
170-
NSAppleEventDescriptor *files = [event paramDescriptorForKeyword:keyDirectObject];
171-
if (files) {
172-
NSInteger count = [files numberOfItems];
173-
for (NSInteger i = 1; i <= count; i++) {
174-
NSURL *url = [NSURL URLWithString:[[files descriptorAtIndex:i] stringValue]];
175-
args.push_back(String::utf8([url.path UTF8String]));
176-
}
162+
for (NSURL *url in urls) {
163+
if ([url isFileURL]) {
164+
args.push_back(String::utf8([url.path UTF8String]));
165+
} else {
166+
args.push_back(vformat("--uri=\"%s\"", String::utf8([url.absoluteString UTF8String])));
177167
}
178168
}
169+
if (!args.is_empty()) {
170+
if (os->get_main_loop()) {
171+
// Application is already running, open a new instance with the URL/files as command line arguments.
172+
os->create_instance(args);
173+
} else if (os->get_cmd_argc() == 0) {
174+
// Application is just started, add to the list of command line arguments and continue.
175+
os->set_cmdline_platform_args(args);
176+
}
177+
}
178+
}
179179

180+
- (void)application:(NSApplication *)sender openFiles:(NSArray<NSString *> *)filenames {
181+
OS_MacOS *os = (OS_MacOS *)OS::get_singleton();
182+
if (!os) {
183+
return;
184+
}
185+
List<String> args;
186+
for (NSString *filename in filenames) {
187+
NSURL *url = [NSURL URLWithString:filename];
188+
args.push_back(String::utf8([url.path UTF8String]));
189+
}
180190
if (!args.is_empty()) {
181191
if (os->get_main_loop()) {
182192
// Application is already running, open a new instance with the URL/files as command line arguments.
183193
os->create_instance(args);
184-
} else {
194+
} else if (os->get_cmd_argc() == 0) {
185195
// Application is just started, add to the list of command line arguments and continue.
186196
os->set_cmdline_platform_args(args);
187197
}
@@ -220,11 +230,23 @@ - (NSMenu *)applicationDockMenu:(NSApplication *)sender {
220230
}
221231
}
222232

233+
- (void)applicationWillTerminate:(NSNotification *)notification {
234+
OS_MacOS *os = (OS_MacOS *)OS::get_singleton();
235+
if (os) {
236+
os->cleanup();
237+
exit(os->get_exit_code());
238+
}
239+
}
240+
223241
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
224242
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
225243
if (ds) {
226244
ds->send_window_event(ds->get_window(DisplayServerMacOS::MAIN_WINDOW_ID), DisplayServerMacOS::WINDOW_EVENT_CLOSE_REQUEST);
227245
}
246+
OS_MacOS *os = (OS_MacOS *)OS::get_singleton();
247+
if (!os || os->os_should_terminate()) {
248+
return NSTerminateNow;
249+
}
228250
return NSTerminateCancel;
229251
}
230252

platform/macos/godot_main_macos.mm

Lines changed: 2 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -59,36 +59,12 @@ int main(int argc, char **argv) {
5959
}
6060
}
6161

62-
OS_MacOS os;
63-
Error err;
62+
OS_MacOS os(argv[0], argc - first_arg, &argv[first_arg]);
6463

6564
// We must override main when testing is enabled.
6665
TEST_MAIN_OVERRIDE
6766

68-
@autoreleasepool {
69-
err = Main::setup(argv[0], argc - first_arg, &argv[first_arg]);
70-
}
71-
72-
if (err != OK) {
73-
if (err == ERR_HELP) { // Returned by --help and --version, so success.
74-
return EXIT_SUCCESS;
75-
}
76-
return EXIT_FAILURE;
77-
}
78-
79-
int ret;
80-
@autoreleasepool {
81-
ret = Main::start();
82-
}
83-
if (ret == EXIT_SUCCESS) {
84-
os.run();
85-
} else {
86-
os.set_exit_code(EXIT_FAILURE);
87-
}
88-
89-
@autoreleasepool {
90-
Main::cleanup();
91-
}
67+
os.run(); // Note: This function will never return.
9268

9369
return os.get_exit_code();
9470
}

platform/macos/os_macos.h

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,13 @@
4040
#include "servers/audio_server.h"
4141

4242
class OS_MacOS : public OS_Unix {
43+
const char *execpath = nullptr;
44+
int argc = 0;
45+
char **argv = nullptr;
46+
47+
id delegate = nullptr;
48+
bool should_terminate = false;
49+
4350
JoypadApple *joypad_apple = nullptr;
4451

4552
#ifdef COREAUDIO_ENABLED
@@ -51,7 +58,7 @@ class OS_MacOS : public OS_Unix {
5158

5259
CrashHandler crash_handler;
5360

54-
CFRunLoopObserverRef pre_wait_observer;
61+
CFRunLoopObserverRef pre_wait_observer = nil;
5562

5663
MainLoop *main_loop = nullptr;
5764

@@ -64,6 +71,8 @@ class OS_MacOS : public OS_Unix {
6471
static _FORCE_INLINE_ String get_framework_executable(const String &p_path);
6572
static void pre_wait_observer_cb(CFRunLoopObserverRef p_observer, CFRunLoopActivity p_activiy, void *p_context);
6673

74+
void terminate();
75+
6776
protected:
6877
virtual void initialize_core() override;
6978
virtual void initialize() override;
@@ -131,8 +140,13 @@ class OS_MacOS : public OS_Unix {
131140
virtual String get_system_ca_certificates() override;
132141
virtual OS::PreferredTextureFormat get_preferred_texture_format() const override;
133142

134-
void run();
143+
void run(); // Runs macOS native event loop.
144+
void start_main(); // Initializes and runs Godot main loop.
145+
void activate();
146+
void cleanup();
147+
bool os_should_terminate() const { return should_terminate; }
148+
int get_cmd_argc() const { return argc; }
135149

136-
OS_MacOS();
150+
OS_MacOS(const char *p_execpath, int p_argc, char **p_argv);
137151
~OS_MacOS();
138152
};

0 commit comments

Comments
 (0)