From c7847d7c81af5adc1a0b9a25e6c381caa06e5810 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Fri, 27 Dec 2019 11:34:40 +0100 Subject: [PATCH] WindowServer+LibGUI: Mark window bitmaps volatile in occluded windows WindowServer now tracks whether windows are occluded (meaning that they are completely covered by one or more opaque windows sitting above them.) This state is communicated to the windows via WindowStateChanged messages, which then allow GWindow to mark its backing store volatile. This reduces the effective memory impact of windows that are not at all visible to the user. Very cool. :^) --- Libraries/LibGUI/GWindow.cpp | 15 +++++++--- Libraries/LibGUI/GWindow.h | 5 ++-- Libraries/LibGUI/GWindowServerConnection.cpp | 2 +- Servers/WindowServer/WSWindow.cpp | 16 ++++++++++ Servers/WindowServer/WSWindow.h | 6 +++- Servers/WindowServer/WSWindowManager.cpp | 31 +++++++++++++++++++- Servers/WindowServer/WSWindowManager.h | 4 +++ Servers/WindowServer/WSWindowSwitcher.cpp | 1 + Servers/WindowServer/WindowClient.ipc | 2 +- 9 files changed, 72 insertions(+), 10 deletions(-) diff --git a/Libraries/LibGUI/GWindow.cpp b/Libraries/LibGUI/GWindow.cpp index 2c9433a482..44627d0d51 100644 --- a/Libraries/LibGUI/GWindow.cpp +++ b/Libraries/LibGUI/GWindow.cpp @@ -298,6 +298,11 @@ bool GWindow::is_visible() const return m_window_id != 0; } +void GWindow::update() +{ + update({ 0, 0, width(), height() }); +} + void GWindow::update(const Rect& a_rect) { if (!m_window_id) @@ -583,17 +588,19 @@ void GWindow::update_all_windows(Badge) } } -void GWindow::notify_state_changed(Badge, bool minimized) +void GWindow::notify_state_changed(Badge, bool minimized, bool occluded) { - // When double buffering is enabled, minimization means we can mark the front bitmap volatile (in addition to the back bitmap.) + // When double buffering is enabled, minimization/occlusion means we can mark the front bitmap volatile (in addition to the back bitmap.) // When double buffering is disabled, there is only the back bitmap (which we can now mark volatile!) RefPtr& bitmap = m_double_buffering_enabled ? m_front_bitmap : m_back_bitmap; if (!bitmap) return; - if (minimized) { + if (minimized || occluded) { bitmap->shared_buffer()->set_volatile(); } else { - if (!bitmap->shared_buffer()->set_nonvolatile()) + if (!bitmap->shared_buffer()->set_nonvolatile()) { bitmap = nullptr; + update(); + } } } diff --git a/Libraries/LibGUI/GWindow.h b/Libraries/LibGUI/GWindow.h index 66f815c5fa..9367d42c99 100644 --- a/Libraries/LibGUI/GWindow.h +++ b/Libraries/LibGUI/GWindow.h @@ -101,7 +101,8 @@ public: const GWidget* focused_widget() const { return m_focused_widget; } void set_focused_widget(GWidget*); - void update(const Rect& = Rect()); + void update(); + void update(const Rect&); void set_global_cursor_tracking_widget(GWidget*); GWidget* global_cursor_tracking_widget() { return m_global_cursor_tracking_widget.ptr(); } @@ -136,7 +137,7 @@ public: void schedule_relayout(); static void update_all_windows(Badge); - void notify_state_changed(Badge, bool minimized); + void notify_state_changed(Badge, bool minimized, bool occluded); protected: GWindow(CObject* parent = nullptr); diff --git a/Libraries/LibGUI/GWindowServerConnection.cpp b/Libraries/LibGUI/GWindowServerConnection.cpp index 9bb86ef936..39e3713c85 100644 --- a/Libraries/LibGUI/GWindowServerConnection.cpp +++ b/Libraries/LibGUI/GWindowServerConnection.cpp @@ -299,5 +299,5 @@ void GWindowServerConnection::handle(const WindowClient::DragCancelled&) void GWindowServerConnection::handle(const WindowClient::WindowStateChanged& message) { if (auto* window = GWindow::from_window_id(message.window_id())) - window->notify_state_changed({}, message.minimized()); + window->notify_state_changed({}, message.minimized(), message.occluded()); } diff --git a/Servers/WindowServer/WSWindow.cpp b/Servers/WindowServer/WSWindow.cpp index d1fd08bfe7..eaf45f4df0 100644 --- a/Servers/WindowServer/WSWindow.cpp +++ b/Servers/WindowServer/WSWindow.cpp @@ -110,6 +110,22 @@ void WSWindow::set_minimized(bool minimized) WSWindowManager::the().notify_minimization_state_changed(*this); } +void WSWindow::set_opacity(float opacity) +{ + if (m_opacity == opacity) + return; + m_opacity = opacity; + WSWindowManager::the().notify_opacity_changed(*this); +} + +void WSWindow::set_occluded(bool occluded) +{ + if (m_occluded == occluded) + return; + m_occluded = occluded; + WSWindowManager::the().notify_occlusion_state_changed(*this); +} + void WSWindow::set_maximized(bool maximized) { if (m_maximized == maximized) diff --git a/Servers/WindowServer/WSWindow.h b/Servers/WindowServer/WSWindow.h index b89cf4541a..e2850b8bf1 100644 --- a/Servers/WindowServer/WSWindow.h +++ b/Servers/WindowServer/WSWindow.h @@ -47,6 +47,9 @@ public: bool is_fullscreen() const { return m_fullscreen; } void set_fullscreen(bool); + bool is_occluded() const { return m_occluded; } + void set_occluded(bool); + bool show_titlebar() const { return m_show_titlebar; } void set_show_titlebar(bool show) { m_show_titlebar = show; } @@ -72,7 +75,7 @@ public: void set_title(const String&); float opacity() const { return m_opacity; } - void set_opacity(float opacity) { m_opacity = opacity; } + void set_opacity(float); int x() const { return m_rect.x(); } int y() const { return m_rect.y(); } @@ -193,6 +196,7 @@ private: bool m_minimized { false }; bool m_maximized { false }; bool m_fullscreen { false }; + bool m_occluded { false }; bool m_show_titlebar { true }; RefPtr m_backing_store; RefPtr m_last_backing_store; diff --git a/Servers/WindowServer/WSWindowManager.cpp b/Servers/WindowServer/WSWindowManager.cpp index 3a96906ad1..2c335c83d6 100644 --- a/Servers/WindowServer/WSWindowManager.cpp +++ b/Servers/WindowServer/WSWindowManager.cpp @@ -425,22 +425,51 @@ void WSWindowManager::notify_rect_changed(WSWindow& window, const Rect& old_rect #endif if (m_switcher.is_visible() && window.type() != WSWindowType::WindowSwitcher) m_switcher.refresh(); + + recompute_occlusions(); + tell_wm_listeners_window_rect_changed(window); m_menu_manager.refresh(); } +void WSWindowManager::recompute_occlusions() +{ + for_each_visible_window_from_back_to_front([&](WSWindow& window) { + if (m_switcher.is_visible()) { + window.set_occluded(false); + } else { + if (any_opaque_window_above_this_one_contains_rect(window, window.frame().rect())) + window.set_occluded(true); + else + window.set_occluded(false); + } + return IterationDecision::Continue; + }); +} + +void WSWindowManager::notify_opacity_changed(WSWindow&) +{ + recompute_occlusions(); +} + void WSWindowManager::notify_minimization_state_changed(WSWindow& window) { tell_wm_listeners_window_state_changed(window); if (window.client()) - window.client()->post_message(WindowClient::WindowStateChanged(window.window_id(), window.is_minimized())); + window.client()->post_message(WindowClient::WindowStateChanged(window.window_id(), window.is_minimized(), window.is_occluded())); if (window.is_active() && window.is_minimized()) pick_new_active_window(); } +void WSWindowManager::notify_occlusion_state_changed(WSWindow& window) +{ + if (window.client()) + window.client()->post_message(WindowClient::WindowStateChanged(window.window_id(), window.is_minimized(), window.is_occluded())); +} + void WSWindowManager::pick_new_active_window() { for_each_visible_window_of_type_from_front_to_back(WSWindowType::Normal, [&](WSWindow& candidate) { diff --git a/Servers/WindowServer/WSWindowManager.h b/Servers/WindowServer/WSWindowManager.h index a7ec2db210..0600b88fd8 100644 --- a/Servers/WindowServer/WSWindowManager.h +++ b/Servers/WindowServer/WSWindowManager.h @@ -68,6 +68,8 @@ public: void notify_title_changed(WSWindow&); void notify_rect_changed(WSWindow&, const Rect& oldRect, const Rect& newRect); void notify_minimization_state_changed(WSWindow&); + void notify_opacity_changed(WSWindow&); + void notify_occlusion_state_changed(WSWindow&); void notify_client_changed_app_menubar(WSClientConnection&); Rect maximized_window_rect(const WSWindow&) const; @@ -199,6 +201,8 @@ private: void tell_wm_listener_about_window_rect(WSWindow& listener, WSWindow&); void pick_new_active_window(); + void recompute_occlusions(); + RefPtr m_arrow_cursor; RefPtr m_hand_cursor; RefPtr m_resize_horizontally_cursor; diff --git a/Servers/WindowServer/WSWindowSwitcher.cpp b/Servers/WindowServer/WSWindowSwitcher.cpp index f98499a43d..2c6622a90d 100644 --- a/Servers/WindowServer/WSWindowSwitcher.cpp +++ b/Servers/WindowServer/WSWindowSwitcher.cpp @@ -28,6 +28,7 @@ void WSWindowSwitcher::set_visible(bool visible) if (m_visible == visible) return; m_visible = visible; + WSWindowManager::the().recompute_occlusions(); if (m_switcher_window) m_switcher_window->set_visible(visible); if (!m_visible) diff --git a/Servers/WindowServer/WindowClient.ipc b/Servers/WindowServer/WindowClient.ipc index a5bba389a5..d443b3264d 100644 --- a/Servers/WindowServer/WindowClient.ipc +++ b/Servers/WindowServer/WindowClient.ipc @@ -12,7 +12,7 @@ endpoint WindowClient = 4 KeyUp(i32 window_id, u8 character, u32 key, u32 modifiers) =| WindowActivated(i32 window_id) =| WindowDeactivated(i32 window_id) =| - WindowStateChanged(i32 window_id, bool minimized) =| + WindowStateChanged(i32 window_id, bool minimized, bool occluded) =| WindowCloseRequest(i32 window_id) =| WindowResized(i32 window_id, Rect old_rect, Rect new_rect) =|