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 <unistd.h>
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<void> handle_launch_detached_message(Client& client)
Result<void> 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<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)
{
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<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)
{
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));
TRY(server->listen(20));
Vector<OwnedPtr<Client>> clients;
Vector<OwnedPtr<os::IPC::ClientConnection>> clients;
Vector<struct pollfd> fds;
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());
os::println("launch: New client connected!");
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++)
{
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();
}
}
}

View File

@ -1,6 +1,5 @@
#include <os/File.h>
#include <os/IPC.h>
#include <os/LocalClient.h>
#include <os/Process.h>
#include <os/ipc/Launcher.h>
#include <signal.h>
@ -13,7 +12,7 @@
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)
{
@ -33,7 +32,7 @@ Result<void> 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<int> 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();

View File

@ -8,30 +8,13 @@
*/
#pragma once
#include <os/Action.h>
#include <os/LocalClient.h>
#include <os/LocalServer.h>
#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<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 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<void> 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 <typename Client, typename T> Result<void> 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<OwnedPtr<ClientConnection>> An error, or the IPC connection object.
*/
static Result<OwnedPtr<ClientConnection>> 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<void> Whether the operation succeded.
*/
template <typename Client> Result<void> 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<void> Whether the operation succeeded.
*/
Result<void> 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<ResponseType> An error, or the response.
*/
template <typename ResponseType, typename T>
Result<ResponseType> 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<ClientConnection&, u8, void*>&& 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<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.
*
* @param error The error code.
* @return Result<void> Whether the operation succeded.
*/
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 = 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<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 T The type of the message.
* @param message The message to send.
* @return Result<ResponseType> An error, or the response.
*/
template <typename ResponseType, typename T> Result<ResponseType> 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<void> Whether the operation succeded.
*/
Result<void> 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<void> Whether the operation succeded.
*/
Result<void> check_for_messages(os::LocalServer::Client& server,
decltype(handle_ipc_server_event) handler = handle_ipc_server_event);
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,25 +11,26 @@
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)
{
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<void> check_for_messages(os::LocalServer::Client& client, decltype(handle_ipc_server_event) handler)
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;
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<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;
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<LocalClient>&& connection) : m_connection(move(connection))
{
}
}

View File

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

View File

@ -14,11 +14,6 @@
#include <ui/ipc/Client.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
{
void handle_socket_event(int, int status)
@ -41,8 +36,9 @@ namespace ui
Result<void> 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<ui::GetScreenRectResponse>(*m_client, request).release_value();
auto response = m_client->send_sync<ui::GetScreenRectResponse>(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<void> App::handle_ipc_event(u8 id)
Result<void> 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;
}
}

View File

@ -43,7 +43,7 @@ namespace ui
ui::CreateWindowRequest request;
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);
@ -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);

View File

@ -1,18 +1,20 @@
#pragma once
#include "IPC.h"
#include "Window.h"
#include <os/LocalServer.h>
#include <os/IPC.h>
struct Client
{
os::LocalServer::Client conn;
OwnedPtr<os::IPC::ClientConnection> conn;
Vector<Window*> windows;
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
: conn(move(client)), windows(), privileged(priv) {}
: conn(move(client)), windows(), privileged(priv)
{
conn->set_message_handler(wind::handle_ipc_message, this);
}
#else
;
#endif

View File

@ -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<void> 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<void> 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<void> 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<void> handle_remove_shm_message(Client& client)
static Result<void> 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<void> handle_set_window_title_message(Client& client)
static Result<void> 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<void> handle_invalidate_message(Client& client)
static Result<void> 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<void> handle_close_window_message(Client& client)
static Result<void> 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<void> handle_get_screen_rect_message(Client& client)
static Result<void> 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<void> handle_set_titlebar_rect_message(Client& client)
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)
{
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<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
{
Result<void> handle_ipc_message(Client& client, u8 id);
Result<void> handle_ipc(Client& client);
void handle_ipc_message(os::IPC::ClientConnection& connection, u8 id, void* c);
}

View File

@ -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;
}

View File

@ -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<OwnedPtr<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 --");
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<int> 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<int> 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<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)));
}
if (fds[3].revents & POLLIN)
@ -172,18 +174,21 @@ Result<int> 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<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)));
}
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)