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;
|
ui::App app;
|
||||||
TRY(app.init(argc, argv));
|
TRY(app.init(argc, argv));
|
||||||
app.set_nonblocking();
|
|
||||||
|
|
||||||
g_window = TRY(ui::Window::create(ui::Rect { 200, 200, 600, 400 }));
|
g_window = TRY(ui::Window::create(ui::Rect { 200, 200, 600, 400 }));
|
||||||
g_window->set_title("Game of Life");
|
g_window->set_title("Game of Life");
|
||||||
|
@ -5,6 +5,7 @@ file(GLOB HEADERS include/os/*.h)
|
|||||||
set(SOURCES
|
set(SOURCES
|
||||||
${HEADERS}
|
${HEADERS}
|
||||||
src/ArgumentParser.cpp
|
src/ArgumentParser.cpp
|
||||||
|
src/EventLoop.cpp
|
||||||
src/File.cpp
|
src/File.cpp
|
||||||
src/FileSystem.cpp
|
src/FileSystem.cpp
|
||||||
src/Process.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
|
#pragma once
|
||||||
#include <luna/HashMap.h>
|
#include <luna/HashMap.h>
|
||||||
|
#include <os/EventLoop.h>
|
||||||
#include <os/LocalClient.h>
|
#include <os/LocalClient.h>
|
||||||
#include <ui/Window.h>
|
#include <ui/Window.h>
|
||||||
|
|
||||||
@ -33,10 +34,9 @@ namespace ui
|
|||||||
void set_should_close(bool b)
|
void set_should_close(bool b)
|
||||||
{
|
{
|
||||||
m_should_close = b;
|
m_should_close = b;
|
||||||
|
if (b) m_loop.quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_nonblocking();
|
|
||||||
|
|
||||||
void set_main_window(Window* window)
|
void set_main_window(Window* window)
|
||||||
{
|
{
|
||||||
check(!m_main_window);
|
check(!m_main_window);
|
||||||
@ -63,6 +63,7 @@ namespace ui
|
|||||||
Window* m_main_window { nullptr };
|
Window* m_main_window { nullptr };
|
||||||
HashMap<int, OwnedPtr<Window>> m_windows;
|
HashMap<int, OwnedPtr<Window>> m_windows;
|
||||||
bool m_should_close { false };
|
bool m_should_close { false };
|
||||||
|
os::EventLoop m_loop;
|
||||||
|
|
||||||
Window* find_window(int id);
|
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);
|
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
|
namespace ui
|
||||||
{
|
{
|
||||||
App* App::s_app { nullptr };
|
App* App::s_app { nullptr };
|
||||||
@ -44,15 +50,16 @@ namespace ui
|
|||||||
parser.parse(argc, argv);
|
parser.parse(argc, argv);
|
||||||
|
|
||||||
m_client = TRY(os::LocalClient::connect(socket_path, true));
|
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 {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<int> App::run()
|
Result<int> App::run()
|
||||||
{
|
{
|
||||||
while (process_events())
|
return m_loop.run();
|
||||||
;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
App& App::the()
|
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()
|
bool App::process_events()
|
||||||
{
|
{
|
||||||
check(m_main_window);
|
check(m_main_window);
|
||||||
|
@ -7,7 +7,6 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
{
|
{
|
||||||
ui::App app;
|
ui::App app;
|
||||||
TRY(app.init(argc, argv));
|
TRY(app.init(argc, argv));
|
||||||
app.set_nonblocking();
|
|
||||||
|
|
||||||
auto* window = TRY(ui::Window::create(ui::Rect { 150, 150, 640, 400 }));
|
auto* window = TRY(ui::Window::create(ui::Rect { 150, 150, 640, 400 }));
|
||||||
app.set_main_window(window);
|
app.set_main_window(window);
|
||||||
|
Loading…
Reference in New Issue
Block a user