Browse Source

Merge pull request #106166 from stuartcarnie/macos_embed_fixes

macOS: Embedded window fixes
Rémi Verschelde 3 months ago
parent
commit
c0ebba60de

+ 8 - 1
drivers/unix/os_unix.cpp

@@ -131,6 +131,8 @@ static void _setup_clock() {
 }
 #endif
 
+struct sigaction old_action;
+
 static void handle_interrupt(int sig) {
 	if (!EngineDebugger::is_active()) {
 		return;
@@ -138,6 +140,11 @@ static void handle_interrupt(int sig) {
 
 	EngineDebugger::get_script_debugger()->set_depth(-1);
 	EngineDebugger::get_script_debugger()->set_lines_left(1);
+
+	// Ensure we call the old action if it was configured.
+	if (old_action.sa_handler && old_action.sa_handler != SIG_IGN && old_action.sa_handler != SIG_DFL) {
+		old_action.sa_handler(sig);
+	}
 }
 
 void OS_Unix::initialize_debugging() {
@@ -145,7 +152,7 @@ void OS_Unix::initialize_debugging() {
 		struct sigaction action;
 		memset(&action, 0, sizeof(action));
 		action.sa_handler = handle_interrupt;
-		sigaction(SIGINT, &action, nullptr);
+		sigaction(SIGINT, &action, &old_action);
 	}
 }
 

+ 1 - 1
editor/plugins/game_view_plugin.cpp

@@ -864,7 +864,7 @@ void GameView::_update_arguments_for_instance(int p_idx, List<String> &r_argumen
 	N = r_arguments.insert_after(N, itos(DisplayServer::get_singleton()->window_get_native_handle(DisplayServer::WINDOW_HANDLE, get_window()->get_window_id())));
 
 #if MACOS_ENABLED
-	r_arguments.push_back("--embedded");
+	N = r_arguments.insert_after(N, "--embedded");
 #endif
 
 	// Be sure to have the correct window size in the embedded_process control.

+ 5 - 2
main/main.cpp

@@ -1399,9 +1399,12 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
 			display_driver = NULL_DISPLAY_DRIVER;
 
 		} else if (arg == "--embedded") { // Enable embedded mode.
-
+#ifdef MACOS_ENABLED
 			display_driver = EMBEDDED_DISPLAY_DRIVER;
-
+#else
+			OS::get_singleton()->print("--embedded is only supported on macOS, aborting.\n");
+			goto error;
+#endif
 		} else if (arg == "--log-file") { // write to log file
 
 			if (N) {

+ 17 - 26
platform/macos/display_server_embedded.h

@@ -33,33 +33,27 @@
 #include "core/input/input.h"
 #include "servers/display_server.h"
 
-#if defined(GLES3_ENABLED)
-#include "embedded_gl_manager.h"
-#include "platform_gl.h"
-#endif // GLES3_ENABLED
-
-#if defined(RD_ENABLED)
-#include "servers/rendering/rendering_device.h"
-
-#if defined(VULKAN_ENABLED)
-#import "rendering_context_driver_vulkan_macos.h"
-#endif // VULKAN_ENABLED
-#if defined(METAL_ENABLED)
-#import "drivers/metal/rendering_context_driver_metal.h"
-#endif
-#endif // RD_ENABLED
-
 @class CAContext;
+@class CALayer;
+class GLManagerEmbedded;
+class RenderingContextDriver;
+class RenderingDevice;
+
+struct DisplayServerEmbeddedState {
+	/// Default to a scale of 2.0, which is the most common.
+	float screen_max_scale = 2.0f;
+	float screen_dpi = 96.0f;
+
+	void serialize(PackedByteArray &r_data);
+	Error deserialize(const PackedByteArray &p_data);
+};
 
 class DisplayServerEmbedded : public DisplayServer {
 	GDCLASS(DisplayServerEmbedded, DisplayServer)
 
 	_THREAD_SAFE_CLASS_
 
-	struct {
-		float screen_max_scale = 1.0f;
-		float screen_dpi = 96.0f;
-	} state;
+	DisplayServerEmbeddedState state;
 
 	NativeMenu *native_menu = nullptr;
 
@@ -70,13 +64,11 @@ class DisplayServerEmbedded : public DisplayServer {
 	HashMap<WindowID, Callable> input_event_callbacks;
 	HashMap<WindowID, Callable> input_text_callbacks;
 
-	float content_scale = 1.0f;
-
 	WindowID window_id_counter = MAIN_WINDOW_ID;
 
-	CAContext *ca_context = nil;
+	CAContext *ca_context = nullptr;
 	// Either be a CAMetalLayer or a CALayer depending on the rendering driver.
-	CALayer *layer = nil;
+	CALayer *layer = nullptr;
 #ifdef GLES3_ENABLED
 	GLManagerEmbedded *gl_manager = nullptr;
 #endif
@@ -222,8 +214,7 @@ public:
 	virtual CursorShape cursor_get_shape() const override;
 	virtual void cursor_set_custom_image(const Ref<Resource> &p_cursor, CursorShape p_shape = CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()) override;
 
-	void update_state(const Dictionary &p_state);
-	void set_content_scale(float p_scale);
+	void set_state(const DisplayServerEmbeddedState &p_state);
 	virtual void swap_buffers() override;
 
 	DisplayServerEmbedded(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error);

+ 49 - 20
platform/macos/display_server_embedded.mm

@@ -30,19 +30,31 @@
 
 #import "display_server_embedded.h"
 
-#import "embedded_debugger.h"
-#import "macos_quartz_core_spi.h"
-
-#import "core/config/project_settings.h"
-#import "core/debugger/engine_debugger.h"
-
 #if defined(GLES3_ENABLED)
-#include "drivers/gles3/rasterizer_gles3.h"
+#import "embedded_gl_manager.h"
+#import "platform_gl.h"
+
+#import "drivers/gles3/rasterizer_gles3.h"
 #endif
 
 #if defined(RD_ENABLED)
 #import "servers/rendering/renderer_rd/renderer_compositor_rd.h"
+#import "servers/rendering/rendering_device.h"
+
+#if defined(VULKAN_ENABLED)
+#import "rendering_context_driver_vulkan_macos.h"
+#endif // VULKAN_ENABLED
+#if defined(METAL_ENABLED)
+#import "drivers/metal/rendering_context_driver_metal.h"
 #endif
+#endif // RD_ENABLED
+
+#import "embedded_debugger.h"
+#import "macos_quartz_core_spi.h"
+
+#import "core/config/project_settings.h"
+#import "core/debugger/engine_debugger.h"
+#import "core/io/marshalls.h"
 
 DisplayServerEmbedded::DisplayServerEmbedded(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error) {
 	EmbeddedDebugger::initialize(this);
@@ -176,15 +188,15 @@ DisplayServerEmbedded::DisplayServerEmbedded(const String &p_rendering_driver, W
 	}
 #endif
 
-	constexpr CGFloat CONTENT_SCALE = 2.0;
-	layer.contentsScale = CONTENT_SCALE;
+	CGFloat scale = screen_get_max_scale();
+	layer.contentsScale = scale;
 	layer.magnificationFilter = kCAFilterNearest;
 	layer.minificationFilter = kCAFilterNearest;
-	layer.opaque = NO; // Never opaque when embedded.
+	layer.opaque = YES; // Always opaque when embedded.
 	layer.actions = @{ @"contents" : [NSNull null] }; // Disable implicit animations for contents.
 	// AppKit frames, bounds and positions are always in points.
 	CGRect bounds = CGRectMake(0, 0, p_resolution.width, p_resolution.height);
-	bounds = CGRectApplyAffineTransform(bounds, CGAffineTransformMakeScale(1.0 / CONTENT_SCALE, 1.0 / CONTENT_SCALE));
+	bounds = CGRectApplyAffineTransform(bounds, CGAffineTransformInvert(CGAffineTransformMakeScale(scale, scale)));
 	layer.bounds = bounds;
 
 	CGSConnectionID connection_id = CGSMainConnectionID();
@@ -582,11 +594,11 @@ void DisplayServerEmbedded::window_set_size(const Size2i p_size, WindowID p_wind
 	[CATransaction begin];
 	[CATransaction setDisableActions:YES];
 
-	// TODO(sgc): Pass scale as argument from parent process.
-	constexpr CGFloat CONTENT_SCALE = 2.0;
+	CGFloat scale = screen_get_max_scale();
 	CGRect bounds = CGRectMake(0, 0, p_size.width, p_size.height);
-	bounds = CGRectApplyAffineTransform(bounds, CGAffineTransformMakeScale(1.0 / CONTENT_SCALE, 1.0 / CONTENT_SCALE));
+	bounds = CGRectApplyAffineTransform(bounds, CGAffineTransformInvert(CGAffineTransformMakeScale(scale, scale)));
 	layer.bounds = bounds;
+	layer.contentsScale = scale;
 
 #if defined(RD_ENABLED)
 	if (rendering_context) {
@@ -685,12 +697,8 @@ void DisplayServerEmbedded::window_set_ime_position(const Point2i &p_pos, Window
 	ime_last_position = p_pos;
 }
 
-void DisplayServerEmbedded::update_state(const Dictionary &p_state) {
-	state.screen_max_scale = p_state["screen_get_max_scale"];
-}
-
-void DisplayServerEmbedded::set_content_scale(float p_scale) {
-	content_scale = p_scale;
+void DisplayServerEmbedded::set_state(const DisplayServerEmbeddedState &p_state) {
+	state = p_state;
 }
 
 void DisplayServerEmbedded::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) {
@@ -742,3 +750,24 @@ void DisplayServerEmbedded::swap_buffers() {
 	}
 #endif
 }
+
+void DisplayServerEmbeddedState::serialize(PackedByteArray &r_data) {
+	r_data.resize(8);
+
+	uint8_t *data = r_data.ptrw();
+	data += encode_float(screen_max_scale, data);
+	data += encode_float(screen_dpi, data);
+
+	// Assert we had enough space.
+	DEV_ASSERT(data - r_data.ptrw() >= r_data.size());
+}
+
+Error DisplayServerEmbeddedState::deserialize(const PackedByteArray &p_data) {
+	const uint8_t *data = p_data.ptr();
+
+	screen_max_scale = decode_float(data);
+	data += sizeof(float);
+	screen_dpi = decode_float(data);
+
+	return OK;
+}

+ 3 - 2
platform/macos/display_server_macos.mm

@@ -3297,6 +3297,7 @@ Error DisplayServerMacOS::embed_process_update(WindowID p_window, const Embedded
 	[CATransaction setDisableActions:YES];
 
 	EmbeddedProcessData *ed = embedded_processes.getptr(p_pid);
+	CGFloat scale = screen_get_max_scale();
 	if (ed == nil) {
 		ed = &embedded_processes.insert(p_pid, EmbeddedProcessData())->value;
 
@@ -3305,7 +3306,7 @@ Error DisplayServerMacOS::embed_process_update(WindowID p_window, const Embedded
 		CALayerHost *host = [CALayerHost new];
 		uint32_t p_context_id = p_process->get_context_id();
 		host.contextId = static_cast<CAContextID>(p_context_id);
-		host.contentsScale = wd->window_object.backingScaleFactor;
+		host.contentsScale = scale;
 		host.contentsGravity = kCAGravityCenter;
 		ed->layer_host = host;
 		[wd->window_view.layer addSublayer:host];
@@ -3313,7 +3314,7 @@ Error DisplayServerMacOS::embed_process_update(WindowID p_window, const Embedded
 
 	Rect2i p_rect = p_process->get_screen_embedded_window_rect();
 	CGRect rect = CGRectMake(p_rect.position.x, p_rect.position.y, p_rect.size.x, p_rect.size.y);
-	rect = CGRectApplyAffineTransform(rect, CGAffineTransformMakeScale(0.5, 0.5));
+	rect = CGRectApplyAffineTransform(rect, CGAffineTransformInvert(CGAffineTransformMakeScale(scale, scale)));
 
 	CGFloat height = wd->window_view.frame.size.height;
 	CGFloat x = rect.origin.x;

+ 7 - 6
platform/macos/editor/embedded_process_macos.h

@@ -102,12 +102,12 @@ public:
 		return embedding_state == EmbeddingState::COMPLETED;
 	}
 
-	virtual bool is_process_focused() const override { return layer_host->has_focus(); }
-	virtual void embed_process(OS::ProcessID p_pid) override;
-	virtual int get_embedded_pid() const override { return current_process_id; }
-	virtual void reset() override;
-	virtual void request_close() override;
-	virtual void queue_update_embedded_process() override { update_embedded_process(); }
+	bool is_process_focused() const override { return layer_host->has_focus(); }
+	void embed_process(OS::ProcessID p_pid) override;
+	int get_embedded_pid() const override { return current_process_id; }
+	void reset() override;
+	void request_close() override;
+	void queue_update_embedded_process() override { update_embedded_process(); }
 
 	Rect2i get_adjusted_embedded_window_rect(const Rect2i &p_rect) const override;
 
@@ -117,4 +117,5 @@ public:
 	_FORCE_INLINE_ DisplayServer::MouseMode get_mouse_mode() const { return mouse_mode; }
 
 	EmbeddedProcessMacOS();
+	~EmbeddedProcessMacOS() override;
 };

+ 21 - 9
platform/macos/editor/embedded_process_macos.mm

@@ -30,6 +30,7 @@
 
 #include "embedded_process_macos.h"
 
+#include "platform/macos/display_server_embedded.h"
 #include "platform/macos/display_server_macos.h"
 
 #include "core/input/input_event_codec.h"
@@ -122,7 +123,7 @@ void EmbeddedProcessMacOS::reset() {
 
 void EmbeddedProcessMacOS::request_close() {
 	if (current_process_id != 0 && is_embedding_completed()) {
-		ds->request_close_embedded_process(current_process_id);
+		script_debugger->send_message("embed:win_event", { DisplayServer::WINDOW_EVENT_CLOSE_REQUEST });
 	}
 }
 
@@ -133,19 +134,22 @@ void EmbeddedProcessMacOS::_try_embed_process() {
 
 	Error err = ds->embed_process_update(window->get_window_id(), this);
 	if (err == OK) {
+		// Replicate some of the DisplayServer state.
+		{
+			DisplayServerEmbeddedState state;
+			state.screen_max_scale = ds->screen_get_max_scale();
+			state.screen_dpi = ds->screen_get_dpi();
+			PackedByteArray data;
+			state.serialize(data);
+			script_debugger->send_message("embed:ds_state", { data });
+		}
+
 		Rect2i rect = get_screen_embedded_window_rect();
 		script_debugger->send_message("embed:window_size", { rect.size });
 		embedding_state = EmbeddingState::COMPLETED;
 		queue_redraw();
 		emit_signal(SNAME("embedding_completed"));
 
-		// Replicate some of the DisplayServer state.
-		{
-			Dictionary state;
-			state["screen_get_max_scale"] = ds->screen_get_max_scale();
-			// script_debugger->send_message("embed:ds_state", { state });
-		}
-
 		// Send initial joystick state.
 		{
 			Input *input = Input::get_singleton();
@@ -209,13 +213,21 @@ EmbeddedProcessMacOS::EmbeddedProcessMacOS() :
 	ED_SHORTCUT("game_view/release_mouse", TTRC("Release Mouse"), KeyModifierMask::ALT | Key::ESCAPE);
 }
 
+EmbeddedProcessMacOS::~EmbeddedProcessMacOS() {
+	if (current_process_id != 0) {
+		// Stop embedding the last process.
+		OS::get_singleton()->kill(current_process_id);
+		reset();
+	}
+}
+
 void LayerHost::_notification(int p_what) {
 	switch (p_what) {
 		case NOTIFICATION_FOCUS_ENTER: {
 			if (script_debugger) {
 				script_debugger->send_message("embed:win_event", { DisplayServer::WINDOW_EVENT_MOUSE_ENTER });
 			}
-			// Temporarily release mouse capture, so we can interact with the editor.
+			// Restore mouse capture, if necessary.
 			DisplayServer *ds = DisplayServer::get_singleton();
 			if (process->get_mouse_mode() != ds->mouse_get_mode()) {
 				// Restore embedded process mouse mode.

+ 1 - 0
platform/macos/embedded_debugger.h

@@ -63,6 +63,7 @@ private:
 	Error _msg_ime_update(const Array &p_args);
 	Error _msg_joy_add(const Array &p_args);
 	Error _msg_joy_del(const Array &p_args);
+	Error _msg_ds_state(const Array &p_args);
 
 public:
 	static Error parse_message(void *p_user, const String &p_msg, const Array &p_args, bool &r_captured);

+ 14 - 0
platform/macos/embedded_debugger.mm

@@ -76,21 +76,25 @@ void EmbeddedDebugger::_init_parse_message_handlers() {
 	parse_message_handlers["ime_update"] = &EmbeddedDebugger::_msg_ime_update;
 	parse_message_handlers["joy_add"] = &EmbeddedDebugger::_msg_joy_add;
 	parse_message_handlers["joy_del"] = &EmbeddedDebugger::_msg_joy_del;
+	parse_message_handlers["ds_state"] = &EmbeddedDebugger::_msg_ds_state;
 }
 
 Error EmbeddedDebugger::_msg_window_size(const Array &p_args) {
+	ERR_FAIL_COND_V_MSG(p_args.size() != 1, ERR_INVALID_PARAMETER, "Invalid number of arguments for 'window_size' message.");
 	Size2i size = p_args[0];
 	ds->window_set_size(size);
 	return OK;
 }
 
 Error EmbeddedDebugger::_msg_mouse_set_mode(const Array &p_args) {
+	ERR_FAIL_COND_V_MSG(p_args.size() != 1, ERR_INVALID_PARAMETER, "Invalid number of arguments for 'mouse_set_mode' message.");
 	DisplayServer::MouseMode mode = p_args[0];
 	ds->mouse_set_mode(mode);
 	return OK;
 }
 
 Error EmbeddedDebugger::_msg_event(const Array &p_args) {
+	ERR_FAIL_COND_V_MSG(p_args.size() != 1, ERR_INVALID_PARAMETER, "Invalid number of arguments for 'event' message.");
 	Input *input = Input::get_singleton();
 	if (!input) {
 		// Ignore if we've received an event before the process has initialized.
@@ -130,6 +134,7 @@ Error EmbeddedDebugger::_msg_event(const Array &p_args) {
 }
 
 Error EmbeddedDebugger::_msg_win_event(const Array &p_args) {
+	ERR_FAIL_COND_V_MSG(p_args.size() != 1, ERR_INVALID_PARAMETER, "Invalid number of arguments for 'win_event' message.");
 	DisplayServer::WindowEvent win_event = p_args[0];
 	ds->send_window_event(win_event, DisplayServer::MAIN_WINDOW_ID);
 	if (win_event == DisplayServer::WindowEvent::WINDOW_EVENT_MOUSE_EXIT) {
@@ -161,6 +166,15 @@ Error EmbeddedDebugger::_msg_joy_del(const Array &p_args) {
 	return OK;
 }
 
+Error EmbeddedDebugger::_msg_ds_state(const Array &p_args) {
+	ERR_FAIL_COND_V_MSG(p_args.size() != 1, ERR_INVALID_PARAMETER, "Invalid number of arguments for 'ds_state' message.");
+	PackedByteArray data = p_args[0];
+	DisplayServerEmbeddedState state;
+	state.deserialize(data);
+	ds->set_state(state);
+	return OK;
+}
+
 Error EmbeddedDebugger::parse_message(void *p_user, const String &p_msg, const Array &p_args, bool &r_captured) {
 	EmbeddedDebugger *self = static_cast<EmbeddedDebugger *>(p_user);
 	r_captured = true;

+ 3 - 1
platform/macos/godot_main_macos.mm

@@ -116,7 +116,9 @@ int main(int argc, char **argv) {
 
 	os->run();
 
+	int exit_code = os->get_exit_code();
+
 	memdelete(os);
 
-	return os->get_exit_code();
+	return exit_code;
 }

+ 14 - 1
platform/macos/os_macos.mm

@@ -923,6 +923,14 @@ void OS_MacOS_NSApp::run() {
 	[NSApp run];
 }
 
+static bool sig_received = false;
+
+static void handle_interrupt(int sig) {
+	if (sig == SIGINT) {
+		sig_received = true;
+	}
+}
+
 void OS_MacOS_NSApp::start_main() {
 	Error err;
 	@autoreleasepool {
@@ -954,7 +962,7 @@ void OS_MacOS_NSApp::start_main() {
 							}
 							joypad_apple->process_joypads();
 
-							if (Main::iteration()) {
+							if (Main::iteration() || sig_received) {
 								terminate();
 							}
 						} @catch (NSException *exception) {
@@ -1020,6 +1028,11 @@ OS_MacOS_NSApp::OS_MacOS_NSApp(const char *p_execpath, int p_argc, char **p_argv
 	ERR_FAIL_NULL(delegate);
 	[NSApp setDelegate:delegate];
 	[NSApp registerUserInterfaceItemSearchHandler:delegate];
+
+	struct sigaction action;
+	memset(&action, 0, sizeof(action));
+	action.sa_handler = handle_interrupt;
+	sigaction(SIGINT, &action, nullptr);
 }
 
 // MARK: - OS_MacOS_Embedded