#include "IPC.h" #include "Screen.h" #include #include #include #include #include #define TRY_OR_IPC_ERROR(expr) \ ({ \ auto _expr_rc = (expr); \ if (!_expr_rc.has_value()) \ { \ g_windows.remove(window); \ delete window; \ os::IPC::send_error(client.conn, _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]) \ { \ os::eprintln("wind: Window id is invalid!"); \ return {}; \ } \ } while (0) static Result handle_create_window_message(Client& client) { ui::CreateWindowRequest request; READ_MESSAGE(request); if (request.decorated) { request.rect.height += Window::titlebar_height(); // Make sure we provide the full contents rect that was asked for. request.rect.pos.y -= Window::titlebar_height(); // Adjust it so the contents begin at the expected coordinates. } request.rect = request.rect.normalized(); auto name = TRY(String::from_cstring("Window")); auto shm_path = TRY(String::format("/wind-shm-%d-%lu"_sv, client.conn.fd(), time(NULL))); auto* window = new (std::nothrow) Window(request.rect, move(name), request.decorated); if (!window) { os::IPC::send_error(client.conn, ENOMEM); return {}; } window->pixels = (u32*)TRY_OR_IPC_ERROR( os::SharedMemory::create(shm_path.view(), window->contents.height * window->contents.width * 4)); TRY_OR_IPC_ERROR(client.windows.try_append(window)); int id = static_cast(client.windows.size() - 1); window->client = &client; window->id = id; 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); CHECK_WINDOW_ID(request); client.windows[request.window]->name = move(name); return {}; } static Result handle_invalidate_message(Client& client) { ui::InvalidateRequest request; READ_MESSAGE(request); CHECK_WINDOW_ID(request); client.windows[request.window]->dirty = true; return {}; } static Result 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 {}; } static Result handle_get_screen_rect_message(Client& client) { ui::GetScreenRectRequest request; READ_MESSAGE(request); // Kinda pointless, but required. ui::GetScreenRectResponse response; response.rect = Screen::the().canvas().rect(); os::IPC::send_async(client.conn, response); 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); case ui::CLOSE_WINDOW_ID: return handle_close_window_message(client); case ui::GET_SCREEN_RECT_ID: return handle_get_screen_rect_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 {}; } if (rc.error() == EINTR) { return {}; } else return rc.release_error(); } return handle_ipc_message(client, id); } }