1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 17:17:44 +00:00

WindowServer: Let the window switcher render itself as a WSWindow.

Much better than drawing directly into the back buffer.
This commit is contained in:
Andreas Kling 2019-03-06 10:03:10 +01:00
parent f3ff402cf3
commit b85fe0bd07
8 changed files with 89 additions and 59 deletions

View file

@ -69,7 +69,7 @@ WSWindow& WSMenu::ensure_menu_window()
next_item_location.move_by(0, height); next_item_location.move_by(0, height);
} }
auto window = make<WSWindow>(*this); auto window = make<WSWindow>(*this, WSWindowType::Menu);
window->set_opacity(0.95f); window->set_opacity(0.95f);
window->set_rect(0, 0, width(), height()); window->set_rect(0, 0, width(), height());
m_menu_window = move(window); m_menu_window = move(window);
@ -107,7 +107,7 @@ void WSMenu::draw()
} }
} }
void WSMenu::on_window_message(WSMessage& message) void WSMenu::on_message(WSMessage& message)
{ {
ASSERT(menu_window()); ASSERT(menu_window());
if (message.type() == WSMessage::MouseMove) { if (message.type() == WSMessage::MouseMove) {

View file

@ -4,7 +4,8 @@
#include <AK/Vector.h> #include <AK/Vector.h>
#include <AK/WeakPtr.h> #include <AK/WeakPtr.h>
#include <SharedGraphics/Rect.h> #include <SharedGraphics/Rect.h>
#include "WSMenuItem.h" #include <WindowServer/WSMenuItem.h>
#include <WindowServer/WSMessageReceiver.h>
class WSClientConnection; class WSClientConnection;
class WSMenuBar; class WSMenuBar;
@ -12,10 +13,10 @@ class WSMessage;
class WSWindow; class WSWindow;
class Font; class Font;
class WSMenu : public Weakable<WSMenu> { class WSMenu final : public WSMessageReceiver {
public: public:
WSMenu(WSClientConnection*, int menu_id, String&& name); WSMenu(WSClientConnection*, int menu_id, String&& name);
~WSMenu(); virtual ~WSMenu() override;
WSClientConnection* client() { return m_client; } WSClientConnection* client() { return m_client; }
const WSClientConnection* client() const { return m_client; } const WSClientConnection* client() const { return m_client; }
@ -58,7 +59,6 @@ public:
int left_padding() const { return 14; } int left_padding() const { return 14; }
int right_padding() const { return 14; } int right_padding() const { return 14; }
void on_window_message(WSMessage&);
void draw(); void draw();
const Font& font() const; const Font& font() const;
@ -73,6 +73,8 @@ public:
void close(); void close();
private: private:
virtual void on_message(WSMessage&) override;
int padding_between_text_and_shortcut() const { return 50; } int padding_between_text_and_shortcut() const { return 50; }
void did_activate(WSMenuItem&); void did_activate(WSMenuItem&);
WSClientConnection* m_client { nullptr }; WSClientConnection* m_client { nullptr };

View file

@ -5,9 +5,9 @@
#include <WindowServer/WSAPITypes.h> #include <WindowServer/WSAPITypes.h>
#include <WindowServer/WSClientConnection.h> #include <WindowServer/WSClientConnection.h>
WSWindow::WSWindow(WSMenu& menu) WSWindow::WSWindow(WSMessageReceiver& internal_owner, WSWindowType type)
: m_type(WSWindowType::Menu) : m_internal_owner(&internal_owner)
, m_menu(&menu) , m_type(type)
{ {
WSWindowManager::the().add_window(*this); WSWindowManager::the().add_window(*this);
} }
@ -36,12 +36,11 @@ void WSWindow::set_title(String&& title)
void WSWindow::set_rect(const Rect& rect) void WSWindow::set_rect(const Rect& rect)
{ {
Rect old_rect; Rect old_rect;
ASSERT(m_client || m_menu);
if (m_rect == rect) if (m_rect == rect)
return; return;
old_rect = m_rect; old_rect = m_rect;
m_rect = rect; m_rect = rect;
if (m_menu && (!m_backing_store || old_rect.size() != rect.size())) { if (!m_client && (!m_backing_store || old_rect.size() != rect.size())) {
m_backing_store = GraphicsBitmap::create(GraphicsBitmap::Format::RGB32, m_rect.size()); m_backing_store = GraphicsBitmap::create(GraphicsBitmap::Format::RGB32, m_rect.size());
} }
WSWindowManager::the().notify_rect_changed(*this, old_rect, rect); WSWindowManager::the().notify_rect_changed(*this, old_rect, rect);
@ -61,10 +60,8 @@ static WSAPI_MouseButton to_api(MouseButton button)
void WSWindow::on_message(WSMessage& message) void WSWindow::on_message(WSMessage& message)
{ {
if (m_menu) { if (m_internal_owner)
m_menu->on_window_message(message); return m_internal_owner->on_message(message);
return;
}
WSAPI_ServerMessage server_message; WSAPI_ServerMessage server_message;
server_message.window_id = window_id(); server_message.window_id = window_id();

View file

@ -13,7 +13,7 @@ class WSMenu;
class WSWindow final : public WSMessageReceiver, public InlineLinkedListNode<WSWindow> { class WSWindow final : public WSMessageReceiver, public InlineLinkedListNode<WSWindow> {
public: public:
WSWindow(WSClientConnection&, int window_id); WSWindow(WSClientConnection&, int window_id);
explicit WSWindow(WSMenu&); WSWindow(WSMessageReceiver&, WSWindowType);
virtual ~WSWindow() override; virtual ~WSWindow() override;
WSClientConnection* client() { return m_client; } WSClientConnection* client() { return m_client; }
@ -85,6 +85,7 @@ public:
private: private:
WSClientConnection* m_client { nullptr }; WSClientConnection* m_client { nullptr };
WSMessageReceiver* m_internal_owner { nullptr };
String m_title; String m_title;
Rect m_rect; Rect m_rect;
WSWindowType m_type { WSWindowType::Normal }; WSWindowType m_type { WSWindowType::Normal };
@ -92,7 +93,6 @@ private:
bool m_visible { true }; bool m_visible { true };
bool m_has_alpha_channel { false }; bool m_has_alpha_channel { false };
bool m_has_painted_since_last_resize { false }; bool m_has_painted_since_last_resize { false };
WSMenu* m_menu { nullptr };
RetainPtr<GraphicsBitmap> m_backing_store; RetainPtr<GraphicsBitmap> m_backing_store;
int m_window_id { -1 }; int m_window_id { -1 };
float m_opacity { 1 }; float m_opacity { 1 };

View file

@ -82,6 +82,16 @@ static inline Rect outer_window_rect(const Rect& window)
return rect; return rect;
} }
static inline Rect outer_window_rect(const WSWindow& window)
{
if (window.type() == WSWindowType::Menu)
return menu_window_rect(window.rect());
if (window.type() == WSWindowType::WindowSwitcher)
return window.rect();
ASSERT(window.type() == WSWindowType::Normal);
return outer_window_rect(window.rect());
}
static WSWindowManager* s_the; static WSWindowManager* s_the;
WSWindowManager& WSWindowManager::the() WSWindowManager& WSWindowManager::the()
@ -384,9 +394,12 @@ void WSWindowManager::paint_window_frame(WSWindow& window)
return; return;
} }
if (window.type() == WSWindowType::WindowSwitcher)
return;
auto titlebar_rect = title_bar_rect(window.rect()); auto titlebar_rect = title_bar_rect(window.rect());
auto titlebar_inner_rect = title_bar_text_rect(window.rect()); auto titlebar_inner_rect = title_bar_text_rect(window.rect());
auto outer_rect = outer_window_rect(window.rect()); auto outer_rect = outer_window_rect(window);
auto border_rect = border_window_rect(window.rect()); auto border_rect = border_window_rect(window.rect());
auto close_button_rect = close_button_rect_for_window(window.rect()); auto close_button_rect = close_button_rect_for_window(window.rect());
@ -466,8 +479,8 @@ void WSWindowManager::add_window(WSWindow& window)
m_windows_in_order.append(&window); m_windows_in_order.append(&window);
if (!active_window()) if (!active_window())
set_active_window(&window); set_active_window(&window);
if (m_switcher.is_visible()) if (m_switcher.is_visible() && window.type() != WSWindowType::WindowSwitcher)
m_switcher.invalidate(); m_switcher.refresh();
} }
void WSWindowManager::move_to_front(WSWindow& window) void WSWindowManager::move_to_front(WSWindow& window)
@ -488,25 +501,25 @@ void WSWindowManager::remove_window(WSWindow& window)
m_windows_in_order.remove(&window); m_windows_in_order.remove(&window);
if (!active_window() && !m_windows.is_empty()) if (!active_window() && !m_windows.is_empty())
set_active_window(*m_windows.begin()); set_active_window(*m_windows.begin());
if (m_switcher.is_visible()) if (m_switcher.is_visible() && window.type() != WSWindowType::WindowSwitcher)
m_switcher.invalidate(); m_switcher.refresh();
} }
void WSWindowManager::notify_title_changed(WSWindow& window) void WSWindowManager::notify_title_changed(WSWindow& window)
{ {
printf("[WM] WSWindow{%p} title set to '%s'\n", &window, window.title().characters()); dbgprintf("[WM] WSWindow{%p} title set to '%s'\n", &window, window.title().characters());
invalidate(outer_window_rect(window.rect())); invalidate(outer_window_rect(window));
if (m_switcher.is_visible()) if (m_switcher.is_visible())
m_switcher.invalidate(); m_switcher.refresh();
} }
void WSWindowManager::notify_rect_changed(WSWindow& window, const Rect& old_rect, const Rect& new_rect) void WSWindowManager::notify_rect_changed(WSWindow& window, const Rect& old_rect, const Rect& new_rect)
{ {
printf("[WM] WSWindow %p rect changed (%d,%d %dx%d) -> (%d,%d %dx%d)\n", &window, old_rect.x(), old_rect.y(), old_rect.width(), old_rect.height(), new_rect.x(), new_rect.y(), new_rect.width(), new_rect.height()); dbgprintf("[WM] WSWindow %p rect changed (%d,%d %dx%d) -> (%d,%d %dx%d)\n", &window, old_rect.x(), old_rect.y(), old_rect.width(), old_rect.height(), new_rect.x(), new_rect.y(), new_rect.width(), new_rect.height());
invalidate(outer_window_rect(old_rect)); invalidate(outer_window_rect(old_rect));
invalidate(outer_window_rect(new_rect)); invalidate(outer_window_rect(new_rect));
if (m_switcher.is_visible()) if (m_switcher.is_visible() && window.type() != WSWindowType::WindowSwitcher)
m_switcher.invalidate(); m_switcher.refresh();
} }
void WSWindowManager::handle_menu_mouse_event(WSMenu& menu, WSMouseEvent& event) void WSWindowManager::handle_menu_mouse_event(WSMenu& menu, WSMouseEvent& event)
@ -578,7 +591,7 @@ void WSWindowManager::start_window_resize(WSWindow& window, WSMouseEvent& event)
{ ResizeDirection::Left, ResizeDirection::None, ResizeDirection::Right }, { ResizeDirection::Left, ResizeDirection::None, ResizeDirection::Right },
{ ResizeDirection::DownLeft, ResizeDirection::Down, ResizeDirection::DownRight }, { ResizeDirection::DownLeft, ResizeDirection::Down, ResizeDirection::DownRight },
}; };
Rect outer_rect = outer_window_rect(window.rect()); Rect outer_rect = outer_window_rect(window);
ASSERT(outer_rect.contains(event.position())); ASSERT(outer_rect.contains(event.position()));
int window_relative_x = event.x() - outer_rect.x(); int window_relative_x = event.x() - outer_rect.x();
int window_relative_y = event.y() - outer_rect.y(); int window_relative_y = event.y() - outer_rect.y();
@ -752,7 +765,7 @@ void WSWindowManager::process_mouse_event(WSMouseEvent& event, WSWindow*& event_
} }
for_each_visible_window_from_front_to_back([&] (WSWindow& window) { for_each_visible_window_from_front_to_back([&] (WSWindow& window) {
if (window.type() != WSWindowType::Menu && outer_window_rect(window.rect()).contains(event.position())) { if (window.type() == WSWindowType::Normal && outer_window_rect(window).contains(event.position())) {
if (m_keyboard_modifiers == Mod_Logo && event.type() == WSMessage::MouseDown && event.button() == MouseButton::Left) { if (m_keyboard_modifiers == Mod_Logo && event.type() == WSMessage::MouseDown && event.button() == MouseButton::Left) {
start_window_drag(window, event); start_window_drag(window, event);
return IterationDecision::Abort; return IterationDecision::Abort;
@ -762,7 +775,7 @@ void WSWindowManager::process_mouse_event(WSMouseEvent& event, WSWindow*& event_
return IterationDecision::Abort; return IterationDecision::Abort;
} }
} }
if (window.type() != WSWindowType::Menu && title_bar_rect(window.rect()).contains(event.position())) { if (window.type() == WSWindowType::Normal && title_bar_rect(window.rect()).contains(event.position())) {
if (event.type() == WSMessage::MouseDown) { if (event.type() == WSMessage::MouseDown) {
move_to_front(window); move_to_front(window);
set_active_window(&window); set_active_window(&window);
@ -777,7 +790,7 @@ void WSWindowManager::process_mouse_event(WSMouseEvent& event, WSWindow*& event_
} }
if (window.rect().contains(event.position())) { if (window.rect().contains(event.position())) {
if (window.type() != WSWindowType::Menu && event.type() == WSMessage::MouseDown) { if (window.type() == WSWindowType::Normal && event.type() == WSMessage::MouseDown) {
move_to_front(window); move_to_front(window);
set_active_window(&window); set_active_window(&window);
} }
@ -815,14 +828,14 @@ void WSWindowManager::compose()
// Maybe there's some way we could know this? // Maybe there's some way we could know this?
continue; continue;
} }
if (outer_window_rect(window->rect()).contains(r)) if (outer_window_rect(*window).contains(r))
return true; return true;
} }
return false; return false;
}; };
auto any_dirty_rect_intersects_window = [&dirty_rects] (const WSWindow& window) { auto any_dirty_rect_intersects_window = [&dirty_rects] (const WSWindow& window) {
auto window_rect = outer_window_rect(window.rect()); auto window_rect = outer_window_rect(window);
for (auto& dirty_rect : dirty_rects.rects()) { for (auto& dirty_rect : dirty_rects.rects()) {
if (dirty_rect.intersects(window_rect)) if (dirty_rect.intersects(window_rect))
return true; return true;
@ -876,7 +889,9 @@ void WSWindowManager::compose()
compose_window(*m_highlight_window); compose_window(*m_highlight_window);
draw_menubar(); draw_menubar();
draw_window_switcher(); if (m_switcher.is_visible())
compose_window(*m_switcher.switcher_window());
draw_cursor(); draw_cursor();
if (m_flash_flush) { if (m_flash_flush) {
@ -949,7 +964,7 @@ void WSWindowManager::draw_menubar()
void WSWindowManager::draw_window_switcher() void WSWindowManager::draw_window_switcher()
{ {
if (m_switcher.is_visible()) if (m_switcher.is_visible())
m_switcher.draw(*m_back_painter); m_switcher.draw();
} }
void WSWindowManager::draw_cursor() void WSWindowManager::draw_cursor()
@ -1009,8 +1024,8 @@ void WSWindowManager::set_highlight_window(WSWindow* window)
void WSWindowManager::set_active_window(WSWindow* window) void WSWindowManager::set_active_window(WSWindow* window)
{ {
if (window->type() == WSWindowType::Menu) { if (window->type() != WSWindowType::Normal) {
dbgprintf("WSWindowManager: Attempted to make a menu window active.\n"); dbgprintf("WSWindowManager: Attempted to make a non-normal window active.\n");
return; return;
} }
@ -1079,7 +1094,11 @@ void WSWindowManager::invalidate(const WSWindow& window)
return; return;
} }
if (window.type() == WSWindowType::Normal) { if (window.type() == WSWindowType::Normal) {
invalidate(outer_window_rect(window.rect())); invalidate(outer_window_rect(window));
return;
}
if (window.type() == WSWindowType::WindowSwitcher) {
invalidate(window.rect());
return; return;
} }
ASSERT_NOT_REACHED(); ASSERT_NOT_REACHED();
@ -1091,7 +1110,7 @@ void WSWindowManager::invalidate(const WSWindow& window, const Rect& rect)
invalidate(window); invalidate(window);
return; return;
} }
auto outer_rect = outer_window_rect(window.rect()); auto outer_rect = outer_window_rect(window);
auto inner_rect = rect; auto inner_rect = rect;
inner_rect.move_by(window.position()); inner_rect.move_by(window.position());
// FIXME: This seems slightly wrong; the inner rect shouldn't intersect the border part of the outer rect. // FIXME: This seems slightly wrong; the inner rect shouldn't intersect the border part of the outer rect.
@ -1107,13 +1126,13 @@ void WSWindowManager::flush(const Rect& a_rect)
dbgprintf("[WM] flush #%u (%d,%d %dx%d)\n", ++m_flush_count, rect.x(), rect.y(), rect.width(), rect.height()); dbgprintf("[WM] flush #%u (%d,%d %dx%d)\n", ++m_flush_count, rect.x(), rect.y(), rect.width(), rect.height());
#endif #endif
RGBA32* front_ptr = m_front_bitmap->scanline(rect.y()) + rect.x(); const RGBA32* front_ptr = m_front_bitmap->scanline(rect.y()) + rect.x();
RGBA32* back_ptr = m_back_bitmap->scanline(rect.y()) + rect.x(); RGBA32* back_ptr = m_back_bitmap->scanline(rect.y()) + rect.x();
size_t pitch = m_back_bitmap->pitch(); size_t pitch = m_back_bitmap->pitch();
for (int y = 0; y < rect.height(); ++y) { for (int y = 0; y < rect.height(); ++y) {
fast_dword_copy(back_ptr, front_ptr, rect.width()); fast_dword_copy(back_ptr, front_ptr, rect.width());
front_ptr = (RGBA32*)((byte*)front_ptr + pitch); front_ptr = (const RGBA32*)((const byte*)front_ptr + pitch);
back_ptr = (RGBA32*)((byte*)back_ptr + pitch); back_ptr = (RGBA32*)((byte*)back_ptr + pitch);
} }
} }

View file

@ -16,11 +16,11 @@ void WSWindowSwitcher::set_visible(bool visible)
if (m_visible == visible) if (m_visible == visible)
return; return;
m_visible = visible; m_visible = visible;
if (!m_visible) { if (m_switcher_window)
WSWindowManager::the().invalidate(m_rect); m_switcher_window->set_visible(visible);
if (!m_visible)
return; return;
} refresh();
invalidate();
} }
WSWindow* WSWindowSwitcher::selected_window() WSWindow* WSWindowSwitcher::selected_window()
@ -53,15 +53,15 @@ void WSWindowSwitcher::on_key_event(const WSKeyEvent& event)
auto* highlight_window = m_windows.at(m_selected_index).ptr(); auto* highlight_window = m_windows.at(m_selected_index).ptr();
ASSERT(highlight_window); ASSERT(highlight_window);
WSWindowManager::the().set_highlight_window(highlight_window); WSWindowManager::the().set_highlight_window(highlight_window);
draw();
WSWindowManager::the().invalidate(m_rect); WSWindowManager::the().invalidate(m_rect);
} }
void WSWindowSwitcher::draw(Painter& painter) void WSWindowSwitcher::draw()
{ {
painter.translate(m_rect.location()); Painter painter(*m_switcher_window->backing_store());
painter.fill_rect({ { }, m_rect.size() }, Color::LightGray); painter.fill_rect({ { }, m_rect.size() }, Color::LightGray);
painter.draw_rect({ { }, m_rect.size() }, Color::DarkGray); painter.draw_rect({ { }, m_rect.size() }, Color::DarkGray);
for (int index = 0; index < m_windows.size(); ++index) { for (int index = 0; index < m_windows.size(); ++index) {
auto& window = *m_windows.at(index); auto& window = *m_windows.at(index);
Rect item_rect { Rect item_rect {
@ -80,16 +80,14 @@ void WSWindowSwitcher::draw(Painter& painter)
text_color = Color::Black; text_color = Color::Black;
rect_text_color = Color::DarkGray; rect_text_color = Color::DarkGray;
} }
painter.set_font(Font::default_bold_font()); painter.set_font(Font::default_bold_font());
painter.draw_text(item_rect, window.title(), TextAlignment::CenterLeft, text_color); painter.draw_text(item_rect, window.title(), TextAlignment::CenterLeft, text_color);
painter.set_font(WSWindowManager::the().font()); painter.set_font(WSWindowManager::the().font());
painter.draw_text(item_rect, window.rect().to_string(), TextAlignment::CenterRight, rect_text_color); painter.draw_text(item_rect, window.rect().to_string(), TextAlignment::CenterRight, rect_text_color);
} }
painter.translate(-m_rect.x(), -m_rect.y());
} }
void WSWindowSwitcher::invalidate() void WSWindowSwitcher::refresh()
{ {
WSWindow* selected_window = nullptr; WSWindow* selected_window = nullptr;
if (m_selected_index > 0 && m_windows[m_selected_index]) if (m_selected_index > 0 && m_windows[m_selected_index])
@ -114,5 +112,13 @@ void WSWindowSwitcher::invalidate()
m_rect.set_width(longest_title * WSWindowManager::the().font().glyph_width() + space_for_window_rect + padding() * 2); m_rect.set_width(longest_title * WSWindowManager::the().font().glyph_width() + space_for_window_rect + padding() * 2);
m_rect.set_height(window_count * item_height() + padding() * 2); m_rect.set_height(window_count * item_height() + padding() * 2);
m_rect.center_within(WSWindowManager::the().m_screen_rect); m_rect.center_within(WSWindowManager::the().m_screen_rect);
WSWindowManager::the().invalidate(m_rect); if (!m_switcher_window)
m_switcher_window = make<WSWindow>(*this, WSWindowType::WindowSwitcher);
m_switcher_window->set_rect(m_rect);
draw();
}
void WSWindowSwitcher::on_message(WSMessage& message)
{
} }

View file

@ -3,15 +3,16 @@
#include <SharedGraphics/Rect.h> #include <SharedGraphics/Rect.h>
#include <AK/Vector.h> #include <AK/Vector.h>
#include <AK/WeakPtr.h> #include <AK/WeakPtr.h>
#include <WindowServer/WSMessageReceiver.h>
class Painter; class Painter;
class WSKeyEvent; class WSKeyEvent;
class WSWindow; class WSWindow;
class WSWindowSwitcher { class WSWindowSwitcher : public WSMessageReceiver {
public: public:
WSWindowSwitcher(); WSWindowSwitcher();
~WSWindowSwitcher(); virtual ~WSWindowSwitcher() override;
bool is_visible() const { return m_visible; } bool is_visible() const { return m_visible; }
void set_visible(bool); void set_visible(bool);
@ -20,17 +21,21 @@ public:
void hide() { set_visible(false); } void hide() { set_visible(false); }
void on_key_event(const WSKeyEvent&); void on_key_event(const WSKeyEvent&);
void invalidate(); void refresh();
void draw(Painter&); void draw();
int item_height() { return 20; } int item_height() { return 20; }
int padding() { return 8; } int padding() { return 8; }
WSWindow* selected_window(); WSWindow* selected_window();
private: WSWindow* switcher_window() { return m_switcher_window.ptr(); }
private:
virtual void on_message(WSMessage&) override;
OwnPtr<WSWindow> m_switcher_window;
Rect m_rect; Rect m_rect;
bool m_visible { false }; bool m_visible { false };
Vector<WeakPtr<WSWindow>> m_windows; Vector<WeakPtr<WSWindow>> m_windows;

View file

@ -3,5 +3,6 @@
enum class WSWindowType { enum class WSWindowType {
Invalid = 0, Invalid = 0,
Normal, Normal,
Menu Menu,
WindowSwitcher,
}; };