// // Created by Stepan Usatiuk on 26.07.2025. // #ifndef EVENT_HPP #define EVENT_HPP #include #include #include #include #include #include #include #include #include enum class EventHandlingResult { DONE, IGNORE, CONTINUE }; class Event {}; struct LoopQuitEvent : public Event {}; template concept IsEvent = std::is_base_of_v; template concept HasHandleFor = requires(H h, E e) { { h.handle(e) } -> std::same_as; }; template concept HandlesAll = (HasHandleFor && ...); template requires(IsEvent && ...) class EventHandler { public: EventHandler() { static_assert(HandlesAll); } }; class EventLoop; class EventQueueBase { public: virtual void process_events() = 0; virtual ~EventQueueBase() = default; }; template class EventQueue : public EventQueueBase { public: EventQueue(EventLoop* loop, HandlerType* handler) : _loop(loop), _handler(handler) {}; std::optional> poll(); void process_events() override { while (auto event = poll()) { std::visit([this](auto&& e) { _handler->handle(e); }, *event); } } template requires std::disjunction_v...> void push(T&& event); private: EventLoop* _loop; HandlerType* _handler; std::list> _events; }; class EventLoop : EventHandler, public EventQueue { public: EventLoop() : EventQueue(this, this) {} template void notify_pending(EventQueue* queue) { std::lock_guard 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 after_callback) { while (_running) { std::list new_events; { std::unique_lock 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 _events; std::mutex _mutex; std::condition_variable _condition; bool _running = true; }; template std::optional> EventQueue::poll() { if (_events.empty()) { return std::nullopt; } auto event = std::move(_events.front()); _events.pop_front(); return event; } template template requires std::disjunction_v...> void EventQueue::push(T&& event) { _events.emplace_back(std::forward(event)); _loop->notify_pending(static_cast(this)); } #endif // EVENT_HPP