Files
cardboy/Firmware/sdk/library/include_public/Event.hpp

140 lines
3.5 KiB
C++

//
// Created by Stepan Usatiuk on 26.07.2025.
//
#ifndef EVENT_HPP
#define EVENT_HPP
#include <algorithm>
#include <concepts>
#include <condition_variable>
#include <list>
#include <mutex>
#include <optional>
#include <type_traits>
#include <variant>
#include <functional>
enum class EventHandlingResult { DONE, IGNORE, CONTINUE };
class Event {};
struct LoopQuitEvent : public Event {};
template<typename T>
concept IsEvent = std::is_base_of_v<Event, T>;
template<typename H, typename E>
concept HasHandleFor = requires(H h, E e) {
{ h.handle(e) } -> std::same_as<EventHandlingResult>;
};
template<typename H, typename... Ts>
concept HandlesAll = (HasHandleFor<H, Ts> && ...);
template<typename Derived, typename... T>
requires(IsEvent<T> && ...)
class EventHandler {
public:
EventHandler() { static_assert(HandlesAll<Derived, T...>); }
};
class EventLoop;
class EventQueueBase {
public:
virtual void process_events() = 0;
virtual ~EventQueueBase() = default;
};
template<typename HandlerType, typename... Ts>
class EventQueue : public EventQueueBase {
public:
EventQueue(EventLoop* loop, HandlerType* handler) : _loop(loop), _handler(handler) {};
std::optional<std::variant<Ts...>> poll();
void process_events() override {
while (auto event = poll()) {
std::visit([this](auto&& e) { _handler->handle(e); }, *event);
}
}
template<typename T>
requires std::disjunction_v<std::is_same<T, Ts>...>
void push(T&& event);
private:
EventLoop* _loop;
HandlerType* _handler;
std::list<std::variant<Ts...>> _events;
};
class EventLoop : EventHandler<EventLoop, LoopQuitEvent>, public EventQueue<EventLoop, LoopQuitEvent> {
public:
EventLoop() : EventQueue<EventLoop, LoopQuitEvent>(this, this) {}
template<typename... Ts>
void notify_pending(EventQueue<Ts...>* queue) {
std::lock_guard<std::mutex> lock(_mutex);
// TODO:
if (std::find(_events.begin(), _events.end(), queue) != _events.end()) {
return; // Already registered
}
_events.push_back(queue);
_condition.notify_all();
}
void run(std::function<void()> after_callback) {
while (_running) {
std::list<EventQueueBase*> new_events;
{
std::unique_lock<std::mutex> lock(_mutex);
_condition.wait(lock, [this] { return !_events.empty() || !_running; });
std::swap(new_events, _events);
}
for (auto queue: new_events) {
queue->process_events();
}
after_callback();
}
}
EventHandlingResult handle(LoopQuitEvent event) {
_running = false;
_condition.notify_all();
return EventHandlingResult::DONE;
}
private:
std::list<EventQueueBase*> _events;
std::mutex _mutex;
std::condition_variable _condition;
bool _running = true;
};
template<typename HandlerType, typename... Ts>
std::optional<std::variant<Ts...>> EventQueue<HandlerType, Ts...>::poll() {
if (_events.empty()) {
return std::nullopt;
}
auto event = std::move(_events.front());
_events.pop_front();
return event;
}
template<typename HandlerType, typename... Ts>
template<typename T>
requires std::disjunction_v<std::is_same<T, Ts>...>
void EventQueue<HandlerType, Ts...>::push(T&& event) {
_events.emplace_back(std::forward<T>(event));
_loop->notify_pending(static_cast<HandlerType*>(this));
}
#endif // EVENT_HPP