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.
This commit is contained in:
apio 2024-02-03 19:16:39 +01:00
parent 6bdf3169d2
commit 909d0ed289
Signed by: apio
GPG Key ID: B8A7D06E42258954
12 changed files with 419 additions and 308 deletions

View File

@ -22,79 +22,29 @@
#include <time.h> #include <time.h>
#include <unistd.h> #include <unistd.h>
struct Client Result<void> handle_launch_detached_message(os::IPC::ClientConnection& 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<void> handle_launch_detached_message(Client& client)
{ {
os::Launcher::LaunchDetachedRequest request; os::Launcher::LaunchDetachedRequest request;
READ_MESSAGE(request); if (!TRY(client.read_message(request))) return {};
auto path = COPY_IPC_STRING(request.command); auto path = COPY_IPC_STRING(request.command);
StringView args[] = { path.view() }; StringView args[] = { path.view() };
auto result = os::Process::spawn(args[0], { args, 1 }, false); os::Process::spawn(args[0], { args, 1 }, false);
if (result.has_error()) os::IPC::send_error(client.conn, result.error());
return {}; return {};
} }
Result<void> 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) switch (id)
{ {
case os::Launcher::LAUNCH_DETACHED_ID: return handle_launch_detached_message(client); case os::Launcher::LAUNCH_DETACHED_ID: handle_launch_detached_message(client); break;
default: os::eprintln("launch: Invalid IPC message from client!"); return err(EINVAL); default: os::eprintln("launch: Invalid IPC message from client!"); return;
} }
} }
static Result<void> 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) void sigchld_handler(int)
{ {
os::Process::wait(os::Process::ANY_CHILD, nullptr); os::Process::wait(os::Process::ANY_CHILD, nullptr);
@ -116,7 +66,7 @@ Result<int> luna_main(int argc, char** argv)
auto server = TRY(os::LocalServer::create(socket_path, false)); auto server = TRY(os::LocalServer::create(socket_path, false));
TRY(server->listen(20)); TRY(server->listen(20));
Vector<OwnedPtr<Client>> clients; Vector<OwnedPtr<os::IPC::ClientConnection>> clients;
Vector<struct pollfd> fds; Vector<struct pollfd> fds;
TRY(fds.try_append({ .fd = server->fd(), .events = POLLIN, .revents = 0 })); TRY(fds.try_append({ .fd = server->fd(), .events = POLLIN, .revents = 0 }));
@ -135,18 +85,20 @@ Result<int> luna_main(int argc, char** argv)
auto client = TRY(server->accept()); auto client = TRY(server->accept());
os::println("launch: New client connected!"); os::println("launch: New client connected!");
TRY(fds.try_append({ .fd = client.fd(), .events = POLLIN, .revents = 0 })); TRY(fds.try_append({ .fd = client.fd(), .events = POLLIN, .revents = 0 }));
OwnedPtr<Client> 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++) 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) if (fds[i + 1].revents & POLLHUP)
{ {
os::println("launch: Client %d disconnected", i); os::println("launch: Client %d disconnected", i);
fds.remove_at(i + 1); fds.remove_at(i + 1);
auto client = clients.remove_at(i); auto client = clients.remove_at(i);
client->conn.disconnect(); client->disconnect();
} }
} }
} }

View File

