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