mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-12-11 17:00:37 +00:00
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:
parent
648ed76085
commit
df129bbe0e
Notes:
sideshowbarker
2024-07-19 10:50:32 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/df129bbe0e3
13 changed files with 97 additions and 202 deletions
|
@ -9,4 +9,5 @@ enum class GWindowType {
|
|||
Taskbar,
|
||||
Tooltip,
|
||||
Menubar,
|
||||
MenuApplet,
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@ OBJS = \
|
|||
WSButton.o \
|
||||
WSCompositor.o \
|
||||
WSMenuManager.o \
|
||||
WSMenuApplet.o \
|
||||
main.o
|
||||
|
||||
APP = WindowServer
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 };
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -9,4 +9,5 @@ enum class WSWindowType {
|
|||
Taskbar,
|
||||
Tooltip,
|
||||
Menubar,
|
||||
MenuApplet,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue