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:
parent
6bdf3169d2
commit
909d0ed289
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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,55 +27,230 @@ 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 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.
|
||||
* @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;
|
||||
TRY(client.send_typed(id));
|
||||
TRY(client.send_typed(error));
|
||||
u8 id = T::ID;
|
||||
TRY(m_connection.send_typed(id));
|
||||
TRY(m_connection.send_typed(message));
|
||||
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 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.
|
||||
* @param message The message to send.
|
||||
* @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)
|
||||
template <typename ResponseType, typename T> Result<ResponseType> send_sync(const T& message)
|
||||
{
|
||||
u8 id = T::ID;
|
||||
TRY(client.send_typed(id));
|
||||
TRY(client.send_typed(message));
|
||||
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.
|
||||
@ -101,7 +259,7 @@ namespace os
|
||||
while (max_other_messages)
|
||||
{
|
||||
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 (response_id == 0) // Error result
|
||||
@ -109,7 +267,7 @@ namespace os
|
||||
while (1)
|
||||
{
|
||||
int code;
|
||||
rc = client.recv_typed(code);
|
||||
rc = m_connection->recv_typed(code);
|
||||
if (rc.has_error() && (rc.error() == EAGAIN || rc.error() == EINTR)) continue;
|
||||
return err(code);
|
||||
}
|
||||
@ -117,7 +275,7 @@ namespace os
|
||||
|
||||
if (response_id != ResponseType::ID)
|
||||
{
|
||||
TRY(handler(client, response_id));
|
||||
m_message_handler(*this, response_id, m_arg);
|
||||
max_other_messages--;
|
||||
continue;
|
||||
}
|
||||
@ -125,7 +283,7 @@ namespace os
|
||||
while (1)
|
||||
{
|
||||
ResponseType response;
|
||||
rc = client.recv_typed(response);
|
||||
rc = m_connection->recv_typed(response);
|
||||
if (rc.has_error() && (rc.error() == EAGAIN || rc.error() == EINTR)) continue;
|
||||
return response;
|
||||
}
|
||||
@ -135,23 +293,32 @@ namespace os
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
* @brief Closes the connection.
|
||||
*/
|
||||
Result<void> check_for_messages(os::LocalClient& client,
|
||||
decltype(handle_ipc_client_event) handler = handle_ipc_client_event);
|
||||
void disconnect()
|
||||
{
|
||||
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.
|
||||
* @param handler The function used to handle messages.
|
||||
* @return Result<void> Whether the operation succeded.
|
||||
* @return int The file descriptor.
|
||||
*/
|
||||
Result<void> check_for_messages(os::LocalServer::Client& server,
|
||||
decltype(handle_ipc_server_event) handler = handle_ipc_server_event);
|
||||
int fd()
|
||||
{
|
||||
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 };
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -11,10 +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)
|
||||
{
|
||||
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;
|
||||
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.
|
||||
@ -23,13 +39,36 @@ namespace os::IPC
|
||||
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;
|
||||
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 +77,11 @@ namespace os::IPC
|
||||
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))
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
83
wind/IPC.cpp
83
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<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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user