WindowServer: Render the global menubar into a separate WSWindow.

Previously we were rendering the whole menubar on every compose(),
even if nothing changed about it. Now it's in its own window and can
be invalidated and painted separately.
This commit is contained in:
Andreas Kling 2019-06-21 08:19:43 +02:00
parent d99b1a9ea0
commit 9ac17c7bc9
Notes: sideshowbarker 2024-07-19 13:32:37 +09:00
10 changed files with 164 additions and 89 deletions

View file

@ -27,6 +27,7 @@ WINDOWSERVER_OBJS = \
WSButton.o \
WSCPUMonitor.o \
WSCompositor.o \
WSMenuBarKeeper.o \
main.o
APP = WindowServer

View file

@ -27,6 +27,7 @@ enum WSAPI_WindowType {
WindowSwitcher,
Taskbar,
Tooltip,
Menubar,
};
struct WSAPI_WindowBackingStoreInfo {

View file

@ -159,7 +159,6 @@ void WSCompositor::compose()
});
draw_geometry_label();
draw_menubar();
}
draw_cursor();
@ -332,59 +331,3 @@ void WSCompositor::draw_cursor()
m_back_painter->blit(cursor_rect.location(), wm.active_cursor().bitmap(), wm.active_cursor().rect());
m_last_cursor_rect = cursor_rect;
}
void WSCompositor::draw_menubar()
{
auto& wm = WSWindowManager::the();
auto menubar_rect = wm.menubar_rect();
m_back_painter->fill_rect(menubar_rect, Color::LightGray);
m_back_painter->draw_line({ 0, menubar_rect.bottom() }, { menubar_rect.right(), menubar_rect.bottom() }, Color::MidGray);
int index = 0;
wm.for_each_active_menubar_menu([&](WSMenu& menu) {
Color text_color = Color::Black;
if (&menu == wm.current_menu()) {
m_back_painter->fill_rect(menu.rect_in_menubar(), wm.menu_selection_color());
text_color = Color::White;
}
m_back_painter->draw_text(
menu.text_rect_in_menubar(),
menu.name(),
index == 1 ? wm.app_menu_font() : wm.menu_font(),
TextAlignment::CenterLeft,
text_color);
++index;
return true;
});
int username_width = Font::default_bold_font().width(wm.m_username);
Rect username_rect {
menubar_rect.right() - wm.menubar_menu_margin() / 2 - Font::default_bold_font().width(wm.m_username),
menubar_rect.y(),
username_width,
menubar_rect.height()
};
m_back_painter->draw_text(username_rect, wm.m_username, Font::default_bold_font(), TextAlignment::CenterRight, Color::Black);
time_t now = time(nullptr);
auto* tm = localtime(&now);
auto time_text = String::format("%4u-%02u-%02u %02u:%02u:%02u",
tm->tm_year + 1900,
tm->tm_mon + 1,
tm->tm_mday,
tm->tm_hour,
tm->tm_min,
tm->tm_sec);
int time_width = wm.font().width(time_text);
Rect time_rect {
username_rect.left() - wm.menubar_menu_margin() / 2 - time_width,
menubar_rect.y(),
time_width,
menubar_rect.height()
};
m_back_painter->draw_text(time_rect, time_text, wm.font(), TextAlignment::CenterRight, Color::Black);
Rect cpu_rect { time_rect.right() - wm.font().width(time_text) - wm.m_cpu_monitor.capacity() - 10, time_rect.y() + 1, wm.m_cpu_monitor.capacity(), time_rect.height() - 2 };
wm.m_cpu_monitor.paint(*m_back_painter, cpu_rect);
}

View file

@ -206,6 +206,9 @@ bool WSEventLoop::on_receive_from_client(int client_id, const WSAPI_ClientMessag
case WSAPI_WindowType::Tooltip:
ws_window_type = WSWindowType::Tooltip;
break;
case WSAPI_WindowType::Menubar:
ws_window_type = WSWindowType::Menubar;
break;
case WSAPI_WindowType::Invalid:
break; // handled below
}

View file

