From 62587d2d19fcab21d4274476cff968afdbfc9b2b Mon Sep 17 00:00:00 2001 From: Markus Sauermann <6299227+Sauermann@users.noreply.github.com> Date: Sat, 16 Dec 2023 01:33:12 +0100 Subject: [PATCH] Fix changing `gui_embed_subwindows` while a child window is displayed Changing `gui_embed_subwindows` while a child window is displayed can lead to crashes or other unintended behavior. This PR adds checks to prevent the change while child windows are displayed. --- scene/main/viewport.cpp | 61 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 8cb7614dfd8..414aed7c07e 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -3848,7 +3848,66 @@ Viewport *Viewport::get_parent_viewport() const { void Viewport::set_embedding_subwindows(bool p_embed) { ERR_THREAD_GUARD; - gui.embed_subwindows_hint = p_embed; + if (gui.embed_subwindows_hint == p_embed) { + return; + } + + bool allow_change = true; + + if (!is_inside_tree()) { + // Change can happen since no child window is displayed. + } else if (gui.embed_subwindows_hint) { + if (!gui.sub_windows.is_empty()) { + // Prevent change when this viewport has embedded windows. + allow_change = false; + } + } else { + Viewport *vp = this; + while (true) { + if (!vp->get_parent()) { + // Root window reached. + break; + } + vp = vp->get_parent()->get_viewport(); + if (vp->is_embedding_subwindows()) { + for (int i = 0; i < vp->gui.sub_windows.size(); i++) { + if (is_ancestor_of(vp->gui.sub_windows[i].window)) { + // Prevent change when this viewport has child windows that are displayed in an ancestor viewport. + allow_change = false; + break; + } + } + } + } + + if (allow_change) { + Vector wl = DisplayServer::get_singleton()->get_window_list(); + for (int index = 0; index < wl.size(); index++) { + DisplayServer::WindowID wid = wl[index]; + if (wid == DisplayServer::INVALID_WINDOW_ID) { + continue; + } + + ObjectID woid = DisplayServer::get_singleton()->window_get_attached_instance_id(wid); + if (woid.is_null()) { + continue; + } + + Window *w = Object::cast_to(ObjectDB::get_instance(woid)); + if (w && is_ancestor_of(w)) { + // Prevent change when this viewport has child windows that are displayed as native windows. + allow_change = false; + break; + } + } + } + } + + if (allow_change) { + gui.embed_subwindows_hint = p_embed; + } else { + WARN_PRINT("Can't change \"gui_embed_subwindows\" while a child window is displayed. Consider hiding all child windows before changing this value."); + } } bool Viewport::is_embedding_subwindows() const {