1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 05:57:45 +00:00

WindowServer: Add basic virtual desktop support

This creates a 2-dimensional array of WindowStack instances, one for
each virtual desktop. The main desktop 0,0 is the main desktop, which
is the desktop used for all stationary windows (e.g. taskbar, desktop).
When adding windows to a desktop, stationary windows are always added
to the main desktop.

When composing the desktop, there are usually two WindowStacks
involved. For stationary windows, the main desktop will be traversed,
and for everything else the current virtual desktop will be iterated.
Iteration is interweaved to preserve the correct order. During the
transition animation, two WindowStacks will be iterated at the same
time.
This commit is contained in:
Tom 2021-06-29 19:51:26 -06:00 committed by Andreas Kling
parent 944e5cfb35
commit 584b144953
11 changed files with 900 additions and 213 deletions

View file

@ -90,17 +90,36 @@ public:
void start_dnd_drag(ClientConnection&, String const& text, Gfx::Bitmap const*, Core::MimeData const&);
void end_dnd_drag();
Window* active_window() { return m_window_stack.active_window(); }
Window const* active_window() const { return m_window_stack.active_window(); }
Window* active_input_window() { return m_active_input_window.ptr(); }
Window const* active_input_window() const { return m_active_input_window.ptr(); }
Window* active_window()
{
VERIFY(m_current_window_stack);
return m_current_window_stack->active_window();
}
Window const* active_window() const
{
VERIFY(m_current_window_stack);
return m_current_window_stack->active_window();
}
Window* active_input_window()
{
VERIFY(m_current_window_stack);
return m_current_window_stack->active_input_window();
}
Window const* active_input_window() const
{
VERIFY(m_current_window_stack);
return m_current_window_stack->active_input_window();
}
ClientConnection const* active_client() const;
Window* window_with_active_menu() { return m_window_with_active_menu; }
Window const* window_with_active_menu() const { return m_window_with_active_menu; }
void set_window_with_active_menu(Window*);
Window const* highlight_window() const { return m_window_stack.highlight_window(); }
Window* highlight_window() { return m_highlight_window; }
Window const* highlight_window() const { return m_highlight_window; }
void set_highlight_window(Window*);
void move_to_front_and_make_active(Window&);
@ -223,6 +242,7 @@ public:
return f(window, true);
}
}
bool is_window_in_modal_stack(Window& window_in_modal_stack, Window& other_window);
Gfx::IntPoint get_recommended_window_position(Gfx::IntPoint const& desired);
@ -231,13 +251,61 @@ public:
void reevaluate_hovered_window(Window* = nullptr);
Window* hovered_window() const { return m_hovered_window.ptr(); }
WindowStack& window_stack() { return m_window_stack; }
void switch_to_window_stack(WindowStack&, Window* = nullptr);
size_t window_stack_rows() const { return m_window_stacks.size(); }
size_t window_stack_columns() const { return m_window_stacks[0].size(); }
WindowStack& current_window_stack()
{
VERIFY(m_current_window_stack);
return *m_current_window_stack;
}
template<typename F>
IterationDecision for_each_window_stack(F f)
{
for (auto& row : m_window_stacks) {
for (auto& stack : row) {
IterationDecision decision = f(stack);
if (decision != IterationDecision::Continue)
return decision;
}
}
return IterationDecision::Continue;
}
WindowStack& window_stack_for_window(Window&);
static constexpr bool is_stationary_window_type(WindowType window_type)
{
switch (window_type) {
case WindowType::Normal:
case WindowType::ToolWindow:
case WindowType::Tooltip:
return false;
default:
return true;
}
}
void did_switch_window_stack(Badge<Compositor>, WindowStack&, WindowStack&);
template<typename Callback>
IterationDecision for_each_visible_window_from_back_to_front(Callback, WindowStack* = nullptr);
template<typename Callback>
IterationDecision for_each_visible_window_from_front_to_back(Callback, WindowStack* = nullptr);
MultiScaleBitmaps const* overlay_rect_shadow() const { return m_overlay_rect_shadow.ptr(); }
private:
RefPtr<Cursor> get_cursor(String const& name);
void notify_new_active_window(Window&);
void notify_new_active_input_window(Window&);
void notify_previous_active_window(Window&);
void notify_previous_active_input_window(Window&);
void process_mouse_event(MouseEvent&);
void process_event_for_doubleclick(Window& window, MouseEvent& event);
bool process_ongoing_window_resize(MouseEvent const&);
@ -260,6 +328,8 @@ private:
void do_move_to_front(Window&, bool, bool);
[[nodiscard]] static WindowStack& get_rendering_window_stacks(WindowStack*&);
RefPtr<Cursor> m_hidden_cursor;
RefPtr<Cursor> m_arrow_cursor;
RefPtr<Cursor> m_hand_cursor;
@ -279,7 +349,9 @@ private:
RefPtr<MultiScaleBitmaps> m_overlay_rect_shadow;
WindowStack m_window_stack;
// Setup 2 rows 1 column by default
NonnullOwnPtrVector<NonnullOwnPtrVector<WindowStack, 3>, 2> m_window_stacks;
WindowStack* m_current_window_stack { nullptr };
struct DoubleClickInfo {
struct ClickMetadata {
@ -317,8 +389,7 @@ private:
bool m_previous_event_was_super_keydown { false };
WeakPtr<Window> m_hovered_window;
WeakPtr<Window> m_active_input_window;
WeakPtr<Window> m_active_input_tracking_window;
WeakPtr<Window> m_highlight_window;
WeakPtr<Window> m_window_with_active_menu;
OwnPtr<WindowGeometryOverlay> m_geometry_overlay;
@ -349,8 +420,85 @@ private:
String m_dnd_text;
RefPtr<Core::MimeData> m_dnd_mime_data;
WindowStack* m_switching_to_window_stack { nullptr };
Vector<WeakPtr<Window>, 4> m_carry_window_to_new_stack;
};
template<typename Callback>
inline IterationDecision WindowManager::for_each_visible_window_from_back_to_front(Callback callback, WindowStack* specific_stack)
{
auto* window_stack = specific_stack;
WindowStack* transitioning_to_window_stack = nullptr;
if (!window_stack)
window_stack = &get_rendering_window_stacks(transitioning_to_window_stack);
auto for_each_window = [&]<WindowType window_type>() {
if constexpr (is_stationary_window_type(window_type)) {
auto& stationary_stack = window_stack->stationary_window_stack();
return stationary_stack.for_each_visible_window_of_type_from_back_to_front(window_type, callback);
} else {
auto decision = window_stack->for_each_visible_window_of_type_from_back_to_front(window_type, callback);
if (decision == IterationDecision::Continue && transitioning_to_window_stack)
decision = transitioning_to_window_stack->for_each_visible_window_of_type_from_back_to_front(window_type, callback);
return decision;
}
};
if (for_each_window.template operator()<WindowType::Desktop>() == IterationDecision::Break)
return IterationDecision::Break;
if (for_each_window.template operator()<WindowType::Normal>() == IterationDecision::Break)
return IterationDecision::Break;
if (for_each_window.template operator()<WindowType::ToolWindow>() == IterationDecision::Break)
return IterationDecision::Break;
if (for_each_window.template operator()<WindowType::Taskbar>() == IterationDecision::Break)
return IterationDecision::Break;
if (for_each_window.template operator()<WindowType::AppletArea>() == IterationDecision::Break)
return IterationDecision::Break;
if (for_each_window.template operator()<WindowType::Notification>() == IterationDecision::Break)
return IterationDecision::Break;
if (for_each_window.template operator()<WindowType::Tooltip>() == IterationDecision::Break)
return IterationDecision::Break;
if (for_each_window.template operator()<WindowType::Menu>() == IterationDecision::Break)
return IterationDecision::Break;
return for_each_window.template operator()<WindowType::WindowSwitcher>();
}
template<typename Callback>
inline IterationDecision WindowManager::for_each_visible_window_from_front_to_back(Callback callback, WindowStack* specific_stack)
{
auto* window_stack = specific_stack;
WindowStack* transitioning_to_window_stack = nullptr;
if (!window_stack)
window_stack = &get_rendering_window_stacks(transitioning_to_window_stack);
auto for_each_window = [&]<WindowType window_type>() {
if constexpr (is_stationary_window_type(window_type)) {
auto& stationary_stack = window_stack->stationary_window_stack();
return stationary_stack.for_each_visible_window_of_type_from_front_to_back(window_type, callback);
} else {
auto decision = window_stack->for_each_visible_window_of_type_from_front_to_back(window_type, callback);
if (decision == IterationDecision::Continue && transitioning_to_window_stack)
decision = transitioning_to_window_stack->for_each_visible_window_of_type_from_front_to_back(window_type, callback);
return decision;
}
};
if (for_each_window.template operator()<WindowType::WindowSwitcher>() == IterationDecision::Break)
return IterationDecision::Break;
if (for_each_window.template operator()<WindowType::Menu>() == IterationDecision::Break)
return IterationDecision::Break;
if (for_each_window.template operator()<WindowType::Tooltip>() == IterationDecision::Break)
return IterationDecision::Break;
if (for_each_window.template operator()<WindowType::Notification>() == IterationDecision::Break)
return IterationDecision::Break;
if (for_each_window.template operator()<WindowType::AppletArea>() == IterationDecision::Break)
return IterationDecision::Break;
if (for_each_window.template operator()<WindowType::Taskbar>() == IterationDecision::Break)
return IterationDecision::Break;
if (for_each_window.template operator()<WindowType::ToolWindow>() == IterationDecision::Break)
return IterationDecision::Break;
if (for_each_window.template operator()<WindowType::Normal>() == IterationDecision::Break)
return IterationDecision::Break;
return for_each_window.template operator()<WindowType::Desktop>();
}
template<typename Callback>
void WindowManager::for_each_window_manager(Callback callback)
{