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.
This commit is contained in:
Tom 2021-06-30 19:12:02 -06:00 committed by Andreas Kling
parent 584b144953
commit 7984c2836d
Notes: sideshowbarker 2024-07-18 11:04:33 +09:00
21 changed files with 383 additions and 64 deletions

View file

@ -50,3 +50,6 @@ Mode=stretch
[Applet]
Order=CPUGraph,MemoryGraph,Network,ClipboardHistory,Audio
[VirtualDesktops]
Rows=2
Columns=2

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1204,4 +1204,5 @@ String Window::computed_title() const
return String::formatted("{} (Not responding)", title);
return title;
}
}

View file

@ -35,6 +35,7 @@ enum WMEventMask {
WindowStateChanges = 1 << 1,
WindowIconChanges = 1 << 2,
WindowRemovals = 1 << 3,
VirtualDesktopChanges = 1 << 4,
};
enum class WindowTileType {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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; }
@ -145,6 +154,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)
{