Compare commits

..

13 commits

Author SHA1 Message Date
Tom Englund
51a4f93ce8
signals: check if listeners is empty in emitInternal (#96)
* signals: use tuple reference and check if listeners is empty

use forward_as_tuple as ít creates a temporary tuple reference instead
of copying/moving each argument. also if guard the emitInternal to only
create locals if there is actually any listeners.

* signal: revert forward_as_tuple

cant use references as signals expect a copy/move.
2025-12-20 17:56:12 +00:00
Tom Englund
5ac060bfcf
signal: check for trivially copyable (#95)
use is_trivially_copyable instead because is_arithmetic_v exludes small
POD structs, enums, and certain trivially copyable types.

also add a if guard in emit if we have no listeners, no point emitting.
2025-12-19 16:12:51 +00:00
1c527b30fe
cli/logger: flush stdout after logging 2025-12-17 20:09:42 +00:00
fe686486ac
version: bump to 0.11.0 2025-12-05 19:18:01 +00:00
2f2413801b
logger: don't crash on failing to print to stdout 2025-12-02 00:58:52 +00:00
Vaxry
9f8e158dbd
memory/shared: add dynamicPointerCast (#92) 2025-12-01 21:18:31 +00:00
EvilLary
7e6346f84b
i18n: fix typo in sorting entries (#94) 2025-11-30 00:54:28 +00:00
Maximilian Seidler
a64517c236
animation: allow/intend for animated vars to be unique pointers (#93) 2025-11-28 17:08:50 +00:00
0168583075
cli/argumentParser: improve formatting of description 2025-11-24 14:54:18 +00:00
5e1a14bc29
cli/argumentParser: allow empty short 2025-11-24 14:44:43 +00:00
bc9803c4b8
version: bump to 0.10.4 2025-11-24 14:13:22 +00:00
SASANO Takayoshi
44c2ba0354
src/cli/ArgumentParser.cpp: clang requires <algorithm> (#91) 2025-11-24 13:53:20 +00:00
96df6f6535
cli/logger: add redirection of connections 2025-11-23 18:30:59 +00:00
18 changed files with 335 additions and 106 deletions

View file

@ -52,7 +52,7 @@ target_include_directories(
PUBLIC "./include" PUBLIC "./include"
PRIVATE "./src") PRIVATE "./src")
set_target_properties(hyprutils PROPERTIES VERSION ${hyprutils_VERSION} set_target_properties(hyprutils PROPERTIES VERSION ${hyprutils_VERSION}
SOVERSION 9) SOVERSION 10)
target_link_libraries(hyprutils PkgConfig::deps) target_link_libraries(hyprutils PkgConfig::deps)
if(BUILD_TESTING) if(BUILD_TESTING)

View file

@ -1 +1 @@
0.10.3 0.11.0

View file

@ -22,6 +22,7 @@ namespace Hyprutils {
}; };
void create(CAnimationManager*, int, Memory::CSharedPointer<CBaseAnimatedVariable>); void create(CAnimationManager*, int, Memory::CSharedPointer<CBaseAnimatedVariable>);
void create2(CAnimationManager*, int, Memory::CWeakPointer<CBaseAnimatedVariable>);
void connectToActive(); void connectToActive();
void disconnectFromActive(); void disconnectFromActive();
@ -136,6 +137,7 @@ namespace Hyprutils {
public: public:
CGenericAnimatedVariable() = default; CGenericAnimatedVariable() = default;
/* Deprecated: use create2 */
void create(const int typeInfo, CAnimationManager* pAnimationManager, Memory::CSharedPointer<CGenericAnimatedVariable<VarType, AnimationContext>> pSelf, void create(const int typeInfo, CAnimationManager* pAnimationManager, Memory::CSharedPointer<CGenericAnimatedVariable<VarType, AnimationContext>> pSelf,
const VarType& initialValue) { const VarType& initialValue) {
m_Begun = initialValue; m_Begun = initialValue;
@ -145,6 +147,16 @@ namespace Hyprutils {
CBaseAnimatedVariable::create(pAnimationManager, typeInfo, pSelf); CBaseAnimatedVariable::create(pAnimationManager, typeInfo, pSelf);
} }
/* Equivalent to create, except that it allows animated variables to be UP's */
void create2(const int typeInfo, CAnimationManager* pAnimationManager, Memory::CWeakPointer<CGenericAnimatedVariable<VarType, AnimationContext>> pSelf,
const VarType& initialValue) {
m_Begun = initialValue;
m_Value = initialValue;
m_Goal = initialValue;
CBaseAnimatedVariable::create2(pAnimationManager, typeInfo, pSelf);
}
CGenericAnimatedVariable(const CGenericAnimatedVariable&) = delete; CGenericAnimatedVariable(const CGenericAnimatedVariable&) = delete;
CGenericAnimatedVariable(CGenericAnimatedVariable&&) = delete; CGenericAnimatedVariable(CGenericAnimatedVariable&&) = delete;
CGenericAnimatedVariable& operator=(const CGenericAnimatedVariable&) = delete; CGenericAnimatedVariable& operator=(const CGenericAnimatedVariable&) = delete;

View file

@ -87,6 +87,9 @@ namespace Hyprutils::CLI {
void log(eLogLevel level, const std::string_view& msg); void log(eLogLevel level, const std::string_view& msg);
CLogger* getLogger();
void redirect(CLogger& logger);
template <typename... Args> template <typename... Args>
// NOLINTNEXTLINE // NOLINTNEXTLINE
void log(eLogLevel level, std::format_string<Args...> fmt, Args&&... args) { void log(eLogLevel level, std::format_string<Args...> fmt, Args&&... args) {

View file

@ -54,11 +54,11 @@ namespace Hyprutils::Memory {
using validHierarchy = std::enable_if_t<std::is_assignable_v<CAtomicSharedPointer<T>&, X>, CAtomicSharedPointer&>; using validHierarchy = std::enable_if_t<std::is_assignable_v<CAtomicSharedPointer<T>&, X>, CAtomicSharedPointer&>;
public: public:
explicit CAtomicSharedPointer(T* object) noexcept : m_ptr(new Atomic_::impl(sc<void*>(object), _delete)) { explicit CAtomicSharedPointer(T* object) noexcept : m_ptr(new Atomic_::impl(sc<void*>(object), _delete), sc<void*>(object)) {
; ;
} }
CAtomicSharedPointer(Impl_::impl_base* impl) noexcept : m_ptr(impl) { CAtomicSharedPointer(Impl_::impl_base* impl, void* data) noexcept : m_ptr(impl, data) {
; ;
} }
@ -219,13 +219,17 @@ namespace Hyprutils::Memory {
return m_ptr.impl_ ? m_ptr.impl_->ref() : 0; return m_ptr.impl_ ? m_ptr.impl_->ref() : 0;
} }
Atomic_::impl* impl() const {
return sc<Atomic_::impl*>(m_ptr.impl_);
}
private: private:
static void _delete(void* p) { static void _delete(void* p) {
std::default_delete<T>{}(sc<T*>(p)); std::default_delete<T>{}(sc<T*>(p));
} }
std::lock_guard<std::recursive_mutex> implLockGuard() const { std::lock_guard<std::recursive_mutex> implLockGuard() const {
return sc<Atomic_::impl*>(m_ptr.impl_)->lockGuard(); return impl()->lockGuard();
} }
CSharedPointer<T> m_ptr; CSharedPointer<T> m_ptr;
@ -391,12 +395,16 @@ namespace Hyprutils::Memory {
if (!m_ptr.impl_->dataNonNull() || m_ptr.impl_->destroying() || !m_ptr.impl_->lockable()) if (!m_ptr.impl_->dataNonNull() || m_ptr.impl_->destroying() || !m_ptr.impl_->lockable())
return {}; return {};
return CAtomicSharedPointer<T>(m_ptr.impl_); return CAtomicSharedPointer<T>(m_ptr.impl_, m_ptr.m_data);
}
Atomic_::impl* impl() const {
return sc<Atomic_::impl*>(m_ptr.impl_);
} }
private: private:
std::lock_guard<std::recursive_mutex> implLockGuard() const { std::lock_guard<std::recursive_mutex> implLockGuard() const {
return sc<Atomic_::impl*>(m_ptr.impl_)->lockGuard(); return impl()->lockGuard();
} }
CWeakPointer<T> m_ptr; CWeakPointer<T> m_ptr;
@ -411,4 +419,19 @@ namespace Hyprutils::Memory {
[[nodiscard]] inline CAtomicSharedPointer<U> makeAtomicShared(Args&&... args) { [[nodiscard]] inline CAtomicSharedPointer<U> makeAtomicShared(Args&&... args) {
return CAtomicSharedPointer<U>(new U(std::forward<Args>(args)...)); return CAtomicSharedPointer<U>(new U(std::forward<Args>(args)...));
} }
template <typename T, typename U>
CAtomicSharedPointer<T> reinterpretPointerCast(const CAtomicSharedPointer<U>& ref) {
return CAtomicSharedPointer<T>(ref.impl(), ref.m_data);
}
template <typename T, typename U>
CAtomicSharedPointer<T> dynamicPointerCast(const CAtomicSharedPointer<U>& ref) {
if (!ref)
return nullptr;
T* newPtr = dynamic_cast<T*>(sc<U*>(ref.impl()->getData()));
if (!newPtr)
return nullptr;
return CAtomicSharedPointer<T>(ref.impl(), newPtr);
}
} }

View file

@ -28,31 +28,33 @@ namespace Hyprutils {
/* creates a new shared pointer managing a resource /* creates a new shared pointer managing a resource
avoid calling. Could duplicate ownership. Prefer makeShared */ avoid calling. Could duplicate ownership. Prefer makeShared */
explicit CSharedPointer(T* object) noexcept : impl_(new Impl_::impl_base(sc<void*>(object), _delete)) { explicit CSharedPointer(T* object) noexcept : impl_(new Impl_::impl_base(sc<void*>(object), _delete)), m_data(sc<void*>(object)) {
increment(); increment();
} }
/* creates a shared pointer from a reference */ /* creates a shared pointer from a reference */
template <typename U, typename = isConstructible<U>> template <typename U, typename = isConstructible<U>>
CSharedPointer(const CSharedPointer<U>& ref) noexcept : impl_(ref.impl_) { CSharedPointer(const CSharedPointer<U>& ref) noexcept : impl_(ref.impl_), m_data(ref.m_data) {
increment(); increment();
} }
CSharedPointer(const CSharedPointer& ref) noexcept : impl_(ref.impl_) { CSharedPointer(const CSharedPointer& ref) noexcept : impl_(ref.impl_), m_data(ref.m_data) {
increment(); increment();
} }
template <typename U, typename = isConstructible<U>> template <typename U, typename = isConstructible<U>>
CSharedPointer(CSharedPointer<U>&& ref) noexcept { CSharedPointer(CSharedPointer<U>&& ref) noexcept {
std::swap(impl_, ref.impl_); std::swap(impl_, ref.impl_);
std::swap(m_data, ref.m_data);
} }
CSharedPointer(CSharedPointer&& ref) noexcept { CSharedPointer(CSharedPointer&& ref) noexcept {
std::swap(impl_, ref.impl_); std::swap(impl_, ref.impl_);
std::swap(m_data, ref.m_data);
} }
/* allows weakPointer to create from an impl */ /* allows weakPointer to create from an impl */
CSharedPointer(Impl_::impl_base* implementation) noexcept : impl_(implementation) { CSharedPointer(Impl_::impl_base* implementation, void* data) noexcept : impl_(implementation), m_data(data) {
increment(); increment();
} }
@ -75,6 +77,7 @@ namespace Hyprutils {
decrement(); decrement();
impl_ = rhs.impl_; impl_ = rhs.impl_;
m_data = rhs.m_data;
increment(); increment();
return *this; return *this;
} }
@ -85,6 +88,7 @@ namespace Hyprutils {
decrement(); decrement();
impl_ = rhs.impl_; impl_ = rhs.impl_;
m_data = rhs.m_data;
increment(); increment();
return *this; return *this;
} }
@ -92,11 +96,13 @@ namespace Hyprutils {
template <typename U> template <typename U>
validHierarchy<const CSharedPointer<U>&> operator=(CSharedPointer<U>&& rhs) { validHierarchy<const CSharedPointer<U>&> operator=(CSharedPointer<U>&& rhs) {
std::swap(impl_, rhs.impl_); std::swap(impl_, rhs.impl_);
std::swap(m_data, rhs.m_data);
return *this; return *this;
} }
CSharedPointer& operator=(CSharedPointer&& rhs) noexcept { CSharedPointer& operator=(CSharedPointer&& rhs) noexcept {
std::swap(impl_, rhs.impl_); std::swap(impl_, rhs.impl_);
std::swap(m_data, rhs.m_data);
return *this; return *this;
} }
@ -104,6 +110,8 @@ namespace Hyprutils {
return impl_ && impl_->dataNonNull(); return impl_ && impl_->dataNonNull();
} }
// this compares that the pointed-to object is the same, but in multiple inheritance,
// different typed pointers can be equal if the object is the same
bool operator==(const CSharedPointer& rhs) const { bool operator==(const CSharedPointer& rhs) const {
return impl_ == rhs.impl_; return impl_ == rhs.impl_;
} }
@ -127,10 +135,11 @@ namespace Hyprutils {
void reset() { void reset() {
decrement(); decrement();
impl_ = nullptr; impl_ = nullptr;
m_data = nullptr;
} }
T* get() const { T* get() const {
return impl_ ? sc<T*>(impl_->getData()) : nullptr; return impl_ && impl_->dataNonNull() ? sc<T*>(m_data) : nullptr;
} }
unsigned int strongRef() const { unsigned int strongRef() const {
@ -139,6 +148,9 @@ namespace Hyprutils {
Impl_::impl_base* impl_ = nullptr; Impl_::impl_base* impl_ = nullptr;
// Never use directly: raw data ptr, could be UAF
void* m_data = nullptr;
private: private:
static void _delete(void* p) { static void _delete(void* p) {
std::default_delete<T>{}(sc<T*>(p)); std::default_delete<T>{}(sc<T*>(p));
@ -188,7 +200,17 @@ namespace Hyprutils {
template <typename T, typename U> template <typename T, typename U>
CSharedPointer<T> reinterpretPointerCast(const CSharedPointer<U>& ref) { CSharedPointer<T> reinterpretPointerCast(const CSharedPointer<U>& ref) {
return CSharedPointer<T>(ref.impl_); return CSharedPointer<T>(ref.impl_, ref.m_data);
}
template <typename T, typename U>
CSharedPointer<T> dynamicPointerCast(const CSharedPointer<U>& ref) {
if (!ref)
return nullptr;
T* newPtr = dynamic_cast<T*>(sc<U*>(ref.impl_->getData()));
if (!newPtr)
return nullptr;
return CSharedPointer<T>(ref.impl_, newPtr);
} }
} }
} }

View file

@ -27,6 +27,7 @@ namespace Hyprutils {
return; return;
impl_ = ref.impl_; impl_ = ref.impl_;
m_data = ref.m_data;
incrementWeak(); incrementWeak();
} }
@ -37,6 +38,7 @@ namespace Hyprutils {
return; return;
impl_ = ref.impl_; impl_ = ref.impl_;
m_data = ref.impl_->getData();
incrementWeak(); incrementWeak();
} }
@ -47,6 +49,7 @@ namespace Hyprutils {
return; return;
impl_ = ref.impl_; impl_ = ref.impl_;
m_data = ref.m_data;
incrementWeak(); incrementWeak();
} }
@ -55,16 +58,19 @@ namespace Hyprutils {
return; return;
impl_ = ref.impl_; impl_ = ref.impl_;
m_data = ref.m_data;
incrementWeak(); incrementWeak();
} }
template <typename U, typename = isConstructible<U>> template <typename U, typename = isConstructible<U>>
CWeakPointer(CWeakPointer<U>&& ref) noexcept { CWeakPointer(CWeakPointer<U>&& ref) noexcept {
std::swap(impl_, ref.impl_); std::swap(impl_, ref.impl_);
std::swap(m_data, ref.m_data);
} }
CWeakPointer(CWeakPointer&& ref) noexcept { CWeakPointer(CWeakPointer&& ref) noexcept {
std::swap(impl_, ref.impl_); std::swap(impl_, ref.impl_);
std::swap(m_data, ref.m_data);
} }
/* create a weak ptr from another weak ptr with assignment */ /* create a weak ptr from another weak ptr with assignment */
@ -75,6 +81,7 @@ namespace Hyprutils {
decrementWeak(); decrementWeak();
impl_ = rhs.impl_; impl_ = rhs.impl_;
m_data = rhs.m_data;
incrementWeak(); incrementWeak();
return *this; return *this;
} }
@ -85,6 +92,7 @@ namespace Hyprutils {
decrementWeak(); decrementWeak();
impl_ = rhs.impl_; impl_ = rhs.impl_;
m_data = rhs.m_data;
incrementWeak(); incrementWeak();
return *this; return *this;
} }
@ -97,6 +105,7 @@ namespace Hyprutils {
decrementWeak(); decrementWeak();
impl_ = rhs.impl_; impl_ = rhs.impl_;
m_data = rhs.m_data;
incrementWeak(); incrementWeak();
return *this; return *this;
} }
@ -126,13 +135,14 @@ namespace Hyprutils {
void reset() { void reset() {
decrementWeak(); decrementWeak();
impl_ = nullptr; impl_ = nullptr;
m_data = nullptr;
} }
CSharedPointer<T> lock() const { CSharedPointer<T> lock() const {
if (!impl_ || !impl_->dataNonNull() || impl_->destroying() || !impl_->lockable()) if (!impl_ || !impl_->dataNonNull() || impl_->destroying() || !impl_->lockable())
return {}; return {};
return CSharedPointer<T>(impl_); return CSharedPointer<T>(impl_, m_data);
} }
/* this returns valid() */ /* this returns valid() */
@ -169,7 +179,7 @@ namespace Hyprutils {
} }
T* get() const { T* get() const {
return impl_ ? sc<T*>(impl_->getData()) : nullptr; return impl_ && impl_->dataNonNull() ? sc<T*>(m_data) : nullptr;
} }
T* operator->() const { T* operator->() const {
@ -182,6 +192,9 @@ namespace Hyprutils {
Impl_::impl_base* impl_ = nullptr; Impl_::impl_base* impl_ = nullptr;
// Never use directly: raw data ptr, could be UAF
void* m_data = nullptr;
private: private:
/* no-op if there is no impl_ */ /* no-op if there is no impl_ */
void decrementWeak() { void decrementWeak() {
@ -207,6 +220,16 @@ namespace Hyprutils {
impl_->incWeak(); impl_->incWeak();
} }
}; };
template <typename T, typename U>
CWeakPointer<T> dynamicPointerCast(const CWeakPointer<U>& ref) {
if (!ref)
return nullptr;
T* newPtr = dynamic_cast<T*>(sc<U*>(ref.impl_->getData()));
if (!newPtr)
return nullptr;
return CWeakPointer<T>(ref.impl_, newPtr);
}
} }
} }

