From 745b0b27fd01c124e93aaf591c2c4c5610dd0d82 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Fri, 1 May 2020 23:53:05 +0200 Subject: [PATCH] WindowServer+LibGUI: Automatically close child windows with parent If a window has child windows when it's destroyed, WindowServer will now automatically tear down all of its children as well. This is communicated to the client program through a vector of window ID's included with the response to WindowServer::DestroyWindow. This does feel a little bit awkward, but managing it on the client side also seems a bit awkward. --- Libraries/LibGUI/Window.cpp | 21 +++++++++---- Libraries/LibGUI/Window.h | 2 ++ Servers/WindowServer/ClientConnection.cpp | 36 ++++++++++++++++------- Servers/WindowServer/ClientConnection.h | 2 ++ Servers/WindowServer/WindowServer.ipc | 2 +- 5 files changed, 47 insertions(+), 16 deletions(-) diff --git a/Libraries/LibGUI/Window.cpp b/Libraries/LibGUI/Window.cpp index 480debb2e8..1e7b4069d6 100644 --- a/Libraries/LibGUI/Window.cpp +++ b/Libraries/LibGUI/Window.cpp @@ -126,18 +126,29 @@ Window* Window::find_parent_window() return nullptr; } -void Window::hide() +void Window::server_did_destroy() { - if (!is_visible()) - return; - reified_windows->remove(m_window_id); - WindowServerConnection::the().send_sync(m_window_id); m_window_id = 0; m_visible = false; m_pending_paint_event_rects.clear(); m_back_bitmap = nullptr; m_front_bitmap = nullptr; m_override_cursor = StandardCursor::None; +} + +void Window::hide() +{ + if (!is_visible()) + return; + reified_windows->remove(m_window_id); + auto response = WindowServerConnection::the().send_sync(m_window_id); + server_did_destroy(); + + for (auto child_window_id : response->destroyed_window_ids()) { + if (auto* window = Window::from_window_id(child_window_id)) { + window->server_did_destroy(); + } + } bool app_has_visible_windows = false; for (auto& window : *all_windows) { diff --git a/Libraries/LibGUI/Window.h b/Libraries/LibGUI/Window.h index 9bb118c26a..01221a86ed 100644 --- a/Libraries/LibGUI/Window.h +++ b/Libraries/LibGUI/Window.h @@ -191,6 +191,8 @@ protected: private: virtual bool is_window() const override final { return true; } + void server_did_destroy(); + RefPtr create_backing_bitmap(const Gfx::Size&); RefPtr create_shared_bitmap(Gfx::BitmapFormat, const Gfx::Size&); void set_current_backing_bitmap(Gfx::Bitmap&, bool flush_immediately = false); diff --git a/Servers/WindowServer/ClientConnection.cpp b/Servers/WindowServer/ClientConnection.cpp index 32b2ba3100..e49143d700 100644 --- a/Servers/WindowServer/ClientConnection.cpp +++ b/Servers/WindowServer/ClientConnection.cpp @@ -472,6 +472,10 @@ OwnPtr ClientConnection::handle(co did_misbehave("CreateWindow with bad parent_window_id"); return nullptr; } + if (parent_window->window_id() == window_id) { + did_misbehave("CreateWindow trying to make a window with itself as parent"); + return nullptr; + } window->set_parent_window(*parent_window); } @@ -497,6 +501,25 @@ OwnPtr ClientConnection::handle(co return make(window_id); } +void ClientConnection::destroy_window(Window& window, Vector& destroyed_window_ids) +{ + for (auto& child_window : window.child_windows()) { + if (!child_window) + continue; + ASSERT(child_window->window_id() != window.window_id()); + destroy_window(*child_window, destroyed_window_ids); + } + + destroyed_window_ids.append(window.window_id()); + + if (window.type() == WindowType::MenuApplet) + AppletManager::the().remove_applet(window); + + WindowManager::the().invalidate(window); + remove_child(window); + m_windows.remove(window.window_id()); +} + OwnPtr ClientConnection::handle(const Messages::WindowServer::DestroyWindow& message) { auto it = m_windows.find(message.window_id()); @@ -505,16 +528,9 @@ OwnPtr ClientConnection::handle(c return nullptr; } auto& window = *(*it).value; - - if (window.type() == WindowType::MenuApplet) - AppletManager::the().remove_applet(window); - - WindowManager::the().invalidate(window); - remove_child(window); - ASSERT(it->value.ptr() == &window); - m_windows.remove(message.window_id()); - - return make(); + Vector destroyed_window_ids; + destroy_window(window, destroyed_window_ids); + return make(destroyed_window_ids); } void ClientConnection::post_paint_message(Window& window, bool ignore_occlusion) diff --git a/Servers/WindowServer/ClientConnection.h b/Servers/WindowServer/ClientConnection.h index 1c11bb2711..92c2f2fa08 100644 --- a/Servers/WindowServer/ClientConnection.h +++ b/Servers/WindowServer/ClientConnection.h @@ -79,6 +79,8 @@ public: private: explicit ClientConnection(Core::LocalSocket&, int client_id); + void destroy_window(Window&, Vector& destroyed_window_ids); + virtual OwnPtr handle(const Messages::WindowServer::Greet&) override; virtual OwnPtr handle(const Messages::WindowServer::CreateMenubar&) override; virtual OwnPtr handle(const Messages::WindowServer::DestroyMenubar&) override; diff --git a/Servers/WindowServer/WindowServer.ipc b/Servers/WindowServer/WindowServer.ipc index d1be40824a..214afa7567 100644 --- a/Servers/WindowServer/WindowServer.ipc +++ b/Servers/WindowServer/WindowServer.ipc @@ -44,7 +44,7 @@ endpoint WindowServer = 2 String title, i32 parent_window_id) => (i32 window_id) - DestroyWindow(i32 window_id) => () + DestroyWindow(i32 window_id) => (Vector destroyed_window_ids) SetWindowTitle(i32 window_id, String title) => () GetWindowTitle(i32 window_id) => (String title)