mirror of
https://github.com/usatiuk/cardboy.git
synced 2025-10-28 23:27:49 +01:00
140 lines
3.5 KiB
C++
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
|