mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 08:32:43 +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
					
				
					 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…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Andreas Kling
						Andreas Kling