@ -1,6 +1,5 @@
#include <os/File.h> #include <os/File.h>
#include <os/IPC.h> #include <os/IPC.h>
#include <os/LocalClient.h>
#include <os/Process.h> #include <os/Process.h>
#include <os/ipc/Launcher.h> #include <os/ipc/Launcher.h>
#include <signal.h> #include <signal.h>
@ -13,7 +12,7 @@
static constexpr ui::Color TASKBAR_COLOR = ui::Color::from_rgb(83, 83, 83); static constexpr ui::Color TASKBAR_COLOR = ui::Color::from_rgb(83, 83, 83);
static OwnedPtr<os::LocalClient> launcher_client; static OwnedPtr<os::IPC::Client> launcher_client;
void sigchld_handler(int) void sigchld_handler(int)
{ {
@ -33,7 +32,7 @@ Result<void> create_widget_group_for_app(ui::HorizontalLayout& layout, StringVie
button->set_action([=] { button->set_action([=] {
os::Launcher::LaunchDetachedRequest request; os::Launcher::LaunchDetachedRequest request;
SET_IPC_STRING(request.command, path.chars()); 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)); auto image = TRY(ui::ImageWidget::load(icon));
@ -51,7 +50,7 @@ Result<int> luna_main(int, char**)
TRY(os::EventLoop::the().register_signal_handler(SIGCHLD, sigchld_handler)); 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(); ui::Rect screen = app.screen_rect();

View File

@ -8,30 +8,13 @@
*/ */
#pragma once #pragma once
#include <os/Action.h>
#include <os/LocalClient.h> #include <os/LocalClient.h>
#include <os/LocalServer.h> #include <os/LocalServer.h>
#define IPC_ENUM_SERVER(name) __##name##_SERVER_ERROR = 0 #define IPC_ENUM_SERVER(name) __##name##_SERVER_ERROR = 0
#define IPC_ENUM_CLIENT(name) __##name##_CLIENT_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<void> Whether the operation succeded.
*/
extern Result<void> 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<void> Whether the operation succeded.
*/
extern Result<void> handle_ipc_server_event(os::LocalServer::Client& conn, u8 id);
namespace os namespace os
{ {
namespace IPC namespace IPC
@ -44,55 +27,230 @@ namespace os
#define SET_IPC_STRING(name, value) strlcpy(name, value, os::IPC::IPC_STRING_LENGTH) #define SET_IPC_STRING(name, value) strlcpy(name, value, os::IPC::IPC_STRING_LENGTH)
/** /**
* @brief Sends an IPC message without waiting for a reply. * @brief Class used to send and receive IPC messages on the server, using an already established connection to
* * the client.
* @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<void> Whether the operation succeded.
*/ */
template <typename Client, typename T> Result<void> send_async(Client& client, const T& message) class ClientConnection
{ {
u8 id = T::ID; public:
TRY(client.send_typed(id)); /**
TRY(client.send_typed(message)); * @brief Creates a new IPC connection object from an os::LocalServer connection.
return {}; *
* @param connection The existing connection to use, obtained after calling accept() on an os::LocalServer
* object.
* @return Result<OwnedPtr<ClientConnection>> An error, or the IPC connection object.
*/
static Result<OwnedPtr<ClientConnection>> adopt_connection(LocalServer::Client&& connection);
/**
* @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<void> Whether the operation succeeded.
*/
Result<void> 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<ClientConnection&, u8, void*>&& handler, void* arg)
{
m_message_handler = move(handler);
m_arg = arg;
} }
/** /**
* @brief Sends an error result to the IPC connection, indicating that an operation could not be performed. * @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<bool> 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 <typename T> Result<bool> 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.
* *
* @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. * @param error The error code.
* @return Result<void> Whether the operation succeded. * @return Result<void> Whether the operation succeded.
*/ */
template <typename Client> Result<void> send_error(Client& client, int error) Result<void> 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<void> Whether the operation succeeded.
*/
template <typename T> Result<void> send_async(const T& message)
{ {
u8 id = 0; u8 id = T::ID;
TRY(client.send_typed(id)); TRY(m_connection.send_typed(id));
TRY(client.send_typed(error)); TRY(m_connection.send_typed(message));
return {}; return {};
} }
/** /**
* @brief Sends an IPC message and waits for a reply (client-only). * @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<ClientConnection&, u8, void*> 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<OwnedPtr<Client>> An error, or a new connection object.
*/
static Result<OwnedPtr<Client>> 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<void> Whether the operation succeeded.
*/
Result<void> 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<Client&, u8, void*>&& 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<bool> 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 <typename T> Result<bool> 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<void> Whether the operation succeeded.
*/
template <typename T> Result<void> 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 ResponseType The type of the response.
* @tparam T The type of the message. * @tparam T The type of the message.
* @param client The connection object being used to communicate. * @param message The message to send.
* @param message The IPC message.
* @param handler The function used to handle messages that do not match the reply.
* @return Result<ResponseType> An error, or the response. * @return Result<ResponseType> An error, or the response.
*/ */
template <typename ResponseType, typename T> template <typename ResponseType, typename T> Result<ResponseType> send_sync(const T& message)
Result<ResponseType> send_sync(os::LocalClient& client, const T& message,
decltype(handle_ipc_client_event) handler = handle_ipc_client_event)
{ {
u8 id = T::ID; u8 id = T::ID;
TRY(client.send_typed(id)); TRY(m_connection->send_typed(id));
TRY(client.send_typed(message)); 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 // We allow receiving 5 messages of different types, but if those have passed and we still don't have a
// reply, fail with ENOMSG. // reply, fail with ENOMSG.
@ -101,7 +259,7 @@ namespace os
while (max_other_messages) while (max_other_messages)
{ {
u8 response_id; u8 response_id;
auto rc = client.recv_typed(response_id); auto rc = m_connection->recv_typed(response_id);
if (rc.has_error() && (rc.error() == EAGAIN || rc.error() == EINTR)) continue; if (rc.has_error() && (rc.error() == EAGAIN || rc.error() == EINTR)) continue;
if (response_id == 0) // Error result if (response_id == 0) // Error result
@ -109,7 +267,7 @@ namespace os
while (1) while (1)
{ {
int code; int code;
rc = client.recv_typed(code); rc = m_connection->recv_typed(code);
if (rc.has_error() && (rc.error() == EAGAIN || rc.error() == EINTR)) continue; if (rc.has_error() && (rc.error() == EAGAIN || rc.error() == EINTR)) continue;
return err(code); return err(code);
} }
@ -117,7 +275,7 @@ namespace os
if (response_id != ResponseType::ID) if (response_id != ResponseType::ID)
{ {
TRY(handler(client, response_id)); m_message_handler(*this, response_id, m_arg);
max_other_messages--; max_other_messages--;
continue; continue;
} }
@ -125,7 +283,7 @@ namespace os
while (1) while (1)
{ {
ResponseType response; ResponseType response;
rc = client.recv_typed(response); rc = m_connection->recv_typed(response);
if (rc.has_error() && (rc.error() == EAGAIN || rc.error() == EINTR)) continue; if (rc.has_error() && (rc.error() == EAGAIN || rc.error() == EINTR)) continue;
return response; return response;
} }
@ -135,23 +293,32 @@ namespace os
} }
/** /**
* @brief Check for new IPC messages on a connection and handle them appropriately. * @brief Closes the connection.
*
* @param client The client connection.
* @param handler The function used to handle messages.
* @return Result<void> Whether the operation succeded.
*/ */
Result<void> check_for_messages(os::LocalClient& client, void disconnect()
decltype(handle_ipc_client_event) handler = handle_ipc_client_event); {
m_connection->disconnect();
}
/** /**
* @brief Check for new IPC messages on a connection and handle them appropriately. * @brief Returns the file descriptor associated with this connection.
* *
* @param server The server connection. * @return int The file descriptor.
* @param handler The function used to handle messages.
* @return Result<void> Whether the operation succeded.
*/ */
Result<void> check_for_messages(os::LocalServer::Client& server, int fd()
decltype(handle_ipc_server_event) handler = handle_ipc_server_event); {
return m_connection->fd();
}
private:
Client(OwnedPtr<LocalClient>&& connection);
OwnedPtr<LocalClient> m_connection;
Function<Client&, u8, void*> m_message_handler;
void* m_arg;
bool m_ipc_in_progress { false };
u8 m_ipc_saved_id { 0 };
};
} }
} }

View File

@ -11,10 +11,26 @@
namespace os::IPC namespace os::IPC
{ {
Result<void> check_for_messages(os::LocalClient& client, decltype(handle_ipc_client_event) handler) Result<OwnedPtr<ClientConnection>> ClientConnection::adopt_connection(LocalServer::Client&& connection)
{ {
return adopt_owned_if_nonnull(new (std::nothrow) ClientConnection(move(connection)));
}
ClientConnection::ClientConnection(LocalServer::Client&& connection) : m_connection(move(connection))
{
}
Result<void> 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; u8 id;
auto rc = client.recv_typed(id); auto rc = m_connection.recv_typed(id);
if (rc.has_error()) if (rc.has_error())
{ {
if (rc.error() == EAGAIN) return {}; // No messages, and the caller does not want us to block. if (rc.error() == EAGAIN) return {}; // No messages, and the caller does not want us to block.
@ -23,13 +39,36 @@ namespace os::IPC
return rc.release_error(); return rc.release_error();
} }
return handler(client, id); m_message_handler(*this, id, m_arg);
return {};
} }
Result<void> check_for_messages(os::LocalServer::Client& client, decltype(handle_ipc_server_event) handler) Result<void> ClientConnection::send_error(int error)
{ {
u8 id = 0;
TRY(m_connection.send_typed(id));
TRY(m_connection.send_typed(error));
return {};
}
Result<OwnedPtr<Client>> 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<void> 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; u8 id;
auto rc = client.recv_typed(id); auto rc = m_connection->recv_typed(id);
if (rc.has_error()) if (rc.has_error())
{ {
if (rc.error() == EAGAIN) return {}; // No messages, and the caller does not want us to block. if (rc.error() == EAGAIN) return {}; // No messages, and the caller does not want us to block.
@ -38,6 +77,11 @@ namespace os::IPC
return rc.release_error(); return rc.release_error();
} }
return handler(client, id); m_message_handler(*this, id, m_arg);
return {};
}
Client::Client(OwnedPtr<LocalClient>&& connection) : m_connection(move(connection))
{
} }
} }

View File

@ -11,7 +11,7 @@
#include <luna/HashMap.h> #include <luna/HashMap.h>
#include <luna/StringView.h> #include <luna/StringView.h>
#include <os/EventLoop.h> #include <os/EventLoop.h>
#include <os/LocalClient.h> #include <os/IPC.h>
#include <ui/Window.h> #include <ui/Window.h>
namespace ui namespace ui
@ -27,7 +27,7 @@ namespace ui
Rect screen_rect(); Rect screen_rect();
os::LocalClient& client() os::IPC::Client& client()
{ {
return *m_client; return *m_client;
} }
@ -52,13 +52,11 @@ namespace ui
Result<void> register_window(OwnedPtr<Window>&& window, Badge<Window>); Result<void> register_window(OwnedPtr<Window>&& window, Badge<Window>);
void unregister_window(Window* window, Badge<Window>); void unregister_window(Window* window, Badge<Window>);
Result<void> handle_ipc_event(u8 id);
static App& the(); static App& the();
private: private:
static App* s_app; static App* s_app;
OwnedPtr<os::LocalClient> m_client; OwnedPtr<os::IPC::Client> m_client;
Window* m_main_window { nullptr }; Window* m_main_window { nullptr };
HashMap<int, OwnedPtr<Window>> m_windows; HashMap<int, OwnedPtr<Window>> m_windows;
bool m_should_close { false }; bool m_should_close { false };
@ -68,6 +66,8 @@ namespace ui
Window* find_window(int id); Window* find_window(int id);
Result<void> handle_ipc_event(os::IPC::Client&, u8 id, void*);
friend void handle_socket_event(int, int); friend void handle_socket_event(int, int);
}; };
} }

