From 44d5388e786e53c8719a9209beb38bdb0930f0b7 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Thu, 5 Dec 2019 19:36:01 +0100 Subject: [PATCH] WindowServer: Add basic menu applet concept It's now possible to create a little applet window that sits inside the system's menubar. This is done using the new CreateMenuApplet IPC call. So far, it's possible to assign a backing store ID, and to invalidate rects for repaint. There is no way to get the events from inside the applet just yet. This will allow us to move the CPU graph and audio thingy to separate applet processes. :^) --- Servers/WindowServer/Makefile | 1 + Servers/WindowServer/WSClientConnection.cpp | 51 +++++++++++++++++++++ Servers/WindowServer/WSClientConnection.h | 6 +++ Servers/WindowServer/WSMenuApplet.cpp | 25 ++++++++++ Servers/WindowServer/WSMenuApplet.h | 33 +++++++++++++ Servers/WindowServer/WSMenuManager.cpp | 45 ++++++++++++++++++ Servers/WindowServer/WSMenuManager.h | 8 ++++ Servers/WindowServer/WindowServer.ipc | 5 ++ 8 files changed, 174 insertions(+) create mode 100644 Servers/WindowServer/WSMenuApplet.cpp create mode 100644 Servers/WindowServer/WSMenuApplet.h diff --git a/Servers/WindowServer/Makefile b/Servers/WindowServer/Makefile index 411ec11953..9d407aa26f 100644 --- a/Servers/WindowServer/Makefile +++ b/Servers/WindowServer/Makefile @@ -17,6 +17,7 @@ OBJS = \ WSCPUMonitor.o \ WSCompositor.o \ WSMenuManager.o \ + WSMenuApplet.o \ main.o APP = WindowServer diff --git a/Servers/WindowServer/WSClientConnection.cpp b/Servers/WindowServer/WSClientConnection.cpp index 3d365d3682..0e23387706 100644 --- a/Servers/WindowServer/WSClientConnection.cpp +++ b/Servers/WindowServer/WSClientConnection.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -628,3 +629,53 @@ void WSClientConnection::handle(const WindowServer::WM_SetWindowTaskbarRect& mes auto& window = *(*it).value; window.set_taskbar_rect(message.rect()); } + +OwnPtr WSClientConnection::handle(const WindowServer::CreateMenuApplet& message) +{ + auto applet = make(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(applet_id); +} + +OwnPtr 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(); +} + +OwnPtr 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(); +} + +OwnPtr 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(); +} diff --git a/Servers/WindowServer/WSClientConnection.h b/Servers/WindowServer/WSClientConnection.h index 9f50a0c901..b7d3bc4987 100644 --- a/Servers/WindowServer/WSClientConnection.h +++ b/Servers/WindowServer/WSClientConnection.h @@ -10,6 +10,7 @@ #include #include +class WSMenuApplet; class WSWindow; class WSMenu; class WSMenuBar; @@ -87,7 +88,12 @@ private: virtual OwnPtr handle(const WindowServer::DismissMenu&) override; virtual OwnPtr handle(const WindowServer::SetWindowIconBitmap&) override; virtual void handle(const WindowServer::WM_SetWindowTaskbarRect&) override; + virtual OwnPtr handle(const WindowServer::CreateMenuApplet&) override; + virtual OwnPtr handle(const WindowServer::DestroyMenuApplet&) override; + virtual OwnPtr handle(const WindowServer::SetMenuAppletBackingStore&) override; + virtual OwnPtr handle(const WindowServer::InvalidateMenuAppletRect&) override; + HashMap> m_menu_applets; HashMap> m_windows; HashMap> m_menubars; HashMap> m_menus; diff --git a/Servers/WindowServer/WSMenuApplet.cpp b/Servers/WindowServer/WSMenuApplet.cpp new file mode 100644 index 0000000000..765c4f23dd --- /dev/null +++ b/Servers/WindowServer/WSMenuApplet.cpp @@ -0,0 +1,25 @@ +#include +#include +#include + +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); +} diff --git a/Servers/WindowServer/WSMenuApplet.h b/Servers/WindowServer/WSMenuApplet.h new file mode 100644 index 0000000000..f51402775b --- /dev/null +++ b/Servers/WindowServer/WSMenuApplet.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include +#include + +class GraphicsBitmap; + +class WSMenuApplet : public Weakable { + 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 m_bitmap; +}; diff --git a/Servers/WindowServer/WSMenuManager.cpp b/Servers/WindowServer/WSMenuManager.cpp index 5be1a6c5a1..7712bedaf6 100644 --- a/Servers/WindowServer/WSMenuManager.cpp +++ b/Servers/WindowServer/WSMenuManager.cpp @@ -129,6 +129,12 @@ void WSMenuManager::draw() auto& audio_bitmap = m_audio_muted ? *m_muted_bitmap : *m_unmuted_bitmap; painter.blit(m_audio_rect.location(), audio_bitmap, audio_bitmap.rect()); + + for (auto& applet : m_applets) { + if (!applet) + continue; + draw_applet(*applet); + } } void WSMenuManager::tick_clock() @@ -282,3 +288,42 @@ void WSMenuManager::close_bar() close_everyone(); m_bar_open = false; } + +void WSMenuManager::add_applet(WSMenuApplet& applet) +{ + int right_edge_x = m_audio_rect.x() - 4; + for (auto& existing_applet : m_applets) { + if (existing_applet) + right_edge_x = existing_applet->rect_in_menubar().x() - 4; + } + + Rect new_applet_rect(right_edge_x - applet.size().width(), 0, applet.size().width(), applet.size().height()); + Rect dummy_menubar_rect(0, 0, 0, 18); + new_applet_rect.center_vertically_within(dummy_menubar_rect); + + applet.set_rect_in_menubar(new_applet_rect); + m_applets.append(applet.make_weak_ptr()); +} + +void WSMenuManager::remove_applet(WSMenuApplet& applet) +{ + m_applets.remove_first_matching([&](auto& entry) { + return &applet == entry.ptr(); + }); +} + +void WSMenuManager::draw_applet(const WSMenuApplet& applet) +{ + if (!applet.bitmap()) + return; + Painter painter(*window().backing_store()); + painter.blit(applet.rect_in_menubar().location(), *applet.bitmap(), applet.bitmap()->rect()); +} + +void WSMenuManager::invalidate_applet(WSMenuApplet& applet, const Rect& rect) +{ + // FIXME: This should only invalidate the applet's own rect, not the whole menubar. + (void)rect; + draw_applet(applet); + window().invalidate(); +} diff --git a/Servers/WindowServer/WSMenuManager.h b/Servers/WindowServer/WSMenuManager.h index 4c65d85402..69a33b7793 100644 --- a/Servers/WindowServer/WSMenuManager.h +++ b/Servers/WindowServer/WSMenuManager.h @@ -3,6 +3,7 @@ #include "WSMenu.h" #include #include +#include #include #include @@ -33,6 +34,10 @@ 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&); + private: void close_menus(const Vector&); @@ -42,6 +47,7 @@ private: void handle_menu_mouse_event(WSMenu&, const WSMouseEvent&); void draw(); + void draw_applet(const WSMenuApplet&); void tick_clock(); RefPtr m_window; @@ -55,6 +61,8 @@ private: RefPtr m_muted_bitmap; RefPtr m_unmuted_bitmap; + Vector> m_applets; + OwnPtr m_audio_client; Rect m_audio_rect; diff --git a/Servers/WindowServer/WindowServer.ipc b/Servers/WindowServer/WindowServer.ipc index 883d820219..3f78821d37 100644 --- a/Servers/WindowServer/WindowServer.ipc +++ b/Servers/WindowServer/WindowServer.ipc @@ -16,6 +16,11 @@ 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,