Repository: godotengine/godot (language: cpp)

Issue:
Game window not closing correctly if you have a dialog open
### Tested versions

Godot v4.4.beta (c394eaa45) 

### System information

Fedora Linux 40 (KDE Plasma) on Wayland - X11 display driver, Multi-window

### Issue description

If you have a dialog open and press the **X** to close the game window. The game window will not close and instead move independent of the game view. 

https://github.com/user-attachments/assets/36e9dcfb-9afe-41ad-a443-649f28a5230e

### Steps to reproduce

^

### Minimal reproduction project (MRP)

[window-close.zip](https://github.com/user-attachments/files/18675433/window-close.zip)


Discussion / hints:
Seems a regression between 4.4 beta 1 and 4.4 beta 2, i'll bisect.
This might be the same issue as #102239.
That is caused by the fact that since beta 2, the game is closed via a command sent to the game using the debugger. In beta 1, the X was killing the game process.

The embedded window probably never moves when a dialog is opened and you move the floating window, even without clicking the X.
> The embedded window probably never moves when a dialog is opened and you move the floating window, even without clicking the X.

It moves with the window

https://github.com/user-attachments/assets/54847e1c-486f-4995-be5b-6aa445a85240
Bisected to #101895, @Hilderin

![Image](https://github.com/user-attachments/assets/7ff2bbf8-9778-404b-80c3-835b437a4824)

<br>

I revert pr #101895 and tested #102239 and the bug related in #102239 persists so seems to not be the same case.

Fix patch:
diff --git a/editor/plugins/embedded_process.cpp b/editor/plugins/embedded_process.cpp
index 1638c437125d..72c87bfc1c6d 100644
--- a/editor/plugins/embedded_process.cpp
+++ b/editor/plugins/embedded_process.cpp
@@ -200,6 +200,12 @@ void EmbeddedProcess::reset() {
 	queue_redraw();
 }
 
+void EmbeddedProcess::request_close() {
+	if (current_process_id != 0 && embedding_completed) {
+		DisplayServer::get_singleton()->request_close_embedded_process(current_process_id);
+	}
+}
+
 void EmbeddedProcess::_try_embed_process() {
 	bool is_visible = is_visible_in_tree();
 	Error err = DisplayServer::get_singleton()->embed_process(window->get_window_id(), current_process_id, get_screen_embedded_window_rect(), is_visible, is_visible && application_has_focus && embedding_grab_focus);
diff --git a/editor/plugins/embedded_process.h b/editor/plugins/embedded_process.h
index 3e800923a75d..abd2c0f36e2f 100644
--- a/editor/plugins/embedded_process.h
+++ b/editor/plugins/embedded_process.h
@@ -76,6 +76,7 @@ class EmbeddedProcess : public Control {
 public:
 	void embed_process(OS::ProcessID p_pid);
 	void reset();
+	void request_close();
 
 	void set_window_size(const Size2i p_window_size);
 	void set_keep_aspect(bool p_keep_aspect);
diff --git a/editor/plugins/game_view_plugin.cpp b/editor/plugins/game_view_plugin.cpp
index a6d73cf9e153..3b8a91c5fc3a 100644
--- a/editor/plugins/game_view_plugin.cpp
+++ b/editor/plugins/game_view_plugin.cpp
@@ -798,16 +798,17 @@ void GameView::_window_close_request() {
 	// Before the parent window closed, we close the embedded game. That prevents
 	// the embedded game to be seen without a parent window for a fraction of second.
 	if (EditorRunBar::get_singleton()->is_playing() && (embedded_process->is_embedding_completed() || embedded_process->is_embedding_in_progress())) {
-		// Try to gracefully close the window. That way, the NOTIFICATION_WM_CLOSE_REQUEST
-		// notification should be propagated in the game process.
-		embedded_process->reset();
-
 		// When the embedding is not complete, we need to kill the process.
 		// If the game is paused, the close request will not be processed by the game, so it's better to kill the process.
 		if (paused || embedded_process->is_embedding_in_progress()) {
+			embedded_process->reset();
 			// Call deferred to prevent the _stop_pressed callback to be executed before the wrapper window
 			// actually closes.
 			callable_mp(EditorRunBar::get_singleton(), &EditorRunBar::stop_playing).call_deferred();
+		} else {
+			// Try to gracefully close the window. That way, the NOTIFICATION_WM_CLOSE_REQUEST
+			// notification should be propagated in the game process.
+			embedded_process->request_close();
 		}
 	}
 }
diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp
index 95181a1c8177..cbf963c59a0e 100644
--- a/platform/linuxbsd/x11/display_server_x11.cpp
+++ b/platform/linuxbsd/x11/display_server_x11.cpp
@@ -5926,7 +5926,7 @@ Error DisplayServerX11::embed_process(WindowID p_window, OS::ProcessID p_pid, co
 	return OK;
 }
 
-Error DisplayServerX11::remove_embedded_process(OS::ProcessID p_pid) {
+Error DisplayServerX11::request_close_embedded_process(OS::ProcessID p_pid) {
 	_THREAD_SAFE_METHOD_
 
 	if (!embedded_processes.has(p_pid)) {
@@ -5956,6 +5956,20 @@ Error DisplayServerX11::remove_embedded_process(OS::ProcessID p_pid) {
 	// Restore default error handler.
 	XSetErrorHandler(oldHandler);
 
+	return OK;
+}
+
+Error DisplayServerX11::remove_embedded_process(OS::ProcessID p_pid) {
+	_THREAD_SAFE_METHOD_
+
+	if (!embedded_processes.has(p_pid)) {
+		return ERR_DOES_NOT_EXIST;
+	}
+
+	EmbeddedProcessData *ep = embedded_processes.get(p_pid);
+
+	request_close_embedded_process(p_pid);
+
 	embedded_processes.erase(p_pid);
 	memdelete(ep);
 
diff --git a/platform/linuxbsd/x11/display_server_x11.h b/platform/linuxbsd/x11/display_server_x11.h
index edd70d454da3..b21d9024627f 100644
--- a/platform/linuxbsd/x11/display_server_x11.h
+++ b/platform/linuxbsd/x11/display_server_x11.h
@@ -540,6 +540,7 @@ class DisplayServerX11 : public DisplayServer {
 	virtual void window_start_resize(WindowResizeEdge p_edge, WindowID p_window) override;
 
 	virtual Error embed_process(WindowID p_window, OS::ProcessID p_pid, const Rect2i &p_rect, bool p_visible, bool p_grab_focus) override;
+	virtual Error request_close_embedded_process(OS::ProcessID p_pid) override;
 	virtual Error remove_embedded_process(OS::ProcessID p_pid) override;
 	virtual OS::ProcessID get_focused_process_id() override;
 
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index d0ddac4b7f9e..6f92cc0d6219 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -3004,7 +3004,7 @@ Error DisplayServerWindows::embed_process(WindowID p_window, OS::ProcessID p_pid
 	return OK;
 }
 
-Error DisplayServerWindows::remove_embedded_process(OS::ProcessID p_pid) {
+Error DisplayServerWindows::request_close_embedded_process(OS::ProcessID p_pid) {
 	_THREAD_SAFE_METHOD_
 
 	if (!embedded_processes.has(p_pid)) {
@@ -3016,6 +3016,20 @@ Error DisplayServerWindows::remove_embedded_process(OS::ProcessID p_pid) {
 	// Send a close message to gracefully close the process.
 	PostMessage(ep->window_handle, WM_CLOSE, 0, 0);
 
+	return OK;
+}
+
+Error DisplayServerWindows::remove_embedded_process(OS::ProcessID p_pid) {
+	_THREAD_SAFE_METHOD_
+
+	if (!embedded_processes.has(p_pid)) {
+		return ERR_DOES_NOT_EXIST;
+	}
+
+	EmbeddedProcessData *ep = embedded_processes.get(p_pid);
+
+	request_close_embedded_process(p_pid);
+
 	// This is a workaround to ensure the parent window correctly regains focus after the
 	// embedded window is closed. When the embedded window is closed while it has focus,
 	// the parent window (the editor) does not become active. It appears focused but is not truly activated.
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index 71de7a73769e..30f71c8c7e18 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -837,6 +837,7 @@ class DisplayServerWindows : public DisplayServer {
 
 	virtual void enable_for_stealing_focus(OS::ProcessID pid) override;
 	virtual Error embed_process(WindowID p_window, OS::ProcessID p_pid, const Rect2i &p_rect, bool p_visible, bool p_grab_focus) override;
+	virtual Error request_close_embedded_process(OS::ProcessID p_pid) override;
 	virtual Error remove_embedded_process(OS::ProcessID p_pid) override;
 	virtual OS::ProcessID get_focused_process_id() override;
 
diff --git a/servers/display_server.cpp b/servers/display_server.cpp
index bf3d428420a7..6dd9044617e3 100644
--- a/servers/display_server.cpp
+++ b/servers/display_server.cpp
@@ -679,6 +679,11 @@ Error DisplayServer::embed_process(WindowID p_window, OS::ProcessID p_pid, const
 	return ERR_UNAVAILABLE;
 }
 
+Error DisplayServer::request_close_embedded_process(OS::ProcessID p_pid) {
+	WARN_PRINT("Embedded process not supported by this display server.");
+	return ERR_UNAVAILABLE;
+}
+
 Error DisplayServer::remove_embedded_process(OS::ProcessID p_pid) {
 	WARN_PRINT("Embedded process not supported by this display server.");
 	return ERR_UNAVAILABLE;
diff --git a/servers/display_server.h b/servers/display_server.h
index 30bb533a5d39..79cf9ffc44bf 100644
--- a/servers/display_server.h
+++ b/servers/display_server.h
@@ -577,6 +577,7 @@ class DisplayServer : public Object {
 	virtual void enable_for_stealing_focus(OS::ProcessID pid);
 
 	virtual Error embed_process(WindowID p_window, OS::ProcessID p_pid, const Rect2i &p_rect, bool p_visible, bool p_grab_focus);
+	virtual Error request_close_embedded_process(OS::ProcessID p_pid);
 	virtual Error remove_embedded_process(OS::ProcessID p_pid);
 	virtual OS::ProcessID get_focused_process_id();
 


Test patch: