From 5892a6bf09fd1ac2014cf6b1cabf199781ecd0b5 Mon Sep 17 00:00:00 2001 From: apio Date: Fri, 6 Oct 2023 22:06:34 +0200 Subject: [PATCH] libos+libui: Add event loops --- apps/gol.cpp | 1 - libos/CMakeLists.txt | 1 + libos/include/os/EventLoop.h | 71 +++++++++++++++++++++++++++++++++++ libos/src/EventLoop.cpp | 72 ++++++++++++++++++++++++++++++++++++ libui/include/ui/App.h | 5 ++- libui/src/App.cpp | 18 +++++---- terminal/main.cpp | 1 - 7 files changed, 157 insertions(+), 12 deletions(-) create mode 100644 libos/include/os/EventLoop.h create mode 100644 libos/src/EventLoop.cpp diff --git a/apps/gol.cpp b/apps/gol.cpp index 464fd31a..2f7c1162 100644 --- a/apps/gol.cpp +++ b/apps/gol.cpp @@ -105,7 +105,6 @@ Result luna_main(int argc, char** argv) { ui::App app; TRY(app.init(argc, argv)); - app.set_nonblocking(); g_window = TRY(ui::Window::create(ui::Rect { 200, 200, 600, 400 })); g_window->set_title("Game of Life"); diff --git a/libos/CMakeLists.txt b/libos/CMakeLists.txt index 321f4b4c..bf069a59 100644 --- a/libos/CMakeLists.txt +++ b/libos/CMakeLists.txt @@ -5,6 +5,7 @@ file(GLOB HEADERS include/os/*.h) set(SOURCES ${HEADERS} src/ArgumentParser.cpp + src/EventLoop.cpp src/File.cpp src/FileSystem.cpp src/Process.cpp diff --git a/libos/include/os/EventLoop.h b/libos/include/os/EventLoop.h new file mode 100644 index 00000000..7d1bb392 --- /dev/null +++ b/libos/include/os/EventLoop.h @@ -0,0 +1,71 @@ +/** + * @file EventLoop.h + * @author apio (cloudapio.eu) + * @brief Base class for event-driven applications. + * + * @copyright Copyright (c) 2023, the Luna authors. + * + */ + +#pragma once +#include +#include +#include + +namespace os +{ + /** + * @brief Event loop implementation. + */ + class EventLoop + { + public: + /** + * @brief Construct a new event loop, which will automatically register itself as the current global event loop. + */ + EventLoop(); + + ~EventLoop(); + + EventLoop(EventLoop&&) = delete; + EventLoop(const EventLoop&) = delete; + + /** + * @brief Fetch the current global event loop. + * + * @return EventLoop& The current global event loop. + */ + static EventLoop& the(); + + /** + * @brief Register a new event listener on a file descriptor. + * + * @param fd The file descriptor to listen on. + * @param listener The callback function to invoke when events occur on the file descriptor. The first parameter + * is the file descriptor registered, and the second argument is the type of event (POLLIN or POLLHUP) + * @return Result Whether the operation succeeded. + */ + Result register_fd_listener(int fd, void (*listener)(int, int)); + + /** + * @brief Run the event loop until it is asked to quit. + * + * @return int The status passed to the quit() method. + */ + int run(); + + /** + * @brief Ask the event loop to quit. + * + * @param status The status value to return from run(). + */ + void quit(int status = 0); + + private: + HashMap m_fd_listeners; + Vector m_pollfds; + + bool m_should_quit { false }; + int m_quit_status { 0 }; + }; +} diff --git a/libos/src/EventLoop.cpp b/libos/src/EventLoop.cpp new file mode 100644 index 00000000..e3e4be4c --- /dev/null +++ b/libos/src/EventLoop.cpp @@ -0,0 +1,72 @@ +/** + * @file EventLoop.cpp + * @author apio (cloudapio.eu) + * @brief Base class for event-driven applications. + * + * @copyright Copyright (c) 2023, the Luna authors. + * + */ + +#include +#include +#include +#include + +static os::EventLoop* s_the = nullptr; + +namespace os +{ + EventLoop::EventLoop() + { + s_the = this; + } + + EventLoop::~EventLoop() + { + s_the = nullptr; + } + + EventLoop& EventLoop::the() + { + check(s_the); + return *s_the; + } + + Result EventLoop::register_fd_listener(int fd, void (*listener)(int, int)) + { + TRY(m_fd_listeners.try_set(fd, listener)); + TRY(m_pollfds.try_append({ .fd = fd, .events = POLLIN, .revents = 0 })); + return {}; + } + + int EventLoop::run() + { + while (!m_should_quit) + { + for (auto& pfd : m_pollfds) { pfd.revents = 0; } + + int rc = poll(m_pollfds.data(), m_pollfds.size(), 1000); + if (!rc) continue; + if (rc < 0 && errno != EINTR) + { + os::println("poll: error: %s", strerror(errno)); + return 1; + } + + for (usize i = 0; i < m_fd_listeners.size(); i++) + { + if (m_pollfds[i].revents) + m_fd_listeners.try_get(m_pollfds[i].fd) + .value()(m_pollfds[i].fd, m_pollfds[i].revents); // Invoke the callback + } + } + + return m_quit_status; + } + + void EventLoop::quit(int status) + { + m_quit_status = status; + m_should_quit = true; + } +} diff --git a/libui/include/ui/App.h b/libui/include/ui/App.h index b297c14b..5f44683b 100644 --- a/libui/include/ui/App.h +++ b/libui/include/ui/App.h @@ -9,6 +9,7 @@ #pragma once #include +#include #include #include @@ -33,10 +34,9 @@ namespace ui void set_should_close(bool b) { m_should_close = b; + if (b) m_loop.quit(); } - void set_nonblocking(); - void set_main_window(Window* window) { check(!m_main_window); @@ -63,6 +63,7 @@ namespace ui Window* m_main_window { nullptr }; HashMap> m_windows; bool m_should_close { false }; + os::EventLoop m_loop; Window* find_window(int id); }; diff --git a/libui/src/App.cpp b/libui/src/App.cpp index 7f4c9c98..26c71f54 100644 --- a/libui/src/App.cpp +++ b/libui/src/App.cpp @@ -19,6 +19,12 @@ Result handle_ipc_client_event(os::LocalClient&, u8 id) return ui::App::the().handle_ipc_event(id); } +void handle_socket_event(int, int status) +{ + if (status & POLLHUP) ui::App::the().set_should_close(true); + if (status & POLLIN) { ui::App::the().process_events(); } +} + namespace ui { App* App::s_app { nullptr }; @@ -44,15 +50,16 @@ namespace ui parser.parse(argc, argv); m_client = TRY(os::LocalClient::connect(socket_path, true)); + fcntl(m_client->fd(), F_SETFL, O_NONBLOCK); + + TRY(m_loop.register_fd_listener(m_client->fd(), handle_socket_event)); return {}; } Result App::run() { - while (process_events()) - ; - return 0; + return m_loop.run(); } App& App::the() @@ -144,11 +151,6 @@ namespace ui } } - void App::set_nonblocking() - { - fcntl(m_client->fd(), F_SETFL, O_NONBLOCK); - } - bool App::process_events() { check(m_main_window); diff --git a/terminal/main.cpp b/terminal/main.cpp index 48e438b9..71d5599e 100644 --- a/terminal/main.cpp +++ b/terminal/main.cpp @@ -7,7 +7,6 @@ Result luna_main(int argc, char** argv) { ui::App app; TRY(app.init(argc, argv)); - app.set_nonblocking(); auto* window = TRY(ui::Window::create(ui::Rect { 150, 150, 640, 400 })); app.set_main_window(window);