summaryrefslogtreecommitdiff
path: root/Userland
diff options
context:
space:
mode:
authorTom <tomut@yahoo.com>2021-06-30 19:12:02 -0600
committerAndreas Kling <kling@serenityos.org>2021-07-03 12:27:23 +0200
commit7984c2836d7a7e472aca383225deed5d50601e06 (patch)
tree477073e39dc6351baa6f404651beecbbf6e30adb /Userland
parent584b144953ba0f715c02b54444a61674a4c99e8a (diff)
downloadserenity-7984c2836d7a7e472aca383225deed5d50601e06.zip
WindowServer: Add API to change virtual desktop settings
This also adds the ability to query how many virtual desktops are set up, and for the Taskbar to be notified when the active virtual desktop has changed.
Diffstat (limited to 'Userland')
-rw-r--r--Userland/Libraries/LibGUI/Desktop.cpp5
-rw-r--r--Userland/Libraries/LibGUI/Desktop.h7
-rw-r--r--Userland/Libraries/LibGUI/Event.h26
-rw-r--r--Userland/Libraries/LibGUI/WindowManagerServerConnection.cpp14
-rw-r--r--Userland/Libraries/LibGUI/WindowManagerServerConnection.h3
-rw-r--r--Userland/Libraries/LibGUI/WindowServerConnection.cpp8
-rw-r--r--Userland/Libraries/LibGUI/WindowServerConnection.h4
-rw-r--r--Userland/Services/WindowServer/ClientConnection.cpp22
-rw-r--r--Userland/Services/WindowServer/ClientConnection.h4
-rw-r--r--Userland/Services/WindowServer/Compositor.cpp80
-rw-r--r--Userland/Services/WindowServer/Compositor.h7
-rw-r--r--Userland/Services/WindowServer/Window.cpp1
-rw-r--r--Userland/Services/WindowServer/Window.h1
-rw-r--r--Userland/Services/WindowServer/WindowClient.ipc4
-rw-r--r--Userland/Services/WindowServer/WindowManager.cpp188
-rw-r--r--Userland/Services/WindowServer/WindowManager.h16
-rw-r--r--Userland/Services/WindowServer/WindowManagerClient.ipc3
-rw-r--r--Userland/Services/WindowServer/WindowServer.ipc3
-rw-r--r--Userland/Services/WindowServer/WindowStack.cpp28
-rw-r--r--Userland/Services/WindowServer/WindowStack.h20
20 files changed, 380 insertions, 64 deletions
diff --git a/Userland/Libraries/LibGUI/Desktop.cpp b/Userland/Libraries/LibGUI/Desktop.cpp
index 6bc688c6ba..3363c448e8 100644
--- a/Userland/Libraries/LibGUI/Desktop.cpp
+++ b/Userland/Libraries/LibGUI/Desktop.cpp
@@ -25,7 +25,7 @@ Desktop::Desktop()
{
}
-void Desktop::did_receive_screen_rects(Badge<WindowServerConnection>, const Vector<Gfx::IntRect, 4>& rects, size_t main_screen_index)
+void Desktop::did_receive_screen_rects(Badge<WindowServerConnection>, const Vector<Gfx::IntRect, 4>& rects, size_t main_screen_index, unsigned virtual_desktop_rows, unsigned virtual_desktop_columns)
{
m_main_screen_index = main_screen_index;
m_rects = rects;
@@ -36,6 +36,9 @@ void Desktop::did_receive_screen_rects(Badge<WindowServerConnection>, const Vect
} else {
m_bounding_rect = {};
}
+
+ m_virtual_desktop_rows = virtual_desktop_rows;
+ m_virtual_desktop_columns = virtual_desktop_columns;
}
void Desktop::set_background_color(const StringView& background_color)
diff --git a/Userland/Libraries/LibGUI/Desktop.h b/Userland/Libraries/LibGUI/Desktop.h
index a915ea4bac..bdcda74973 100644
--- a/Userland/Libraries/LibGUI/Desktop.h
+++ b/Userland/Libraries/LibGUI/Desktop.h
@@ -36,14 +36,19 @@ public:
const Vector<Gfx::IntRect, 4>& rects() const { return m_rects; }
size_t main_screen_index() const { return m_main_screen_index; }
+ unsigned virtual_desktop_rows() const { return m_virtual_desktop_rows; }
+ unsigned virtual_desktop_columns() const { return m_virtual_desktop_columns; }
+
int taskbar_height() const { return TaskbarWindow::taskbar_height(); }
- void did_receive_screen_rects(Badge<WindowServerConnection>, const Vector<Gfx::IntRect, 4>&, size_t);
+ void did_receive_screen_rects(Badge<WindowServerConnection>, const Vector<Gfx::IntRect, 4>&, size_t, unsigned, unsigned);
private:
Vector<Gfx::IntRect, default_screen_rect_count> m_rects;
size_t m_main_screen_index { 0 };
Gfx::IntRect m_bounding_rect;
+ unsigned m_virtual_desktop_rows { 1 };
+ unsigned m_virtual_desktop_columns { 1 };
};
}
diff --git a/Userland/Libraries/LibGUI/Event.h b/Userland/Libraries/LibGUI/Event.h
index 92035a8d05..446e020beb 100644
--- a/Userland/Libraries/LibGUI/Event.h
+++ b/Userland/Libraries/LibGUI/Event.h
@@ -64,6 +64,7 @@ public:
WM_AppletAreaSizeChanged,
WM_SuperKeyPressed,
WM_SuperSpaceKeyPressed,
+ WM_VirtualDesktopChanged,
__End_WM_Events,
};
@@ -135,13 +136,15 @@ public:
class WMWindowStateChangedEvent : public WMEvent {
public:
- WMWindowStateChangedEvent(int client_id, int window_id, int parent_client_id, int parent_window_id, const StringView& title, const Gfx::IntRect& rect, bool is_active, bool is_modal, WindowType window_type, bool is_minimized, bool is_frameless, Optional<int> progress)
+ WMWindowStateChangedEvent(int client_id, int window_id, int parent_client_id, int parent_window_id, const StringView& title, const Gfx::IntRect& rect, unsigned virtual_desktop_row, unsigned virtual_desktop_column, bool is_active, bool is_modal, WindowType window_type, bool is_minimized, bool is_frameless, Optional<int> progress)
: WMEvent(Event::Type::WM_WindowStateChanged, client_id, window_id)
, m_parent_client_id(parent_client_id)
, m_parent_window_id(parent_window_id)
, m_title(title)
, m_rect(rect)
, m_window_type(window_type)
+ , m_virtual_desktop_row(virtual_desktop_row)
+ , m_virtual_desktop_column(virtual_desktop_column)
, m_active(is_active)
, m_modal(is_modal)
, m_minimized(is_minimized)
@@ -160,6 +163,8 @@ public:
bool is_minimized() const { return m_minimized; }
bool is_frameless() const { return m_frameless; }
Optional<int> progress() const { return m_progress; }
+ unsigned virtual_desktop_row() const { return m_virtual_desktop_row; }
+ unsigned virtual_desktop_column() const { return m_virtual_desktop_column; }
private:
int m_parent_client_id;
@@ -167,6 +172,8 @@ private:
String m_title;
Gfx::IntRect m_rect;
WindowType m_window_type;
+ unsigned m_virtual_desktop_row;
+ unsigned m_virtual_desktop_column;
bool m_active;
bool m_modal;
bool m_minimized;
@@ -202,6 +209,23 @@ private:
RefPtr<Gfx::Bitmap> m_bitmap;
};
+class WMVirtualDesktopChangedEvent : public WMEvent {
+public:
+ explicit WMVirtualDesktopChangedEvent(int client_id, unsigned current_row, unsigned current_column)
+ : WMEvent(Event::Type::WM_VirtualDesktopChanged, client_id, 0)
+ , m_current_row(current_row)
+ , m_current_column(current_column)
+ {
+ }
+
+ unsigned current_row() const { return m_current_row; }
+ unsigned current_column() const { return m_current_column; }
+
+private:
+ const unsigned m_current_row;
+ const unsigned m_current_column;
+};
+
class MultiPaintEvent final : public Event {
public:
explicit MultiPaintEvent(const Vector<Gfx::IntRect, 32>& rects, const Gfx::IntSize& window_size)
diff --git a/Userland/Libraries/LibGUI/WindowManagerServerConnection.cpp b/Userland/Libraries/LibGUI/WindowManagerServerConnection.cpp
index b3bc301624..b9b2f7c84c 100644
--- a/Userland/Libraries/LibGUI/WindowManagerServerConnection.cpp
+++ b/Userland/Libraries/LibGUI/WindowManagerServerConnection.cpp
@@ -20,11 +20,12 @@ WindowManagerServerConnection& WindowManagerServerConnection::the()
}
void WindowManagerServerConnection::window_state_changed(i32 wm_id, i32 client_id, i32 window_id,
- i32 parent_client_id, i32 parent_window_id, bool is_active, bool is_minimized, bool is_modal,
- bool is_frameless, i32 window_type, String const& title, Gfx::IntRect const& rect, Optional<i32> const& progress)
+ i32 parent_client_id, i32 parent_window_id, u32 virtual_desktop_row, u32 virtual_desktop_column,
+ bool is_active, bool is_minimized, bool is_modal, bool is_frameless, i32 window_type,
+ String const& title, Gfx::IntRect const& rect, Optional<i32> const& progress)
{
if (auto* window = Window::from_window_id(wm_id))
- Core::EventLoop::current().post_event(*window, make<WMWindowStateChangedEvent>(client_id, window_id, parent_client_id, parent_window_id, title, rect, is_active, is_modal, static_cast<WindowType>(window_type), is_minimized, is_frameless, progress));
+ Core::EventLoop::current().post_event(*window, make<WMWindowStateChangedEvent>(client_id, window_id, parent_client_id, parent_window_id, title, rect, virtual_desktop_row, virtual_desktop_column, is_active, is_modal, static_cast<WindowType>(window_type), is_minimized, is_frameless, progress));
}
void WindowManagerServerConnection::applet_area_size_changed(i32 wm_id, const Gfx::IntSize& size)
@@ -63,4 +64,11 @@ void WindowManagerServerConnection::super_space_key_pressed(i32 wm_id)
if (auto* window = Window::from_window_id(wm_id))
Core::EventLoop::current().post_event(*window, make<WMSuperSpaceKeyPressedEvent>(wm_id));
}
+
+void WindowManagerServerConnection::virtual_desktop_changed(i32 wm_id, u32 row, u32 column)
+{
+ if (auto* window = Window::from_window_id(wm_id))
+ Core::EventLoop::current().post_event(*window, make<WMVirtualDesktopChangedEvent>(wm_id, row, column));
+}
+
}
diff --git a/Userland/Libraries/LibGUI/WindowManagerServerConnection.h b/Userland/Libraries/LibGUI/WindowManagerServerConnection.h
index 990dbd1841..b91d4e9005 100644
--- a/Userland/Libraries/LibGUI/WindowManagerServerConnection.h
+++ b/Userland/Libraries/LibGUI/WindowManagerServerConnection.h
@@ -27,12 +27,13 @@ public:
private:
virtual void window_removed(i32, i32, i32) override;
- virtual void window_state_changed(i32, i32, i32, i32, i32, bool, bool, bool, bool, i32, String const&, Gfx::IntRect const&, Optional<i32> const&) override;
+ virtual void window_state_changed(i32, i32, i32, i32, i32, u32, u32, bool, bool, bool, bool, i32, String const&, Gfx::IntRect const&, Optional<i32> const&) override;
virtual void window_icon_bitmap_changed(i32, i32, i32, Gfx::ShareableBitmap const&) override;
virtual void window_rect_changed(i32, i32, i32, Gfx::IntRect const&) override;
virtual void applet_area_size_changed(i32, Gfx::IntSize const&) override;
virtual void super_key_pressed(i32) override;
virtual void super_space_key_pressed(i32) override;
+ virtual void virtual_desktop_changed(i32, u32, u32) override;
};
}
diff --git a/Userland/Libraries/LibGUI/WindowServerConnection.cpp b/Userland/Libraries/LibGUI/WindowServerConnection.cpp
index a0ddbf0e98..e91d244f76 100644
--- a/Userland/Libraries/LibGUI/WindowServerConnection.cpp
+++ b/Userland/Libraries/LibGUI/WindowServerConnection.cpp
@@ -48,12 +48,12 @@ WindowServerConnection::WindowServerConnection()
// All we have to do is wait for it to arrive. This avoids a round-trip during application startup.
auto message = wait_for_specific_message<Messages::WindowClient::FastGreet>();
set_system_theme_from_anonymous_buffer(message->theme_buffer());
- Desktop::the().did_receive_screen_rects({}, message->screen_rects(), message->main_screen_index());
+ Desktop::the().did_receive_screen_rects({}, message->screen_rects(), message->main_screen_index(), message->virtual_desktop_rows(), message->virtual_desktop_columns());
Gfx::FontDatabase::set_default_font_query(message->default_font_query());
Gfx::FontDatabase::set_fixed_width_font_query(message->fixed_width_font_query());
}
-void WindowServerConnection::fast_greet(Vector<Gfx::IntRect> const&, u32, Core::AnonymousBuffer const&, String const&, String const&)
+void WindowServerConnection::fast_greet(Vector<Gfx::IntRect> const&, u32, u32, u32, Core::AnonymousBuffer const&, String const&, String const&)
{
// NOTE: This message is handled in the constructor.
}
@@ -311,9 +311,9 @@ void WindowServerConnection::menu_item_left(i32 menu_id, u32 identifier)
Core::EventLoop::current().post_event(*app, make<ActionEvent>(GUI::Event::ActionLeave, *action));
}
-void WindowServerConnection::screen_rects_changed(Vector<Gfx::IntRect> const& rects, u32 main_screen_index)
+void WindowServerConnection::screen_rects_changed(Vector<Gfx::IntRect> const& rects, u32 main_screen_index, u32 virtual_desktop_rows, u32 virtual_desktop_columns)
{
- Desktop::the().did_receive_screen_rects({}, rects, main_screen_index);
+ Desktop::the().did_receive_screen_rects({}, rects, main_screen_index, virtual_desktop_rows, virtual_desktop_columns);
Window::for_each_window({}, [&](auto& window) {
Core::EventLoop::current().post_event(window, make<ScreenRectsChangeEvent>(rects, main_screen_index));
});
diff --git a/Userland/Libraries/LibGUI/WindowServerConnection.h b/Userland/Libraries/LibGUI/WindowServerConnection.h
index f8df378dd8..d68aa7aad2 100644
--- a/Userland/Libraries/LibGUI/WindowServerConnection.h
+++ b/Userland/Libraries/LibGUI/WindowServerConnection.h
@@ -23,7 +23,7 @@ public:
private:
WindowServerConnection();
- virtual void fast_greet(Vector<Gfx::IntRect> const&, u32, Core::AnonymousBuffer const&, String const&, String const&) override;
+ virtual void fast_greet(Vector<Gfx::IntRect> const&, u32, u32, u32, Core::AnonymousBuffer const&, String const&, String const&) override;
virtual void paint(i32, Gfx::IntSize const&, Vector<Gfx::IntRect> const&) override;
virtual void mouse_move(i32, Gfx::IntPoint const&, u32, u32, u32, i32, bool, Vector<String> const&) override;
virtual void mouse_down(i32, Gfx::IntPoint const&, u32, u32, u32, i32) override;
@@ -44,7 +44,7 @@ private:
virtual void menu_item_entered(i32, u32) override;
virtual void menu_item_left(i32, u32) override;
virtual void menu_visibility_did_change(i32, bool) override;
- virtual void screen_rects_changed(Vector<Gfx::IntRect> const&, u32) override;
+ virtual void screen_rects_changed(Vector<Gfx::IntRect> const&, u32, u32, u32) override;
virtual void set_wallpaper_finished(bool) override;
virtual void drag_dropped(i32, Gfx::IntPoint const&, String const&, HashMap<String, ByteBuffer> const&) override;
virtual void drag_accepted() override;
diff --git a/Userland/Services/WindowServer/ClientConnection.cpp b/Userland/Services/WindowServer/ClientConnection.cpp
index b7ad467f53..19461657fa 100644
--- a/Userland/Services/WindowServer/ClientConnection.cpp
+++ b/Userland/Services/WindowServer/ClientConnection.cpp
@@ -53,7 +53,8 @@ ClientConnection::ClientConnection(NonnullRefPtr<Core::LocalSocket> client_socke
s_connections = new HashMap<int, NonnullRefPtr<ClientConnection>>;
s_connections->set(client_id, *this);
- async_fast_greet(Screen::rects(), Screen::main().index(), Gfx::current_system_theme_buffer(), Gfx::FontDatabase::default_font_query(), Gfx::FontDatabase::fixed_width_font_query());
+ auto& wm = WindowManager::the();
+ async_fast_greet(Screen::rects(), Screen::main().index(), wm.window_stack_rows(), wm.window_stack_columns(), Gfx::current_system_theme_buffer(), Gfx::FontDatabase::default_font_query(), Gfx::FontDatabase::fixed_width_font_query());
}
ClientConnection::~ClientConnection()
@@ -84,9 +85,10 @@ void ClientConnection::die()
});
}
-void ClientConnection::notify_about_new_screen_rects(Vector<Gfx::IntRect, 4> const& rects, size_t main_screen_index)
+void ClientConnection::notify_about_new_screen_rects()
{
- async_screen_rects_changed(rects, main_screen_index);
+ auto& wm = WindowManager::the();
+ async_screen_rects_changed(Screen::rects(), Screen::main().index(), wm.window_stack_rows(), wm.window_stack_columns());
}
void ClientConnection::create_menubar(i32 menubar_id)
@@ -323,6 +325,20 @@ Messages::WindowServer::SaveScreenLayoutResponse ClientConnection::save_screen_l
return { success, move(error_msg) };
}
+Messages::WindowServer::ApplyVirtualDesktopSettingsResponse ClientConnection::apply_virtual_desktop_settings(u32 rows, u32 columns, bool save)
+{
+ if (rows == 0 || columns == 0 || rows > WindowManager::max_window_stack_rows || columns > WindowManager::max_window_stack_columns)
+ return { false };
+
+ return { WindowManager::the().apply_virtual_desktop_settings(rows, columns, save) };
+}
+
+Messages::WindowServer::GetVirtualDesktopSettingsResponse ClientConnection::get_virtual_desktop_settings()
+{
+ auto& wm = WindowManager::the();
+ return { (unsigned)wm.window_stack_rows(), (unsigned)wm.window_stack_columns(), WindowManager::max_window_stack_rows, WindowManager::max_window_stack_columns };
+}
+
void ClientConnection::show_screen_numbers(bool show)
{
if (m_show_screen_number == show)
diff --git a/Userland/Services/WindowServer/ClientConnection.h b/Userland/Services/WindowServer/ClientConnection.h
index 40e27ca1bd..6214a3f696 100644
--- a/Userland/Services/WindowServer/ClientConnection.h
+++ b/Userland/Services/WindowServer/ClientConnection.h
@@ -42,7 +42,7 @@ public:
static ClientConnection* from_client_id(int client_id);
static void for_each_client(Function<void(ClientConnection&)>);
- void notify_about_new_screen_rects(const Vector<Gfx::IntRect, 4>&, size_t);
+ void notify_about_new_screen_rects();
void post_paint_message(Window&, bool ignore_occlusion = false);
Menu* find_menu_by_id(int menu_id)
@@ -130,6 +130,8 @@ private:
virtual Messages::WindowServer::SetScreenLayoutResponse set_screen_layout(ScreenLayout const&, bool) override;
virtual Messages::WindowServer::GetScreenLayoutResponse get_screen_layout() override;
virtual Messages::WindowServer::SaveScreenLayoutResponse save_screen_layout() override;
+ virtual Messages::WindowServer::ApplyVirtualDesktopSettingsResponse apply_virtual_desktop_settings(u32, u32, bool) override;
+ virtual Messages::WindowServer::GetVirtualDesktopSettingsResponse get_virtual_desktop_settings() override;
virtual void show_screen_numbers(bool) override;
virtual void set_window_cursor(i32, i32) override;
virtual void set_window_custom_cursor(i32, Gfx::ShareableBitmap const&) override;
diff --git a/Userland/Services/WindowServer/Compositor.cpp b/Userland/Services/WindowServer/Compositor.cpp
index f6bbb4458f..91d81357e6 100644
--- a/Userland/Services/WindowServer/Compositor.cpp
+++ b/Userland/Services/WindowServer/Compositor.cpp
@@ -1287,11 +1287,7 @@ void Compositor::update_animations(Screen& screen, Gfx::DisjointRectSet& flush_r
void Compositor::create_window_stack_switch_overlay(WindowStack& target_stack)
{
- if (m_stack_switch_overlay_timer) {
- // Cancel any timer, we're going to delete the overlay
- m_stack_switch_overlay_timer->stop();
- m_stack_switch_overlay_timer = nullptr;
- }
+ stop_window_stack_switch_overlay_timer();
Screen::for_each([&](auto& screen) {
auto& screen_data = m_screen_data[screen.index()];
screen_data.m_window_stack_switch_overlay = nullptr; // delete it first
@@ -1301,20 +1297,45 @@ void Compositor::create_window_stack_switch_overlay(WindowStack& target_stack)
});
}
+void Compositor::remove_window_stack_switch_overlays()
+{
+ Screen::for_each([&](auto& screen) {
+ auto& screen_data = m_screen_data[screen.index()];
+ screen_data.m_window_stack_switch_overlay = nullptr;
+ return IterationDecision::Continue;
+ });
+}
+
+void Compositor::stop_window_stack_switch_overlay_timer()
+{
+ if (m_stack_switch_overlay_timer) {
+ // Cancel any timer, we're going to delete the overlay
+ m_stack_switch_overlay_timer->stop();
+ m_stack_switch_overlay_timer = nullptr;
+ }
+}
+
void Compositor::start_window_stack_switch_overlay_timer()
{
if (m_stack_switch_overlay_timer) {
m_stack_switch_overlay_timer->stop();
m_stack_switch_overlay_timer = nullptr;
}
+ bool have_overlay = false;
+ Screen::for_each([&](auto& screen) {
+ auto& screen_data = m_screen_data[screen.index()];
+ if (screen_data.m_window_stack_switch_overlay) {
+ have_overlay = true;
+ return IterationDecision::Break;
+ }
+ return IterationDecision::Continue;
+ });
+ if (!have_overlay)
+ return;
m_stack_switch_overlay_timer = Core::Timer::create_single_shot(
500,
[this] {
- Screen::for_each([&](auto& screen) {
- auto& screen_data = m_screen_data[screen.index()];
- screen_data.m_window_stack_switch_overlay = nullptr;
- return IterationDecision::Continue;
- });
+ remove_window_stack_switch_overlays();
},
this);
m_stack_switch_overlay_timer->start();
@@ -1349,7 +1370,26 @@ void Compositor::finish_window_stack_switch()
start_window_stack_switch_overlay_timer();
}
-void Compositor::switch_to_window_stack(WindowStack& new_window_stack)
+void Compositor::set_current_window_stack_no_transition(WindowStack& new_window_stack)
+{
+ if (m_transitioning_to_window_stack) {
+ finish_window_stack_switch();
+ VERIFY(!m_window_stack_transition_animation);
+ VERIFY(!m_transitioning_to_window_stack);
+ }
+ if (m_current_window_stack == &new_window_stack)
+ return;
+ m_current_window_stack = &new_window_stack;
+ invalidate_for_window_stack_merge_or_change();
+}
+
+void Compositor::invalidate_for_window_stack_merge_or_change()
+{
+ invalidate_occlusions();
+ invalidate_screen();
+}
+
+void Compositor::switch_to_window_stack(WindowStack& new_window_stack, bool show_overlay)
{
if (m_transitioning_to_window_stack) {
if (m_transitioning_to_window_stack == &new_window_stack)
@@ -1363,8 +1403,13 @@ void Compositor::switch_to_window_stack(WindowStack& new_window_stack)
if (&new_window_stack == m_current_window_stack) {
// So that the user knows which stack they're on, show the overlay briefly
- create_window_stack_switch_overlay(*m_current_window_stack);
- start_window_stack_switch_overlay_timer();
+ if (show_overlay) {
+ create_window_stack_switch_overlay(*m_current_window_stack);
+ start_window_stack_switch_overlay_timer();
+ } else {
+ stop_window_stack_switch_overlay_timer();
+ remove_window_stack_switch_overlays();
+ }
return;
}
VERIFY(!m_transitioning_to_window_stack);
@@ -1387,8 +1432,13 @@ void Compositor::switch_to_window_stack(WindowStack& new_window_stack)
m_transitioning_to_window_stack->set_transition_offset({}, { -delta_x, -delta_y });
m_current_window_stack->set_transition_offset({}, {});
- create_window_stack_switch_overlay(*m_transitioning_to_window_stack);
- // We start the timer when the animation ends!
+ if (show_overlay) {
+ // We start the timer when the animation ends!
+ create_window_stack_switch_overlay(*m_transitioning_to_window_stack);
+ } else {
+ stop_window_stack_switch_overlay_timer();
+ remove_window_stack_switch_overlays();
+ }
VERIFY(!m_window_stack_transition_animation);
m_window_stack_transition_animation = Animation::create();
diff --git a/Userland/Services/WindowServer/Compositor.h b/Userland/Services/WindowServer/Compositor.h
index f869d9d51f..9e8f5db827 100644
--- a/Userland/Services/WindowServer/Compositor.h
+++ b/Userland/Services/WindowServer/Compositor.h
@@ -112,7 +112,9 @@ public:
}
bool is_switching_window_stacks() const { return m_transitioning_to_window_stack != nullptr; }
- void switch_to_window_stack(WindowStack&);
+ void switch_to_window_stack(WindowStack&, bool);
+ void set_current_window_stack_no_transition(WindowStack&);
+ void invalidate_for_window_stack_merge_or_change();
void did_construct_window_manager(Badge<WindowManager>);
@@ -142,6 +144,8 @@ private:
Gfx::IntPoint window_transition_offset(Window&);
void update_animations(Screen&, Gfx::DisjointRectSet& flush_rects);
void create_window_stack_switch_overlay(WindowStack&);
+ void remove_window_stack_switch_overlays();
+ void stop_window_stack_switch_overlay_timer();
void start_window_stack_switch_overlay_timer();
void finish_window_stack_switch();
@@ -227,7 +231,6 @@ private:
WindowStack* m_current_window_stack { nullptr };
WindowStack* m_transitioning_to_window_stack { nullptr };
RefPtr<Animation> m_window_stack_transition_animation;
- OwnPtr<WindowStackSwitchOverlay> m_stack_switch_overlay;
RefPtr<Core::Timer> m_stack_switch_overlay_timer;
size_t m_show_screen_number_count { 0 };
diff --git a/Userland/Services/WindowServer/Window.cpp b/Userland/Services/WindowServer/Window.cpp
index 76f92ac1c1..c070e3bf27 100644
--- a/Userland/Services/WindowServer/Window.cpp
+++ b/Userland/Services/WindowServer/Window.cpp
@@ -1204,4 +1204,5 @@ String Window::computed_title() const
return String::formatted("{} (Not responding)", title);
return title;
}
+
}
diff --git a/Userland/Services/WindowServer/Window.h b/Userland/Services/WindowServer/Window.h
index f6728da7f5..8af13a8a5b 100644
--- a/Userland/Services/WindowServer/Window.h
+++ b/Userland/Services/WindowServer/Window.h
@@ -35,6 +35,7 @@ enum WMEventMask {
WindowStateChanges = 1 << 1,
WindowIconChanges = 1 << 2,
WindowRemovals = 1 << 3,
+ VirtualDesktopChanges = 1 << 4,
};
enum class WindowTileType {
diff --git a/Userland/Services/WindowServer/WindowClient.ipc b/Userland/Services/WindowServer/WindowClient.ipc
index 28fcee3262..9971f82198 100644
--- a/Userland/Services/WindowServer/WindowClient.ipc
+++ b/Userland/Services/WindowServer/WindowClient.ipc
@@ -3,7 +3,7 @@
endpoint WindowClient
{
- fast_greet(Vector<Gfx::IntRect> screen_rects, u32 main_screen_index, Core::AnonymousBuffer theme_buffer, String default_font_query, String fixed_width_font_query) =|
+ fast_greet(Vector<Gfx::IntRect> screen_rects, u32 main_screen_index, u32 virtual_desktop_rows, u32 virtual_desktop_columns, Core::AnonymousBuffer theme_buffer, String default_font_query, String fixed_width_font_query) =|
paint(i32 window_id, Gfx::IntSize window_size, Vector<Gfx::IntRect> rects) =|
mouse_move(i32 window_id, Gfx::IntPoint mouse_position, u32 button, u32 buttons, u32 modifiers, i32 wheel_delta, bool is_drag, Vector<String> mime_types) =|
@@ -28,7 +28,7 @@ endpoint WindowClient
menu_item_left(i32 menu_id, u32 identifier) =|
menu_visibility_did_change(i32 menu_id, bool visible) =|
- screen_rects_changed(Vector<Gfx::IntRect> rects, u32 main_screen_index) =|
+ screen_rects_changed(Vector<Gfx::IntRect> rects, u32 main_screen_index, u32 virtual_desktop_rows, u32 virtual_desktop_columns) =|
set_wallpaper_finished(bool success) =|
diff --git a/Userland/Services/WindowServer/WindowManager.cpp b/Userland/Services/WindowServer/WindowManager.cpp
index 039efac1bc..0dd823016c 100644
--- a/Userland/Services/WindowServer/WindowManager.cpp
+++ b/Userland/Services/WindowServer/WindowManager.cpp
@@ -40,23 +40,13 @@ WindowManager::WindowManager(Gfx::PaletteImpl const& palette)
s_the = this;
{
- // Create the default window stacks
- auto row_count = m_window_stacks.capacity();
- VERIFY(row_count > 0);
- for (size_t row_index = 0; row_index < row_count; row_index++) {
- auto row = adopt_own(*new RemoveReference<decltype(m_window_stacks[0])>());
- auto column_count = row->capacity();
- VERIFY(column_count > 0);
- for (size_t column_index = 0; column_index < column_count; column_index++)
- row->append(adopt_own(*new WindowStack(row_index, column_index)));
- m_window_stacks.append(move(row));
- }
-
- m_current_window_stack = &m_window_stacks[0][0];
- for (auto& row : m_window_stacks) {
- for (auto& stack : row)
- stack.set_stationary_window_stack(*m_current_window_stack);
- }
+ // If we haven't created any window stacks, at least create the stationary/main window stack
+ auto row = adopt_own(*new RemoveReference<decltype(m_window_stacks[0])>());
+ auto main_window_stack = adopt_own(*new WindowStack(0, 0));
+ main_window_stack->set_stationary_window_stack(*main_window_stack);
+ m_current_window_stack = main_window_stack.ptr();
+ row->append(move(main_window_stack));
+ m_window_stacks.append(move(row));
}
reload_config();
@@ -78,6 +68,14 @@ void WindowManager::reload_config()
{
m_config = Core::ConfigFile::open("/etc/WindowServer.ini");
+ unsigned virtual_desktop_rows = (unsigned)m_config->read_num_entry("VirtualDesktop", "Rows", default_window_stack_rows);
+ unsigned virtual_desktop_columns = (unsigned)m_config->read_num_entry("VirtualDesktop", "Columns", default_window_stack_columns);
+ if (virtual_desktop_rows == 0 || virtual_desktop_columns == 0 || virtual_desktop_rows > max_window_stack_rows || virtual_desktop_columns > max_window_stack_columns) {
+ virtual_desktop_rows = default_window_stack_rows;
+ virtual_desktop_columns = default_window_stack_columns;
+ }
+ apply_virtual_desktop_settings(virtual_desktop_rows, virtual_desktop_columns, false);
+
m_double_click_speed = m_config->read_num_entry("Input", "DoubleClickSpeed", 250);
auto* current_cursor = Compositor::the().current_cursor();
@@ -139,9 +137,7 @@ bool WindowManager::set_screen_layout(ScreenLayout&& screen_layout, bool save, S
Compositor::the().screen_resolution_changed();
- ClientConnection::for_each_client([&](ClientConnection& client) {
- client.notify_about_new_screen_rects(Screen::rects(), Screen::main().index());
- });
+ tell_wms_screen_rects_changed();
for_each_window_stack([&](auto& window_stack) {
window_stack.for_each_window([](Window& window) {
@@ -171,6 +167,113 @@ bool WindowManager::save_screen_layout(String& error_msg)
return true;
}
+bool WindowManager::apply_virtual_desktop_settings(unsigned rows, unsigned columns, bool save)
+{
+ VERIFY(rows != 0);
+ VERIFY(rows <= max_window_stack_rows);
+ VERIFY(columns != 0);
+ VERIFY(columns <= max_window_stack_columns);
+
+ auto current_rows = window_stack_rows();
+ auto current_columns = window_stack_columns();
+ if (rows != current_rows || columns != current_columns) {
+ auto& current_window_stack = this->current_window_stack();
+ auto current_stack_row = current_window_stack.row();
+ auto current_stack_column = current_window_stack.column();
+ bool need_rerender = false;
+ bool removing_current_stack = current_stack_row > rows - 1 || current_stack_column > columns - 1;
+ auto new_current_row = min(current_stack_row, rows - 1);
+ auto new_current_column = min(current_stack_column, columns - 1);
+
+ // Collect all windows that were moved. We can't tell the wms at this point because
+ // the current window stack may not be valid anymore, until after the move is complete
+ Vector<Window*, 32> windows_moved;
+
+ auto merge_window_stack = [&](WindowStack& from_stack, WindowStack& into_stack) {
+ auto move_to = WindowStack::MoveAllWindowsTo::Back;
+
+ // TODO: Figure out a better algorithm. We basically always layer it on top, unless
+ // it's either being moved to window stack we're viewing or that we will be viewing.
+ // In that case we would want to make sure the window stack ends up on top (with no
+ // change to the active window)
+ bool moving_to_new_current_stack = into_stack.row() == new_current_row && into_stack.column() == new_current_column;
+ if (moving_to_new_current_stack)
+ move_to = WindowStack::MoveAllWindowsTo::Front;
+ from_stack.move_all_windows(into_stack, windows_moved, move_to);
+ };
+
+ // While we have too many rows, merge each row too many into the new bottom row
+ while (current_rows > rows) {
+ auto& row = m_window_stacks[rows];
+ for (size_t column_index = 0; column_index < row.size(); column_index++) {
+ merge_window_stack(row[column_index], m_window_stacks[rows - 1][column_index]);
+ if (rows - 1 == current_stack_row && column_index == current_stack_column)
+ need_rerender = true;
+ }
+ m_window_stacks.remove(rows);
+ current_rows--;
+ }
+ // While we have too many columns, merge each column too many into the new right most column
+ while (current_columns > columns) {
+ for (size_t row_index = 0; row_index < current_rows; row_index++) {
+ auto& row = m_window_stacks[row_index];
+ merge_window_stack(row[columns], row[columns - 1]);
+ if (row_index == current_stack_row && columns - 1 == current_stack_column)
+ need_rerender = true;
+ row.remove(columns);
+ }
+ current_columns--;
+ }
+ // Add more rows if necessary
+ while (rows > current_rows) {
+ auto row = adopt_own(*new RemoveReference<decltype(m_window_stacks[0])>());
+ for (size_t column_index = 0; column_index < columns; column_index++) {
+ auto window_stack = adopt_own(*new WindowStack(current_rows, column_index));
+ window_stack->set_stationary_window_stack(m_window_stacks[0][0]);
+ row->append(move(window_stack));
+ }
+ m_window_stacks.append(move(row));
+ current_rows++;
+ }
+ // Add more columns if necessary
+ while (columns > current_columns) {
+ for (size_t row_index = 0; row_index < current_rows; row_index++) {
+ auto& row = m_window_stacks[row_index];
+ while (row.size() < columns) {
+ auto window_stack = adopt_own(*new WindowStack(row_index, row.size()));
+ window_stack->set_stationary_window_stack(m_window_stacks[0][0]);
+ row.append(move(window_stack));
+ }
+ }
+ current_columns++;
+ }
+
+ if (removing_current_stack) {
+ // If we're on a window stack that was removed, we need to move...
+ m_current_window_stack = &m_window_stacks[new_current_row][new_current_column];
+ Compositor::the().set_current_window_stack_no_transition(*m_current_window_stack);
+ need_rerender = false; // The compositor already called invalidate_for_window_stack_merge_or_change for us
+ }
+
+ for (auto* window_moved : windows_moved)
+ WindowManager::the().tell_wms_window_state_changed(*window_moved);
+
+ tell_wms_screen_rects_changed(); // updates the available virtual desktops
+ if (current_stack_row != new_current_row || current_stack_column != new_current_column)
+ tell_wms_current_window_stack_changed();
+
+ if (need_rerender)
+ Compositor::the().invalidate_for_window_stack_merge_or_change();
+ }
+
+ if (save) {
+ m_config->write_num_entry("VirtualDesktop", "Rows", window_stack_rows());
+ m_config->write_num_entry("VirtualDesktop", "Columns", window_stack_columns());
+ return m_config->sync();
+ }
+ return true;
+}
+
void WindowManager::set_acceleration_factor(double factor)
{
ScreenInput::the().set_acceleration_factor(factor);
@@ -322,6 +425,8 @@ void WindowManager::greet_window_manager(WMClientConnection& conn)
if (conn.window_id() < 0)
return;
+ tell_wm_about_current_window_stack(conn);
+
for_each_window_stack([&](auto& window_stack) {
window_stack.for_each_window([&](Window& other_window) {
//if (conn.window_id() != other_window.window_id()) {
@@ -345,7 +450,12 @@ void WindowManager::tell_wm_about_window(WMClientConnection& conn, Window& windo
if (window.is_internal())
return;
auto* parent = window.parent_window();
- conn.async_window_state_changed(conn.window_id(), window.client_id(), window.window_id(), parent ? parent->client_id() : -1, parent ? parent->window_id() : -1, window.is_active(), window.is_minimized(), window.is_modal_dont_unparent(), window.is_frameless(), (i32)window.type(), window.computed_title(), window.rect(), window.progress());
+ auto* window_stack = &current_window_stack();
+ if (!is_stationary_window_type(window.type())) {
+ if (auto* stack = window.outer_stack())
+ window_stack = stack;
+ }
+ conn.async_window_state_changed(conn.window_id(), window.client_id(), window.window_id(), parent ? parent->client_id() : -1, parent ? parent->window_id() : -1, window_stack->row(), window_stack->column(), window.is_active(), window.is_minimized(), window.is_modal_dont_unparent(), window.is_frameless(), (i32)window.type(), window.computed_title(), window.rect(), window.progress());
}
void WindowManager::tell_wm_about_window_rect(WMClientConnection& conn, Window& window)
@@ -370,6 +480,16 @@ void WindowManager::tell_wm_about_window_icon(WMClientConnection& conn, Window&
conn.async_window_icon_bitmap_changed(conn.window_id(), window.client_id(), window.window_id(), window.icon().to_shareable_bitmap());
}
+void WindowManager::tell_wm_about_current_window_stack(WMClientConnection& conn)
+{
+ if (conn.window_id() < 0)
+ return;
+ if (!(conn.event_mask() & WMEventMask::VirtualDesktopChanges))
+ return;
+ auto& window_stack = current_window_stack();
+ conn.async_virtual_desktop_changed(conn.window_id(), window_stack.row(), window_stack.column());
+}
+
void WindowManager::tell_wms_window_state_changed(Window& window)
{
for_each_window_manager([&](WMClientConnection& conn) {
@@ -394,6 +514,13 @@ void WindowManager::tell_wms_window_rect_changed(Window& window)
});
}
+void WindowManager::tell_wms_screen_rects_changed()
+{
+ ClientConnection::for_each_client([&](ClientConnection& client) {
+ client.notify_about_new_screen_rects();
+ });
+}
+
void WindowManager::tell_wms_applet_area_size_changed(Gfx::IntSize const& size)
{
for_each_window_manager([&](WMClientConnection& conn) {
@@ -427,6 +554,14 @@ void WindowManager::tell_wms_super_space_key_pressed()
});
}
+void WindowManager::tell_wms_current_window_stack_changed()
+{
+ for_each_window_manager([&](WMClientConnection& conn) {
+ tell_wm_about_current_window_stack(conn);
+ return IterationDecision::Continue;
+ });
+}
+
static bool window_type_has_title(WindowType type)
{
return type == WindowType::Normal || type == WindowType::ToolWindow;
@@ -1283,7 +1418,7 @@ bool WindowManager::is_window_in_modal_stack(Window& window_in_modal_stack, Wind
return result == IterationDecision::Break;
}
-void WindowManager::switch_to_window_stack(WindowStack& window_stack, Window* carry_window)
+void WindowManager::switch_to_window_stack(WindowStack& window_stack, Window* carry_window, bool show_overlay)
{
m_carry_window_to_new_stack.clear();
m_switching_to_window_stack = &window_stack;
@@ -1334,7 +1469,7 @@ void WindowManager::switch_to_window_stack(WindowStack& window_stack, Window* ca
m_current_window_stack = &window_stack;
}
- Compositor::the().switch_to_window_stack(window_stack);
+ Compositor::the().switch_to_window_stack(window_stack, show_overlay);
}
void WindowManager::did_switch_window_stack(Badge<Compositor>, WindowStack& previous_stack, WindowStack& new_stack)
@@ -1343,7 +1478,6 @@ void WindowManager::did_switch_window_stack(Badge<Compositor>, WindowStack& prev
// We are being notified by the compositor, it should not be switching right now!
VERIFY(!Compositor::the().is_switching_window_stacks());
- VERIFY(&current_window_stack() == &new_stack);
if (m_switching_to_window_stack == &new_stack) {
m_switching_to_window_stack = nullptr;
@@ -1352,8 +1486,10 @@ void WindowManager::did_switch_window_stack(Badge<Compositor>, WindowStack& prev
// carried to when the user rapidly tries to switch stacks, so make sure to
// only reset the moving flag if we arrived at our final destination
for (auto& window_ref : m_carry_window_to_new_stack) {
- if (auto* window = window_ref.ptr())
+ if (auto* window = window_ref.ptr()) {
window->set_moving_to_another_stack(false);
+ tell_wms_window_state_changed(*window);
+ }
}
m_carry_window_to_new_stack.clear();
}
@@ -1378,6 +1514,8 @@ void WindowManager::did_switch_window_stack(Badge<Compositor>, WindowStack& prev
pick_new_active_window(nullptr);
reevaluate_hovered_window();
+
+ tell_wms_current_window_stack_changed();
}
void WindowManager::process_key_event(KeyEvent& event)
diff --git a/Userland/Services/WindowServer/WindowManager.h b/Userland/Services/WindowServer/WindowManager.h
index 6d1b64e572..803657f205 100644
--- a/Userland/Services/WindowServer/WindowManager.h
+++ b/Userland/Services/WindowServer/WindowManager.h
@@ -60,6 +60,13 @@ class WindowManager : public Core::Object {
friend class WindowSwitcher;
public:
+ static constexpr size_t default_window_stack_rows = 2;
+ static constexpr size_t default_window_stack_columns = 2;
+ static_assert(default_window_stack_rows >= 1);
+ static_assert(default_window_stack_columns >= 1);
+ static constexpr unsigned max_window_stack_rows = 16;
+ static constexpr unsigned max_window_stack_columns = 16;
+
static WindowManager& the();
explicit WindowManager(Gfx::PaletteImpl const&);
@@ -173,9 +180,11 @@ public:
void tell_wms_window_state_changed(Window&);
void tell_wms_window_icon_changed(Window&);
void tell_wms_window_rect_changed(Window&);
+ void tell_wms_screen_rects_changed();
void tell_wms_applet_area_size_changed(Gfx::IntSize const&);
void tell_wms_super_key_pressed();
void tell_wms_super_space_key_pressed();
+ void tell_wms_current_window_stack_changed();
bool is_active_window_or_accessory(Window&) const;
@@ -251,11 +260,13 @@ public:
void reevaluate_hovered_window(Window* = nullptr);
Window* hovered_window() const { return m_hovered_window.ptr(); }
- void switch_to_window_stack(WindowStack&, Window* = nullptr);
+ void switch_to_window_stack(WindowStack&, Window* = nullptr, bool show_overlay = true);
size_t window_stack_rows() const { return m_window_stacks.size(); }
size_t window_stack_columns() const { return m_window_stacks[0].size(); }
+ bool apply_virtual_desktop_settings(unsigned rows, unsigned columns, bool save);
+
WindowStack& current_window_stack()
{
VERIFY(m_current_window_stack);
@@ -324,6 +335,7 @@ private:
void tell_wm_about_window(WMClientConnection& conn, Window&);
void tell_wm_about_window_icon(WMClientConnection& conn, Window&);
void tell_wm_about_window_rect(WMClientConnection& conn, Window&);
+ void tell_wm_about_current_window_stack(WMClientConnection&);
bool pick_new_active_window(Window*);
void do_move_to_front(Window&, bool, bool);
@@ -350,7 +362,7 @@ private:
RefPtr<MultiScaleBitmaps> m_overlay_rect_shadow;
// Setup 2 rows 1 column by default
- NonnullOwnPtrVector<NonnullOwnPtrVector<WindowStack, 3>, 2> m_window_stacks;
+ NonnullOwnPtrVector<NonnullOwnPtrVector<WindowStack, default_window_stack_columns>, default_window_stack_rows> m_window_stacks;
WindowStack* m_current_window_stack { nullptr };
struct DoubleClickInfo {
diff --git a/Userland/Services/WindowServer/WindowManagerClient.ipc b/Userland/Services/WindowServer/WindowManagerClient.ipc
index 220a2d6543..615f93e2a7 100644
--- a/Userland/Services/WindowServer/WindowManagerClient.ipc
+++ b/Userland/Services/WindowServer/WindowManagerClient.ipc
@@ -3,10 +3,11 @@
endpoint WindowManagerClient
{
window_removed(i32 wm_id, i32 client_id, i32 window_id) =|
- window_state_changed(i32 wm_id, i32 client_id, i32 window_id, i32 parent_client_id, i32 parent_window_id, bool is_active, bool is_minimized, bool is_modal, bool is_frameless, i32 window_type, [UTF8] String title, Gfx::IntRect rect, Optional<i32> progress) =|
+ window_state_changed(i32 wm_id, i32 client_id, i32 window_id, i32 parent_client_id, i32 parent_window_id, u32 virtual_desktop_row, u32 virtual_desktop_column, bool is_active, bool is_minimized, bool is_modal, bool is_frameless, i32 window_type, [UTF8] String title, Gfx::IntRect rect, Optional<i32> progress) =|
window_icon_bitmap_changed(i32 wm_id, i32 client_id, i32 window_id, Gfx::ShareableBitmap bitmap) =|
window_rect_changed(i32 wm_id, i32 client_id, i32 window_id, Gfx::IntRect rect) =|
applet_area_size_changed(i32 wm_id, Gfx::IntSize size) =|
super_key_pressed(i32 wm_id) =|
super_space_key_pressed(i32 wm_id) =|
+ virtual_desktop_changed(i32 wm_id, u32 row, u32 column) =|
}
diff --git a/Userland/Services/WindowServer/WindowServer.ipc b/Userland/Services/WindowServer/WindowServer.ipc
index d3d81b6ef9..0653607472 100644
--- a/Userland/Services/WindowServer/WindowServer.ipc
+++ b/Userland/Services/WindowServer/WindowServer.ipc
@@ -102,6 +102,9 @@ endpoint WindowServer
save_screen_layout() => (bool success, String error_msg)
show_screen_numbers(bool show) =|
+ apply_virtual_desktop_settings(u32 rows, u32 columns, bool save) => (bool success)
+ get_virtual_desktop_settings() => (u32 rows, u32 columns, u32 max_rows, u32 max_columns)
+
set_window_icon_bitmap(i32 window_id, Gfx::ShareableBitmap icon) =|
get_wallpaper() => (String path)
diff --git a/Userland/Services/WindowServer/WindowStack.cpp b/Userland/Services/WindowServer/WindowStack.cpp
index f10a9641b2..261f2bb6df 100644
--- a/Userland/Services/WindowServer/WindowStack.cpp
+++ b/Userland/Services/WindowServer/WindowStack.cpp
@@ -26,6 +26,13 @@ void WindowStack::add(Window& window)
window.set_outer_stack({}, this);
}
+void WindowStack::add_to_back(Window& window)
+{
+ VERIFY(window.outer_stack() == nullptr);
+ m_windows.prepend(window);
+ window.set_outer_stack({}, this);
+}
+
void WindowStack::remove(Window& window)
{
VERIFY(window.outer_stack() == this);
@@ -47,6 +54,27 @@ void WindowStack::move_to_front(Window& window)
m_windows.append(window);
}
+void WindowStack::move_all_windows(WindowStack& new_window_stack, Vector<Window*, 32>& windows_moved, MoveAllWindowsTo move_to)
+{
+ VERIFY(this != &new_window_stack);
+ if (move_to == MoveAllWindowsTo::Front) {
+ while (auto* window = m_windows.take_first()) {
+ window->set_outer_stack({}, nullptr);
+ new_window_stack.add(*window);
+ windows_moved.append(window);
+ }
+ } else {
+ while (auto* window = m_windows.take_last()) {
+ window->set_outer_stack({}, nullptr);
+ new_window_stack.add_to_back(*window);
+ windows_moved.append(window);
+ }
+ }
+ m_active_window = nullptr;
+ m_active_input_window = nullptr;
+ m_active_input_tracking_window = nullptr;
+}
+
Window* WindowStack::window_at(Gfx::IntPoint const& position, IncludeWindowFrame include_window_frame) const
{
auto result = hit_test(position);
diff --git a/Userland/Services/WindowServer/WindowStack.h b/Userland/Services/WindowServer/WindowStack.h
index a4aaec944e..513ed3c3ca 100644
--- a/Userland/Services/WindowServer/WindowStack.h
+++ b/Userland/Services/WindowServer/WindowStack.h
@@ -19,9 +19,16 @@ public:
bool is_empty() const { return m_windows.is_empty(); }
void add(Window&);
+ void add_to_back(Window&);
void remove(Window&);
void move_to_front(Window&);
+ enum class MoveAllWindowsTo {
+ Front,
+ Back
+ };
+ void move_all_windows(WindowStack&, Vector<Window*, 32>&, MoveAllWindowsTo);
+
enum class IncludeWindowFrame {
Yes,
No,
@@ -38,6 +45,8 @@ public:
template<typename Callback>
void for_each_window(Callback);
+ template<typename Callback>
+ IterationDecision for_each_window_from_back_to_front(Callback);
Window::List& windows() { return m_windows; }
@@ -146,6 +155,17 @@ inline void WindowStack::for_each_window(Callback callback)
}
template<typename Callback>
+inline IterationDecision WindowStack::for_each_window_from_back_to_front(Callback callback)
+{
+ for (auto& window : m_windows) {
+ IterationDecision decision = callback(window);
+ if (decision != IterationDecision::Break)
+ return decision;
+ }
+ return IterationDecision::Continue;
+}
+
+template<typename Callback>
inline IterationDecision WindowStack::for_each_window_of_type_from_front_to_back(WindowType type, Callback callback, bool ignore_highlight)
{
auto* highlight_window = this->highlight_window();