Browse Source

[macOS] Process menu callback after event processing step to avoid event queue corruption.

bruvzg 2 years ago
parent
commit
b66931946b
2 changed files with 21 additions and 5 deletions
  1. 6 0
      platform/macos/display_server_macos.h
  2. 15 5
      platform/macos/display_server_macos.mm

+ 6 - 0
platform/macos/display_server_macos.h

@@ -179,6 +179,12 @@ private:
 
 	IOPMAssertionID screen_keep_on_assertion = kIOPMNullAssertionID;
 
+	struct MenuCall {
+		Variant tag;
+		Callable callback;
+	};
+	Vector<MenuCall> deferred_menu_calls;
+
 	const NSMenu *_get_menu_root(const String &p_menu_root) const;
 	NSMenu *_get_menu_root(const String &p_menu_root);
 

+ 15 - 5
platform/macos/display_server_macos.mm

@@ -565,11 +565,11 @@ void DisplayServerMacOS::menu_callback(id p_sender) {
 		}
 
 		if (value->callback != Callable()) {
-			Variant tag = value->meta;
-			Variant *tagp = &tag;
-			Variant ret;
-			Callable::CallError ce;
-			value->callback.callp((const Variant **)&tagp, 1, ret, ce);
+			MenuCall mc;
+			mc.tag = value->meta;
+			mc.callback = value->callback;
+			deferred_menu_calls.push_back(mc);
+			// Do not run callback from here! If it is opening a new window or calling process_events, it will corrupt OS event queue and crash.
 		}
 	}
 }
@@ -3280,6 +3280,16 @@ void DisplayServerMacOS::process_events() {
 		[NSApp sendEvent:event];
 	}
 
+	// Process "menu_callback"s.
+	for (MenuCall &E : deferred_menu_calls) {
+		Variant tag = E.tag;
+		Variant *tagp = &tag;
+		Variant ret;
+		Callable::CallError ce;
+		E.callback.callp((const Variant **)&tagp, 1, ret, ce);
+	}
+	deferred_menu_calls.clear();
+
 	if (!drop_events) {
 		_process_key_events();
 		Input::get_singleton()->flush_buffered_events();