@ -0,0 +1,108 @@
#include <LibCore/CTimer.h>
#include <SharedGraphics/Font.h>
#include <SharedGraphics/Painter.h>
#include <WindowServer/WSMenuBarKeeper.h>
#include <WindowServer/WSWindowManager.h>
#include <time.h>
#include <unistd.h>
WSMenuBarKeeper::WSMenuBarKeeper()
{
m_username = getlogin();
new CTimer(300, [this] {
static time_t last_update_time;
time_t now = time(nullptr);
if (now != last_update_time || m_cpu_monitor.is_dirty()) {
tick_clock();
last_update_time = now;
m_cpu_monitor.set_dirty(false);
}
});
}
WSMenuBarKeeper::~WSMenuBarKeeper()
{
}
void WSMenuBarKeeper::setup()
{
m_window = make<WSWindow>(*this, WSWindowType::Menubar);
m_window->set_rect(WSWindowManager::the().menubar_rect());
}
void WSMenuBarKeeper::draw()
{
auto& wm = WSWindowManager::the();
auto menubar_rect = wm.menubar_rect();
Painter painter(*window().backing_store());
painter.fill_rect(menubar_rect, Color::LightGray);
painter.draw_line({ 0, menubar_rect.bottom() }, { menubar_rect.right(), menubar_rect.bottom() }, Color::MidGray);
int index = 0;
wm.for_each_active_menubar_menu([&](WSMenu& menu) {
Color text_color = Color::Black;
if (&menu == wm.current_menu()) {
painter.fill_rect(menu.rect_in_menubar(), wm.menu_selection_color());
text_color = Color::White;
}
painter.draw_text(
menu.text_rect_in_menubar(),
menu.name(),
index == 1 ? wm.app_menu_font() : wm.menu_font(),
TextAlignment::CenterLeft,
text_color);
++index;
return true;
});
int username_width = Font::default_bold_font().width(m_username);
Rect username_rect {
menubar_rect.right() - wm.menubar_menu_margin() / 2 - Font::default_bold_font().width(m_username),
menubar_rect.y(),
username_width,
menubar_rect.height()
};
painter.draw_text(username_rect, m_username, Font::default_bold_font(), TextAlignment::CenterRight, Color::Black);
time_t now = time(nullptr);
auto* tm = localtime(&now);
auto time_text = String::format("%4u-%02u-%02u %02u:%02u:%02u",
tm->tm_year + 1900,
tm->tm_mon + 1,
tm->tm_mday,
tm->tm_hour,
tm->tm_min,
tm->tm_sec);
int time_width = wm.font().width(time_text);
Rect time_rect {
username_rect.left() - wm.menubar_menu_margin() / 2 - time_width,
menubar_rect.y(),
time_width,
menubar_rect.height()
};
painter.draw_text(time_rect, time_text, wm.font(), TextAlignment::CenterRight, Color::Black);
Rect cpu_rect { time_rect.right() - wm.font().width(time_text) - m_cpu_monitor.capacity() - 10, time_rect.y() + 1, m_cpu_monitor.capacity(), time_rect.height() - 2 };
m_cpu_monitor.paint(painter, cpu_rect);
}
void WSMenuBarKeeper::tick_clock()
{
refresh();
}
void WSMenuBarKeeper::refresh()
{
if (!m_window)
return;
draw();
window().invalidate();
}
void WSMenuBarKeeper::event(CEvent& event)
{
return CObject::event(event);
}

View file

@ -0,0 +1,28 @@
#pragma once
#include <LibCore/CObject.h>
#include <WindowServer/WSCPUMonitor.h>
#include <WindowServer/WSWindow.h>
class WSMenuBarKeeper final : public CObject {
public:
WSMenuBarKeeper();
virtual ~WSMenuBarKeeper() override;
WSWindow& window() { return *m_window; }
const WSWindow& window() const { return *m_window; }
void draw();
void refresh();
void setup();
virtual void event(CEvent&) override;
virtual const char* class_name() const override { return "WSMenuBarKeeper"; }
private:
void tick_clock();
OwnPtr<WSWindow> m_window;
WSCPUMonitor m_cpu_monitor;
String m_username;
};

View file

@ -138,6 +138,8 @@ static WSAPI_WindowType to_api(WSWindowType ws_type)
return WSAPI_WindowType::Taskbar;
case WSWindowType::Tooltip:
return WSAPI_WindowType::Tooltip;
case WSWindowType::Menubar:
return WSAPI_WindowType::Menubar;
default:
ASSERT_NOT_REACHED();
}

View file