View file

@ -26,10 +26,13 @@ namespace Hyprutils {
template <typename... Args> template <typename... Args>
class CSignalT : public CSignalBase { class CSignalT : public CSignalBase {
template <typename T> template <typename T>
using RefArg = std::conditional_t<std::is_reference_v<T> || std::is_arithmetic_v<T>, T, const T&>; using RefArg = std::conditional_t<std::is_trivially_copyable_v<T>, T, const T&>;
public: public:
void emit(RefArg<Args>... args) { void emit(RefArg<Args>... args) {
if (m_vListeners.empty() && m_vStaticListeners.empty())
return;
if constexpr (sizeof...(Args) == 0) if constexpr (sizeof...(Args) == 0)
emitInternal(nullptr); emitInternal(nullptr);
else { else {

View file

@ -13,7 +13,16 @@ static const std::string DEFAULTSTYLE = "";
void CBaseAnimatedVariable::create(CAnimationManager* pManager, int typeInfo, SP<CBaseAnimatedVariable> pSelf) { void CBaseAnimatedVariable::create(CAnimationManager* pManager, int typeInfo, SP<CBaseAnimatedVariable> pSelf) {
m_Type = typeInfo; m_Type = typeInfo;
m_pSelf = pSelf; m_pSelf = std::move(pSelf);
m_pAnimationManager = pManager;
m_pSignals = pManager->getSignals();
m_bDummy = false;
}
void CBaseAnimatedVariable::create2(CAnimationManager* pManager, int typeInfo, WP<CBaseAnimatedVariable> pSelf) {
m_Type = typeInfo;
m_pSelf = std::move(pSelf);
m_pAnimationManager = pManager; m_pAnimationManager = pManager;
m_pSignals = pManager->getSignals(); m_pSignals = pManager->getSignals();
@ -37,28 +46,22 @@ void CBaseAnimatedVariable::disconnectFromActive() {
} }
bool Hyprutils::Animation::CBaseAnimatedVariable::enabled() const { bool Hyprutils::Animation::CBaseAnimatedVariable::enabled() const {
if (const auto PCONFIG = m_pConfig.lock()) { if (m_pConfig && m_pConfig->pValues)
const auto PVALUES = PCONFIG->pValues.lock(); return m_pConfig->pValues->internalEnabled;
return PVALUES ? PVALUES->internalEnabled : false;
}
return false; return false;
} }
const std::string& CBaseAnimatedVariable::getBezierName() const { const std::string& CBaseAnimatedVariable::getBezierName() const {
if (const auto PCONFIG = m_pConfig.lock()) { if (m_pConfig && m_pConfig->pValues)
const auto PVALUES = PCONFIG->pValues.lock(); return m_pConfig->pValues->internalBezier;
return PVALUES ? PVALUES->internalBezier : DEFAULTBEZIERNAME;
}
return DEFAULTBEZIERNAME; return DEFAULTBEZIERNAME;
} }
const std::string& CBaseAnimatedVariable::getStyle() const { const std::string& CBaseAnimatedVariable::getStyle() const {
if (const auto PCONFIG = m_pConfig.lock()) { if (m_pConfig && m_pConfig->pValues)
const auto PVALUES = PCONFIG->pValues.lock(); return m_pConfig->pValues->internalStyle;
return PVALUES ? PVALUES->internalStyle : DEFAULTSTYLE;
}
return DEFAULTSTYLE; return DEFAULTSTYLE;
} }
@ -66,10 +69,8 @@ const std::string& CBaseAnimatedVariable::getStyle() const {
float CBaseAnimatedVariable::getPercent() const { float CBaseAnimatedVariable::getPercent() const {
const auto DURATIONPASSED = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - animationBegin).count(); const auto DURATIONPASSED = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - animationBegin).count();
if (const auto PCONFIG = m_pConfig.lock()) { if (m_pConfig && m_pConfig->pValues)
const auto PVALUES = PCONFIG->pValues.lock(); return std::clamp((DURATIONPASSED / 100.f) / m_pConfig->pValues->internalSpeed, 0.f, 1.f);
return PVALUES ? std::clamp((DURATIONPASSED / 100.f) / PVALUES->internalSpeed, 0.f, 1.f) : 1.f;
}
return 1.f; return 1.f;
} }
@ -78,14 +79,7 @@ float CBaseAnimatedVariable::getCurveValue() const {
if (!m_bIsBeingAnimated || isAnimationManagerDead()) if (!m_bIsBeingAnimated || isAnimationManagerDead())
return 1.f; return 1.f;
std::string bezierName = ""; const auto BEZIER = m_pAnimationManager->getBezier(getBezierName());
if (const auto PCONFIG = m_pConfig.lock()) {
const auto PVALUES = PCONFIG->pValues.lock();
if (PVALUES)
bezierName = PVALUES->internalBezier;
}
const auto BEZIER = m_pAnimationManager->getBezier(bezierName);
if (!BEZIER) if (!BEZIER)
return 1.f; return 1.f;

View file

@ -64,14 +64,13 @@ void CAnimationManager::rotateActive() {
std::vector<CWeakPointer<CBaseAnimatedVariable>> active; std::vector<CWeakPointer<CBaseAnimatedVariable>> active;
active.reserve(m_vActiveAnimatedVariables.size()); // avoid reallocations active.reserve(m_vActiveAnimatedVariables.size()); // avoid reallocations
for (auto const& av : m_vActiveAnimatedVariables) { for (auto const& av : m_vActiveAnimatedVariables) {
const auto PAV = av.lock(); if (!av)
if (!PAV)
continue; continue;
if (PAV->ok() && PAV->isBeingAnimated()) if (av->ok() && av->isBeingAnimated())
active.emplace_back(av); active.emplace_back(av);
else else
PAV->m_bIsConnectedToActive = false; av->m_bIsConnectedToActive = false;
} }
m_vActiveAnimatedVariables = std::move(active); m_vActiveAnimatedVariables = std::move(active);

View file

@ -2,6 +2,7 @@
#include <format> #include <format>
#include <vector> #include <vector>
#include <algorithm>
#include <hyprutils/string/String.hpp> #include <hyprutils/string/String.hpp>
#include <hyprutils/memory/Casts.hpp> #include <hyprutils/memory/Casts.hpp>
@ -95,12 +96,17 @@ CArgumentParserImpl::CArgumentParserImpl(const std::span<const char*>& args) {
} }
std::vector<SArgumentKey>::iterator CArgumentParserImpl::getValue(const std::string_view& sv) { std::vector<SArgumentKey>::iterator CArgumentParserImpl::getValue(const std::string_view& sv) {
if (sv.empty())
return m_values.end();
auto it = std::ranges::find_if(m_values, [&sv](const auto& e) { return e.full == sv || e.abbrev == sv; }); auto it = std::ranges::find_if(m_values, [&sv](const auto& e) { return e.full == sv || e.abbrev == sv; });
return it; return it;
} }
std::expected<void, std::string> CArgumentParserImpl::registerOption(const std::string_view& name, const std::string_view& abbrev, const std::string_view& description, std::expected<void, std::string> CArgumentParserImpl::registerOption(const std::string_view& name, const std::string_view& abbrev, const std::string_view& description,
eArgumentType type) { eArgumentType type) {
if (name.empty())
return std::unexpected("Name cannot be empty");
if (getValue(name) != m_values.end() || getValue(abbrev) != m_values.end()) if (getValue(name) != m_values.end() || getValue(abbrev) != m_values.end())
return std::unexpected("Value already exists"); return std::unexpected("Value already exists");
@ -247,12 +253,16 @@ std::string CArgumentParserImpl::getDescription(const std::string_view& header,
rolling += pad(maxArgWidth - lenUsed); rolling += pad(maxArgWidth - lenUsed);
lenUsed = maxArgWidth; lenUsed = maxArgWidth;
if (!v.abbrev.empty()) {
rolling += " -" + v.abbrev; rolling += " -" + v.abbrev;
lenUsed += 2 + v.abbrev.size(); lenUsed += 2 + v.abbrev.size();
rolling += " "; rolling += " ";
rolling += TYPE_STRS[v.argType]; rolling += TYPE_STRS[v.argType];
lenUsed += std::string_view{TYPE_STRS[v.argType]}.length() + 1; lenUsed += std::string_view{TYPE_STRS[v.argType]}.length() + 1;
rolling += pad(maxArgWidth + maxShortWidth - lenUsed); rolling += pad(maxArgWidth + maxShortWidth - lenUsed);
} else
rolling += pad(maxShortWidth);
lenUsed = maxArgWidth + maxShortWidth; lenUsed = maxArgWidth + maxShortWidth;
rolling += " | "; rolling += " | ";
@ -269,7 +279,7 @@ std::string CArgumentParserImpl::getDescription(const std::string_view& header,
for (size_t i = 1; i < ROWS.size(); ++i) { for (size_t i = 1; i < ROWS.size(); ++i) {
lenUsed = LEN_START_DESC; lenUsed = LEN_START_DESC;
rolling += ""; rolling += "";
rolling += pad(LEN_START_DESC); rolling += pad(LEN_START_DESC);
rolling += ROWS[i]; rolling += ROWS[i];
lenUsed += ROWS[i].size(); lenUsed += ROWS[i].size();

View file

@ -131,8 +131,14 @@ void CLoggerImpl::log(eLogLevel level, const std::string_view& msg, const std::s
logMsg += "]: "; logMsg += "]: ";
logMsg += msg; logMsg += msg;
if (m_stdoutEnabled) if (m_stdoutEnabled) {
try {
std::println("{}{}", m_colorEnabled ? logPrefixColor : logPrefix, logMsg); std::println("{}{}", m_colorEnabled ? logPrefixColor : logPrefix, logMsg);
std::fflush(stdout);
} catch (std::exception& e) {
; // this could be e.g. stdout closed
}
}
if (m_fileEnabled) if (m_fileEnabled)
m_logOfs << logPrefix << logMsg << "\n"; m_logOfs << logPrefix << logMsg << "\n";
@ -179,3 +185,15 @@ void CLoggerConnection::log(eLogLevel level, const std::string_view& msg) {
m_impl->log(level, msg, m_name); m_impl->log(level, msg, m_name);
} }
CLogger* CLoggerConnection::getLogger() {
if (!m_impl)
return nullptr;
return m_logger;
}
void CLoggerConnection::redirect(CLogger& logger) {
m_impl = logger.m_impl;
m_logger = &logger;
}

View file

@ -131,7 +131,7 @@ std::string CI18nEngine::localizeEntry(const std::string& locale, uint64_t key,
return std::string{rawStr}; return std::string{rawStr};
// build the new string. First, sort our entries // build the new string. First, sort our entries
std::ranges::sort(rangesFound, [](const auto& a, const auto& b) { return a.begin - b.begin; }); std::ranges::sort(rangesFound, [](const auto& a, const auto& b) { return a.begin < b.begin; });
// calc the size // calc the size
size_t stringLen = 0; size_t stringLen = 0;

View file

@ -10,18 +10,18 @@ using namespace Hyprutils::Memory;
#define WP CWeakPointer #define WP CWeakPointer
void Hyprutils::Signal::CSignalBase::emitInternal(void* args) { void Hyprutils::Signal::CSignalBase::emitInternal(void* args) {
if (!m_vListeners.empty()) {
std::vector<SP<CSignalListener>> listeners; std::vector<SP<CSignalListener>> listeners;
listeners.reserve(m_vListeners.size()); listeners.reserve(m_vListeners.size());
for (auto& l : m_vListeners) {
for (const auto& l : m_vListeners) {
if (l.expired()) if (l.expired())
continue; continue;
listeners.emplace_back(l.lock()); listeners.emplace_back(l.lock());
} }
auto statics = m_vStaticListeners; for (const auto& l : listeners) {
for (auto& l : listeners) {
// if there is only one lock, it means the event is only held by the listeners // if there is only one lock, it means the event is only held by the listeners
// vector and was removed during our iteration // vector and was removed during our iteration
if (l.strongRef() == 1) if (l.strongRef() == 1)
@ -30,12 +30,17 @@ void Hyprutils::Signal::CSignalBase::emitInternal(void* args) {
l->emitInternal(args); l->emitInternal(args);
} }
for (auto& l : statics) {
l->emitInternal(args);
}
// release SPs // release SPs
listeners.clear(); listeners.clear();
}
if (!m_vStaticListeners.empty()) {
const auto statics = m_vStaticListeners;
for (const auto& l : statics) {
l->emitInternal(args);
}
}
// we cannot release any expired refs here as one of the listeners could've removed this object and // we cannot release any expired refs here as one of the listeners could've removed this object and
// as such we'd be doing a UAF // as such we'd be doing a UAF

View file

@ -21,7 +21,7 @@ template <typename VarType>
using CAnimatedVariable = CGenericAnimatedVariable<VarType, EmtpyContext>; using CAnimatedVariable = CGenericAnimatedVariable<VarType, EmtpyContext>;
template <typename VarType> template <typename VarType>
using PANIMVAR = SP<CAnimatedVariable<VarType>>; using PANIMVAR = UP<CAnimatedVariable<VarType>>;
template <typename VarType> template <typename VarType>
using PANIMVARREF = WP<CAnimatedVariable<VarType>>; using PANIMVARREF = WP<CAnimatedVariable<VarType>>;
@ -47,8 +47,7 @@ CAnimationConfigTree animationTree;
class CMyAnimationManager : public CAnimationManager { class CMyAnimationManager : public CAnimationManager {
public: public:
void tick() { void tick() {
for (size_t i = 0; i < m_vActiveAnimatedVariables.size(); i++) { for (const auto& PAV : m_vActiveAnimatedVariables) {
const auto PAV = m_vActiveAnimatedVariables[i].lock();
if (!PAV || !PAV->ok() || !PAV->isBeingAnimated()) if (!PAV || !PAV->ok() || !PAV->isBeingAnimated())
continue; continue;
@ -68,7 +67,7 @@ class CMyAnimationManager : public CAnimationManager {
if (!avInt) if (!avInt)
std::cout << "Dynamic cast upcast failed\n"; std::cout << "Dynamic cast upcast failed\n";
const auto DELTA = avInt->goal() - avInt->value(); const auto DELTA = avInt->goal() - avInt->begun();
avInt->value() = avInt->begun() + (DELTA * POINTY); avInt->value() = avInt->begun() + (DELTA * POINTY);
} break; } break;
case eAVTypes::TEST: { case eAVTypes::TEST: {
@ -93,11 +92,10 @@ class CMyAnimationManager : public CAnimationManager {
template <typename VarType> template <typename VarType>
void createAnimation(const VarType& v, PANIMVAR<VarType>& av, const std::string& animationConfigName) { void createAnimation(const VarType& v, PANIMVAR<VarType>& av, const std::string& animationConfigName) {
constexpr const eAVTypes EAVTYPE = std::is_same_v<VarType, int> ? eAVTypes::INT : eAVTypes::TEST; constexpr const eAVTypes EAVTYPE = std::is_same_v<VarType, int> ? eAVTypes::INT : eAVTypes::TEST;
const auto PAV = makeShared<CGenericAnimatedVariable<VarType, EmtpyContext>>(); av = makeUnique<CGenericAnimatedVariable<VarType, EmtpyContext>>();
PAV->create(EAVTYPE, sc<CAnimationManager*>(this), PAV, v); av->create2(EAVTYPE, sc<CAnimationManager*>(this), av, v);
PAV->setConfig(animationTree.getConfig(animationConfigName)); av->setConfig(animationTree.getConfig(animationConfigName));
av = std::move(PAV);
} }
virtual void scheduleTick() { virtual void scheduleTick() {
@ -298,7 +296,7 @@ TEST(Animation, animation) {
// test adding / removing vars during a tick // test adding / removing vars during a tick
s.m_iA->resetAllCallbacks(); s.m_iA->resetAllCallbacks();
s.m_iA->setUpdateCallback([&vars](WP<CBaseAnimatedVariable> v) { s.m_iA->setUpdateCallback([&vars](WP<CBaseAnimatedVariable> v) {
if (v.lock() != vars.back()) if (v.get() != vars.back().get())
vars.back()->warp(); vars.back()->warp();
}); });
s.m_iA->setCallbackOnEnd([&s, &vars](auto) { s.m_iA->setCallbackOnEnd([&s, &vars](auto) {
@ -350,7 +348,7 @@ TEST(Animation, animation) {
*s.m_iA = 5; *s.m_iA = 5;
s.m_iA->setCallbackOnEnd([&endCallbackRan](WP<CBaseAnimatedVariable> v) { s.m_iA->setCallbackOnEnd([&endCallbackRan](WP<CBaseAnimatedVariable> v) {
endCallbackRan++; endCallbackRan++;
const auto PAV = dc<CAnimatedVariable<int>*>(v.lock().get()); const auto PAV = dc<CAnimatedVariable<int>*>(v.get());
*PAV = 10; *PAV = 10;
PAV->setCallbackOnEnd([&endCallbackRan](WP<CBaseAnimatedVariable> v) { endCallbackRan++; }); PAV->setCallbackOnEnd([&endCallbackRan](WP<CBaseAnimatedVariable> v) { endCallbackRan++; });

View file

@ -10,12 +10,12 @@ using namespace Hyprutils;
constexpr const char* DESC_TEST = R"#(┏ My description constexpr const char* DESC_TEST = R"#(┏ My description
--hello -h | Says hello --hello -h | Says hello
--hello2 -e | Says hello 2 --hello2 | Says hello 2
--value -v [float] | Sets a valueeeeeee --value -v [float] | Sets a valueeeeeee
--longlonglonglongintopt -l [int] | Long long --longlonglonglongintopt -l [int] | Long long
maaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa maaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaan maaan man maaan man maaan aaaaaaaaaaan maaan man maaan man maaan
man maaan man man maaan man
)#"; )#";
@ -25,7 +25,7 @@ TEST(CLI, ArgumentParser) {
CArgumentParser parser(argv); CArgumentParser parser(argv);
EXPECT_TRUE(parser.registerBoolOption("hello", "h", "Says hello")); EXPECT_TRUE(parser.registerBoolOption("hello", "h", "Says hello"));
EXPECT_TRUE(parser.registerBoolOption("hello2", "e", "Says hello 2")); EXPECT_TRUE(parser.registerBoolOption("hello2", "", "Says hello 2"));
EXPECT_TRUE(parser.registerFloatOption("value", "v", "Sets a valueeeeeee")); EXPECT_TRUE(parser.registerFloatOption("value", "v", "Sets a valueeeeeee"));
EXPECT_TRUE(parser.registerIntOption("longlonglonglongintopt", "l", "Long long maaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaan maaan man maaan man maaan man maaan man")); EXPECT_TRUE(parser.registerIntOption("longlonglonglongintopt", "l", "Long long maaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaan maaan man maaan man maaan man maaan man"));
@ -49,6 +49,8 @@ TEST(CLI, ArgumentParser) {
EXPECT_TRUE(parser2.registerBoolOption("hello2", "e", "Says hello 2")); EXPECT_TRUE(parser2.registerBoolOption("hello2", "e", "Says hello 2"));
EXPECT_TRUE(parser2.registerFloatOption("value", "v", "Sets a valueeeeeee")); EXPECT_TRUE(parser2.registerFloatOption("value", "v", "Sets a valueeeeeee"));
EXPECT_TRUE(parser2.registerIntOption("longlonglonglongintopt", "l", "Long long maaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaan maaan man maaan man maaan man maaan man")); EXPECT_TRUE(parser2.registerIntOption("longlonglonglongintopt", "l", "Long long maaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaan maaan man maaan man maaan man maaan man"));
EXPECT_TRUE(parser2.registerFloatOption("value2", "", "Sets a valueeeeeee 2"));
EXPECT_TRUE(!parser2.registerFloatOption("", "a", "Sets a valueeeeeee 2"));
auto result2 = parser2.parse(); auto result2 = parser2.parse();

View file

@ -75,4 +75,6 @@ TEST(I18n, Engine) {
EXPECT_EQ(engine.localizeEntry("ts", 42069 /* invalid key */, {{"count", "1"}}), ""); EXPECT_EQ(engine.localizeEntry("ts", 42069 /* invalid key */, {{"count", "1"}}), "");
EXPECT_EQ(engine.localizeEntry("ts_TST", TXT_KEY_FALLBACK, {{"var1", "hi"}, {"var2", "!"}}), "Hello hi world !"); EXPECT_EQ(engine.localizeEntry("ts_TST", TXT_KEY_FALLBACK, {{"var1", "hi"}, {"var2", "!"}}), "Hello hi world !");
// Order shouldn't matter
EXPECT_EQ(engine.localizeEntry("ts_TST", TXT_KEY_FALLBACK, {{"var2", "!"}, {"var1", "hi"}}), "Hello hi world !");
} }

View file

@ -123,6 +123,119 @@ static void testAtomicImpl() {
} }
} }
class InterfaceA {
public:
virtual ~InterfaceA() = default;
int m_ifaceAInt = 69;
int m_ifaceAShit = 1;
};
class InterfaceB {
public:
virtual ~InterfaceB() = default;
int m_ifaceBInt = 2;
int m_ifaceBShit = 3;
};
class CChild : public InterfaceA, public InterfaceB {
public:
virtual ~CChild() = default;
int m_childInt = 4;
};
class CChildA : public InterfaceA {
public:
int m_childAInt = 4;
};
static void testHierarchy() {
// Same test for atomic and non-atomic
{
SP<CChildA> childA = makeShared<CChildA>();
auto ifaceA = SP<InterfaceA>(childA);
EXPECT_TRUE(ifaceA);
EXPECT_EQ(ifaceA->m_ifaceAInt, 69);
auto ifaceB = dynamicPointerCast<InterfaceA>(SP<CChildA>{});
EXPECT_TRUE(!ifaceB);
}
{
SP<CChild> child = makeShared<CChild>();
SP<InterfaceA> ifaceA = dynamicPointerCast<InterfaceA>(child);
SP<InterfaceB> ifaceB = dynamicPointerCast<InterfaceB>(child);
EXPECT_TRUE(ifaceA);
EXPECT_TRUE(ifaceB);
EXPECT_EQ(ifaceA->m_ifaceAInt, 69);
EXPECT_EQ(ifaceB->m_ifaceBInt, 2);
WP<InterfaceA> ifaceAWeak = ifaceA;
child.reset();
EXPECT_TRUE(ifaceAWeak);
EXPECT_TRUE(ifaceA);
EXPECT_EQ(ifaceAWeak->m_ifaceAInt, 69);
EXPECT_EQ(ifaceA->m_ifaceAInt, 69);
ifaceA.reset();
EXPECT_TRUE(ifaceAWeak);
EXPECT_EQ(ifaceAWeak->m_ifaceAInt, 69);
EXPECT_TRUE(ifaceB);
EXPECT_EQ(ifaceB->m_ifaceBInt, 2);
ifaceB.reset();
EXPECT_TRUE(!ifaceAWeak);
}
//
{
ASP<CChildA> childA = makeAtomicShared<CChildA>();
auto ifaceA = ASP<InterfaceA>(childA);
EXPECT_TRUE(ifaceA);
EXPECT_EQ(ifaceA->m_ifaceAInt, 69);
auto ifaceB = dynamicPointerCast<InterfaceA>(ASP<CChildA>{});
EXPECT_TRUE(!ifaceB);
}
{
ASP<CChild> child = makeAtomicShared<CChild>();
ASP<InterfaceA> ifaceA = dynamicPointerCast<InterfaceA>(child);
ASP<InterfaceB> ifaceB = dynamicPointerCast<InterfaceB>(child);
EXPECT_TRUE(ifaceA);
EXPECT_TRUE(ifaceB);
EXPECT_EQ(ifaceA->m_ifaceAInt, 69);
EXPECT_EQ(ifaceB->m_ifaceBInt, 2);
AWP<InterfaceA> ifaceAWeak = ifaceA;
AWP<InterfaceB> ifaceBWeak = dynamicPointerCast<InterfaceB>(ifaceA);
child.reset();
EXPECT_TRUE(ifaceAWeak);
EXPECT_TRUE(ifaceBWeak);
EXPECT_TRUE(ifaceA);
EXPECT_EQ(ifaceAWeak->m_ifaceAInt, 69);
EXPECT_EQ(ifaceA->m_ifaceAInt, 69);
EXPECT_EQ(ifaceBWeak->m_ifaceBInt, 2);
ifaceA.reset();
EXPECT_TRUE(ifaceAWeak);
EXPECT_EQ(ifaceAWeak->m_ifaceAInt, 69);
EXPECT_TRUE(ifaceB);
EXPECT_EQ(ifaceB->m_ifaceBInt, 2);
EXPECT_EQ(ifaceBWeak->m_ifaceBInt, 2);
ifaceB.reset();
EXPECT_TRUE(!ifaceAWeak);
EXPECT_TRUE(!ifaceBWeak);
}
// test for leaks
for (size_t i = 0; i < 10000; ++i) {
auto child = makeAtomicShared<CChild>();
auto child2 = makeShared<CChild>();
}
}
TEST(Memory, memory) { TEST(Memory, memory) {
SP<int> intPtr = makeShared<int>(10); SP<int> intPtr = makeShared<int>(10);
SP<int> intPtr2 = makeShared<int>(-1337); SP<int> intPtr2 = makeShared<int>(-1337);
@ -176,4 +289,6 @@ TEST(Memory, memory) {
EXPECT_EQ(*intPtr2, 10); EXPECT_EQ(*intPtr2, 10);
testAtomicImpl(); testAtomicImpl();
testHierarchy();
} }