/** * @file App.cpp * @author apio (cloudapio.eu) * @brief UI application event loop. * * @copyright Copyright (c) 2023, the Luna authors. * */ #include #include #include #include #include #include Result handle_ipc_client_event(os::LocalClient&, u8 id) { return ui::App::the().handle_ipc_event(id); } namespace ui { App* App::s_app { nullptr }; App::App() { s_app = this; } App::~App() { s_app = nullptr; } Result App::init(int argc, char** argv) { StringView socket_path = "/tmp/wind.sock"; os::ArgumentParser parser; parser.add_description("A UI application."_sv); parser.add_system_program_info(argv[0]); parser.add_value_argument(socket_path, 's', "socket"_sv, "the path for the local IPC socket"_sv); parser.parse(argc, argv); m_client = TRY(os::LocalClient::connect(socket_path, true)); return {}; } Result App::run() { while (process_events()) ; return 0; } App& App::the() { check(s_app); return *s_app; } Rect App::screen_rect() { ui::GetScreenRectRequest request {}; auto response = os::IPC::send_sync(*m_client, request).release_value(); return response.rect; } Result App::register_window(OwnedPtr&& window, Badge) { int id = window->id(); check(TRY(m_windows.try_set(id, move(window)))); return {}; } void App::unregister_window(Window* window, Badge) { int id = window->id(); check(m_windows.try_remove(id)); } Window* App::find_window(int id) { auto* window = m_windows.try_get_ref(id); check(window); 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 App::handle_ipc_event(u8 id) { switch (id) { case WINDOW_CLOSE_REQUEST_ID: { WindowCloseRequest request; READ_MESSAGE(request); os::eprintln("ui: Window close request from server! Shall comply."); auto* window = find_window(request.window); window->close(); return {}; } case MOUSE_EVENT_REQUEST_ID: { MouseEventRequest request; READ_MESSAGE(request); auto* window = find_window(request.window); auto move_result = window->handle_mouse_move(request.position).value_or(ui::EventResult::DidNotHandle); auto button_result = window->handle_mouse_buttons(request.position, request.buttons).value_or(ui::EventResult::DidNotHandle); if (move_result == ui::EventResult::DidHandle || button_result == ui::EventResult::DidHandle) window->draw(); return {}; } case MOUSE_LEAVE_REQUEST_ID: { MouseLeaveRequest request; READ_MESSAGE(request); auto* window = find_window(request.window); if (window->handle_mouse_leave().value_or(ui::EventResult::DidNotHandle) == ui::EventResult::DidHandle) window->draw(); return {}; } case KEY_EVENT_REQUEST_ID: { KeyEventRequest request; READ_MESSAGE(request); auto* window = find_window(request.window); if (window->handle_key_event(request).value_or(ui::EventResult::DidNotHandle) == ui::EventResult::DidHandle) window->draw(); return {}; } default: fail("Unexpected IPC request from server!"); } } void App::set_nonblocking() { fcntl(m_client->fd(), F_SETFL, O_NONBLOCK); } bool App::process_events() { check(m_main_window); os::IPC::check_for_messages(*m_client).release_value(); return !m_should_close; } }