#include "IPC.h" #include #include #include #include #include #include #include #include #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 create_shm_region(const char* path, int* outfd, ui::Rect rect) { int fd = shm_open(path, O_RDWR | O_CREAT | O_EXCL, 0600); if (fd < 0) { os::eprintln("wind: could not create shared memory region: shm_open failed (%s) - %s", path, strerror(errno)); return err(errno); } usize size = align_up(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; } 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) static Result handle_create_window_message(Client& client) { ui::CreateWindowRequest request; READ_MESSAGE(request); 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")); auto* window = new (std::nothrow) Window(request.rect, move(name)); 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(client.windows.size() - 1); ui::CreateWindowResponse response; response.window = id; SET_IPC_STRING(response.shm_path, shm_path.chars()); os::IPC::send_async(client.conn, response); return {}; } static Result 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); if ((usize)request.window >= client.windows.size()) { os::eprintln("wind: Window id out of range!"); return {}; } client.windows[request.window]->name = move(name); return {}; } static Result handle_invalidate_message(Client& client) { ui::InvalidateRequest request; READ_MESSAGE(request); if ((usize)request.window >= client.windows.size()) { os::eprintln("wind: Window id out of range!"); return {}; } client.windows[request.window]->dirty = true; return {}; } namespace wind { Result 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); default: os::eprintln("wind: Invalid IPC message from client!"); return err(EINVAL); } } Result handle_ipc(Client& client) { if (client.rpc_in_progress) return handle_ipc_message(client, client.rpc_id); u8 id; auto rc = client.conn.recv_typed(id); if (rc.has_error()) { if (rc.error() == EAGAIN) { return {}; } else return rc.release_error(); } return handle_ipc_message(client, id); } }