rework event listeners to add different queues for listeners

This commit is contained in:
hjfod 2023-04-05 18:17:53 +03:00
parent ed8d230733
commit 993cd6fd7e
2 changed files with 109 additions and 57 deletions

View file

@ -10,6 +10,7 @@
namespace geode {
class Mod;
class Event;
class EventListenerProtocol;
Mod* getMod();
@ -18,10 +19,40 @@ namespace geode {
Stop
};
struct GEODE_DLL EventListenerPool {
virtual bool add(EventListenerProtocol* listener) = 0;
virtual void remove(EventListenerProtocol* listener) = 0;
virtual void handle(Event* event) = 0;
virtual ~EventListenerPool() = default;
EventListenerPool() = default;
EventListenerPool(EventListenerPool const&) = delete;
EventListenerPool(EventListenerPool&&) = delete;
static EventListenerPool* getDefault();
};
class GEODE_DLL DefaultEventListenerPool : public EventListenerPool {
protected:
std::atomic_size_t m_locked = 0;
std::vector<EventListenerProtocol*> m_listeners;
std::unordered_set<EventListenerProtocol*> m_toAdd;
std::unordered_set<EventListenerProtocol*> m_toRemove;
public:
bool add(EventListenerProtocol* listener) override;
void remove(EventListenerProtocol* listener) override;
void handle(Event* event) override;
static DefaultEventListenerPool* get();
};
struct GEODE_DLL EventListenerProtocol {
virtual void enable();
virtual void disable();
virtual ListenerResult passThrough(Event*) = 0;
bool enable();
void disable();
virtual EventListenerPool* getPool() const;
virtual ListenerResult handle(Event*) = 0;
virtual ~EventListenerProtocol();
};
@ -45,6 +76,10 @@ namespace geode {
ListenerResult handle(utils::MiniFunction<Callback> fn, T* e) {
return fn(e);
}
EventListenerPool* getPool() const {
return EventListenerPool::getDefault();
}
};
template <typename T>
@ -61,9 +96,8 @@ namespace geode {
requires std::is_class_v<C>
using MemberFn = typename to_member<C, Callback>::value;
ListenerResult passThrough(Event* e) override {
ListenerResult handle(Event* e) override {
if (m_callback) {
// it is so silly to use dynamic cast in an interbinary context
if (auto myev = cast::typeinfo_cast<typename T::Event*>(e)) {
return m_filter.handle(m_callback, myev);
}
@ -71,6 +105,10 @@ namespace geode {
return ListenerResult::Propagate;
}
EventListenerPool* getPool() const override {
return m_filter.getPool();
}
EventListener(T filter = T()) {
this->enable();
}
@ -137,7 +175,8 @@ namespace geode {
private:
friend EventListenerProtocol;
static std::unordered_set<EventListenerProtocol*>& removedListeners();
protected:
virtual EventListenerPool* getPool() const;
public:
Mod* sender;
@ -149,18 +188,5 @@ namespace geode {
}
virtual ~Event();
/**
* Get all active event listeners. You may use this to sort listeners
* that have an explicit order. You should never add/remove listeners
* manually however - use the enable() and disable() functions for that
* @warning Do not add/remove listeners manually
*/
static std::vector<EventListenerProtocol*>& listeners();
/**
* Move an event listener to the front of the queue so it is always hit
* first
*/
static void prioritize(EventListenerProtocol* listener);
};
}

View file

@ -1,17 +1,71 @@
#include <Geode/loader/Event.hpp>
#include <Geode/utils/ranges.hpp>
#include <mutex>
using namespace geode::prelude;
void EventListenerProtocol::enable() {
if (!ranges::contains(Event::listeners(), this)) {
Event::listeners().push_back(this);
bool DefaultEventListenerPool::add(EventListenerProtocol* listener) {
if (m_locked) {
m_toAdd.insert(listener);
}
else {
m_listeners.push_back(listener);
}
return true;
}
void DefaultEventListenerPool::remove(EventListenerProtocol* listener) {
if (m_locked) {
m_toRemove.insert(listener);
}
else {
ranges::remove(m_listeners, listener);
}
}
void DefaultEventListenerPool::handle(Event* event) {
m_locked += 1;
for (auto h : m_listeners) {
// if an event listener gets destroyed in the middle of this loop, we
// need to handle that
if (m_toRemove.contains(h)) continue;
if (h->handle(event) == ListenerResult::Stop) {
break;
}
}
m_locked -= 1;
// only clear listeners once nothing is iterating (if there are recursive handle calls)
if (m_locked == 0) {
for (auto listener : m_toAdd) {
m_listeners.push_back(listener);
}
for (auto listener : m_toRemove) {
ranges::remove(m_listeners, listener);
}
m_toAdd.clear();
m_toRemove.clear();
}
}
DefaultEventListenerPool* DefaultEventListenerPool::get() {
static auto inst = new DefaultEventListenerPool();
return inst;
}
EventListenerPool* EventListenerPool::getDefault() {
return DefaultEventListenerPool::get();
}
EventListenerPool* EventListenerProtocol::getPool() const {
return EventListenerPool::getDefault();
}
bool EventListenerProtocol::enable() {
return this->getPool()->add(this);
}
void EventListenerProtocol::disable() {
Event::removedListeners().insert(this);
ranges::remove(Event::listeners(), this);
this->getPool()->remove(this);
}
EventListenerProtocol::~EventListenerProtocol() {
@ -20,39 +74,11 @@ EventListenerProtocol::~EventListenerProtocol() {
Event::~Event() {}
EventListenerPool* Event::getPool() const {
return EventListenerPool::getDefault();
}
void Event::postFrom(Mod* m) {
if (m) this->sender = m;
auto& listeners = Event::listeners();
listeners.erase(std::remove_if(
listeners.begin(),
listeners.end(),
[](auto& a) {
return Event::removedListeners().contains(a);
}
), listeners.end());
Event::removedListeners().clear();
std::vector<EventListenerProtocol*> listeners_copy = Event::listeners();
for (auto h : listeners_copy) {
// if an event listener gets destroyed in the middle of this loop, we
// need to handle that
if (Event::removedListeners().count(h)) continue;
if (h->passThrough(this) == ListenerResult::Stop) {
break;
}
}
}
std::unordered_set<EventListenerProtocol*>& Event::removedListeners() {
static std::unordered_set<EventListenerProtocol*> listeners;
return listeners;
}
std::vector<EventListenerProtocol*>& Event::listeners() {
static std::vector<EventListenerProtocol*> listeners;
return listeners;
}
void Event::prioritize(EventListenerProtocol* listener) {
ranges::move(Event::listeners(), listener, 0);
this->getPool()->handle(this);
}