mirror of
https://github.com/hyprwm/hyprutils.git
synced 2025-12-20 07:00:10 +01:00
parent
d46bd32da5
commit
1b8090e5d8
5 changed files with 148 additions and 52 deletions
|
|
@ -6,39 +6,27 @@
|
||||||
|
|
||||||
namespace Hyprutils {
|
namespace Hyprutils {
|
||||||
namespace Signal {
|
namespace Signal {
|
||||||
class CSignal;
|
class CUntypedSignal;
|
||||||
|
|
||||||
class CSignalListener {
|
class CSignalListener {
|
||||||
public:
|
public:
|
||||||
CSignalListener(std::function<void(std::any)> handler);
|
|
||||||
|
|
||||||
CSignalListener(CSignalListener&&) = delete;
|
CSignalListener(CSignalListener&&) = delete;
|
||||||
CSignalListener(CSignalListener&) = delete;
|
CSignalListener(CSignalListener&) = delete;
|
||||||
CSignalListener(const CSignalListener&) = delete;
|
CSignalListener(const 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:
|
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;
|
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 <any>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <tuple>
|
||||||
#include <hyprutils/memory/WeakPtr.hpp>
|
#include <hyprutils/memory/WeakPtr.hpp>
|
||||||
#include "./Listener.hpp"
|
#include "./Listener.hpp"
|
||||||
|
|
||||||
namespace Hyprutils {
|
namespace Hyprutils {
|
||||||
namespace Signal {
|
namespace Signal {
|
||||||
class CSignal {
|
class CUntypedSignal {
|
||||||
public:
|
protected:
|
||||||
void emit(std::any data = {});
|
CHyprSignalListener registerListenerInternal(std::function<void(void*)> handler);
|
||||||
|
void registerStaticListenerInternal(std::function<void(void*)> handler);
|
||||||
|
void emitInternal(void* args);
|
||||||
|
|
||||||
//
|
std::vector<Hyprutils::Memory::CWeakPointer<CSignalListener>> m_vListeners;
|
||||||
[[nodiscard("Listener is unregistered when the ptr is lost")]] CHyprSignalListener registerListener(std::function<void(std::any)> handler);
|
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.
|
// 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(Args...)> handler) {
|
||||||
void registerStaticListener(std::function<void(void*, std::any)> handler, void* owner);
|
registerStaticListenerInternal([handler](void* argsPtr) { std::apply(handler, *static_cast<std::tuple<Args...>*>(argsPtr)); });
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
template <typename Owner>
|
||||||
std::vector<Hyprutils::Memory::CWeakPointer<CSignalListener>> m_vListeners;
|
void registerStaticListener(std::function<void(Owner*, Args...)> handler, Owner* owner) {
|
||||||
std::vector<std::unique_ptr<CStaticSignalListener>> m_vStaticListeners;
|
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 <hyprutils/signal/Listener.hpp>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
using namespace Hyprutils::Signal;
|
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)
|
if (!m_fHandler)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_fHandler(data);
|
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::CSignalListener::emit(std::any data) {
|
||||||
;
|
auto dataTuple = std::tuple<std::any>(data);
|
||||||
}
|
emitInternal(static_cast<void*>(&dataTuple));
|
||||||
|
|
||||||
void Hyprutils::Signal::CStaticSignalListener::emit(std::any data) {
|
|
||||||
m_fHandler(m_pOwner, data);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ using namespace Hyprutils::Memory;
|
||||||
#define SP CSharedPointer
|
#define SP CSharedPointer
|
||||||
#define WP CWeakPointer
|
#define WP CWeakPointer
|
||||||
|
|
||||||
void Hyprutils::Signal::CSignal::emit(std::any data) {
|
void Hyprutils::Signal::CUntypedSignal::emitInternal(void* args) {
|
||||||
std::vector<SP<CSignalListener>> listeners;
|
std::vector<SP<CSignalListener>> listeners;
|
||||||
for (auto& l : m_vListeners) {
|
for (auto& l : m_vListeners) {
|
||||||
if (l.expired())
|
if (l.expired())
|
||||||
|
|
@ -17,7 +17,7 @@ void Hyprutils::Signal::CSignal::emit(std::any data) {
|
||||||
listeners.emplace_back(l.lock());
|
listeners.emplace_back(l.lock());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<CStaticSignalListener*> statics;
|
std::vector<CSignalListener*> statics;
|
||||||
statics.reserve(m_vStaticListeners.size());
|
statics.reserve(m_vStaticListeners.size());
|
||||||
for (auto& l : m_vStaticListeners) {
|
for (auto& l : m_vStaticListeners) {
|
||||||
statics.emplace_back(l.get());
|
statics.emplace_back(l.get());
|
||||||
|
|
@ -29,11 +29,11 @@ void Hyprutils::Signal::CSignal::emit(std::any data) {
|
||||||
if (l.strongRef() == 1)
|
if (l.strongRef() == 1)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
l->emit(data);
|
l->emitInternal(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& l : statics) {
|
for (auto& l : statics) {
|
||||||
l->emit(data);
|
l->emitInternal(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
// release SPs
|
// release SPs
|
||||||
|
|
@ -43,8 +43,8 @@ void Hyprutils::Signal::CSignal::emit(std::any data) {
|
||||||
// as such we'd be doing a UAF
|
// as such we'd be doing a UAF
|
||||||
}
|
}
|
||||||
|
|
||||||
CHyprSignalListener Hyprutils::Signal::CSignal::registerListener(std::function<void(std::any)> handler) {
|
CHyprSignalListener Hyprutils::Signal::CUntypedSignal::registerListenerInternal(std::function<void(void*)> handler) {
|
||||||
CHyprSignalListener listener = makeShared<CSignalListener>(handler);
|
CHyprSignalListener listener = SP<CSignalListener>(new CSignalListener(handler));
|
||||||
m_vListeners.emplace_back(listener);
|
m_vListeners.emplace_back(listener);
|
||||||
|
|
||||||
// housekeeping: remove any stale listeners
|
// housekeeping: remove any stale listeners
|
||||||
|
|
@ -53,6 +53,18 @@ CHyprSignalListener Hyprutils::Signal::CSignal::registerListener(std::function<v
|
||||||
return listener;
|
return listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Hyprutils::Signal::CSignal::registerStaticListener(std::function<void(void*, std::any)> handler, void* owner) {
|
void Hyprutils::Signal::CUntypedSignal::registerStaticListenerInternal(std::function<void(void*)> handler) {
|
||||||
m_vStaticListeners.emplace_back(std::make_unique<CStaticSignalListener>(handler, owner));
|
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/signal/Signal.hpp>
|
||||||
#include <hyprutils/memory/WeakPtr.hpp>
|
#include <hyprutils/memory/WeakPtr.hpp>
|
||||||
#include "shared.hpp"
|
#include "shared.hpp"
|
||||||
|
|
@ -5,9 +6,7 @@
|
||||||
using namespace Hyprutils::Signal;
|
using namespace Hyprutils::Signal;
|
||||||
using namespace Hyprutils::Memory;
|
using namespace Hyprutils::Memory;
|
||||||
|
|
||||||
int main(int argc, char** argv, char** envp) {
|
void legacy(int& ret) {
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
CSignal signal;
|
CSignal signal;
|
||||||
int data = 0;
|
int data = 0;
|
||||||
auto listener = signal.registerListener([&]([[maybe_unused]] std::any d) { data = 1; });
|
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();
|
signal.emit();
|
||||||
|
|
||||||
EXPECT(data, 0);
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue