libos+libui: Add event loops
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
apio 2023-10-06 22:06:34 +02:00
parent f5aed95b8b
commit 5892a6bf09
Signed by: apio
GPG Key ID: B8A7D06E42258954
7 changed files with 157 additions and 12 deletions

View File

@ -105,7 +105,6 @@ Result<int> 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");

View File

@ -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

View File

@ -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 <luna/HashMap.h>
#include <luna/Vector.h>
#include <sys/poll.h>
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<void> Whether the operation succeeded.
*/
Result<void> 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<int, void (*)(int, int)> m_fd_listeners;
Vector<struct pollfd> m_pollfds;
bool m_should_quit { false };
int m_quit_status { 0 };
};
}

72
libos/src/EventLoop.cpp Normal file
View File

@ -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 <errno.h>
#include <os/EventLoop.h>
#include <os/File.h>
#include <string.h>
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<void> 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;
}
}

View File

@ -9,6 +9,7 @@
#pragma once
#include <luna/HashMap.h>
#include <os/EventLoop.h>
#include <os/LocalClient.h>
#include <ui/Window.h>
@ -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<int, OwnedPtr<Window>> m_windows;
bool m_should_close { false };
os::EventLoop m_loop;
Window* find_window(int id);
};

View File

@ -19,6 +19,12 @@ Result<void> 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<int> 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);

View File

@ -7,7 +7,6 @@ Result<int> 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);