Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package hyprutils for openSUSE:Factory checked in at 2026-07-20 09:59:58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/hyprutils (Old) and /work/SRC/openSUSE:Factory/.hyprutils.new.24530 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "hyprutils" Mon Jul 20 09:59:58 2026 rev:13 rq:1366675 version:0.14.0 Changes: -------- --- /work/SRC/openSUSE:Factory/hyprutils/hyprutils.changes 2026-05-10 16:48:17.816712062 +0200 +++ /work/SRC/openSUSE:Factory/.hyprutils.new.24530/hyprutils.changes 2026-07-20 10:02:01.265543619 +0200 @@ -1,0 +2,13 @@ +Fri Jul 17 23:12:02 UTC 2026 - Avindra Goolcharan <[email protected]> + +- Update to version 0.14.0: + * animations: advance springs by elapsed time + * animation: expose spring + * animation/variable: set m_Begun for setValueAndWarp + * memory: add move assignment to CWeakPointer + * memory: fix implicit comparison of unrelated types + * memory: fix virtual inheritance casts + * memory: add operators, fix a nullptr deref, and follow #115 with uniqueptr + * memory: assert on locking unique pointer + +------------------------------------------------------------------- Old: ---- hyprutils-0.13.1.tar.xz New: ---- hyprutils-0.14.0.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ hyprutils.spec ++++++ --- /var/tmp/diff_new_pack.xU7SS1/_old 2026-07-20 10:02:02.913599081 +0200 +++ /var/tmp/diff_new_pack.xU7SS1/_new 2026-07-20 10:02:02.929599621 +0200 @@ -17,10 +17,10 @@ # -%define sover 12 +%define sover 13 Name: hyprutils -Version: 0.13.1 +Version: 0.14.0 Release: 0 Summary: Utilities used across the Hypr* ecosystem License: BSD-3-Clause ++++++ _service ++++++ --- /var/tmp/diff_new_pack.xU7SS1/_old 2026-07-20 10:02:03.297612005 +0200 +++ /var/tmp/diff_new_pack.xU7SS1/_new 2026-07-20 10:02:03.353613890 +0200 @@ -3,7 +3,7 @@ <service name="obs_scm" mode="manual"> <param name="url">https://github.com/hyprwm/hyprutils.git</param> <param name="scm">git</param> - <param name="revision">v0.13.1</param> + <param name="revision">v0.14.0</param> <param name="versionformat">@PARENT_TAG@</param> <param name="versionrewrite-pattern">v(.*)</param> </service> ++++++ hyprutils-0.13.1.tar.xz -> hyprutils-0.14.0.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hyprutils-0.13.1/CMakeLists.txt new/hyprutils-0.14.0/CMakeLists.txt --- old/hyprutils-0.13.1/CMakeLists.txt 2026-05-08 12:06:10.000000000 +0200 +++ new/hyprutils-0.14.0/CMakeLists.txt 2026-07-17 23:23:33.000000000 +0200 @@ -52,7 +52,7 @@ PUBLIC "./include" PRIVATE "./src") set_target_properties(hyprutils PROPERTIES VERSION ${hyprutils_VERSION} - SOVERSION 12) + SOVERSION 13) target_link_libraries(hyprutils PkgConfig::deps) if(BUILD_TESTING) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hyprutils-0.13.1/VERSION new/hyprutils-0.14.0/VERSION --- old/hyprutils-0.13.1/VERSION 2026-05-08 12:06:10.000000000 +0200 +++ new/hyprutils-0.14.0/VERSION 2026-07-17 23:23:33.000000000 +0200 @@ -1 +1 @@ -0.13.1 +0.14.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hyprutils-0.13.1/include/hyprutils/animation/AnimatedVariable.hpp new/hyprutils-0.14.0/include/hyprutils/animation/AnimatedVariable.hpp --- old/hyprutils-0.13.1/include/hyprutils/animation/AnimatedVariable.hpp 2026-05-08 12:06:10.000000000 +0200 +++ new/hyprutils-0.14.0/include/hyprutils/animation/AnimatedVariable.hpp 2026-07-17 23:23:33.000000000 +0200 @@ -264,6 +264,7 @@ /* Sets the actual value and goal*/ void setValueAndWarp(const VarType& v) { m_Goal = v; + m_Begun = v; m_bIsBeingAnimated = true; warp(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hyprutils-0.13.1/include/hyprutils/animation/Spring.hpp new/hyprutils-0.14.0/include/hyprutils/animation/Spring.hpp --- old/hyprutils-0.13.1/include/hyprutils/animation/Spring.hpp 1970-01-01 01:00:00.000000000 +0100 +++ new/hyprutils-0.14.0/include/hyprutils/animation/Spring.hpp 2026-07-17 23:23:33.000000000 +0200 @@ -0,0 +1,9 @@ +#pragma once + +#include <hyprutils/animation/AnimationManager.hpp> + +#include <chrono> + +namespace Hyprutils::Animation { + void advanceSpring(float& value, float& velocity, const SSpringCurve& spring, std::chrono::duration<float> elapsed); +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hyprutils-0.13.1/include/hyprutils/memory/Atomic.hpp new/hyprutils-0.14.0/include/hyprutils/memory/Atomic.hpp --- old/hyprutils-0.13.1/include/hyprutils/memory/Atomic.hpp 2026-05-08 12:06:10.000000000 +0200 +++ new/hyprutils-0.14.0/include/hyprutils/memory/Atomic.hpp 2026-07-17 23:23:33.000000000 +0200 @@ -54,7 +54,7 @@ using validHierarchy = std::enable_if_t<std::is_assignable_v<CAtomicSharedPointer<T>&, X>, CAtomicSharedPointer&>; public: - explicit CAtomicSharedPointer(T* object) noexcept : m_ptr(new Atomic_::impl(sc<void*>(object), _delete), sc<void*>(object)) { + explicit CAtomicSharedPointer(T* object) noexcept : m_ptr(new Atomic_::impl(Impl_::dataPointer(object), _delete), Impl_::dataPointer(object)) { ; } @@ -203,8 +203,8 @@ return m_ptr.get(); } - operator bool() const { - return m_ptr; + explicit operator bool() const { + return static_cast<bool>(m_ptr); } bool operator==(const CAtomicSharedPointer& rhs) const { @@ -301,6 +301,9 @@ validHierarchy<const CAtomicWeakPointer<U>&> operator=(const CAtomicWeakPointer<U>& rhs) { reset(); + if (!rhs.m_ptr.impl_) + return *this; + auto lg = rhs.implLockGuard(); m_ptr = rhs.m_ptr; return *this; @@ -312,6 +315,9 @@ reset(); + if (!rhs.m_ptr.impl_) + return *this; + auto lg = rhs.implLockGuard(); m_ptr = rhs.m_ptr; return *this; @@ -366,8 +372,8 @@ return m_ptr.get(); } - operator bool() const { - return m_ptr; + explicit operator bool() const { + return static_cast<bool>(m_ptr); } bool operator==(const CAtomicWeakPointer& rhs) const { @@ -426,26 +432,26 @@ template <typename T, typename U> CAtomicSharedPointer<T> reinterpretPointerCast(const CAtomicSharedPointer<U>& ref) { - return CAtomicSharedPointer<T>(ref.impl(), ref.m_data); + return CAtomicSharedPointer<T>(ref.impl(), Impl_::dataPointer(ref.get())); } 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())); + T* newPtr = dynamic_cast<T*>(ref.get()); if (!newPtr) return nullptr; - return CAtomicSharedPointer<T>(ref.impl(), newPtr); + return CAtomicSharedPointer<T>(ref.impl(), Impl_::dataPointer(newPtr)); } template <typename T, typename U> CAtomicWeakPointer<T> dynamicPointerCast(const CAtomicWeakPointer<U>& ref) { if (!ref) return nullptr; - T* newPtr = dynamic_cast<T*>(sc<U*>(ref.impl()->getData())); + T* newPtr = dynamic_cast<T*>(ref.get()); if (!newPtr) return nullptr; - return CAtomicWeakPointer<T>(ref.impl(), newPtr); + return CAtomicWeakPointer<T>(ref.impl(), Impl_::dataPointer(newPtr)); } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hyprutils-0.13.1/include/hyprutils/memory/ImplBase.hpp new/hyprutils-0.14.0/include/hyprutils/memory/ImplBase.hpp --- old/hyprutils-0.13.1/include/hyprutils/memory/ImplBase.hpp 2026-05-08 12:06:10.000000000 +0200 +++ new/hyprutils-0.14.0/include/hyprutils/memory/ImplBase.hpp 2026-07-17 23:23:33.000000000 +0200 @@ -6,6 +6,11 @@ namespace Hyprutils { namespace Memory { namespace Impl_ { + template <typename T> + constexpr void* dataPointer(T* ptr) noexcept { + return const_cast<void*>(static_cast<const void*>(ptr)); + } + class impl_base { public: using DeleteFn = void (*)(void*); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hyprutils-0.13.1/include/hyprutils/memory/SharedPtr.hpp new/hyprutils-0.14.0/include/hyprutils/memory/SharedPtr.hpp --- old/hyprutils-0.13.1/include/hyprutils/memory/SharedPtr.hpp 2026-05-08 12:06:10.000000000 +0200 +++ new/hyprutils-0.14.0/include/hyprutils/memory/SharedPtr.hpp 2026-07-17 23:23:33.000000000 +0200 @@ -28,13 +28,13 @@ /* creates a new shared pointer managing a resource avoid calling. Could duplicate ownership. Prefer makeShared */ - explicit CSharedPointer(T* object) noexcept : impl_(new Impl_::impl_base(sc<void*>(object), _delete)), m_data(sc<void*>(object)) { + explicit CSharedPointer(T* object) noexcept : impl_(new Impl_::impl_base(Impl_::dataPointer(object), _delete)), m_data(Impl_::dataPointer(object)) { increment(); } /* creates a shared pointer from a reference */ template <typename U, typename = isConstructible<U>> - CSharedPointer(const CSharedPointer<U>& ref) noexcept : impl_(ref.impl_), m_data(ref.m_data) { + CSharedPointer(const CSharedPointer<U>& ref) noexcept : impl_(ref.impl_), m_data(Impl_::dataPointer(sc<T*>(ref.get()))) { increment(); } @@ -44,8 +44,10 @@ template <typename U, typename = isConstructible<U>> CSharedPointer(CSharedPointer<U>&& ref) noexcept { - std::swap(impl_, ref.impl_); - std::swap(m_data, ref.m_data); + impl_ = ref.impl_; + m_data = Impl_::dataPointer(sc<T*>(ref.get())); + ref.impl_ = nullptr; + ref.m_data = nullptr; } CSharedPointer(CSharedPointer&& ref) noexcept { @@ -72,12 +74,14 @@ template <typename U> validHierarchy<const CSharedPointer<U>&> operator=(const CSharedPointer<U>& rhs) { - if (impl_ == rhs.impl_) + if (impl_ == rhs.impl_) { + m_data = Impl_::dataPointer(sc<T*>(rhs.get())); return *this; + } decrement(impl_); impl_ = rhs.impl_; - m_data = rhs.m_data; + m_data = Impl_::dataPointer(sc<T*>(rhs.get())); increment(); return *this; } @@ -95,8 +99,11 @@ template <typename U> validHierarchy<const CSharedPointer<U>&> operator=(CSharedPointer<U>&& rhs) { + auto* rhsData = rhs.get(); + std::swap(impl_, rhs.impl_); std::swap(m_data, rhs.m_data); + m_data = Impl_::dataPointer(sc<T*>(rhsData)); return *this; } @@ -106,7 +113,7 @@ return *this; } - operator bool() const { + explicit operator bool() const { return impl_ && impl_->dataNonNull(); } @@ -116,6 +123,14 @@ return impl_ == rhs.impl_; } + bool operator==(std::nullptr_t) const { + return !(impl_ && impl_->dataNonNull()); + } + + bool operator!=(std::nullptr_t) const { + return impl_ && impl_->dataNonNull(); + } + bool operator()(const CSharedPointer& lhs, const CSharedPointer& rhs) const { return rc<uintptr_t>(lhs.impl_) < rc<uintptr_t>(rhs.impl_); } @@ -206,10 +221,10 @@ CSharedPointer<T> dynamicPointerCast(const CSharedPointer<U>& ref) { if (!ref) return nullptr; - T* newPtr = dynamic_cast<T*>(sc<U*>(ref.impl_->getData())); + T* newPtr = dynamic_cast<T*>(ref.get()); if (!newPtr) return nullptr; - return CSharedPointer<T>(ref.impl_, newPtr); + return CSharedPointer<T>(ref.impl_, Impl_::dataPointer(newPtr)); } } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hyprutils-0.13.1/include/hyprutils/memory/UniquePtr.hpp new/hyprutils-0.14.0/include/hyprutils/memory/UniquePtr.hpp --- old/hyprutils-0.13.1/include/hyprutils/memory/UniquePtr.hpp 2026-05-08 12:06:10.000000000 +0200 +++ new/hyprutils-0.14.0/include/hyprutils/memory/UniquePtr.hpp 2026-07-17 23:23:33.000000000 +0200 @@ -22,7 +22,8 @@ /* creates a new unique pointer managing a resource avoid calling. Could duplicate ownership. Prefer makeUnique */ - explicit CUniquePointer(T* object) noexcept : impl_(new Impl_::impl_base(sc<void*>(object), [](void* p) { std::default_delete<T>{}(sc<T*>(p)); }, false)) { + explicit CUniquePointer(T* object) noexcept : + impl_(new Impl_::impl_base(Impl_::dataPointer(object), [](void* p) { std::default_delete<T>{}(sc<T*>(p)); }, false)), m_data(Impl_::dataPointer(object)) { increment(); } @@ -33,11 +34,15 @@ template <typename U, typename = isConstructible<U>> CUniquePointer(CUniquePointer<U>&& ref) noexcept { - std::swap(impl_, ref.impl_); + impl_ = ref.impl_; + m_data = Impl_::dataPointer(sc<T*>(ref.get())); + ref.impl_ = nullptr; + ref.m_data = nullptr; } CUniquePointer(CUniquePointer&& ref) noexcept { std::swap(impl_, ref.impl_); + std::swap(m_data, ref.m_data); } /* creates an empty unique pointer with no implementation */ @@ -58,23 +63,44 @@ template <typename U> validHierarchy<const CUniquePointer<U>&> operator=(CUniquePointer<U>&& rhs) { + auto* rhsData = rhs.get(); + std::swap(impl_, rhs.impl_); + std::swap(m_data, rhs.m_data); + m_data = Impl_::dataPointer(sc<T*>(rhsData)); return *this; } CUniquePointer& operator=(CUniquePointer&& rhs) noexcept { std::swap(impl_, rhs.impl_); + std::swap(m_data, rhs.m_data); return *this; } - operator bool() const { + explicit operator bool() const { return impl_; } + bool operator==(const CUniquePointer& rhs) const { + return impl_ == rhs.impl_; + } + + bool operator==(std::nullptr_t) const { + return !impl_; + } + + bool operator!=(std::nullptr_t) const { + return impl_ != nullptr; + } + bool operator()(const CUniquePointer& lhs, const CUniquePointer& rhs) const { return rc<uintptr_t>(lhs.impl_) < rc<uintptr_t>(rhs.impl_); } + bool operator<(const CUniquePointer& rhs) const { + return rc<uintptr_t>(impl_) < rc<uintptr_t>(rhs.impl_); + } + T* operator->() const { return get(); } @@ -85,15 +111,19 @@ void reset() { decrement(); - impl_ = nullptr; + impl_ = nullptr; + m_data = nullptr; } T* get() const { - return impl_ ? sc<T*>(impl_->getData()) : nullptr; + return impl_ && impl_->dataNonNull() ? sc<T*>(m_data) : nullptr; } Impl_::impl_base* impl_ = nullptr; + // Never use directly: raw data ptr, could be UAF + void* m_data = nullptr; + private: /* no-op if there is no impl_ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hyprutils-0.13.1/include/hyprutils/memory/WeakPtr.hpp new/hyprutils-0.14.0/include/hyprutils/memory/WeakPtr.hpp --- old/hyprutils-0.13.1/include/hyprutils/memory/WeakPtr.hpp 2026-05-08 12:06:10.000000000 +0200 +++ new/hyprutils-0.14.0/include/hyprutils/memory/WeakPtr.hpp 2026-07-17 23:23:33.000000000 +0200 @@ -3,6 +3,7 @@ #include "./SharedPtr.hpp" #include "./UniquePtr.hpp" #include "./Casts.hpp" +#include "../misc/HyprAssert.hpp" /* This is a Hyprland implementation of std::weak_ptr. @@ -27,7 +28,7 @@ return; impl_ = ref.impl_; - m_data = ref.m_data; + m_data = Impl_::dataPointer(sc<T*>(ref.get())); incrementWeak(); } @@ -38,7 +39,7 @@ return; impl_ = ref.impl_; - m_data = ref.impl_->getData(); + m_data = Impl_::dataPointer(sc<T*>(ref.get())); incrementWeak(); } @@ -49,7 +50,7 @@ return; impl_ = ref.impl_; - m_data = ref.m_data; + m_data = Impl_::dataPointer(sc<T*>(ref.get())); incrementWeak(); } @@ -72,8 +73,10 @@ template <typename U, typename = isConstructible<U>> CWeakPointer(CWeakPointer<U>&& ref) noexcept { - std::swap(impl_, ref.impl_); - std::swap(m_data, ref.m_data); + impl_ = ref.impl_; + m_data = Impl_::dataPointer(sc<T*>(ref.get())); + ref.impl_ = nullptr; + ref.m_data = nullptr; } CWeakPointer(CWeakPointer&& ref) noexcept { @@ -84,12 +87,14 @@ /* create a weak ptr from another weak ptr with assignment */ template <typename U> validHierarchy<const CWeakPointer<U>&> operator=(const CWeakPointer<U>& rhs) { - if (impl_ == rhs.impl_) + if (impl_ == rhs.impl_) { + m_data = Impl_::dataPointer(sc<T*>(rhs.get())); return *this; + } decrementWeak(); impl_ = rhs.impl_; - m_data = rhs.m_data; + m_data = Impl_::dataPointer(sc<T*>(rhs.get())); incrementWeak(); return *this; } @@ -105,15 +110,37 @@ return *this; } + template <typename U> + validHierarchy<const CWeakPointer<U>&> operator=(CWeakPointer<U>&& rhs) noexcept { + auto* rhsData = rhs.get(); + + std::swap(impl_, rhs.impl_); + std::swap(m_data, rhs.m_data); + m_data = Impl_::dataPointer(sc<T*>(rhsData)); + return *this; + } + + CWeakPointer& operator=(CWeakPointer&& rhs) noexcept { + if (this == &rhs) + return *this; + + std::swap(impl_, rhs.impl_); + std::swap(m_data, rhs.m_data); + + return *this; + } + /* create a weak ptr from a shared ptr with assignment */ template <typename U> validHierarchy<const CWeakPointer<U>&> operator=(const CSharedPointer<U>& rhs) { - if (rc<uintptr_t>(impl_) == rc<uintptr_t>(rhs.impl_)) + if (rc<uintptr_t>(impl_) == rc<uintptr_t>(rhs.impl_)) { + m_data = Impl_::dataPointer(sc<T*>(rhs.get())); return *this; + } decrementWeak(); impl_ = rhs.impl_; - m_data = rhs.m_data; + m_data = Impl_::dataPointer(sc<T*>(rhs.get())); incrementWeak(); return *this; } @@ -147,14 +174,20 @@ } CSharedPointer<T> lock() const { - if (!impl_ || !impl_->dataNonNull() || impl_->destroying() || !impl_->lockable()) + if (!impl_ || !impl_->dataNonNull() || impl_->destroying()) + return {}; + + // a weak ptr over a CUniquePointer can never be locked: a shared ptr would + HYPRUTILS_ASSERT_MSG(impl_->lockable(), "tried to lock a CWeakPointer over a CUniquePointer"); + + if (!impl_->lockable()) return {}; return CSharedPointer<T>(impl_, m_data); } /* this returns valid() */ - operator bool() const { + explicit operator bool() const { return valid(); } @@ -233,10 +266,10 @@ CWeakPointer<T> dynamicPointerCast(const CWeakPointer<U>& ref) { if (!ref) return nullptr; - T* newPtr = dynamic_cast<T*>(sc<U*>(ref.impl_->getData())); + T* newPtr = dynamic_cast<T*>(ref.get()); if (!newPtr) return nullptr; - return CWeakPointer<T>(ref.impl_, newPtr); + return CWeakPointer<T>(ref.impl_, Impl_::dataPointer(newPtr)); } } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hyprutils-0.13.1/include/hyprutils/misc/HyprAssert.hpp new/hyprutils-0.14.0/include/hyprutils/misc/HyprAssert.hpp --- old/hyprutils-0.13.1/include/hyprutils/misc/HyprAssert.hpp 1970-01-01 01:00:00.000000000 +0100 +++ new/hyprutils-0.14.0/include/hyprutils/misc/HyprAssert.hpp 2026-07-17 23:23:33.000000000 +0200 @@ -0,0 +1,20 @@ +#pragma once + +#include <exception> +#include <iostream> + +#define HYPRUTILS_ASSERT(cond) \ + do { \ + if (!(cond)) { \ + std::cerr << "Assertion failed: " << #cond << " @ " << __FILE__ << ":" << __LINE__ << "\n"; \ + std::terminate(); \ + } \ + } while (0) + +#define HYPRUTILS_ASSERT_MSG(cond, msg) \ + do { \ + if (!(cond)) { \ + std::cerr << "Assertion failed: " << #cond << " (" << msg << ") @ " << __FILE__ << ":" << __LINE__ << "\n"; \ + std::terminate(); \ + } \ + } while (0) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hyprutils-0.13.1/src/animation/AnimatedVariable.cpp new/hyprutils-0.14.0/src/animation/AnimatedVariable.cpp --- old/hyprutils-0.13.1/src/animation/AnimatedVariable.cpp 2026-05-08 12:06:10.000000000 +0200 +++ new/hyprutils-0.14.0/src/animation/AnimatedVariable.cpp 2026-07-17 23:23:33.000000000 +0200 @@ -1,6 +1,7 @@ #include <hyprutils/animation/AnimatedVariable.hpp> #include <hyprutils/animation/AnimationManager.hpp> #include <hyprutils/memory/WeakPtr.hpp> +#include <hyprutils/animation/Spring.hpp> #include <algorithm> #include <cmath> @@ -122,29 +123,10 @@ return {.value = 1.F, .finished = true}; const auto NOW = std::chrono::steady_clock::now(); - float dt = std::chrono::duration<float>(NOW - springLastStep).count(); + const auto DT = NOW - springLastStep; springLastStep = NOW; - constexpr float MINDELTA = 1.F / 240.F; - if (dt <= 0.F) - dt = MINDELTA; - else - dt = std::clamp(dt, MINDELTA, 0.05F); - - if (dt > 0.F) { - constexpr const float FIXEDSTEP = 1.F / 240.F; - const int SUBSTEPS = std::clamp(static_cast<int>(std::ceil(dt / FIXEDSTEP)), 1, 16); - const float STEPTIME = dt / SUBSTEPS; - const float MASS = std::max(SPRING->mass, 0.0001f); - - for (int i = 0; i < SUBSTEPS; ++i) { - const float displacement = m_fSpringValue - 1.f; - const float acceleration = ((-SPRING->stiffness * displacement) - (SPRING->damping * m_fSpringVelocity)) / MASS; - - m_fSpringVelocity += acceleration * STEPTIME; - m_fSpringValue += m_fSpringVelocity * STEPTIME; - } - } + advanceSpring(m_fSpringValue, m_fSpringVelocity, *SPRING, DT); const bool FINISHED = std::abs(1.F - m_fSpringValue) <= SPRING->valueEpsilon && std::abs(m_fSpringVelocity) <= SPRING->velocityEpsilon; if (FINISHED) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hyprutils-0.13.1/src/animation/Spring.cpp new/hyprutils-0.14.0/src/animation/Spring.cpp --- old/hyprutils-0.13.1/src/animation/Spring.cpp 1970-01-01 01:00:00.000000000 +0100 +++ new/hyprutils-0.14.0/src/animation/Spring.cpp 2026-07-17 23:23:33.000000000 +0200 @@ -0,0 +1,56 @@ +#include <hyprutils/animation/Spring.hpp> + +#include <algorithm> +#include <cmath> + +using namespace Hyprutils; +using namespace Hyprutils::Animation; + +void Animation::advanceSpring(float& value, float& velocity, const SSpringCurve& spring, std::chrono::duration<float> elapsed) { + const float DT = std::max(elapsed.count(), 0.F); + if (DT <= 0.F) + return; + + const float MASS = std::max(spring.mass, 0.0001F); + const float STIFFNESS = std::max(spring.stiffness, 0.0001F); + const float DAMPING = std::max(spring.damping, 0.F); + + const float DISPLACEMENT = value - 1.F; + const float OMEGA0 = std::sqrt(STIFFNESS / MASS); + const float GAMMA = DAMPING / (2.F * MASS); + + if (GAMMA < OMEGA0) { + const float OMEGAD = std::sqrt((OMEGA0 * OMEGA0) - (GAMMA * GAMMA)); + const float EXP = std::exp(-GAMMA * DT); + const float SIN = std::sin(OMEGAD * DT); + const float COS = std::cos(OMEGAD * DT); + + const float NEW_DISPLACEMENT = EXP * ((DISPLACEMENT * COS) + (((velocity + (GAMMA * DISPLACEMENT)) / OMEGAD) * SIN)); + const float NEW_VELOCITY = EXP * ((velocity * COS) - (((GAMMA * velocity) + (OMEGA0 * OMEGA0 * DISPLACEMENT)) / OMEGAD) * SIN); + + value = 1.F + NEW_DISPLACEMENT; + velocity = NEW_VELOCITY; + return; + } + + const float CRITICAL_EPSILON = std::max(OMEGA0, 1.F) * 0.0001F; + if (std::abs(GAMMA - OMEGA0) <= CRITICAL_EPSILON) { + const float EXP = std::exp(-GAMMA * DT); + const float B = velocity + (GAMMA * DISPLACEMENT); + + value = 1.F + (EXP * (DISPLACEMENT + (B * DT))); + velocity = EXP * (velocity - (GAMMA * B * DT)); + return; + } + + const float ROOT = std::sqrt((GAMMA * GAMMA) - (OMEGA0 * OMEGA0)); + const float R1 = -GAMMA + ROOT; + const float R2 = -GAMMA - ROOT; + const float A = (velocity - (R2 * DISPLACEMENT)) / (R1 - R2); + const float B = DISPLACEMENT - A; + const float E1 = std::exp(R1 * DT); + const float E2 = std::exp(R2 * DT); + + value = 1.F + (A * E1) + (B * E2); + velocity = (A * R1 * E1) + (B * R2 * E2); +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hyprutils-0.13.1/tests/animation/Animation.cpp new/hyprutils-0.14.0/tests/animation/Animation.cpp --- old/hyprutils-0.13.1/tests/animation/Animation.cpp 2026-05-08 12:06:10.000000000 +0200 +++ new/hyprutils-0.14.0/tests/animation/Animation.cpp 2026-07-17 23:23:33.000000000 +0200 @@ -6,6 +6,9 @@ #include <hyprutils/animation/AnimatedVariable.hpp> #include <hyprutils/memory/WeakPtr.hpp> #include <hyprutils/memory/UniquePtr.hpp> +#include <hyprutils/animation/Spring.hpp> + +#include <chrono> #define SP CSharedPointer #define WP CWeakPointer @@ -391,49 +394,72 @@ EXPECT_EQ(pAnimationManager.get(), nullptr); } -TEST(Animation, springRetargetPreservesVelocity) { - config(); - - pAnimationManager->addSpringWithName("momentum", - { - .stiffness = 120.f, - .damping = 8.f, - .mass = 1.f, - .valueEpsilon = 0.001f, - .velocityEpsilon = 0.001f, - }); - - animationTree.createNode("spring_global"); - animationTree.createNode("spring_velocity", "spring_global"); - animationTree.setConfigForNode("spring_global", 1, 1.f, "spring:momentum"); +TEST(Animation, springAdvanceUsesElapsedTime) { + const SSpringCurve SPRING = { + .stiffness = 100.f, + .damping = 20.f, + .mass = 1.f, + .valueEpsilon = 0.001f, + .velocityEpsilon = 0.001f, + }; + + float oneMsValue = 0.F; + float oneMsVelocity = 0.F; + float hundredMsValue = 0.F; + float hundredMsVelocity = 0.F; + float negativeValue = 0.F; + float negativeVelocity = 0.F; + + advanceSpring(oneMsValue, oneMsVelocity, SPRING, std::chrono::milliseconds(1)); + advanceSpring(hundredMsValue, hundredMsVelocity, SPRING, std::chrono::milliseconds(120)); + advanceSpring(negativeValue, negativeVelocity, SPRING, std::chrono::milliseconds(-1)); + + EXPECT_GT(oneMsValue, 0.F); + EXPECT_GT(hundredMsValue, oneMsValue); + EXPECT_EQ(negativeValue, 0.F); + EXPECT_EQ(negativeVelocity, 0.F); +} - PANIMVAR<int> av; - pAnimationManager->createAnimation(0, av, "spring_velocity"); +TEST(Animation, springAdvancesAcrossLateTicks) { + const SSpringCurve SPRING = { + .stiffness = 100.f, + .damping = 20.f, + .mass = 1.f, + .valueEpsilon = 0.001f, + .velocityEpsilon = 0.001f, + }; + + float lateValue = 0.F; + float lateVelocity = 0.F; + float cappedValue = 0.F; + float cappedVelocity = 0.F; - *av = 100; + advanceSpring(lateValue, lateVelocity, SPRING, std::chrono::milliseconds(120)); + advanceSpring(cappedValue, cappedVelocity, SPRING, std::chrono::milliseconds(50)); - int ticks = 0; - while (av->value() < 40 && ticks++ < 300) { - pAnimationManager->tick(); - } - - const auto RETARGETPOINT = av->value(); - EXPECT_GT(RETARGETPOINT, 20); - - PANIMVAR<int> fromRest; - pAnimationManager->createAnimation(RETARGETPOINT, fromRest, "spring_velocity"); + EXPECT_GT(lateValue, cappedValue); + EXPECT_GT(lateValue, 0.25F); +} - *av = 0; - *fromRest = 0; - EXPECT_EQ(av->value(), RETARGETPOINT); +TEST(Animation, springDoesNotAdvanceFasterThanElapsedTime) { + const SSpringCurve SPRING = { + .stiffness = 38.f, + .damping = 8.f, + .mass = 2.4f, + .valueEpsilon = 0.001f, + .velocityEpsilon = 0.001f, + }; + + float repeatedValue = 0.F; + float repeatedVelocity = 0.F; + float singleValue = 0.F; + float singleVelocity = 0.F; - pAnimationManager->tick(); - EXPECT_GT(av->value(), fromRest->value()); + for (size_t i = 0; i < 132; ++i) + advanceSpring(repeatedValue, repeatedVelocity, SPRING, std::chrono::milliseconds(1)); - while (pAnimationManager->shouldTickForNext() && ticks++ < 2000) { - pAnimationManager->tick(); - } + advanceSpring(singleValue, singleVelocity, SPRING, std::chrono::milliseconds(132)); - EXPECT_EQ(av->value(), 0); - EXPECT_EQ(fromRest->value(), 0); + EXPECT_NEAR(repeatedValue, singleValue, 0.0001F); + EXPECT_LT(repeatedValue, 0.5F); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hyprutils-0.13.1/tests/memory/Memory.cpp new/hyprutils-0.14.0/tests/memory/Memory.cpp --- old/hyprutils-0.13.1/tests/memory/Memory.cpp 2026-05-08 12:06:10.000000000 +0200 +++ new/hyprutils-0.14.0/tests/memory/Memory.cpp 2026-07-17 23:23:33.000000000 +0200 @@ -19,6 +19,20 @@ #define NTHREADS 8 #define ITERATIONS 10000 +namespace { + class Peepee {}; + class Poopoo {}; +} + +template <typename A, typename B> +concept EqualityComparable = requires(A a, B b) { a == b; }; + +static_assert(!EqualityComparable<SP<Peepee>, SP<Poopoo>>); +static_assert(!EqualityComparable<WP<Peepee>, WP<Poopoo>>); +static_assert(!EqualityComparable<UP<Peepee>, UP<Poopoo>>); +static_assert(!EqualityComparable<ASP<Peepee>, ASP<Poopoo>>); +static_assert(!EqualityComparable<AWP<Peepee>, AWP<Poopoo>>); + static void testAtomicImpl() { { // Using makeShared here could lead to invalid refcounts. @@ -43,7 +57,7 @@ // Actual count is not incremented in a thread-safe manner here, so we can't check it. // We just want to check that the concurent refcounting doesn't cause any memory corruption. shared.reset(); - EXPECT_EQ(shared, false); + EXPECT_FALSE(shared); } { @@ -73,7 +87,7 @@ EXPECT_EQ(weak.valid(), false); auto shared2 = weak.lock(); - EXPECT_EQ(shared, false); + EXPECT_FALSE(shared); EXPECT_EQ(shared2.get(), nullptr); EXPECT_EQ(shared.strongRef(), 0); EXPECT_EQ(weak.valid(), false); @@ -148,6 +162,35 @@ int m_childAInt = 4; }; +class IVirtualRoot { + public: + virtual ~IVirtualRoot() = default; + int m_rootInt = 5; +}; + +class IVirtualLeft : public virtual IVirtualRoot { + public: + virtual ~IVirtualLeft() = default; + int m_leftInt = 6; +}; + +class IVirtualRight : public virtual IVirtualRoot { + public: + virtual ~IVirtualRight() = default; + int m_rightInt = 7; +}; + +class CVirtualChild : public IVirtualLeft, public IVirtualRight { + public: + virtual ~CVirtualChild() = default; + int m_virtualChildInt = 8; +}; + +class CConstObject { + public: + int m_value = 9; +}; + static void testHierarchy() { // Same test for atomic and non-atomic { @@ -243,6 +286,167 @@ } } +static void testVirtualHierarchy() { + { + SP<CVirtualChild> child = makeShared<CVirtualChild>(); + + SP<IVirtualRoot> root = child; + SP<IVirtualRight> right = dynamicPointerCast<IVirtualRight>(root); + SP<CVirtualChild> recastChild = dynamicPointerCast<CVirtualChild>(root); + + EXPECT_TRUE(root); + EXPECT_TRUE(right); + EXPECT_TRUE(recastChild); + EXPECT_EQ(root->m_rootInt, 5); + EXPECT_EQ(right->m_rightInt, 7); + EXPECT_EQ(recastChild->m_virtualChildInt, 8); + + SP<IVirtualRoot> rootFromCast = dynamicPointerCast<IVirtualRoot>(child); + EXPECT_TRUE(rootFromCast); + EXPECT_EQ(rootFromCast->m_rootInt, 5); + + WP<IVirtualRoot> rootWeak = child; + WP<IVirtualRight> rightWeak = dynamicPointerCast<IVirtualRight>(rootWeak); + + EXPECT_TRUE(rootWeak); + EXPECT_TRUE(rightWeak); + EXPECT_EQ(rootWeak->m_rootInt, 5); + EXPECT_EQ(rightWeak->m_rightInt, 7); + EXPECT_EQ(rightWeak.lock()->m_rightInt, 7); + } + + { + ASP<CVirtualChild> child = makeAtomicShared<CVirtualChild>(); + + ASP<IVirtualRoot> root = child; + ASP<IVirtualRight> right = dynamicPointerCast<IVirtualRight>(root); + ASP<CVirtualChild> recastChild = dynamicPointerCast<CVirtualChild>(root); + + EXPECT_TRUE(root); + EXPECT_TRUE(right); + EXPECT_TRUE(recastChild); + EXPECT_EQ(root->m_rootInt, 5); + EXPECT_EQ(right->m_rightInt, 7); + EXPECT_EQ(recastChild->m_virtualChildInt, 8); + + ASP<IVirtualRoot> rootFromCast = dynamicPointerCast<IVirtualRoot>(child); + EXPECT_TRUE(rootFromCast); + EXPECT_EQ(rootFromCast->m_rootInt, 5); + + AWP<IVirtualRoot> rootWeak = root; + AWP<IVirtualRight> rightWeak = dynamicPointerCast<IVirtualRight>(rootWeak); + + EXPECT_TRUE(rootWeak); + EXPECT_TRUE(rightWeak); + EXPECT_EQ(rootWeak->m_rootInt, 5); + EXPECT_EQ(rightWeak->m_rightInt, 7); + EXPECT_EQ(rightWeak.lock()->m_rightInt, 7); + } + + { + UP<CVirtualChild> child = makeUnique<CVirtualChild>(); + EXPECT_TRUE(child); + EXPECT_EQ(child->m_virtualChildInt, 8); + + UP<IVirtualRight> right = makeUnique<CVirtualChild>(); + UP<IVirtualRoot> root = makeUnique<CVirtualChild>(); + + EXPECT_TRUE(right); + EXPECT_TRUE(root); + EXPECT_EQ(right->m_rightInt, 7); + EXPECT_EQ(root->m_rootInt, 5); + + UP<IVirtualRoot> rootMoved = std::move(root); + EXPECT_TRUE(rootMoved); + EXPECT_FALSE(root); + EXPECT_EQ(rootMoved->m_rootInt, 5); + + UP<IVirtualRight> rightAssigned; + rightAssigned = makeUnique<CVirtualChild>(); + EXPECT_TRUE(rightAssigned); + EXPECT_EQ(rightAssigned->m_rightInt, 7); + + WP<IVirtualRoot> rootWeak = right; + WP<IVirtualRight> rightWeak = right; + + EXPECT_TRUE(rootWeak); + EXPECT_TRUE(rightWeak); + EXPECT_EQ(rootWeak->m_rootInt, 5); + EXPECT_EQ(rightWeak->m_rightInt, 7); + + // locking a weak ptr over a unique ptr is not allowed + EXPECT_DEBUG_DEATH(EXPECT_EQ(rightWeak.lock(), nullptr), ""); + } +} + +static void testConstPointers() { + { + SP<const CConstObject> shared = makeShared<const CConstObject>(); + WP<const CConstObject> weak = shared; + + EXPECT_TRUE(shared); + EXPECT_TRUE(weak); + EXPECT_EQ(shared->m_value, 9); + EXPECT_EQ(weak->m_value, 9); + } + + { + UP<const CConstObject> unique = makeUnique<const CConstObject>(); + WP<const CConstObject> weak = unique; + + EXPECT_TRUE(unique); + EXPECT_TRUE(weak); + EXPECT_EQ(unique->m_value, 9); + EXPECT_EQ(weak->m_value, 9); + } + + { + SP<const CVirtualChild> child = makeShared<const CVirtualChild>(); + + SP<const IVirtualRoot> root = child; + SP<const IVirtualRight> right = dynamicPointerCast<const IVirtualRight>(root); + SP<const CVirtualChild> recastChild = dynamicPointerCast<const CVirtualChild>(root); + + EXPECT_TRUE(root); + EXPECT_TRUE(right); + EXPECT_TRUE(recastChild); + EXPECT_EQ(root->m_rootInt, 5); + EXPECT_EQ(right->m_rightInt, 7); + EXPECT_EQ(recastChild->m_virtualChildInt, 8); + + WP<const IVirtualRoot> rootWeak = child; + WP<const IVirtualRight> rightWeak = dynamicPointerCast<const IVirtualRight>(rootWeak); + + EXPECT_TRUE(rootWeak); + EXPECT_TRUE(rightWeak); + EXPECT_EQ(rootWeak->m_rootInt, 5); + EXPECT_EQ(rightWeak->m_rightInt, 7); + } + + { + ASP<const CVirtualChild> child = makeAtomicShared<const CVirtualChild>(); + + ASP<const IVirtualRoot> root = child; + ASP<const IVirtualRight> right = dynamicPointerCast<const IVirtualRight>(root); + ASP<const CVirtualChild> recastChild = dynamicPointerCast<const CVirtualChild>(root); + + EXPECT_TRUE(root); + EXPECT_TRUE(right); + EXPECT_TRUE(recastChild); + EXPECT_EQ(root->m_rootInt, 5); + EXPECT_EQ(right->m_rightInt, 7); + EXPECT_EQ(recastChild->m_virtualChildInt, 8); + + AWP<const IVirtualRoot> rootWeak = root; + AWP<const IVirtualRight> rightWeak = dynamicPointerCast<const IVirtualRight>(rootWeak); + + EXPECT_TRUE(rootWeak); + EXPECT_TRUE(rightWeak); + EXPECT_EQ(rootWeak->m_rootInt, 5); + EXPECT_EQ(rightWeak->m_rightInt, 7); + } +} + class CSelfDestruct { public: SP<CSelfDestruct> self; @@ -285,8 +489,8 @@ EXPECT_EQ(weakUnique.expired(), false); EXPECT_EQ(intUnique.impl_->wref(), 1); - SP<int> sharedFromUnique = weakUnique.lock(); - EXPECT_EQ(sharedFromUnique, nullptr); + // locking a weak ptr over a unique ptr is not allowed + EXPECT_DEBUG_DEATH(EXPECT_EQ(weakUnique.lock(), nullptr), ""); std::vector<SP<int>> sps; sps.push_back(intPtr); @@ -320,5 +524,9 @@ testHierarchy(); + testVirtualHierarchy(); + + testConstPointers(); + testSelfDestruct(); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hyprutils-0.13.1/tests/memory/WeakPtrMove.cpp new/hyprutils-0.14.0/tests/memory/WeakPtrMove.cpp --- old/hyprutils-0.13.1/tests/memory/WeakPtrMove.cpp 1970-01-01 01:00:00.000000000 +0100 +++ new/hyprutils-0.14.0/tests/memory/WeakPtrMove.cpp 2026-07-17 23:23:33.000000000 +0200 @@ -0,0 +1,65 @@ +#include <hyprutils/memory/SharedPtr.hpp> +#include <hyprutils/memory/WeakPtr.hpp> + +#include <gtest/gtest.h> +#include <utility> + +using namespace Hyprutils::Memory; + +namespace { + class IBase { + public: + virtual ~IBase() = default; + int value = 0; + }; + + class CDerived : public IBase {}; +} + +TEST(Memory, weakPointerMoveAssignment) { + auto lhsOwner = makeShared<int>(1); + auto rhsOwner = makeShared<int>(2); + + CWeakPointer<int> lhs = lhsOwner; + CWeakPointer<int> rhs = rhsOwner; + + auto* lhsImpl = lhs.impl_; + auto* rhsImpl = rhs.impl_; + + ASSERT_EQ(lhsImpl->wref(), 1U); + ASSERT_EQ(rhsImpl->wref(), 1U); + + lhs = std::move(rhs); + + EXPECT_EQ(lhs.impl_, rhsImpl); + EXPECT_EQ(rhs.impl_, lhsImpl); + EXPECT_EQ(lhsImpl->wref(), 1U); + EXPECT_EQ(rhsImpl->wref(), 1U); + EXPECT_EQ(*lhs, 2); + EXPECT_EQ(*rhs, 1); +} + +TEST(Memory, weakPointerConvertingMoveAssignment) { + auto lhsOwner = makeShared<CDerived>(); + auto rhsOwner = makeShared<CDerived>(); + lhsOwner->value = 1; + rhsOwner->value = 2; + + CWeakPointer<IBase> lhs = lhsOwner; + CWeakPointer<CDerived> rhs = rhsOwner; + + auto* lhsImpl = lhs.impl_; + auto* rhsImpl = rhs.impl_; + + ASSERT_EQ(lhsImpl->wref(), 1U); + ASSERT_EQ(rhsImpl->wref(), 1U); + + lhs = std::move(rhs); + + EXPECT_EQ(lhs.impl_, rhsImpl); + EXPECT_EQ(rhs.impl_, lhsImpl); + EXPECT_EQ(lhsImpl->wref(), 1U); + EXPECT_EQ(rhsImpl->wref(), 1U); + EXPECT_EQ(lhs->value, 2); + EXPECT_EQ(rhs->value, 1); +} ++++++ hyprutils.obsinfo ++++++ --- /var/tmp/diff_new_pack.xU7SS1/_old 2026-07-20 10:02:04.261644448 +0200 +++ /var/tmp/diff_new_pack.xU7SS1/_new 2026-07-20 10:02:04.285645256 +0200 @@ -1,5 +1,5 @@ name: hyprutils -version: 0.13.1 -mtime: 1778234770 -commit: a2dbd8a4cc51f7cbe4224732668392bb1aa79df2 +version: 0.14.0 +mtime: 1784323413 +commit: 5f03477ab3a005ff27c527486f551883535aea2f
