libos+libui: Add event loops
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
f5aed95b8b
commit
5892a6bf09
@ -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");
|
||||
|
@ -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
|
||||
|
71
libos/include/os/EventLoop.h
Normal file
71
libos/include/os/EventLoop.h
Normal 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
72
libos/src/EventLoop.cpp
Normal 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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user