1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 07:48:11 +00:00

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. :^)
This commit is contained in:
Andreas Kling 2019-12-27 11:34:40 +01:00
parent 5d1acdda82
commit c7847d7c81
9 changed files with 72 additions and 10 deletions

View file

@ -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<GWindowServerConnection>)
}
}
void GWindow::notify_state_changed(Badge<GWindowServerConnection>, bool minimized)
void GWindow::notify_state_changed(Badge<GWindowServerConnection>, 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<GraphicsBitmap>& 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();
}
}
}

View file

@ -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<GWindowServerConnection>);
void notify_state_changed(Badge<GWindowServerConnection>, bool minimized);
void notify_state_changed(Badge<GWindowServerConnection>, bool minimized, bool occluded);
protected:
GWindow(CObject* parent = nullptr);

View file

@ -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());
}

View file

@ -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)

View file

@ -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<GraphicsBitmap> m_backing_store;
RefPtr<GraphicsBitmap> m_last_backing_store;

View file

@ -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) {

View file

@ -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<WSCursor> m_arrow_cursor;
RefPtr<WSCursor> m_hand_cursor;
RefPtr<WSCursor> m_resize_horizontally_cursor;

View file

@ -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)

View file

@ -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) =|