From 820b1ae2ba383a9122aed12c10135ecc0c7e3a53 Mon Sep 17 00:00:00 2001 From: apio Date: Tue, 15 Aug 2023 10:23:37 +0200 Subject: [PATCH] libui+gclient: Add basic OOP wrappers around the IPC protocol --- apps/gclient.cpp | 92 +++++---------------------------------- libui/CMakeLists.txt | 2 + libui/include/ui/App.h | 41 +++++++++++++++++ libui/include/ui/Window.h | 38 ++++++++++++++++ libui/src/App.cpp | 54 +++++++++++++++++++++++ libui/src/Window.cpp | 77 ++++++++++++++++++++++++++++++++ 6 files changed, 223 insertions(+), 81 deletions(-) create mode 100644 libui/include/ui/App.h create mode 100644 libui/include/ui/Window.h create mode 100644 libui/src/App.cpp create mode 100644 libui/src/Window.cpp diff --git a/apps/gclient.cpp b/apps/gclient.cpp index 9afe7e68..3d9558a8 100644 --- a/apps/gclient.cpp +++ b/apps/gclient.cpp @@ -1,94 +1,24 @@ -#include -#include #include -#include -#include -#include -#include -#include - -struct Window -{ - ui::Canvas canvas; - int id; - - void set_title(os::LocalClient& client, const char* title) - { - ui::SetWindowTitleRequest request; - request.window = id; - SET_IPC_STRING(request.title, title); - os::IPC::send_async(client, request); - } - - void redraw(os::LocalClient& client) - { - ui::InvalidateRequest request; - request.window = id; - os::IPC::send_async(client, request); - } -}; +#include +#include +#include Result handle_ipc_client_event(os::LocalClient&, u8) { todo(); } -static Result create_shm_region(const char* path, int* outfd, ui::Rect rect) -{ - int fd = shm_open(path, O_RDWR, 0600); - shm_unlink(path); - if (fd < 0) return err(errno); - - usize size = rect.width * rect.height * 4; // 4 bytes per pixel - - void* p = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (p == MAP_FAILED) - { - shm_unlink(path); - close(fd); - return 0; - } - - if (outfd) *outfd = fd; - else - close(fd); - return (u32*)p; -} - -Result create_window(os::LocalClient& client, ui::Rect rect) -{ - ui::CreateWindowRequest request; - request.rect = rect; - auto response = TRY(os::IPC::send_sync(client, request)); - u32* pixels = TRY(create_shm_region(response.shm_path, nullptr, rect)); - return Window { ui::Canvas { rect.width, rect.height, rect.width, (u8*)pixels }, response.window }; -} - Result luna_main(int argc, char** argv) { - StringView socket_path = "/tmp/wind.sock"; + ui::App app; + TRY(app.init(argc, argv)); - os::ArgumentParser parser; - parser.add_description("A graphical user interface client."_sv); - parser.add_system_program_info("gclient"_sv); - parser.add_value_argument(socket_path, 's', "socket"_sv, "the path for the local IPC socket"_sv); - parser.parse(argc, argv); + auto window = TRY(ui::Window::create(ui::Rect { 200, 200, 400, 300 })); + os::println("gclient: Created new window!"); - auto client = TRY(os::LocalClient::connect(socket_path, false)); + window->set_title("Example Window"); + window->canvas().fill(ui::CYAN); + window->update(); - Window window = TRY(create_window(*client, ui::Rect { 200, 200, 400, 300 })); - os::println("Created new window with id %d!", window.id); - - sleep(3); - - window.set_title(*client, "Example Window"); - - sleep(3); - - window.canvas.fill(ui::CYAN); - window.redraw(*client); - - sleep(3); - - return 0; + return app.run(); } diff --git a/libui/CMakeLists.txt b/libui/CMakeLists.txt index 4a1adaa2..81270417 100644 --- a/libui/CMakeLists.txt +++ b/libui/CMakeLists.txt @@ -10,6 +10,8 @@ set(SOURCES src/Rect.cpp src/Font.cpp src/Image.cpp + src/App.cpp + src/Window.cpp ) add_library(ui ${SOURCES}) diff --git a/libui/include/ui/App.h b/libui/include/ui/App.h new file mode 100644 index 00000000..1f3b899f --- /dev/null +++ b/libui/include/ui/App.h @@ -0,0 +1,41 @@ +/** + * @file App.h + * @author apio (cloudapio.eu) + * @brief UI application event loop. + * + * @copyright Copyright (c) 2023, the Luna authors. + * + */ + +#pragma once +#include + +namespace ui +{ + class App + { + public: + App(); + ~App(); + + Result init(int, char**); + Result run(); + + os::LocalClient& client() + { + return *m_client; + } + + void set_should_close(bool b) + { + m_should_close = b; + } + + static App& the(); + + private: + static App* s_app; + OwnedPtr m_client; + bool m_should_close { false }; + }; +} diff --git a/libui/include/ui/Window.h b/libui/include/ui/Window.h new file mode 100644 index 00000000..0c0dae1c --- /dev/null +++ b/libui/include/ui/Window.h @@ -0,0 +1,38 @@ +/** + * @file Window.h + * @author apio (cloudapio.eu) + * @brief UI windows. + * + * @copyright Copyright (c) 2023, the Luna authors. + * + */ + +#pragma once +#include +#include +#include +#include + +namespace ui +{ + class Window + { + public: + static Result> create(Rect rect); + + void set_title(StringView title); + + Canvas& canvas() + { + return m_canvas; + } + + void update(); + + ~Window(); + + private: + int m_id; + Canvas m_canvas; + }; +} diff --git a/libui/src/App.cpp b/libui/src/App.cpp new file mode 100644 index 00000000..d7686c98 --- /dev/null +++ b/libui/src/App.cpp @@ -0,0 +1,54 @@ +/** + * @file App.cpp + * @author apio (cloudapio.eu) + * @brief UI application event loop. + * + * @copyright Copyright (c) 2023, the Luna authors. + * + */ + +#include +#include +#include + +namespace ui +{ + App* App::s_app { nullptr }; + + App::App() + { + s_app = this; + } + + App::~App() + { + s_app = nullptr; + } + + Result App::init(int argc, char** argv) + { + StringView socket_path = "/tmp/wind.sock"; + + os::ArgumentParser parser; + parser.add_description("A UI application."_sv); + parser.add_system_program_info(argv[0]); + parser.add_value_argument(socket_path, 's', "socket"_sv, "the path for the local IPC socket"_sv); + parser.parse(argc, argv); + + m_client = TRY(os::LocalClient::connect(socket_path, true)); + + return {}; + } + + Result App::run() + { + while (!m_should_close) { TRY(os::IPC::check_for_messages(*m_client)); } + return 0; + } + + App& App::the() + { + check(s_app); + return *s_app; + } +} diff --git a/libui/src/Window.cpp b/libui/src/Window.cpp new file mode 100644 index 00000000..d56c1941 --- /dev/null +++ b/libui/src/Window.cpp @@ -0,0 +1,77 @@ +/** + * @file Window.cpp + * @author apio (cloudapio.eu) + * @brief UI windows. + * + * @copyright Copyright (c) 2023, the Luna authors. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +static Result create_shm_region(const char* path, int* outfd, ui::Rect rect) +{ + int fd = shm_open(path, O_RDWR, 0600); + shm_unlink(path); + if (fd < 0) return err(errno); + + usize size = rect.width * rect.height * 4; // 4 bytes per pixel + + void* p = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (p == MAP_FAILED) + { + shm_unlink(path); + close(fd); + return 0; + } + + if (outfd) *outfd = fd; + else + close(fd); + return (u32*)p; +} + +namespace ui +{ + Result> Window::create(Rect rect) + { + auto window = TRY(make_owned()); + + ui::CreateWindowRequest request; + request.rect = rect; + auto response = TRY(os::IPC::send_sync(App::the().client(), request)); + + u32* pixels = TRY(create_shm_region(response.shm_path, nullptr, rect)); + + window->m_canvas = ui::Canvas { rect.width, rect.height, rect.width, (u8*)pixels }; + window->m_id = response.window; + + return window; + } + + Window::~Window() + { + if (m_canvas.ptr) munmap(m_canvas.ptr, ((usize)m_canvas.width) * ((usize)m_canvas.height) * 4); + } + + void Window::set_title(StringView title) + { + ui::SetWindowTitleRequest request; + request.window = m_id; + SET_IPC_STRING(request.title, title.chars()); + os::IPC::send_async(App::the().client(), request); + } + + void Window::update() + { + ui::InvalidateRequest request; + request.window = m_id; + os::IPC::send_async(App::the().client(), request); + } +}