Luna/wind/IPC.cpp

192 lines
7.9 KiB
C++
Raw Normal View History

2023-08-14 16:15:29 +00:00
#include "IPC.h"
#include <errno.h>
#include <luna/Alignment.h>
2023-08-14 16:15:29 +00:00
#include <luna/String.h>
#include <os/File.h>
#include <string.h>
#include <sys/mman.h>
#include <time.h>
#include <unistd.h>
2023-08-14 16:15:29 +00:00
#define TRY_OR_IPC_ERROR(expr) \
({ \
auto _expr_rc = (expr); \
if (!_expr_rc.has_value()) \
{ \
delete window; \
os::IPC::send_error(client.conn, _expr_rc.error()); \
return {}; \
} \
_expr_rc.release_value(); \
})
static Result<u32*> create_shm_region(const char* path, int* outfd, ui::Rect rect)
2023-08-14 16:15:29 +00:00
{
int fd = shm_open(path, O_RDWR | O_CREAT | O_EXCL, 0600);
if (fd < 0)
2023-08-14 16:15:29 +00:00
{
os::eprintln("wind: could not create shared memory region: shm_open failed (%s) - %s", path, strerror(errno));
return err(errno);
}
usize size = align_up<PAGE_SIZE>(rect.width * rect.height * 4); // 4 bytes per pixel
if (ftruncate(fd, size) < 0)
{
os::eprintln("wind: could not create shared memory region: ftruncate failed (%d, %zu) - %s", fd, size,
strerror(errno));
shm_unlink(path);
close(fd);
return 0;
}
void* p = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (p == MAP_FAILED)
{
os::eprintln("wind: could not create shared memory region: mmap failed (%zu, %d) - %s", size, fd,
strerror(errno));
shm_unlink(path);
close(fd);
return 0;
2023-08-14 16:15:29 +00:00
}
if (outfd) *outfd = fd;
else
close(fd);
return (u32*)p;
}
#define READ_MESSAGE(request) \
do { \
auto rc = client.conn.recv_typed(request); \
if (rc.has_error()) \
{ \
if (rc.error() == EAGAIN) \
{ \
client.rpc_in_progress = true; \
client.rpc_id = decltype(request)::ID; \
return {}; \
} \
else \
return rc.release_error(); \
} \
} while (0)
#define CHECK_WINDOW_ID(request) \
do { \
if ((usize)request.window >= client.windows.size() || !client.windows[request.window]) \
{ \
os::eprintln("wind: Window id is invalid!"); \
return {}; \
} \
} while (0)
static Result<void> handle_create_window_message(Client& client)
{
ui::CreateWindowRequest request;
READ_MESSAGE(request);
2023-08-14 16:15:29 +00:00
request.rect = request.rect.normalized();
request.rect.height += Window::titlebar_height(); // Make sure we provide the full contents rect that was asked for.
auto name = TRY(String::from_cstring("Window"));
2023-08-14 16:15:29 +00:00
auto* window = new (std::nothrow) Window(request.rect, move(name));
2023-08-14 16:15:29 +00:00
if (!window)
{
os::IPC::send_error(client.conn, ENOMEM);
return {};
}
auto shm_path = TRY_OR_IPC_ERROR(String::format("/wind-shm-%d-%lu"_sv, client.conn.fd(), time(NULL)));
window->pixels = TRY_OR_IPC_ERROR(create_shm_region(shm_path.chars(), nullptr, window->contents));
TRY_OR_IPC_ERROR(client.windows.try_append(window));
int id = static_cast<int>(client.windows.size() - 1);
2023-08-14 16:15:29 +00:00
window->client = &client;
window->id = id;
2023-08-14 16:15:29 +00:00
ui::CreateWindowResponse response;
response.window = id;
SET_IPC_STRING(response.shm_path, shm_path.chars());
2023-08-14 16:15:29 +00:00
os::IPC::send_async(client.conn, response);
return {};
}
static Result<void> handle_set_window_title_message(Client& client)
{
ui::SetWindowTitleRequest request;
READ_MESSAGE(request);
auto name = COPY_IPC_STRING(request.title);
os::println("wind: SetWindowTitle(\"%s\") for window %d", name.chars(), request.window);
CHECK_WINDOW_ID(request);
client.windows[request.window]->name = move(name);
return {};
}
static Result<void> handle_invalidate_message(Client& client)
{
ui::InvalidateRequest request;
READ_MESSAGE(request);
CHECK_WINDOW_ID(request);
client.windows[request.window]->dirty = true;
return {};
}
static Result<void> handle_close_window_message(Client& client)
{
ui::CloseWindowRequest request;
READ_MESSAGE(request);
CHECK_WINDOW_ID(request);
auto* window = client.windows[request.window];
client.windows[request.window] = nullptr;
g_windows.remove(window);
delete window;
return {};
}
2023-08-14 16:15:29 +00:00
namespace wind
{
Result<void> handle_ipc_message(Client& client, u8 id)
{
client.rpc_in_progress = false;
switch (id)
{
case ui::CREATE_WINDOW_ID: return handle_create_window_message(client);
case ui::SET_WINDOW_TITLE_ID: return handle_set_window_title_message(client);
case ui::INVALIDATE_ID: return handle_invalidate_message(client);
case ui::CLOSE_WINDOW_ID: return handle_close_window_message(client);
2023-08-14 16:15:29 +00:00
default: os::eprintln("wind: Invalid IPC message from client!"); return err(EINVAL);
}
}
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 {}; }
else
return rc.release_error();
}
return handle_ipc_message(client, id);
}
}