From 909d0ed289fd93eabd5da2124047f6feb24bae48 Mon Sep 17 00:00:00 2001 From: apio Date: Sat, 3 Feb 2024 19:16:39 +0100 Subject: [PATCH] libos+wind+apps: Make IPC code object-oriented and add functionality for properly receiving messages This functionality previously had to be repeated across all server programs using the IPC API. --- apps/launch.cpp | 74 ++------ apps/taskbar.cpp | 7 +- libos/include/os/IPC.h | 387 +++++++++++++++++++++++++++++------------ libos/src/IPC.cpp | 74 ++++++-- libui/include/ui/App.h | 10 +- libui/src/App.cpp | 37 ++-- libui/src/Window.cpp | 12 +- wind/Client.h | 14 +- wind/IPC.cpp | 83 +++------ wind/IPC.h | 4 +- wind/Mouse.cpp | 4 +- wind/main.cpp | 21 ++- 12 files changed, 419 insertions(+), 308 deletions(-) diff --git a/apps/launch.cpp b/apps/launch.cpp index d6b171a0..9189e32d 100644 --- a/apps/launch.cpp +++ b/apps/launch.cpp @@ -22,79 +22,29 @@ #include #include -struct Client -{ - os::LocalServer::Client conn; - u8 rpc_id { 0 }; - bool rpc_in_progress { false }; - - Client(os::LocalServer::Client&& client) : conn(move(client)) {}; -}; - -#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 {}; \ - } \ - if (rc.error() == EINTR) \ - { \ - client.rpc_in_progress = true; \ - client.rpc_id = decltype(request)::ID; \ - return {}; \ - } \ - else \ - return rc.release_error(); \ - } \ - } while (0) - -Result handle_launch_detached_message(Client& client) +Result handle_launch_detached_message(os::IPC::ClientConnection& client) { os::Launcher::LaunchDetachedRequest request; - READ_MESSAGE(request); + if (!TRY(client.read_message(request))) return {}; auto path = COPY_IPC_STRING(request.command); StringView args[] = { path.view() }; - auto result = os::Process::spawn(args[0], { args, 1 }, false); - if (result.has_error()) os::IPC::send_error(client.conn, result.error()); + os::Process::spawn(args[0], { args, 1 }, false); return {}; } -Result handle_ipc_message(Client& client, u8 id) +void handle_ipc_message(os::IPC::ClientConnection& client, u8 id, void*) { - client.rpc_in_progress = false; switch (id) { - case os::Launcher::LAUNCH_DETACHED_ID: return handle_launch_detached_message(client); - default: os::eprintln("launch: Invalid IPC message from client!"); return err(EINVAL); + case os::Launcher::LAUNCH_DETACHED_ID: handle_launch_detached_message(client); break; + default: os::eprintln("launch: Invalid IPC message from client!"); return; } } -static Result handle_ipc(Client& client) -{ - if (client.rpc_in_progress) return handle_ipc_message(client, client.rpc_id); - - u8 id; - auto rc = client.conn.recv_typed(id); - if (rc.has_error()) - { - if (rc.error() == EAGAIN) { return {}; } - if (rc.error() == EINTR) { return {}; } - else - return rc.release_error(); - } - - return handle_ipc_message(client, id); -} - void sigchld_handler(int) { os::Process::wait(os::Process::ANY_CHILD, nullptr); @@ -116,7 +66,7 @@ Result luna_main(int argc, char** argv) auto server = TRY(os::LocalServer::create(socket_path, false)); TRY(server->listen(20)); - Vector> clients; + Vector> clients; Vector fds; TRY(fds.try_append({ .fd = server->fd(), .events = POLLIN, .revents = 0 })); @@ -135,18 +85,20 @@ Result luna_main(int argc, char** argv) auto client = TRY(server->accept()); os::println("launch: New client connected!"); TRY(fds.try_append({ .fd = client.fd(), .events = POLLIN, .revents = 0 })); - OwnedPtr c = TRY(adopt_owned_if_nonnull(new Client(move(client)))); - TRY(clients.try_append(move(c))); + + auto connection = TRY(os::IPC::ClientConnection::adopt_connection(move(client))); + connection->set_message_handler(handle_ipc_message, nullptr); + TRY(clients.try_append(move(connection))); } for (usize i = 0; i < clients.size(); i++) { - if (fds[i + 1].revents & POLLIN) handle_ipc(*clients[i]); + if (fds[i + 1].revents & POLLIN) clients[i]->check_for_messages(); if (fds[i + 1].revents & POLLHUP) { os::println("launch: Client %d disconnected", i); fds.remove_at(i + 1); auto client = clients.remove_at(i); - client->conn.disconnect(); + client->disconnect(); } } } diff --git a/apps/taskbar.cpp b/apps/taskbar.cpp index ee22b85d..cf770c12 100644 --- a/apps/taskbar.cpp +++ b/apps/taskbar.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include #include @@ -13,7 +12,7 @@ static constexpr ui::Color TASKBAR_COLOR = ui::Color::from_rgb(83, 83, 83); -static OwnedPtr launcher_client; +static OwnedPtr launcher_client; void sigchld_handler(int) { @@ -33,7 +32,7 @@ Result create_widget_group_for_app(ui::HorizontalLayout& layout, StringVie button->set_action([=] { os::Launcher::LaunchDetachedRequest request; SET_IPC_STRING(request.command, path.chars()); - os::IPC::send_async(*launcher_client, request); + launcher_client->send_async(request); }); auto image = TRY(ui::ImageWidget::load(icon)); @@ -51,7 +50,7 @@ Result luna_main(int, char**) TRY(os::EventLoop::the().register_signal_handler(SIGCHLD, sigchld_handler)); - launcher_client = TRY(os::LocalClient::connect("/tmp/launch.sock", false)); + launcher_client = TRY(os::IPC::Client::connect("/tmp/launch.sock", false)); ui::Rect screen = app.screen_rect(); diff --git a/libos/include/os/IPC.h b/libos/include/os/IPC.h index fe185a15..7dd56fa5 100644 --- a/libos/include/os/IPC.h +++ b/libos/include/os/IPC.h @@ -8,30 +8,13 @@ */ #pragma once +#include #include #include #define IPC_ENUM_SERVER(name) __##name##_SERVER_ERROR = 0 #define IPC_ENUM_CLIENT(name) __##name##_CLIENT_ERROR = 0 -/** - * @brief Called to handle IPC events (client-side). - * - * @param conn The connection object being used. - * @param id The ID of the message. - * @return Result Whether the operation succeded. - */ -extern Result handle_ipc_client_event(os::LocalClient& conn, u8 id); - -/** - * @brief Called to handle IPC events (server-side). - * - * @param conn The connection object being used. - * @param id The ID of the message. - * @return Result Whether the operation succeded. - */ -extern Result handle_ipc_server_event(os::LocalServer::Client& conn, u8 id); - namespace os { namespace IPC @@ -44,114 +27,298 @@ namespace os #define SET_IPC_STRING(name, value) strlcpy(name, value, os::IPC::IPC_STRING_LENGTH) /** - * @brief Sends an IPC message without waiting for a reply. - * - * @tparam Client The type of the client interface being used to communicate. - * @tparam T The type of the message. - * @param client The connection object being used to communicate. - * @param message The IPC message. - * @return Result Whether the operation succeded. + * @brief Class used to send and receive IPC messages on the server, using an already established connection to + * the client. */ - template Result send_async(Client& client, const T& message) + class ClientConnection { - u8 id = T::ID; - TRY(client.send_typed(id)); - TRY(client.send_typed(message)); - return {}; - } + public: + /** + * @brief Creates a new IPC connection object from an os::LocalServer connection. + * + * @param connection The existing connection to use, obtained after calling accept() on an os::LocalServer + * object. + * @return Result> An error, or the IPC connection object. + */ + static Result> adopt_connection(LocalServer::Client&& connection); - /** - * @brief Sends an error result to the IPC connection, indicating that an operation could not be performed. - * - * @tparam Client The type of the client interface being used to communicate. - * @param client The connection object being used to communicate. - * @param error The error code. - * @return Result Whether the operation succeded. - */ - template Result send_error(Client& client, int error) - { - u8 id = 0; - TRY(client.send_typed(id)); - TRY(client.send_typed(error)); - return {}; - } + /** + * @brief Check for new messages from the client. The message handler will be called if there is a new + * message. If the connection is non-blocking, this function returns EAGAIN if there are no new + * messages, otherwise it blocks until one is received. + * + * @return Result Whether the operation succeeded. + */ + Result check_for_messages(); - /** - * @brief Sends an IPC message and waits for a reply (client-only). - * - * @tparam ResponseType The type of the response. - * @tparam T The type of the message. - * @param client The connection object being used to communicate. - * @param message The IPC message. - * @param handler The function used to handle messages that do not match the reply. - * @return Result An error, or the response. - */ - template - Result send_sync(os::LocalClient& client, const T& message, - decltype(handle_ipc_client_event) handler = handle_ipc_client_event) - { - u8 id = T::ID; - TRY(client.send_typed(id)); - TRY(client.send_typed(message)); - - // We allow receiving 5 messages of different types, but if those have passed and we still don't have a - // reply, fail with ENOMSG. - int max_other_messages = 5; - - while (max_other_messages) + /** + * @brief Set the message handler for this connection. This function is required and will be called every + * time a new message is received, passing the connection object and the ID of the message type. + * + * @param handler The message handler to use. + * @param arg An arbitrary argument to pass to the handler. + */ + void set_message_handler(Function&& handler, void* arg) { - u8 response_id; - auto rc = client.recv_typed(response_id); - if (rc.has_error() && (rc.error() == EAGAIN || rc.error() == EINTR)) continue; + m_message_handler = move(handler); + m_arg = arg; + } - if (response_id == 0) // Error result + /** + * @brief Read message data from the connection. This function should be called only after the other side of + * the connection has signaled that it is going to send a message of the specified type, for example inside + * the message handler. + * + * @tparam T The type of the message to read. + * @param out The variable in which to store the message. + * @return Result An error, or whether the message was actually read and stored. If this value is + * false, this function will have to be called again, either because the connection is non-blocking and the + * message data has not been sent yet or because a signal interrupted the call. + */ + template Result read_message(T& out) + { + auto rc = m_connection.recv_typed(out); + if (rc.has_error()) { + if (rc.error() == EAGAIN) + { + m_ipc_in_progress = true; + m_ipc_saved_id = T::ID; + return false; + } + if (rc.error() == EINTR) + { + m_ipc_in_progress = true; + m_ipc_saved_id = T::ID; + return false; + } + else + return rc.release_error(); + } + return true; + } + + /** + * @brief Sends an error code, indicating that an operation could not be performed. It is best to send an + * error back only when the client is expecting it, that is to say, when they are waiting for a reply to a + * synchronous message. + * + * @param error The error code. + * @return Result Whether the operation succeded. + */ + Result send_error(int error); + + /** + * @brief Sends a message without waiting for a reply. + * + * @tparam T The type of the message. + * @param message The message to send. + * @return Result Whether the operation succeeded. + */ + template Result send_async(const T& message) + { + u8 id = T::ID; + TRY(m_connection.send_typed(id)); + TRY(m_connection.send_typed(message)); + return {}; + } + + /** + * @brief Closes the connection. + */ + void disconnect() + { + m_connection.disconnect(); + } + + /** + * @brief Returns the file descriptor associated with this connection. + * + * @return int The file descriptor. + */ + int fd() + { + return m_connection.fd(); + } + + private: + ClientConnection(LocalServer::Client&& connection); + + LocalServer::Client m_connection; + Function m_message_handler; + void* m_arg; + + bool m_ipc_in_progress { false }; + u8 m_ipc_saved_id { 0 }; + }; + + /** + * @brief Class used to send and receive IPC messages on the client. + */ + class Client + { + public: + /** + * @brief Connect to an IPC server and return a connection object. + * + * @param path The path of the socket used by the IPC server. + * @param blocking Whether the connection should block when waiting for messages. + * @return Result> An error, or a new connection object. + */ + static Result> connect(StringView path, bool blocking); + + /** + * @brief Check for new messages from the server. The message handler will be called if there is a new + * message. If the connection is non-blocking, this function returns EAGAIN if there are no new + * messages, otherwise it blocks until one is received. + * + * @return Result Whether the operation succeeded. + */ + Result check_for_messages(); + + /** + * @brief Set the message handler for this connection. This function is required and will be called every + * time a new message is received, passing the connection object and the ID of the message type. + * + * @param handler The message handler to use. + * @param arg An arbitrary argument to pass to the handler. + */ + void set_message_handler(Function&& handler, void* arg) + { + m_message_handler = move(handler); + m_arg = arg; + } + + /** + * @brief Read message data from the connection. This function should be called only after the other side of + * the connection has signaled that it is going to send a message of the specified type, for example inside + * the message handler. + * + * @tparam T The type of the message to read. + * @param out The variable in which to store the message. + * @return Result An error, or whether the message was actually read and stored. If this value is + * false, this function will have to be called again, either because the connection is non-blocking and the + * message data has not been sent yet or because a signal interrupted the call. + */ + template Result read_message(T& out) + { + auto rc = m_connection->recv_typed(out); + if (rc.has_error()) + { + if (rc.error() == EAGAIN) + { + m_ipc_in_progress = true; + m_ipc_saved_id = T::ID; + return false; + } + if (rc.error() == EINTR) + { + m_ipc_in_progress = true; + m_ipc_saved_id = T::ID; + return false; + } + else + return rc.release_error(); + } + return true; + } + + /** + * @brief Sends a message without waiting for a reply. + * + * @tparam T The type of the message. + * @param message The message to send. + * @return Result Whether the operation succeeded. + */ + template Result send_async(const T& message) + { + u8 id = T::ID; + TRY(m_connection->send_typed(id)); + TRY(m_connection->send_typed(message)); + return {}; + } + + /** + * @brief Sends a message and waits for a reply. + * + * @tparam ResponseType The type of the response. + * @tparam T The type of the message. + * @param message The message to send. + * @return Result An error, or the response. + */ + template Result send_sync(const T& message) + { + u8 id = T::ID; + TRY(m_connection->send_typed(id)); + TRY(m_connection->send_typed(message)); + + // We allow receiving 5 messages of different types, but if those have passed and we still don't have a + // reply, fail with ENOMSG. + int max_other_messages = 5; + + while (max_other_messages) + { + u8 response_id; + auto rc = m_connection->recv_typed(response_id); + if (rc.has_error() && (rc.error() == EAGAIN || rc.error() == EINTR)) continue; + + if (response_id == 0) // Error result + { + while (1) + { + int code; + rc = m_connection->recv_typed(code); + if (rc.has_error() && (rc.error() == EAGAIN || rc.error() == EINTR)) continue; + return err(code); + } + } + + if (response_id != ResponseType::ID) + { + m_message_handler(*this, response_id, m_arg); + max_other_messages--; + continue; + } + while (1) { - int code; - rc = client.recv_typed(code); + ResponseType response; + rc = m_connection->recv_typed(response); if (rc.has_error() && (rc.error() == EAGAIN || rc.error() == EINTR)) continue; - return err(code); + return response; } } - if (response_id != ResponseType::ID) - { - TRY(handler(client, response_id)); - max_other_messages--; - continue; - } - - while (1) - { - ResponseType response; - rc = client.recv_typed(response); - if (rc.has_error() && (rc.error() == EAGAIN || rc.error() == EINTR)) continue; - return response; - } + return err(ENOMSG); } - return err(ENOMSG); - } + /** + * @brief Closes the connection. + */ + void disconnect() + { + m_connection->disconnect(); + } - /** - * @brief Check for new IPC messages on a connection and handle them appropriately. - * - * @param client The client connection. - * @param handler The function used to handle messages. - * @return Result Whether the operation succeded. - */ - Result check_for_messages(os::LocalClient& client, - decltype(handle_ipc_client_event) handler = handle_ipc_client_event); + /** + * @brief Returns the file descriptor associated with this connection. + * + * @return int The file descriptor. + */ + int fd() + { + return m_connection->fd(); + } - /** - * @brief Check for new IPC messages on a connection and handle them appropriately. - * - * @param server The server connection. - * @param handler The function used to handle messages. - * @return Result Whether the operation succeded. - */ - Result check_for_messages(os::LocalServer::Client& server, - decltype(handle_ipc_server_event) handler = handle_ipc_server_event); + private: + Client(OwnedPtr&& connection); + + OwnedPtr m_connection; + Function m_message_handler; + void* m_arg; + + bool m_ipc_in_progress { false }; + u8 m_ipc_saved_id { 0 }; + }; } } diff --git a/libos/src/IPC.cpp b/libos/src/IPC.cpp index b0be457d..7fd6a1ae 100644 --- a/libos/src/IPC.cpp +++ b/libos/src/IPC.cpp @@ -11,25 +11,26 @@ namespace os::IPC { - Result check_for_messages(os::LocalClient& client, decltype(handle_ipc_client_event) handler) + Result> ClientConnection::adopt_connection(LocalServer::Client&& connection) { - u8 id; - auto rc = client.recv_typed(id); - if (rc.has_error()) - { - if (rc.error() == EAGAIN) return {}; // No messages, and the caller does not want us to block. - if (rc.error() == EINTR) - return {}; // Let the caller check for anything having happened because a signal handler ran. - return rc.release_error(); - } - - return handler(client, id); + return adopt_owned_if_nonnull(new (std::nothrow) ClientConnection(move(connection))); } - Result check_for_messages(os::LocalServer::Client& client, decltype(handle_ipc_server_event) handler) + ClientConnection::ClientConnection(LocalServer::Client&& connection) : m_connection(move(connection)) { + } + + Result ClientConnection::check_for_messages() + { + if (m_ipc_in_progress) + { + m_ipc_in_progress = false; + m_message_handler(*this, m_ipc_saved_id, m_arg); + return {}; + } + u8 id; - auto rc = client.recv_typed(id); + auto rc = m_connection.recv_typed(id); if (rc.has_error()) { if (rc.error() == EAGAIN) return {}; // No messages, and the caller does not want us to block. @@ -38,6 +39,49 @@ namespace os::IPC return rc.release_error(); } - return handler(client, id); + m_message_handler(*this, id, m_arg); + return {}; + } + + Result ClientConnection::send_error(int error) + { + u8 id = 0; + TRY(m_connection.send_typed(id)); + TRY(m_connection.send_typed(error)); + return {}; + } + + Result> Client::connect(StringView path, bool blocking) + { + auto connection = TRY(LocalClient::connect(path, blocking)); + + return adopt_owned_if_nonnull(new (std::nothrow) Client(move(connection))); + } + + Result Client::check_for_messages() + { + if (m_ipc_in_progress) + { + m_ipc_in_progress = false; + m_message_handler(*this, m_ipc_saved_id, m_arg); + return {}; + } + + u8 id; + auto rc = m_connection->recv_typed(id); + if (rc.has_error()) + { + if (rc.error() == EAGAIN) return {}; // No messages, and the caller does not want us to block. + if (rc.error() == EINTR) + return {}; // Let the caller check for anything having happened because a signal handler ran. + return rc.release_error(); + } + + m_message_handler(*this, id, m_arg); + return {}; + } + + Client::Client(OwnedPtr&& connection) : m_connection(move(connection)) + { } } diff --git a/libui/include/ui/App.h b/libui/include/ui/App.h index be63caf9..8a0d45e0 100644 --- a/libui/include/ui/App.h +++ b/libui/include/ui/App.h @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include namespace ui @@ -27,7 +27,7 @@ namespace ui Rect screen_rect(); - os::LocalClient& client() + os::IPC::Client& client() { return *m_client; } @@ -52,13 +52,11 @@ namespace ui Result register_window(OwnedPtr&& window, Badge); void unregister_window(Window* window, Badge); - Result handle_ipc_event(u8 id); - static App& the(); private: static App* s_app; - OwnedPtr m_client; + OwnedPtr m_client; Window* m_main_window { nullptr }; HashMap> m_windows; bool m_should_close { false }; @@ -68,6 +66,8 @@ namespace ui Window* find_window(int id); + Result handle_ipc_event(os::IPC::Client&, u8 id, void*); + friend void handle_socket_event(int, int); }; } diff --git a/libui/src/App.cpp b/libui/src/App.cpp index fc8574c1..3b21950e 100644 --- a/libui/src/App.cpp +++ b/libui/src/App.cpp @@ -14,11 +14,6 @@ #include #include -Result handle_ipc_client_event(os::LocalClient&, u8 id) -{ - return ui::App::the().handle_ipc_event(id); -} - namespace ui { void handle_socket_event(int, int status) @@ -41,8 +36,9 @@ namespace ui Result App::init(StringView socket_path) { - m_client = TRY(os::LocalClient::connect(socket_path, true)); - fcntl(m_client->fd(), F_SETFL, O_NONBLOCK); + m_client = TRY(os::IPC::Client::connect(socket_path, false)); + m_client->set_message_handler( + [this](os::IPC::Client& client, u8 id, void* arg) { this->handle_ipc_event(client, id, arg); }, nullptr); TRY(m_loop.register_fd_listener(m_client->fd(), handle_socket_event)); @@ -65,7 +61,7 @@ namespace ui Rect App::screen_rect() { ui::GetScreenRectRequest request {}; - auto response = os::IPC::send_sync(*m_client, request).release_value(); + auto response = m_client->send_sync(request).release_value(); return response.rect; } @@ -89,26 +85,13 @@ namespace ui return window->ptr(); } -#define READ_MESSAGE(request) \ - do { \ - auto rc = m_client->recv_typed(request); \ - if (rc.has_error()) \ - { \ - if (rc.error() == EAGAIN) { continue; } \ - if (rc.error() == EINTR) { continue; } \ - else \ - return rc.release_error(); \ - } \ - break; \ - } while (true) - - Result App::handle_ipc_event(u8 id) + Result App::handle_ipc_event(os::IPC::Client&, u8 id, void*) { switch (id) { case WINDOW_CLOSE_REQUEST_ID: { WindowCloseRequest request; - READ_MESSAGE(request); + if (!TRY(m_client->read_message(request))) return {}; os::eprintln("ui: Window close request from server! Shall comply."); auto* window = find_window(request.window); window->close(); @@ -116,7 +99,7 @@ namespace ui } case MOUSE_EVENT_REQUEST_ID: { MouseEventRequest request; - READ_MESSAGE(request); + if (!TRY(m_client->read_message(request))) return {}; auto* window = find_window(request.window); auto move_result = window->handle_mouse_move(request.position).value_or(ui::EventResult::DidNotHandle); auto button_result = @@ -127,7 +110,7 @@ namespace ui } case MOUSE_LEAVE_REQUEST_ID: { MouseLeaveRequest request; - READ_MESSAGE(request); + if (!TRY(m_client->read_message(request))) return {}; auto* window = find_window(request.window); if (window->handle_mouse_leave().value_or(ui::EventResult::DidNotHandle) == ui::EventResult::DidHandle) window->draw(); @@ -135,7 +118,7 @@ namespace ui } case KEY_EVENT_REQUEST_ID: { KeyEventRequest request; - READ_MESSAGE(request); + if (!TRY(m_client->read_message(request))) return {}; auto* window = find_window(request.window); if (window->handle_key_event(request).value_or(ui::EventResult::DidNotHandle) == ui::EventResult::DidHandle) window->draw(); @@ -148,7 +131,7 @@ namespace ui bool App::process_events() { check(m_main_window); - os::IPC::check_for_messages(*m_client).release_value(); + m_client->check_for_messages().release_value(); return !m_should_close; } } diff --git a/libui/src/Window.cpp b/libui/src/Window.cpp index 15ccc31a..77670a46 100644 --- a/libui/src/Window.cpp +++ b/libui/src/Window.cpp @@ -43,7 +43,7 @@ namespace ui ui::CreateWindowRequest request; request.rect = rect; - auto response = TRY(os::IPC::send_sync(App::the().client(), request)); + auto response = TRY(App::the().client().send_sync(request)); auto path = COPY_IPC_STRING(response.shm_path); @@ -62,7 +62,7 @@ namespace ui ui::SetTitlebarRectRequest titlebar_request; titlebar_request.titlebar_rect = window->m_titlebar_canvas.rect(); titlebar_request.window = response.window; - os::IPC::send_async(App::the().client(), titlebar_request); + App::the().client().send_async(titlebar_request); } else { @@ -74,7 +74,7 @@ namespace ui ui::RemoveSharedMemoryRequest shm_request; shm_request.window = response.window; - os::IPC::send_async(App::the().client(), shm_request); + App::the().client().send_async(shm_request); App::the().register_window(move(window), {}); @@ -91,7 +91,7 @@ namespace ui ui::SetWindowTitleRequest request; request.window = m_id; SET_IPC_STRING(request.title, title.chars()); - os::IPC::send_async(App::the().client(), request); + App::the().client().send_async(request); m_name = String::from_string_view(title).release_value(); draw(); @@ -101,7 +101,7 @@ namespace ui { ui::InvalidateRequest request; request.window = m_id; - os::IPC::send_async(App::the().client(), request); + App::the().client().send_async(request); } void Window::close() @@ -110,7 +110,7 @@ namespace ui ui::CloseWindowRequest request; request.window = m_id; - os::IPC::send_async(app.client(), request); + app.client().send_async(request); if (this == app.main_window()) app.set_should_close(true); diff --git a/wind/Client.h b/wind/Client.h index 1d337589..031c6d0d 100644 --- a/wind/Client.h +++ b/wind/Client.h @@ -1,18 +1,20 @@ #pragma once +#include "IPC.h" #include "Window.h" -#include +#include struct Client { - os::LocalServer::Client conn; + OwnedPtr conn; Vector windows; const bool privileged { false }; - bool rpc_in_progress { false }; - u8 rpc_id { 0 }; - Client(os::LocalServer::Client&& client, bool priv) + Client(OwnedPtr&& client, bool priv) #ifdef CLIENT_IMPLEMENTATION - : conn(move(client)), windows(), privileged(priv) {} + : conn(move(client)), windows(), privileged(priv) + { + conn->set_message_handler(wind::handle_ipc_message, this); + } #else ; #endif diff --git a/wind/IPC.cpp b/wind/IPC.cpp index 543425bd..39bc978d 100644 --- a/wind/IPC.cpp +++ b/wind/IPC.cpp @@ -13,34 +13,12 @@ auto _expr_rc = (expr); \ if (!_expr_rc.has_value()) \ { \ - os::IPC::send_error(client.conn, _expr_rc.error()); \ + client.conn->send_error(_expr_rc.error()); \ return {}; \ } \ _expr_rc.release_value(); \ }) -#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 {}; \ - } \ - if (rc.error() == EINTR) \ - { \ - client.rpc_in_progress = true; \ - client.rpc_id = decltype(request)::ID; \ - return {}; \ - } \ - else \ - return rc.release_error(); \ - } \ - } while (0) - #define CHECK_WINDOW_ID(request) \ do { \ if ((usize)request.window >= client.windows.size() || !client.windows[request.window]) \ @@ -53,18 +31,18 @@ static Result handle_create_window_message(Client& client) { ui::CreateWindowRequest request; - READ_MESSAGE(request); + if (!TRY(client.conn->read_message(request))) return {}; request.rect = request.rect.normalized(); auto name = TRY_OR_IPC_ERROR(String::from_cstring("Window")); - auto shm_path = TRY_OR_IPC_ERROR(String::format("/wind-shm-%d-%lu"_sv, client.conn.fd(), time(NULL))); + auto shm_path = TRY_OR_IPC_ERROR(String::format("/wind-shm-%d-%lu"_sv, client.conn->fd(), time(NULL))); auto* window = new (std::nothrow) Window(request.rect, move(name)); if (!window) { - os::IPC::send_error(client.conn, ENOMEM); + client.conn->send_error(ENOMEM); return {}; } @@ -89,14 +67,14 @@ static Result handle_create_window_message(Client& client) response.window = id; SET_IPC_STRING(response.shm_path, shm_path.chars()); window->shm_path = move(shm_path); - os::IPC::send_async(client.conn, response); + client.conn->send_async(response); return {}; } static Result handle_remove_shm_message(Client& client) { ui::RemoveSharedMemoryRequest request; - READ_MESSAGE(request); + if (!TRY(client.conn->read_message(request))) return {}; CHECK_WINDOW_ID(request); @@ -108,7 +86,7 @@ static Result handle_remove_shm_message(Client& client) static Result handle_set_window_title_message(Client& client) { ui::SetWindowTitleRequest request; - READ_MESSAGE(request); + if (!TRY(client.conn->read_message(request))) return {}; auto name = COPY_IPC_STRING(request.title); @@ -124,7 +102,7 @@ static Result handle_set_window_title_message(Client& client) static Result handle_invalidate_message(Client& client) { ui::InvalidateRequest request; - READ_MESSAGE(request); + if (!TRY(client.conn->read_message(request))) return {}; CHECK_WINDOW_ID(request); @@ -136,7 +114,7 @@ static Result handle_invalidate_message(Client& client) static Result handle_close_window_message(Client& client) { ui::CloseWindowRequest request; - READ_MESSAGE(request); + if (!TRY(client.conn->read_message(request))) return {}; CHECK_WINDOW_ID(request); @@ -152,11 +130,11 @@ static Result handle_close_window_message(Client& client) static Result handle_get_screen_rect_message(Client& client) { ui::GetScreenRectRequest request; - READ_MESSAGE(request); // Kinda pointless, but required. + if (!TRY(client.conn->read_message(request))) return {}; // Kinda pointless, but required. ui::GetScreenRectResponse response; response.rect = Screen::the().canvas().rect(); - os::IPC::send_async(client.conn, response); + client.conn->send_async(response); return {}; } @@ -164,7 +142,7 @@ static Result handle_get_screen_rect_message(Client& client) static Result handle_set_titlebar_rect_message(Client& client) { ui::SetTitlebarRectRequest request; - READ_MESSAGE(request); + if (!TRY(client.conn->read_message(request))) return {}; request.titlebar_rect = request.titlebar_rect.normalized(); @@ -185,36 +163,19 @@ static Result handle_set_titlebar_rect_message(Client& client) namespace wind { - Result handle_ipc_message(Client& client, u8 id) + void handle_ipc_message(os::IPC::ClientConnection&, u8 id, void* c) { - client.rpc_in_progress = false; + Client& client = *(Client*)c; switch (id) { - case ui::CREATE_WINDOW_ID: return handle_create_window_message(client); - case ui::REMOVE_SHM_ID: return handle_remove_shm_message(client); - case ui::SET_WINDOW_TITLE_ID: return handle_set_window_title_message(client); - case ui::INVALIDATE_ID: return handle_invalidate_message(client); - case ui::CLOSE_WINDOW_ID: return handle_close_window_message(client); - case ui::GET_SCREEN_RECT_ID: return handle_get_screen_rect_message(client); - case ui::SET_TITLEBAR_RECT_ID: return handle_set_titlebar_rect_message(client); - default: os::eprintln("wind: Invalid IPC message from client!"); return err(EINVAL); + case ui::CREATE_WINDOW_ID: handle_create_window_message(client); break; + case ui::REMOVE_SHM_ID: handle_remove_shm_message(client); break; + case ui::SET_WINDOW_TITLE_ID: handle_set_window_title_message(client); break; + case ui::INVALIDATE_ID: handle_invalidate_message(client); break; + case ui::CLOSE_WINDOW_ID: handle_close_window_message(client); break; + case ui::GET_SCREEN_RECT_ID: handle_get_screen_rect_message(client); break; + case ui::SET_TITLEBAR_RECT_ID: handle_set_titlebar_rect_message(client); break; + default: os::eprintln("wind: Invalid IPC message from client!"); return; } } - - Result handle_ipc(Client& client) - { - if (client.rpc_in_progress) return handle_ipc_message(client, client.rpc_id); - - u8 id; - auto rc = client.conn.recv_typed(id); - if (rc.has_error()) - { - if (rc.error() == EAGAIN) { return {}; } - if (rc.error() == EINTR) { return {}; } - else - return rc.release_error(); - } - - return handle_ipc_message(client, id); - } } diff --git a/wind/IPC.h b/wind/IPC.h index 4c085302..f91ac70c 100644 --- a/wind/IPC.h +++ b/wind/IPC.h @@ -4,7 +4,5 @@ namespace wind { - Result handle_ipc_message(Client& client, u8 id); - - Result handle_ipc(Client& client); + void handle_ipc_message(os::IPC::ClientConnection& connection, u8 id, void* c); } diff --git a/wind/Mouse.cpp b/wind/Mouse.cpp index 8da532e2..64379cc9 100644 --- a/wind/Mouse.cpp +++ b/wind/Mouse.cpp @@ -91,7 +91,7 @@ void Mouse::update(const moon::MousePacket& packet) request.window = window->id; request.position = window->surface.relative(m_position); request.buttons = packet.buttons; - os::IPC::send_async(window->client->conn, request); + window->client->conn->send_async(request); new_active_window = window; break; } @@ -103,7 +103,7 @@ void Mouse::update(const moon::MousePacket& packet) { ui::MouseLeaveRequest request; request.window = m_active_window->id; - os::IPC::send_async(m_active_window->client->conn, request); + m_active_window->client->conn->send_async(request); } m_active_window = new_active_window; } diff --git a/wind/main.cpp b/wind/main.cpp index d17775fc..34604f4a 100644 --- a/wind/main.cpp +++ b/wind/main.cpp @@ -1,6 +1,5 @@ #define CLIENT_IMPLEMENTATION #include "Client.h" -#include "IPC.h" #include "Keyboard.h" #include "Mouse.h" #include "Screen.h" @@ -31,14 +30,14 @@ static void debug(const Vector>& clients) for (const auto& client : clients) { - os::println("Client with fd %d, owns %zu windows", client->conn.fd(), client->windows.size()); + os::println("Client with fd %d, owns %zu windows", client->conn->fd(), client->windows.size()); } os::println("-- wind: Listing windows --"); for (const auto& window : g_windows) { - os::println("Window of client (fd %d), id %d, %sdirty (\"%s\") (%d,%d,%d,%d)", window->client->conn.fd(), + os::println("Window of client (fd %d), id %d, %sdirty (\"%s\") (%d,%d,%d,%d)", window->client->conn->fd(), window->id, window->dirty ? "" : "not ", window->name.chars(), window->surface.pos.x, window->surface.pos.y, window->surface.width, window->surface.height); } @@ -156,7 +155,7 @@ Result luna_main(int argc, char** argv) { auto* window = g_windows.last().value(); request.window = window->id; - os::IPC::send_async(window->client->conn, request); + window->client->conn->send_async(request); } } if (fds[2].revents & POLLIN) @@ -164,7 +163,10 @@ Result luna_main(int argc, char** argv) auto client = TRY(server->accept()); os::println("wind: New client connected!"); TRY(fds.try_append({ .fd = client.fd(), .events = POLLIN, .revents = 0 })); - OwnedPtr c = TRY(adopt_owned_if_nonnull(new Client(move(client), false))); + + auto connection = TRY(os::IPC::ClientConnection::adopt_connection(move(client))); + + OwnedPtr c = TRY(adopt_owned_if_nonnull(new Client(move(connection), false))); TRY(clients.try_append(move(c))); } if (fds[3].revents & POLLIN) @@ -172,18 +174,21 @@ Result luna_main(int argc, char** argv) auto client = TRY(system_server->accept()); os::println("wind: New privileged client connected!"); TRY(fds.try_append({ .fd = client.fd(), .events = POLLIN, .revents = 0 })); - OwnedPtr c = TRY(adopt_owned_if_nonnull(new Client(move(client), true))); + + auto connection = TRY(os::IPC::ClientConnection::adopt_connection(move(client))); + + OwnedPtr c = TRY(adopt_owned_if_nonnull(new Client(move(connection), true))); TRY(clients.try_append(move(c))); } for (usize i = 0; i < clients.size(); i++) { - if (fds[i + 4].revents & POLLIN) wind::handle_ipc(*clients[i]); + if (fds[i + 4].revents & POLLIN) clients[i]->conn->check_for_messages(); if (fds[i + 4].revents & POLLHUP) { os::println("wind: Client %d disconnected", i); fds.remove_at(i + 4); auto client = clients.remove_at(i); - client->conn.disconnect(); + client->conn->disconnect(); for (auto& window : client->windows) { if (window)