@ -38,8 +38,6 @@ WSWindowManager::WSWindowManager()
{
s_the = this;
m_username = getlogin();
reload_config(false);
struct AppMenuItem {
@ -101,15 +99,7 @@ WSWindowManager::WSWindowManager()
// NOTE: This ensures that the system menu has the correct dimensions.
set_current_menubar(nullptr);
new CTimer(300, [this] {
static time_t last_update_time;
time_t now = time(nullptr);
if (now != last_update_time || m_cpu_monitor.is_dirty()) {
tick_clock();
last_update_time = now;
m_cpu_monitor.set_dirty(false);
}
});
m_menubar_keeper.setup();
invalidate();
WSCompositor::the().compose();
@ -198,11 +188,6 @@ const Font& WSWindowManager::app_menu_font() const
return Font::default_bold_font();
}
void WSWindowManager::tick_clock()
{
invalidate(menubar_rect());
}
void WSWindowManager::set_resolution(int width, int height)
{
WSCompositor::the().set_resolution(width, height);
@ -252,7 +237,7 @@ void WSWindowManager::set_current_menubar(WSMenuBar* menubar)
++index;
return true;
});
invalidate(menubar_rect());
m_menubar_keeper.refresh();
}
void WSWindowManager::add_window(WSWindow& window)
@ -416,6 +401,7 @@ void WSWindowManager::handle_menu_mouse_event(WSMenu& menu, const WSMouseEvent&
menu_window.set_visible(true);
}
m_current_menu = menu.make_weak_ptr();
m_menubar_keeper.refresh();
return;
}
if (event.type() == WSMouseEvent::MouseDown && event.button() == MouseButton::Left) {
@ -429,6 +415,7 @@ void WSWindowManager::close_current_menu()
if (m_current_menu && m_current_menu->menu_window())
m_current_menu->menu_window()->set_visible(false);
m_current_menu = nullptr;
m_menubar_keeper.refresh();
}
void WSWindowManager::handle_menubar_mouse_event(const WSMouseEvent& event)
@ -1004,7 +991,7 @@ void WSWindowManager::notify_client_changed_app_menubar(WSClientConnection& clie
{
if (active_client() == &client)
set_current_menubar(client.app_menubar());
invalidate(menubar_rect());
m_menubar_keeper.refresh();
}
const WSCursor& WSWindowManager::active_cursor() const

View file

@ -1,6 +1,5 @@
#pragma once
#include "WSMenuBar.h"
#include <AK/HashMap.h>
#include <AK/HashTable.h>
#include <AK/InlineLinkedList.h>
@ -11,9 +10,10 @@
#include <SharedGraphics/DisjointRectSet.h>
#include <SharedGraphics/Painter.h>
#include <SharedGraphics/Rect.h>
#include <WindowServer/WSCPUMonitor.h>
#include <WindowServer/WSCursor.h>
#include <WindowServer/WSEvent.h>
#include <WindowServer/WSMenuBar.h>
#include <WindowServer/WSMenuBarKeeper.h>
#include <WindowServer/WSWindow.h>
#include <WindowServer/WSWindowSwitcher.h>
#include <WindowServer/WSWindowType.h>
@ -131,6 +131,14 @@ public:
const WSWindow* active_fullscreen_window() const { return (m_active_window && m_active_window->is_fullscreen()) ? m_active_window : nullptr; }
WSWindow* active_fullscreen_window() { return (m_active_window && m_active_window->is_fullscreen()) ? m_active_window : nullptr; }
template<typename Callback>
void for_each_active_menubar_menu(Callback callback)
{
callback(*m_system_menu);
if (m_current_menubar)
m_current_menubar->for_each_menu(callback);
}
private:
Retained<WSCursor> get_cursor(const String& name);
Retained<WSCursor> get_cursor(const String& name, const Point& hotspot);
@ -159,18 +167,9 @@ private:
template<typename Callback>
void for_each_window(Callback);
template<typename Callback>
void for_each_active_menubar_menu(Callback callback)
{
callback(*m_system_menu);
if (m_current_menubar)
m_current_menubar->for_each_menu(callback);
}
void close_current_menu();
virtual void event(CEvent&) override;
void paint_window_frame(const WSWindow&);
void tick_clock();
void tell_wm_listener_about_window(WSWindow& listener, WSWindow&);
void tell_wm_listener_about_window_icon(WSWindow& listener, WSWindow&);
void tell_wm_listener_about_window_rect(WSWindow& listener, WSWindow&);
@ -241,13 +240,11 @@ private:
WeakPtr<WSMenu> m_current_menu;
WSWindowSwitcher m_switcher;
WSMenuBarKeeper m_menubar_keeper;
String m_username;
WeakPtr<WSButton> m_cursor_tracking_button;
WeakPtr<WSButton> m_hovered_button;
WSCPUMonitor m_cpu_monitor;
RetainPtr<CConfigFile> m_wm_config;
};
@ -285,6 +282,8 @@ IterationDecision WSWindowManager::for_each_visible_window_from_back_to_front(Ca
return IterationDecision::Break;
if (for_each_visible_window_of_type_from_back_to_front(WSWindowType::Tooltip, callback) == IterationDecision::Break)
return IterationDecision::Break;
if (for_each_visible_window_of_type_from_back_to_front(WSWindowType::Menubar, callback) == IterationDecision::Break)
return IterationDecision::Break;
if (for_each_visible_window_of_type_from_back_to_front(WSWindowType::Menu, callback) == IterationDecision::Break)
return IterationDecision::Break;
return for_each_visible_window_of_type_from_back_to_front(WSWindowType::WindowSwitcher, callback);
@ -320,6 +319,8 @@ IterationDecision WSWindowManager::for_each_visible_window_from_front_to_back(Ca
return IterationDecision::Break;
if (for_each_visible_window_of_type_from_front_to_back(WSWindowType::Menu, callback) == IterationDecision::Break)
return IterationDecision::Break;
if (for_each_visible_window_of_type_from_front_to_back(WSWindowType::Menubar, callback) == IterationDecision::Break)
return IterationDecision::Break;
if (for_each_visible_window_of_type_from_front_to_back(WSWindowType::Taskbar, callback) == IterationDecision::Break)
return IterationDecision::Break;
if (for_each_visible_window_of_type_from_front_to_back(WSWindowType::Tooltip, callback) == IterationDecision::Break)

View file

@ -7,4 +7,5 @@ enum class WSWindowType {
WindowSwitcher,
Taskbar,
Tooltip,
Menubar,
};