summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Base/etc/WindowServer.ini3
-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
21 files changed, 383 insertions, 64 deletions
diff --git a/Base/etc/WindowServer.ini b/Base/etc/WindowServer.ini
index 62843c13dc..18a20ac35d 100644
--- a/Base/etc/WindowServer.ini
+++ b/Base/etc/WindowServer.ini
@@ -50,3 +50,6 @@ Mode=stretch
[Applet]
Order=CPUGraph,MemoryGraph,Network,ClipboardHistory,Audio
+[VirtualDesktops]
+Rows=2
+Columns=2
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();