mirror of
https://github.com/geode-sdk/geode.git
synced 2024-11-23 07:57:51 -05:00
rework event listeners to add different queues for listeners
This commit is contained in:
parent
ed8d230733
commit
993cd6fd7e
2 changed files with 109 additions and 57 deletions
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue