mirror of
https://github.com/hyprwm/hyprutils.git
synced 2025-12-20 05:50:11 +01:00
parent
d46bd32da5
commit
1b8090e5d8
5 changed files with 148 additions and 52 deletions
|
|
@ -6,39 +6,27 @@
|
|||
|
||||
namespace Hyprutils {
|
||||
namespace Signal {
|
||||
class CSignal;
|
||||
class CUntypedSignal;
|
||||
|
||||
class CSignalListener {
|
||||
public:
|
||||
CSignalListener(std::function<void(std::any)> handler);
|
||||
|
||||
CSignalListener(CSignalListener&&) = delete;
|
||||
CSignalListener(CSignalListener&) = delete;
|
||||
CSignalListener(const CSignalListener&) = delete;
|
||||
CSignalListener(const CSignalListener&&) = delete;
|
||||
|
||||
void emit(std::any data);
|
||||
[[deprecated("Relic of the legacy untyped signal API. Using this with CSignalT is undefined behavior.")]] void emit(std::any data);
|
||||
|
||||
private:
|
||||
std::function<void(std::any)> m_fHandler;
|
||||
CSignalListener(std::function<void(void*)> handler);
|
||||
|
||||
void emitInternal(void* args);
|
||||
|
||||
std::function<void(void*)> m_fHandler;
|
||||
|
||||
friend class CUntypedSignal;
|
||||
};
|
||||
|
||||
typedef Hyprutils::Memory::CSharedPointer<CSignalListener> CHyprSignalListener;
|
||||
|
||||
class CStaticSignalListener {
|
||||
public:
|
||||
CStaticSignalListener(std::function<void(void*, std::any)> handler, void* owner);
|
||||
|
||||
CStaticSignalListener(CStaticSignalListener&&) = delete;
|
||||
CStaticSignalListener(CStaticSignalListener&) = delete;
|
||||
CStaticSignalListener(const CStaticSignalListener&) = delete;
|
||||
CStaticSignalListener(const CStaticSignalListener&&) = delete;
|
||||
|
||||
void emit(std::any data);
|
||||
|
||||
private:
|
||||
void* m_pOwner = nullptr;
|
||||
std::function<void(void*, std::any)> m_fHandler;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,25 +4,51 @@
|
|||
#include <any>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
#include <hyprutils/memory/WeakPtr.hpp>
|
||||
#include "./Listener.hpp"
|
||||
|
||||
namespace Hyprutils {
|
||||
namespace Signal {
|
||||
class CSignal {
|
||||
public:
|
||||
void emit(std::any data = {});
|
||||
class CUntypedSignal {
|
||||
protected:
|
||||
CHyprSignalListener registerListenerInternal(std::function<void(void*)> handler);
|
||||
void registerStaticListenerInternal(std::function<void(void*)> handler);
|
||||
void emitInternal(void* args);
|
||||
|
||||
//
|
||||
[[nodiscard("Listener is unregistered when the ptr is lost")]] CHyprSignalListener registerListener(std::function<void(std::any)> handler);
|
||||
std::vector<Hyprutils::Memory::CWeakPointer<CSignalListener>> m_vListeners;
|
||||
std::vector<std::unique_ptr<CSignalListener>> m_vStaticListeners;
|
||||
};
|
||||
|
||||
template <typename... Args>
|
||||
class CSignalT : public CUntypedSignal {
|
||||
public:
|
||||
void emit(Args... args) {
|
||||
auto argsTuple = std::make_tuple(args...);
|
||||
emitInternal(&argsTuple);
|
||||
}
|
||||
|
||||
[[nodiscard("Listener is unregistered when the ptr is lost")]] CHyprSignalListener registerListener(std::function<void(Args...)> handler) {
|
||||
return registerListenerInternal([handler](void* argsPtr) { std::apply(handler, *static_cast<std::tuple<Args...>*>(argsPtr)); });
|
||||
}
|
||||
|
||||
// this is for static listeners. They die with this signal.
|
||||
// TODO: can we somehow rid of the void* data and make it a custom this?
|
||||
void registerStaticListener(std::function<void(void*, std::any)> handler, void* owner);
|
||||
void registerStaticListener(std::function<void(Args...)> handler) {
|
||||
registerStaticListenerInternal([handler](void* argsPtr) { std::apply(handler, *static_cast<std::tuple<Args...>*>(argsPtr)); });
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<Hyprutils::Memory::CWeakPointer<CSignalListener>> m_vListeners;
|
||||
std::vector<std::unique_ptr<CStaticSignalListener>> m_vStaticListeners;
|
||||
template <typename Owner>
|
||||
void registerStaticListener(std::function<void(Owner*, Args...)> handler, Owner* owner) {
|
||||
registerStaticListener([owner, handler](Args... args) { handler(owner, args...); });
|
||||
}
|
||||
};
|
||||
|
||||
// compat
|
||||
class CSignal : public CSignalT<std::any> {
|
||||
public:
|
||||
void emit(std::any data = {});
|
||||
[[nodiscard("Listener is unregistered when the ptr is lost")]] CHyprSignalListener registerListener(std::function<void(std::any)> handler);
|
||||
void registerStaticListener(std::function<void(void*, std::any)> handler, void* owner);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +1,20 @@
|
|||
#include <hyprutils/signal/Listener.hpp>
|
||||
#include <tuple>
|
||||
|
||||
using namespace Hyprutils::Signal;
|
||||
|
||||
Hyprutils::Signal::CSignalListener::CSignalListener(std::function<void(std::any)> handler) : m_fHandler(handler) {
|
||||
Hyprutils::Signal::CSignalListener::CSignalListener(std::function<void(void*)> handler) : m_fHandler(handler) {
|
||||
;
|
||||
}
|
||||
|
||||
void Hyprutils::Signal::CSignalListener::emit(std::any data) {
|
||||
void Hyprutils::Signal::CSignalListener::emitInternal(void* data) {
|
||||
if (!m_fHandler)
|
||||
return;
|
||||
|
||||
m_fHandler(data);
|
||||
}
|
||||
|
||||
Hyprutils::Signal::CStaticSignalListener::CStaticSignalListener(std::function<void(void*, std::any)> handler, void* owner) : m_pOwner(owner), m_fHandler(handler) {
|
||||
;
|
||||
}
|
||||
|
||||
void Hyprutils::Signal::CStaticSignalListener::emit(std::any data) {
|
||||
m_fHandler(m_pOwner, data);
|
||||
void Hyprutils::Signal::CSignalListener::emit(std::any data) {
|
||||
auto dataTuple = std::tuple<std::any>(data);
|
||||
emitInternal(static_cast<void*>(&dataTuple));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ using namespace Hyprutils::Memory;
|
|||
#define SP CSharedPointer
|
||||
#define WP CWeakPointer
|
||||
|
||||
void Hyprutils::Signal::CSignal::emit(std::any data) {
|
||||
void Hyprutils::Signal::CUntypedSignal::emitInternal(void* args) {
|
||||
std::vector<SP<CSignalListener>> listeners;
|
||||
for (auto& l : m_vListeners) {
|
||||
if (l.expired())
|
||||
|
|
@ -17,7 +17,7 @@ void Hyprutils::Signal::CSignal::emit(std::any data) {
|
|||
listeners.emplace_back(l.lock());
|
||||
}
|
||||
|
||||
std::vector<CStaticSignalListener*> statics;
|
||||
std::vector<CSignalListener*> statics;
|
||||
statics.reserve(m_vStaticListeners.size());
|
||||
for (auto& l : m_vStaticListeners) {
|
||||
statics.emplace_back(l.get());
|
||||
|
|
@ -29,11 +29,11 @@ void Hyprutils::Signal::CSignal::emit(std::any data) {
|
|||
if (l.strongRef() == 1)
|
||||
continue;
|
||||
|
||||
l->emit(data);
|
||||
l->emitInternal(args);
|
||||
}
|
||||
|
||||
for (auto& l : statics) {
|
||||
l->emit(data);
|
||||
l->emitInternal(args);
|
||||
}
|
||||
|
||||
// release SPs
|
||||
|
|
@ -43,8 +43,8 @@ void Hyprutils::Signal::CSignal::emit(std::any data) {
|
|||
// as such we'd be doing a UAF
|
||||
}
|
||||
|
||||
CHyprSignalListener Hyprutils::Signal::CSignal::registerListener(std::function<void(std::any)> handler) {
|
||||
CHyprSignalListener listener = makeShared<CSignalListener>(handler);
|
||||
CHyprSignalListener Hyprutils::Signal::CUntypedSignal::registerListenerInternal(std::function<void(void*)> handler) {
|
||||
CHyprSignalListener listener = SP<CSignalListener>(new CSignalListener(handler));
|
||||
m_vListeners.emplace_back(listener);
|
||||
|
||||
// housekeeping: remove any stale listeners
|
||||
|
|
@ -53,6 +53,18 @@ CHyprSignalListener Hyprutils::Signal::CSignal::registerListener(std::function<v
|
|||
return listener;
|
||||
}
|
||||
|
||||
void Hyprutils::Signal::CSignal::registerStaticListener(std::function<void(void*, std::any)> handler, void* owner) {
|
||||
m_vStaticListeners.emplace_back(std::make_unique<CStaticSignalListener>(handler, owner));
|
||||
void Hyprutils::Signal::CUntypedSignal::registerStaticListenerInternal(std::function<void(void*)> handler) {
|
||||
m_vStaticListeners.emplace_back(std::unique_ptr<CSignalListener>(new CSignalListener(handler)));
|
||||
}
|
||||
|
||||
void Hyprutils::Signal::CSignal::emit(std::any data) {
|
||||
CSignalT::emit(data);
|
||||
}
|
||||
|
||||
CHyprSignalListener Hyprutils::Signal::CSignal::registerListener(std::function<void(std::any)> handler) {
|
||||
return CSignalT::registerListener(handler);
|
||||
}
|
||||
|
||||
void Hyprutils::Signal::CSignal::registerStaticListener(std::function<void(void*, std::any)> handler, void* owner) {
|
||||
CSignalT<std::any>::registerStaticListener<void>(handler, owner);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#include <any>
|
||||
#include <hyprutils/signal/Signal.hpp>
|
||||
#include <hyprutils/memory/WeakPtr.hpp>
|
||||
#include "shared.hpp"
|
||||
|
|
@ -5,9 +6,7 @@
|
|||
using namespace Hyprutils::Signal;
|
||||
using namespace Hyprutils::Memory;
|
||||
|
||||
int main(int argc, char** argv, char** envp) {
|
||||
int ret = 0;
|
||||
|
||||
void legacy(int& ret) {
|
||||
CSignal signal;
|
||||
int data = 0;
|
||||
auto listener = signal.registerListener([&]([[maybe_unused]] std::any d) { data = 1; });
|
||||
|
|
@ -23,6 +22,79 @@ int main(int argc, char** argv, char** envp) {
|
|||
signal.emit();
|
||||
|
||||
EXPECT(data, 0);
|
||||
}
|
||||
|
||||
void legacyListenerEmit(int& ret) {
|
||||
int data = 0;
|
||||
CSignal signal;
|
||||
auto listener = signal.registerListener([&](std::any d) { data = std::any_cast<int>(d); });
|
||||
|
||||
listener->emit(1); // not a typo
|
||||
EXPECT(data, 1);
|
||||
}
|
||||
|
||||
void empty(int& ret) {
|
||||
int data = 0;
|
||||
|
||||
CSignalT<> signal;
|
||||
auto listener = signal.registerListener([&] { data = 1; });
|
||||
|
||||
signal.emit();
|
||||
EXPECT(data, 1);
|
||||
|
||||
data = 0;
|
||||
listener.reset();
|
||||
signal.emit();
|
||||
EXPECT(data, 0);
|
||||
}
|
||||
|
||||
void typed(int& ret) {
|
||||
int data = 0;
|
||||
|
||||
CSignalT<int> signal;
|
||||
auto listener = signal.registerListener([&](int newData) { data = newData; });
|
||||
|
||||
signal.emit(1);
|
||||
EXPECT(data, 1);
|
||||
}
|
||||
|
||||
void typedMany(int& ret) {
|
||||
int data1 = 0;
|
||||
int data2 = 0;
|
||||
int data3 = 0;
|
||||
|
||||
CSignalT<int, int, int> signal;
|
||||
auto listener = signal.registerListener([&](int d1, int d2, int d3) {
|
||||
data1 = d1;
|
||||
data2 = d2;
|
||||
data3 = d3;
|
||||
});
|
||||
|
||||
signal.emit(1, 2, 3);
|
||||
EXPECT(data1, 1);
|
||||
EXPECT(data2, 2);
|
||||
EXPECT(data3, 3);
|
||||
}
|
||||
|
||||
void staticListener(int& ret) {
|
||||
struct STestOwner {
|
||||
int data = 0;
|
||||
} owner;
|
||||
|
||||
CSignalT<int> signal;
|
||||
signal.registerStaticListener<STestOwner>([&](STestOwner* owner, int newData) { owner->data = newData; }, &owner);
|
||||
|
||||
signal.emit(1);
|
||||
EXPECT(owner.data, 1);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv, char** envp) {
|
||||
int ret = 0;
|
||||
legacy(ret);
|
||||
legacyListenerEmit(ret);
|
||||
empty(ret);
|
||||
typed(ret);
|
||||
typedMany(ret);
|
||||
staticListener(ret);
|
||||
return ret;
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue