From 60f9c0e5a3b168d28f574c81cec5ec8a719a3313 Mon Sep 17 00:00:00 2001 From: apio Date: Mon, 14 Aug 2023 20:08:05 +0200 Subject: [PATCH] wind+gclient: Add SetWindowTitle and support shm buffers --- apps/CMakeLists.txt | 1 + apps/gclient.cpp | 68 ++++++++++++++--- libui/include/ui/ipc/Client.h | 1 + libui/include/ui/ipc/Server.h | 19 ++++- wind/IPC.cpp | 140 ++++++++++++++++++++++++++++------ wind/Window.cpp | 14 +++- wind/Window.h | 6 +- wind/main.cpp | 2 +- 8 files changed, 212 insertions(+), 39 deletions(-) diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index 8c3f9211..9d7f383a 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -48,3 +48,4 @@ luna_app(input.cpp input) luna_app(shmem-test.cpp shmem-test) luna_app(touch.cpp touch) luna_app(gclient.cpp gclient) +target_link_libraries(gclient PUBLIC ui) diff --git a/apps/gclient.cpp b/apps/gclient.cpp index 3d4fde4b..9afe7e68 100644 --- a/apps/gclient.cpp +++ b/apps/gclient.cpp @@ -1,22 +1,67 @@ +#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); + } +}; + Result handle_ipc_client_event(os::LocalClient&, u8) { todo(); } -Result create_window(os::LocalClient& client, ui::Rect rect, StringView name, ui::Color color) +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; - SET_IPC_STRING(request.name, name.chars()); - request.color = color; auto response = TRY(os::IPC::send_sync(client, request)); - return response.window; + 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) @@ -31,16 +76,19 @@ Result luna_main(int argc, char** argv) auto client = TRY(os::LocalClient::connect(socket_path, false)); - int id = TRY(create_window(*client, ui::Rect { 200, 200, 400, 300 }, "My Window", ui::CYAN)); - os::println("Created new window with id %d!", id); + Window window = TRY(create_window(*client, ui::Rect { 200, 200, 400, 300 })); + os::println("Created new window with id %d!", window.id); sleep(3); - id = - TRY(create_window(*client, ui::Rect { 100, 100, 200, 150 }, "Super Long Name that is Way Too Long ", ui::BLUE)); - os::println("Created new window with id %d!", id); + window.set_title(*client, "Example Window"); - sleep(6); + sleep(3); + + window.canvas.fill(ui::CYAN); + window.redraw(*client); + + sleep(3); return 0; } diff --git a/libui/include/ui/ipc/Client.h b/libui/include/ui/ipc/Client.h index 876f50ef..7ebaf919 100644 --- a/libui/include/ui/ipc/Client.h +++ b/libui/include/ui/ipc/Client.h @@ -23,5 +23,6 @@ namespace ui static constexpr u8 id = CREATE_WINDOW_RESPONSE_ID; int window; + IPC_STRING(shm_path); }; } diff --git a/libui/include/ui/ipc/Server.h b/libui/include/ui/ipc/Server.h index bfa4afbe..da73b759 100644 --- a/libui/include/ui/ipc/Server.h +++ b/libui/include/ui/ipc/Server.h @@ -19,6 +19,8 @@ namespace ui { IPC_ENUM_SERVER(ui), CREATE_WINDOW_ID, + SET_WINDOW_TITLE_ID, + INVALIDATE_ID, }; struct CreateWindowRequest @@ -27,7 +29,20 @@ namespace ui static constexpr u8 id = CREATE_WINDOW_ID; ui::Rect rect; - IPC_STRING(name); - ui::Color color; + }; + + struct SetWindowTitleRequest + { + static constexpr u8 id = SET_WINDOW_TITLE_ID; + + int window; + IPC_STRING(title); + }; + + struct InvalidateRequest + { + static constexpr u8 id = INVALIDATE_ID; + + int window; }; } diff --git a/wind/IPC.cpp b/wind/IPC.cpp index e63dd3c0..ba265a1f 100644 --- a/wind/IPC.cpp +++ b/wind/IPC.cpp @@ -1,50 +1,144 @@ #include "IPC.h" +#include +#include #include #include +#include +#include +#include +#include + +#define TRY_OR_IPC_ERROR(expr) \ + ({ \ + auto _expr_rc = (expr); \ + if (!_expr_rc.has_value()) \ + { \ + delete window; \ + os::IPC::send_error(client.conn, _expr_rc.error()); \ + return {}; \ + } \ + _expr_rc.release_value(); \ + }) + +static Result create_shm_region(const char* path, int* outfd, ui::Rect rect) +{ + int fd = shm_open(path, O_RDWR | O_CREAT | O_EXCL, 0600); + if (fd < 0) + { + os::eprintln("wind: could not create shared memory region: shm_open failed (%s) - %s", path, strerror(errno)); + return err(errno); + } + + usize size = align_up(rect.width * rect.height * 4); // 4 bytes per pixel + + if (ftruncate(fd, size) < 0) + { + os::eprintln("wind: could not create shared memory region: ftruncate failed (%d, %zu) - %s", fd, size, + strerror(errno)); + shm_unlink(path); + close(fd); + return 0; + } + + void* p = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (p == MAP_FAILED) + { + os::eprintln("wind: could not create shared memory region: mmap failed (%zu, %d) - %s", size, fd, + strerror(errno)); + shm_unlink(path); + close(fd); + return 0; + } + + if (outfd) *outfd = fd; + else + close(fd); + return (u32*)p; +} + +#define READ_MESSAGE(request) \ + do { \ + auto rc = client.conn.recv_typed(request); \ + if (rc.has_error()) \ + { \ + if (rc.error() == EAGAIN) \ + { \ + client.rpc_in_progress = true; \ + client.rpc_id = decltype(request)::id; \ + return {}; \ + } \ + else \ + return rc.release_error(); \ + } \ + } while (0) static Result handle_create_window_message(Client& client) { ui::CreateWindowRequest request; - auto rc = client.conn.recv_typed(request); - if (rc.has_error()) - { - if (rc.error() == EAGAIN) - { - client.rpc_in_progress = true; - client.rpc_id = ui::CREATE_WINDOW_ID; - return {}; - } - else - return rc.release_error(); - } + READ_MESSAGE(request); request.rect = request.rect.normalized(); request.rect.height += Window::titlebar_height(); // Make sure we provide the full contents rect that was asked for. - auto name = COPY_IPC_STRING(request.name); + auto name = TRY(String::from_cstring("Window")); - auto* window = new (std::nothrow) Window(request.rect, request.color, move(name)); + auto* window = new (std::nothrow) Window(request.rect, move(name)); if (!window) { os::IPC::send_error(client.conn, ENOMEM); return {}; } - int id = static_cast(client.windows.size()); - rc = client.windows.try_append(window); - if (rc.has_error()) - { - delete window; - os::IPC::send_error(client.conn, rc.error()); - return {}; - } + auto shm_path = TRY_OR_IPC_ERROR(String::format("/wind-shm-%d-%lu"_sv, client.conn.fd(), time(NULL))); + + window->pixels = TRY_OR_IPC_ERROR(create_shm_region(shm_path.chars(), nullptr, window->contents)); + + TRY_OR_IPC_ERROR(client.windows.try_append(window)); + int id = static_cast(client.windows.size() - 1); ui::CreateWindowResponse response; response.window = id; + SET_IPC_STRING(response.shm_path, shm_path.chars()); os::IPC::send_async(client.conn, response); return {}; } +static Result handle_set_window_title_message(Client& client) +{ + ui::SetWindowTitleRequest request; + READ_MESSAGE(request); + + auto name = COPY_IPC_STRING(request.title); + + os::println("wind: SetWindowTitle(\"%s\") for window %d", name.chars(), request.window); + + if ((usize)request.window >= client.windows.size()) + { + os::eprintln("wind: Window id out of range!"); + return {}; + } + + client.windows[request.window]->name = move(name); + + return {}; +} + +static Result handle_invalidate_message(Client& client) +{ + ui::InvalidateRequest request; + READ_MESSAGE(request); + + if ((usize)request.window >= client.windows.size()) + { + os::eprintln("wind: Window id out of range!"); + return {}; + } + + client.windows[request.window]->dirty = true; + + return {}; +} + namespace wind { Result handle_ipc_message(Client& client, u8 id) @@ -53,6 +147,8 @@ namespace wind switch (id) { case ui::CREATE_WINDOW_ID: return handle_create_window_message(client); + case ui::SET_WINDOW_TITLE_ID: return handle_set_window_title_message(client); + case ui::INVALIDATE_ID: return handle_invalidate_message(client); default: os::eprintln("wind: Invalid IPC message from client!"); return err(EINVAL); } } diff --git a/wind/Window.cpp b/wind/Window.cpp index bfe6a8b1..6e6e4eca 100644 --- a/wind/Window.cpp +++ b/wind/Window.cpp @@ -1,6 +1,7 @@ #include "Window.h" #include #include +#include #include #include @@ -8,8 +9,10 @@ LinkedList g_windows; void Window::draw(ui::Canvas& screen) { + dirty = false; + auto window = screen.subcanvas(surface); - window.subcanvas(contents).fill(color); + window.subcanvas(contents).fill(pixels, contents.width); wchar_t buffer[4096]; Utf8StringDecoder decoder(name.chars()); @@ -38,7 +41,7 @@ void Window::focus() g_windows.append(this); } -Window::Window(ui::Rect r, ui::Color c, String&& n) : surface(r), color(c), name(move(n)) +Window::Window(ui::Rect r, String&& n) : surface(r), name(move(n)) { auto font = ui::Font::default_font(); if (surface.width < 36) surface.width = 36; @@ -54,3 +57,10 @@ int Window::titlebar_height() auto font = ui::Font::default_font(); return font->height() + 20; } + +Window::~Window() +{ + usize size = contents.width * contents.height * 4; + + munmap(pixels, size); +} diff --git a/wind/Window.h b/wind/Window.h index b181e3e0..e4458fa7 100644 --- a/wind/Window.h +++ b/wind/Window.h @@ -11,12 +11,14 @@ struct Window : public LinkedListNode ui::Rect titlebar; ui::Rect close_button; ui::Rect contents; - ui::Color color; + u32* pixels; String name; + bool dirty { false }; static int titlebar_height(); - Window(ui::Rect, ui::Color, String&&); + Window(ui::Rect, String&&); + ~Window(); void focus(); diff --git a/wind/main.cpp b/wind/main.cpp index 90889c89..a6f64cc3 100644 --- a/wind/main.cpp +++ b/wind/main.cpp @@ -90,7 +90,7 @@ Result luna_main(int argc, char** argv) TRY(fds.try_append({ .fd = keyboard->fd(), .events = POLLIN, .revents = 0 })); TRY(fds.try_append({ .fd = server->fd(), .events = POLLIN, .revents = 0 })); - TRY(os::Security::pledge("stdio rpath unix signal proc", NULL)); + TRY(os::Security::pledge("stdio rpath wpath cpath unix signal proc", NULL)); while (1) {