View File

@ -14,11 +14,6 @@
#include <ui/ipc/Client.h> #include <ui/ipc/Client.h>
#include <ui/ipc/Server.h> #include <ui/ipc/Server.h>
Result<void> handle_ipc_client_event(os::LocalClient&, u8 id)
{
return ui::App::the().handle_ipc_event(id);
}
namespace ui namespace ui
{ {
void handle_socket_event(int, int status) void handle_socket_event(int, int status)
@ -41,8 +36,9 @@ namespace ui
Result<void> App::init(StringView socket_path) Result<void> App::init(StringView socket_path)
{ {
m_client = TRY(os::LocalClient::connect(socket_path, true)); m_client = TRY(os::IPC::Client::connect(socket_path, false));
fcntl(m_client->fd(), F_SETFL, O_NONBLOCK); 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)); TRY(m_loop.register_fd_listener(m_client->fd(), handle_socket_event));
@ -65,7 +61,7 @@ namespace ui
Rect App::screen_rect() Rect App::screen_rect()
{ {
ui::GetScreenRectRequest request {}; ui::GetScreenRectRequest request {};
auto response = os::IPC::send_sync<ui::GetScreenRectResponse>(*m_client, request).release_value(); auto response = m_client->send_sync<ui::GetScreenRectResponse>(request).release_value();
return response.rect; return response.rect;
} }
@ -89,26 +85,13 @@ namespace ui
return window->ptr(); return window->ptr();
} }
#define READ_MESSAGE(request) \ Result<void> App::handle_ipc_event(os::IPC::Client&, u8 id, void*)
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<void> App::handle_ipc_event(u8 id)
{ {
switch (id) switch (id)
{ {
case WINDOW_CLOSE_REQUEST_ID: { case WINDOW_CLOSE_REQUEST_ID: {
WindowCloseRequest request; WindowCloseRequest request;
READ_MESSAGE(request); if (!TRY(m_client->read_message(request))) return {};
os::eprintln("ui: Window close request from server! Shall comply."); os::eprintln("ui: Window close request from server! Shall comply.");
auto* window = find_window(request.window); auto* window = find_window(request.window);
window->close(); window->close();
@ -116,7 +99,7 @@ namespace ui
} }
case MOUSE_EVENT_REQUEST_ID: { case MOUSE_EVENT_REQUEST_ID: {
MouseEventRequest request; MouseEventRequest request;
READ_MESSAGE(request); if (!TRY(m_client->read_message(request))) return {};
auto* window = find_window(request.window); auto* window = find_window(request.window);
auto move_result = window->handle_mouse_move(request.position).value_or(ui::EventResult::DidNotHandle); auto move_result = window->handle_mouse_move(request.position).value_or(ui::EventResult::DidNotHandle);
auto button_result = auto button_result =
@ -127,7 +110,7 @@ namespace ui
} }
case MOUSE_LEAVE_REQUEST_ID: { case MOUSE_LEAVE_REQUEST_ID: {
MouseLeaveRequest request; MouseLeaveRequest request;
READ_MESSAGE(request); if (!TRY(m_client->read_message(request))) return {};
auto* window = find_window(request.window); auto* window = find_window(request.window);
if (window->handle_mouse_leave().value_or(ui::EventResult::DidNotHandle) == ui::EventResult::DidHandle) if (window->handle_mouse_leave().value_or(ui::EventResult::DidNotHandle) == ui::EventResult::DidHandle)
window->draw(); window->draw();
@ -135,7 +118,7 @@ namespace ui
} }
case KEY_EVENT_REQUEST_ID: { case KEY_EVENT_REQUEST_ID: {
KeyEventRequest request; KeyEventRequest request;
READ_MESSAGE(request); if (!TRY(m_client->read_message(request))) return {};
auto* window = find_window(request.window); auto* window = find_window(request.window);
if (window->handle_key_event(request).value_or(ui::EventResult::DidNotHandle) == ui::EventResult::DidHandle) if (window->handle_key_event(request).value_or(ui::EventResult::DidNotHandle) == ui::EventResult::DidHandle)
window->draw(); window->draw();
@ -148,7 +131,7 @@ namespace ui
bool App::process_events() bool App::process_events()
{ {
check(m_main_window); check(m_main_window);
os::IPC::check_for_messages(*m_client).release_value(); m_client->check_for_messages().release_value();
return !m_should_close; return !m_should_close;
} }
} }

View File

@ -43,7 +43,7 @@ namespace ui
ui::CreateWindowRequest request; ui::CreateWindowRequest request;
request.rect = rect; request.rect = rect;
auto response = TRY(os::IPC::send_sync<ui::CreateWindowResponse>(App::the().client(), request)); auto response = TRY(App::the().client().send_sync<ui::CreateWindowResponse>(request));
auto path = COPY_IPC_STRING(response.shm_path); auto path = COPY_IPC_STRING(response.shm_path);
@ -62,7 +62,7 @@ namespace ui
ui::SetTitlebarRectRequest titlebar_request; ui::SetTitlebarRectRequest titlebar_request;
titlebar_request.titlebar_rect = window->m_titlebar_canvas.rect(); titlebar_request.titlebar_rect = window->m_titlebar_canvas.rect();
titlebar_request.window = response.window; titlebar_request.window = response.window;
os::IPC::send_async(App::the().client(), titlebar_request); App::the().client().send_async(titlebar_request);
} }
else else
{ {
@ -74,7 +74,7 @@ namespace ui
ui::RemoveSharedMemoryRequest shm_request; ui::RemoveSharedMemoryRequest shm_request;
shm_request.window = response.window; 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), {}); App::the().register_window(move(window), {});
@ -91,7 +91,7 @@ namespace ui
ui::SetWindowTitleRequest request; ui::SetWindowTitleRequest request;
request.window = m_id; request.window = m_id;
SET_IPC_STRING(request.title, title.chars()); 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(); m_name = String::from_string_view(title).release_value();
draw(); draw();
@ -101,7 +101,7 @@ namespace ui
{ {
ui::InvalidateRequest request; ui::InvalidateRequest request;
request.window = m_id; request.window = m_id;
os::IPC::send_async(App::the().client(), request); App::the().client().send_async(request);
} }
void Window::close() void Window::close()
@ -110,7 +110,7 @@ namespace ui
ui::CloseWindowRequest request; ui::CloseWindowRequest request;
request.window = m_id; 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); if (this == app.main_window()) app.set_should_close(true);

View File

@ -1,18 +1,20 @@
#pragma once #pragma once
#include "IPC.h"
#include "Window.h" #include "Window.h"
#include <os/LocalServer.h> #include <os/IPC.h>
struct Client struct Client
{ {
os::LocalServer::Client conn; OwnedPtr<os::IPC::ClientConnection> conn;
Vector<Window*> windows; Vector<Window*> windows;
const bool privileged { false }; const bool privileged { false };
bool rpc_in_progress { false };
u8 rpc_id { 0 };
Client(os::LocalServer::Client&& client, bool priv) Client(OwnedPtr<os::IPC::ClientConnection>&& client, bool priv)
#ifdef CLIENT_IMPLEMENTATION #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 #else
; ;
#endif #endif

View File

@ -13,34 +13,12 @@
auto _expr_rc = (expr); \ auto _expr_rc = (expr); \
if (!_expr_rc.has_value()) \ if (!_expr_rc.has_value()) \
{ \ { \
os::IPC::send_error(client.conn, _expr_rc.error()); \ client.conn->send_error(_expr_rc.error()); \
return {}; \ return {}; \
} \ } \
_expr_rc.release_value(); \ _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) \ #define CHECK_WINDOW_ID(request) \
do { \ do { \
if ((usize)request.window >= client.windows.size() || !client.windows[request.window]) \ if ((usize)request.window >= client.windows.size() || !client.windows[request.window]) \
@ -53,18 +31,18 @@
static Result<void> handle_create_window_message(Client& client) static Result<void> handle_create_window_message(Client& client)
{ {
ui::CreateWindowRequest request; ui::CreateWindowRequest request;
READ_MESSAGE(request); if (!TRY(client.conn->read_message(request))) return {};
request.rect = request.rect.normalized(); request.rect = request.rect.normalized();
auto name = TRY_OR_IPC_ERROR(String::from_cstring("Window")); 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)); auto* window = new (std::nothrow) Window(request.rect, move(name));
if (!window) if (!window)
{ {
os::IPC::send_error(client.conn, ENOMEM); client.conn->send_error(ENOMEM);
return {}; return {};
} }
@ -89,14 +67,14 @@ static Result<void> handle_create_window_message(Client& client)
response.window = id; response.window = id;
SET_IPC_STRING(response.shm_path, shm_path.chars()); SET_IPC_STRING(response.shm_path, shm_path.chars());
window->shm_path = move(shm_path); window->shm_path = move(shm_path);
os::IPC::send_async(client.conn, response); client.conn->send_async(response);
return {}; return {};
} }
static Result<void> handle_remove_shm_message(Client& client) static Result<void> handle_remove_shm_message(Client& client)
{ {
ui::RemoveSharedMemoryRequest request; ui::RemoveSharedMemoryRequest request;
READ_MESSAGE(request); if (!TRY(client.conn->read_message(request))) return {};
CHECK_WINDOW_ID(request); CHECK_WINDOW_ID(request);
@ -108,7 +86,7 @@ static Result<void> handle_remove_shm_message(Client& client)
static Result<void> handle_set_window_title_message(Client& client) static Result<void> handle_set_window_title_message(Client& client)
{ {
ui::SetWindowTitleRequest request; ui::SetWindowTitleRequest request;
READ_MESSAGE(request); if (!TRY(client.conn->read_message(request))) return {};
auto name = COPY_IPC_STRING(request.title); auto name = COPY_IPC_STRING(request.title);
@ -124,7 +102,7 @@ static Result<void> handle_set_window_title_message(Client& client)
static Result<void> handle_invalidate_message(Client& client) static Result<void> handle_invalidate_message(Client& client)
{ {
ui::InvalidateRequest request; ui::InvalidateRequest request;
READ_MESSAGE(request); if (!TRY(client.conn->read_message(request))) return {};
CHECK_WINDOW_ID(request); CHECK_WINDOW_ID(request);
@ -136,7 +114,7 @@ static Result<void> handle_invalidate_message(Client& client)
static Result<void> handle_close_window_message(Client& client) static Result<void> handle_close_window_message(Client& client)
{ {
ui::CloseWindowRequest request; ui::CloseWindowRequest request;
READ_MESSAGE(request); if (!TRY(client.conn->read_message(request))) return {};
CHECK_WINDOW_ID(request); CHECK_WINDOW_ID(request);
@ -152,11 +130,11 @@ static Result<void> handle_close_window_message(Client& client)
static Result<void> handle_get_screen_rect_message(Client& client) static Result<void> handle_get_screen_rect_message(Client& client)
{ {
ui::GetScreenRectRequest request; 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; ui::GetScreenRectResponse response;
response.rect = Screen::the().canvas().rect(); response.rect = Screen::the().canvas().rect();
os::IPC::send_async(client.conn, response); client.conn->send_async(response);
return {}; return {};
} }
@ -164,7 +142,7 @@ static Result<void> handle_get_screen_rect_message(Client& client)
static Result<void> handle_set_titlebar_rect_message(Client& client) static Result<void> handle_set_titlebar_rect_message(Client& client)
{ {
ui::SetTitlebarRectRequest request; ui::SetTitlebarRectRequest request;
READ_MESSAGE(request); if (!TRY(client.conn->read_message(request))) return {};
request.titlebar_rect = request.titlebar_rect.normalized(); request.titlebar_rect = request.titlebar_rect.normalized();
@ -185,36 +163,19 @@ static Result<void> handle_set_titlebar_rect_message(Client& client)
namespace wind namespace wind
{ {
Result<void> 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) switch (id)
{ {
case ui::CREATE_WINDOW_ID: return handle_create_window_message(client); case ui::CREATE_WINDOW_ID: handle_create_window_message(client); break;
case ui::REMOVE_SHM_ID: return handle_remove_shm_message(client); case ui::REMOVE_SHM_ID: handle_remove_shm_message(client); break;
case ui::SET_WINDOW_TITLE_ID: return handle_set_window_title_message(client); case ui::SET_WINDOW_TITLE_ID: handle_set_window_title_message(client); break;
case ui::INVALIDATE_ID: return handle_invalidate_message(client); case ui::INVALIDATE_ID: handle_invalidate_message(client); break;
case ui::CLOSE_WINDOW_ID: return handle_close_window_message(client); case ui::CLOSE_WINDOW_ID: handle_close_window_message(client); break;
case ui::GET_SCREEN_RECT_ID: return handle_get_screen_rect_message(client); case ui::GET_SCREEN_RECT_ID: handle_get_screen_rect_message(client); break;
case ui::SET_TITLEBAR_RECT_ID: return handle_set_titlebar_rect_message(client); case ui::SET_TITLEBAR_RECT_ID: handle_set_titlebar_rect_message(client); break;
default: os::eprintln("wind: Invalid IPC message from client!"); return err(EINVAL); default: os::eprintln("wind: Invalid IPC message from client!"); return;
} }
} }
Result<void> 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);
}
} }

View File

@ -4,7 +4,5 @@
namespace wind namespace wind
{ {
Result<void> handle_ipc_message(Client& client, u8 id); void handle_ipc_message(os::IPC::ClientConnection& connection, u8 id, void* c);
Result<void> handle_ipc(Client& client);
} }

View File

@ -91,7 +91,7 @@ void Mouse::update(const moon::MousePacket& packet)
request.window = window->id; request.window = window->id;
request.position = window->surface.relative(m_position); request.position = window->surface.relative(m_position);
request.buttons = packet.buttons; request.buttons = packet.buttons;
os::IPC::send_async(window->client->conn, request); window->client->conn->send_async(request);
new_active_window = window; new_active_window = window;
break; break;
} }
@ -103,7 +103,7 @@ void Mouse::update(const moon::MousePacket& packet)
{ {
ui::MouseLeaveRequest request; ui::MouseLeaveRequest request;
request.window = m_active_window->id; 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; m_active_window = new_active_window;
} }

View File

@ -1,6 +1,5 @@
#define CLIENT_IMPLEMENTATION #define CLIENT_IMPLEMENTATION
#include "Client.h" #include "Client.h"
#include "IPC.h"
#include "Keyboard.h" #include "Keyboard.h"
#include "Mouse.h" #include "Mouse.h"
#include "Screen.h" #include "Screen.h"
@ -31,14 +30,14 @@ static void debug(const Vector<OwnedPtr<Client>>& clients)
for (const auto& client : 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 --"); os::println("-- wind: Listing windows --");
for (const auto& window : g_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->id, window->dirty ? "" : "not ", window->name.chars(), window->surface.pos.x,
window->surface.pos.y, window->surface.width, window->surface.height); window->surface.pos.y, window->surface.width, window->surface.height);
} }
@ -156,7 +155,7 @@ Result<int> luna_main(int argc, char** argv)
{ {
auto* window = g_windows.last().value(); auto* window = g_windows.last().value();
request.window = window->id; request.window = window->id;
os::IPC::send_async(window->client->conn, request); window->client->conn->send_async(request);
} }
} }
if (fds[2].revents & POLLIN) if (fds[2].revents & POLLIN)
@ -164,7 +163,10 @@ Result<int> luna_main(int argc, char** argv)
auto client = TRY(server->accept()); auto client = TRY(server->accept());
os::println("wind: New client connected!"); os::println("wind: New client connected!");
TRY(fds.try_append({ .fd = client.fd(), .events = POLLIN, .revents = 0 })); TRY(fds.try_append({ .fd = client.fd(), .events = POLLIN, .revents = 0 }));
OwnedPtr<Client> c = TRY(adopt_owned_if_nonnull(new Client(move(client), false)));
auto connection = TRY(os::IPC::ClientConnection::adopt_connection(move(client)));
OwnedPtr<Client> c = TRY(adopt_owned_if_nonnull(new Client(move(connection), false)));
TRY(clients.try_append(move(c))); TRY(clients.try_append(move(c)));
} }
if (fds[3].revents & POLLIN) if (fds[3].revents & POLLIN)
@ -172,18 +174,21 @@ Result<int> luna_main(int argc, char** argv)
auto client = TRY(system_server->accept()); auto client = TRY(system_server->accept());
os::println("wind: New privileged client connected!"); os::println("wind: New privileged client connected!");
TRY(fds.try_append({ .fd = client.fd(), .events = POLLIN, .revents = 0 })); TRY(fds.try_append({ .fd = client.fd(), .events = POLLIN, .revents = 0 }));
OwnedPtr<Client> c = TRY(adopt_owned_if_nonnull(new Client(move(client), true)));
auto connection = TRY(os::IPC::ClientConnection::adopt_connection(move(client)));
OwnedPtr<Client> c = TRY(adopt_owned_if_nonnull(new Client(move(connection), true)));
TRY(clients.try_append(move(c))); TRY(clients.try_append(move(c)));
} }
for (usize i = 0; i < clients.size(); i++) 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) if (fds[i + 4].revents & POLLHUP)
{ {
os::println("wind: Client %d disconnected", i); os::println("wind: Client %d disconnected", i);
fds.remove_at(i + 4); fds.remove_at(i + 4);
auto client = clients.remove_at(i); auto client = clients.remove_at(i);
client->conn.disconnect(); client->conn->disconnect();
for (auto& window : client->windows) for (auto& window : client->windows)
{ {
if (window) if (window)