WindowServer+CPUGraph: Make menu applets be "regular" windows

Instead of implementing menu applets as their own thing, they are now
WSWindows of WSWindowType::MenuApplet.

This makes it much easier to work with them on the client side, since
you can just create a GWindow with the right type and you're in the
menubar doing applet stuff :^)
This commit is contained in:
Andreas Kling 2019-12-16 15:05:45 +01:00
parent 648ed76085
commit df129bbe0e
Notes: sideshowbarker 2024-07-19 10:50:32 +09:00
13 changed files with 97 additions and 202 deletions

View file

@ -9,4 +9,5 @@ enum class GWindowType {
Taskbar,
Tooltip,
Menubar,
MenuApplet,
};

View file

@ -1,87 +1,83 @@
#include <AK/CircularQueue.h>
#include <LibCore/CProcessStatisticsReader.h>
#include <LibCore/CTimer.h>
#include <LibDraw/GraphicsBitmap.h>
#include <LibGUI/GApplication.h>
#include <LibGUI/GPainter.h>
#include <LibGUI/GWindowServerConnection.h>
#include <LibGUI/GWidget.h>
#include <LibGUI/GWindow.h>
NonnullRefPtr<GraphicsBitmap> create_shared_bitmap(const Size& size)
{
ASSERT(GWindowServerConnection::the().server_pid());
ASSERT(!size.is_empty());
size_t pitch = round_up_to_power_of_two(size.width() * sizeof(RGBA32), 16);
size_t size_in_bytes = size.height() * pitch;
auto shared_buffer = SharedBuffer::create_with_size(size_in_bytes);
ASSERT(shared_buffer);
shared_buffer->share_with(GWindowServerConnection::the().server_pid());
return GraphicsBitmap::create_with_shared_buffer(GraphicsBitmap::Format::RGBA32, *shared_buffer, size);
}
class GraphWidget final : public GWidget {
C_OBJECT(GraphWidget)
public:
GraphWidget()
: GWidget(nullptr)
{
start_timer(1000);
}
static void get_cpu_usage(unsigned& busy, unsigned& idle)
{
busy = 0;
idle = 0;
virtual ~GraphWidget() override {}
auto all_processes = CProcessStatisticsReader::get_all();
private:
virtual void timer_event(CTimerEvent&) override
{
unsigned busy;
unsigned idle;
get_cpu_usage(busy, idle);
unsigned busy_diff = busy - m_last_busy;
unsigned idle_diff = idle - m_last_idle;
m_last_busy = busy;
m_last_idle = idle;
float cpu = (float)busy_diff / (float)(busy_diff + idle_diff);
m_cpu_history.enqueue(cpu);
update();
}
for (auto& it : all_processes) {
for (auto& jt : it.value.threads) {
if (it.value.pid == 0)
idle += jt.times_scheduled;
else
busy += jt.times_scheduled;
virtual void paint_event(GPaintEvent& event) override
{
GPainter painter(*this);
painter.add_clip_rect(event.rect());
painter.fill_rect(event.rect(), Color::Black);
int i = m_cpu_history.capacity() - m_cpu_history.size();
for (auto cpu_usage : m_cpu_history) {
painter.draw_line(
{ i, rect().bottom() },
{ i, (int)(height() - (cpu_usage * (float)height())) },
Color::from_rgb(0xaa6d4b));
++i;
}
}
}
static void get_cpu_usage(unsigned& busy, unsigned& idle)
{
busy = 0;
idle = 0;
auto all_processes = CProcessStatisticsReader::get_all();
for (auto& it : all_processes) {
for (auto& jt : it.value.threads) {
if (it.value.pid == 0)
idle += jt.times_scheduled;
else
busy += jt.times_scheduled;
}
}
}
CircularQueue<float, 30> m_cpu_history;
unsigned m_last_busy { 0 };
unsigned m_last_idle { 0 };
};
int main(int argc, char** argv)
{
GApplication app(argc, argv);
Size applet_size(30, 16);
Rect applet_rect({}, applet_size);
CircularQueue<float, 30> cpu_history;
i32 applet_id = GWindowServerConnection::the().send_sync<WindowServer::CreateMenuApplet>(applet_size)->applet_id();
auto bitmap = create_shared_bitmap(applet_size);
GWindowServerConnection::the().send_sync<WindowServer::SetMenuAppletBackingStore>(applet_id, bitmap->shared_buffer_id());
unsigned last_busy = 0;
unsigned last_idle = 0;
auto repaint = [&] {
unsigned busy;
unsigned idle;
get_cpu_usage(busy, idle);
unsigned busy_diff = busy - last_busy;
unsigned idle_diff = idle - last_idle;
last_busy = busy;
last_idle = idle;
float cpu = (float)busy_diff / (float)(busy_diff + idle_diff);
cpu_history.enqueue(cpu);
GPainter painter(*bitmap);
painter.fill_rect(applet_rect, Color::Black);
int i = cpu_history.capacity() - cpu_history.size();
for (auto cpu_usage : cpu_history) {
painter.draw_line(
{ applet_rect.x() + i, applet_rect.bottom() },
{ applet_rect.x() + i, (int)(applet_rect.y() + (applet_rect.height() - (cpu_usage * (float)applet_rect.height()))) },
Color::from_rgb(0xaa6d4b));
++i;
}
GWindowServerConnection::the().send_sync<WindowServer::InvalidateMenuAppletRect>(applet_id, applet_rect);
};
repaint();
auto timer = CTimer::construct(1000, [&] {
repaint();
});
auto window = GWindow::construct();
window->set_window_type(GWindowType::MenuApplet);
window->resize(30, 16);
auto widget = GraphWidget::construct();
window->set_main_widget(widget);
window->show();
return app.exec();
}

View file

@ -16,7 +16,6 @@ OBJS = \
WSButton.o \
WSCompositor.o \
WSMenuManager.o \
WSMenuApplet.o \
main.o
APP = WindowServer

View file

@ -6,7 +6,6 @@
#include <WindowServer/WSCompositor.h>
#include <WindowServer/WSEventLoop.h>
#include <WindowServer/WSMenu.h>
#include <WindowServer/WSMenuApplet.h>
#include <WindowServer/WSMenuBar.h>
#include <WindowServer/WSMenuItem.h>
#include <WindowServer/WSScreen.h>
@ -407,6 +406,8 @@ OwnPtr<WindowServer::CreateWindowResponse> WSClientConnection::handle(const Wind
window->set_size_increment(message.size_increment());
window->set_base_size(message.base_size());
window->invalidate();
if (window->type() == WSWindowType::MenuApplet)
WSWindowManager::the().menu_manager().add_applet(*window);
m_windows.set(window_id, move(window));
return make<WindowServer::CreateWindowResponse>(window_id);
}
@ -419,6 +420,10 @@ OwnPtr<WindowServer::DestroyWindowResponse> WSClientConnection::handle(const Win
return nullptr;
}
auto& window = *(*it).value;
if (window.type() == WSWindowType::MenuApplet)
WSWindowManager::the().menu_manager().remove_applet(window);
WSWindowManager::the().invalidate(window);
remove_child(window);
ASSERT(it->value.ptr() == &window);
@ -629,56 +634,6 @@ void WSClientConnection::handle(const WindowServer::WM_SetWindowTaskbarRect& mes
window.set_taskbar_rect(message.rect());
}
OwnPtr<WindowServer::CreateMenuAppletResponse> WSClientConnection::handle(const WindowServer::CreateMenuApplet& message)
{
auto applet = make<WSMenuApplet>(message.size());
auto applet_id = applet->applet_id();
WSWindowManager::the().menu_manager().add_applet(*applet);
m_menu_applets.set(applet_id, move(applet));
return make<WindowServer::CreateMenuAppletResponse>(applet_id);
}
OwnPtr<WindowServer::DestroyMenuAppletResponse> WSClientConnection::handle(const WindowServer::DestroyMenuApplet& message)
{
auto it = m_menu_applets.find(message.applet_id());
if (it == m_menu_applets.end()) {
did_misbehave("DestroyApplet: Invalid applet ID");
return nullptr;
}
WSWindowManager::the().menu_manager().remove_applet(*it->value);
m_menu_applets.remove(message.applet_id());
return make<WindowServer::DestroyMenuAppletResponse>();
}
OwnPtr<WindowServer::SetMenuAppletBackingStoreResponse> WSClientConnection::handle(const WindowServer::SetMenuAppletBackingStore& message)
{
auto it = m_menu_applets.find(message.applet_id());
if (it == m_menu_applets.end()) {
did_misbehave("SetAppletBackingStore: Invalid applet ID");
return nullptr;
}
auto shared_buffer = SharedBuffer::create_from_shared_buffer_id(message.shared_buffer_id());
ssize_t size_in_bytes = it->value->size().area() * sizeof(RGBA32);
if (size_in_bytes > shared_buffer->size()) {
did_misbehave("SetAppletBackingStore: Shared buffer is too small for applet size");
return nullptr;
}
auto bitmap = GraphicsBitmap::create_with_shared_buffer(GraphicsBitmap::Format::RGBA32, *shared_buffer, it->value->size());
it->value->set_bitmap(bitmap);
return make<WindowServer::SetMenuAppletBackingStoreResponse>();
}
OwnPtr<WindowServer::InvalidateMenuAppletRectResponse> WSClientConnection::handle(const WindowServer::InvalidateMenuAppletRect& message)
{
auto it = m_menu_applets.find(message.applet_id());
if (it == m_menu_applets.end()) {
did_misbehave("InvalidateAppletRect: Invalid applet ID");
return nullptr;
}
it->value->invalidate(message.rect());
return make<WindowServer::InvalidateMenuAppletRectResponse>();
}
OwnPtr<WindowServer::StartDragResponse> WSClientConnection::handle(const WindowServer::StartDrag& message)
{
auto& wm = WSWindowManager::the();

View file

@ -10,7 +10,6 @@
#include <WindowServer/WSEvent.h>
#include <WindowServer/WindowServerEndpoint.h>
class WSMenuApplet;
class WSWindow;
class WSMenu;
class WSMenuBar;
@ -88,13 +87,8 @@ private:
virtual OwnPtr<WindowServer::DismissMenuResponse> handle(const WindowServer::DismissMenu&) override;
virtual OwnPtr<WindowServer::SetWindowIconBitmapResponse> handle(const WindowServer::SetWindowIconBitmap&) override;
virtual void handle(const WindowServer::WM_SetWindowTaskbarRect&) override;
virtual OwnPtr<WindowServer::CreateMenuAppletResponse> handle(const WindowServer::CreateMenuApplet&) override;
virtual OwnPtr<WindowServer::DestroyMenuAppletResponse> handle(const WindowServer::DestroyMenuApplet&) override;
virtual OwnPtr<WindowServer::SetMenuAppletBackingStoreResponse> handle(const WindowServer::SetMenuAppletBackingStore&) override;
virtual OwnPtr<WindowServer::InvalidateMenuAppletRectResponse> handle(const WindowServer::InvalidateMenuAppletRect&) override;
virtual OwnPtr<WindowServer::StartDragResponse> handle(const WindowServer::StartDrag&) override;
HashMap<i32, NonnullOwnPtr<WSMenuApplet>> m_menu_applets;
HashMap<int, NonnullRefPtr<WSWindow>> m_windows;
HashMap<int, NonnullOwnPtr<WSMenuBar>> m_menubars;
HashMap<int, NonnullRefPtr<WSMenu>> m_menus;

View file

@ -1,25 +0,0 @@
#include <WindowServer/WSMenuApplet.h>
#include <WindowServer/WSMenuManager.h>
#include <WindowServer/WSWindowManager.h>
static i32 s_next_applet_id = 1;
WSMenuApplet::WSMenuApplet(const Size& size)
: m_applet_id(s_next_applet_id++)
, m_size(size)
{
}
WSMenuApplet::~WSMenuApplet()
{
}
void WSMenuApplet::set_bitmap(GraphicsBitmap* bitmap)
{
m_bitmap = bitmap;
}
void WSMenuApplet::invalidate(const Rect& rect)
{
WSWindowManager::the().menu_manager().invalidate_applet(*this, rect);
}

View file

@ -1,33 +0,0 @@
#pragma once
#include <AK/Noncopyable.h>
#include <AK/Weakable.h>
#include <LibDraw/Rect.h>
#include <LibDraw/Size.h>
class GraphicsBitmap;
class WSMenuApplet : public Weakable<WSMenuApplet> {
AK_MAKE_NONCOPYABLE(WSMenuApplet)
AK_MAKE_NONMOVABLE(WSMenuApplet)
public:
explicit WSMenuApplet(const Size&);
~WSMenuApplet();
i32 applet_id() const { return m_applet_id; }
Size size() const { return m_size; }
void set_bitmap(GraphicsBitmap*);
const GraphicsBitmap* bitmap() const { return m_bitmap; }
void invalidate(const Rect&);
const Rect& rect_in_menubar() const { return m_rect_in_menubar; }
void set_rect_in_menubar(const Rect& rect) { m_rect_in_menubar = rect; }
private:
i32 m_applet_id { -1 };
Size m_size;
Rect m_rect_in_menubar;
RefPtr<GraphicsBitmap> m_bitmap;
};

View file

@ -298,7 +298,7 @@ void WSMenuManager::close_bar()
m_bar_open = false;
}
void WSMenuManager::add_applet(WSMenuApplet& applet)
void WSMenuManager::add_applet(WSWindow& applet)
{
int right_edge_x = m_audio_rect.x() - 4;
for (auto& existing_applet : m_applets) {
@ -314,22 +314,22 @@ void WSMenuManager::add_applet(WSMenuApplet& applet)
m_applets.append(applet.make_weak_ptr());
}
void WSMenuManager::remove_applet(WSMenuApplet& applet)
void WSMenuManager::remove_applet(WSWindow& applet)
{
m_applets.remove_first_matching([&](auto& entry) {
return &applet == entry.ptr();
});
}
void WSMenuManager::draw_applet(const WSMenuApplet& applet)
void WSMenuManager::draw_applet(const WSWindow& applet)
{
if (!applet.bitmap())
if (!applet.backing_store())
return;
Painter painter(*window().backing_store());
painter.blit(applet.rect_in_menubar().location(), *applet.bitmap(), applet.bitmap()->rect());
painter.blit(applet.rect_in_menubar().location(), *applet.backing_store(), applet.backing_store()->rect());
}
void WSMenuManager::invalidate_applet(WSMenuApplet& applet, const Rect& rect)
void WSMenuManager::invalidate_applet(const WSWindow& applet, const Rect& rect)
{
// FIXME: This should only invalidate the applet's own rect, not the whole menubar.
(void)rect;

View file

@ -3,7 +3,6 @@
#include "WSMenu.h"
#include <LibCore/CObject.h>
#include <LibCore/CTimer.h>
#include <WindowServer/WSMenuApplet.h>
#include <WindowServer/WSWindow.h>
class AClientConnection;
@ -36,9 +35,9 @@ public:
void close_everyone_not_in_lineage(WSMenu&);
void close_menu_and_descendants(WSMenu&);
void add_applet(WSMenuApplet&);
void remove_applet(WSMenuApplet&);
void invalidate_applet(WSMenuApplet&, const Rect&);
void add_applet(WSWindow&);
void remove_applet(WSWindow&);
void invalidate_applet(const WSWindow&, const Rect&);
private:
void close_menus(const Vector<WSMenu*>&);
@ -49,7 +48,7 @@ private:
void handle_menu_mouse_event(WSMenu&, const WSMouseEvent&);
void draw();
void draw_applet(const WSMenuApplet&);
void draw_applet(const WSWindow&);
void tick_clock();
RefPtr<WSWindow> m_window;
@ -62,7 +61,7 @@ private:
RefPtr<GraphicsBitmap> m_muted_bitmap;
RefPtr<GraphicsBitmap> m_unmuted_bitmap;
Vector<WeakPtr<WSMenuApplet>> m_applets;
Vector<WeakPtr<WSWindow>> m_applets;
OwnPtr<AClientConnection> m_audio_client;

View file

@ -118,12 +118,19 @@ public:
virtual void event(CEvent&) override;
// Only used by WSWindowType::MenuApplet. Perhaps it could be a WSWindow subclass? I don't know.
void set_rect_in_menubar(const Rect& rect) { m_rect_in_menubar = rect; }
const Rect& rect_in_menubar() const { return m_rect_in_menubar; }
const GraphicsBitmap* backing_store() const { return m_backing_store.ptr(); }
GraphicsBitmap* backing_store() { return m_backing_store.ptr(); }
void set_backing_store(RefPtr<GraphicsBitmap>&& backing_store)
{
m_last_backing_store = move(m_backing_store);
m_backing_store = move(backing_store);
}
void swap_backing_stores()
{
swap(m_backing_store, m_last_backing_store);
@ -200,6 +207,7 @@ private:
unsigned m_wm_event_mask { 0 };
DisjointRectSet m_pending_paint_rects;
Rect m_unmaximized_rect;
Rect m_rect_in_menubar;
RefPtr<WSMenu> m_window_menu;
int m_minimize_animation_step { -1 };
};

View file

@ -1072,6 +1072,11 @@ void WSWindowManager::invalidate(const WSWindow& window)
void WSWindowManager::invalidate(const WSWindow& window, const Rect& rect)
{
if (window.type() == WSWindowType::MenuApplet) {
menu_manager().invalidate_applet(window, rect);
return;
}
if (rect.is_empty()) {
invalidate(window);
return;

View file

@ -9,4 +9,5 @@ enum class WSWindowType {
Taskbar,
Tooltip,
Menubar,
MenuApplet,
};

View file

@ -16,11 +16,6 @@ endpoint WindowServer = 2
UpdateMenuItem(i32 menu_id, i32 identifier, i32 submenu_id, String text, bool enabled, bool checkable, bool checked, String shortcut) => ()
CreateMenuApplet(Size size) => (i32 applet_id)
DestroyMenuApplet(i32 applet_id) => ()
SetMenuAppletBackingStore(i32 applet_id, i32 shared_buffer_id) => ()
InvalidateMenuAppletRect(i32 applet_id, Rect rect) => ()
CreateWindow(
Rect rect,
bool has_alpha_channel,