Compare commits
4 Commits
7c4f2c9d43
...
a538f3757e
| Author | SHA1 | Date |
|---|---|---|
|
|
a538f3757e | |
|
|
d947729d68 | |
|
|
c9d9f5c1de | |
|
|
91e09ff1e1 |
|
|
@ -50,7 +50,7 @@ target_include_directories(
|
|||
PUBLIC "./include"
|
||||
PRIVATE "./src")
|
||||
set_target_properties(hyprutils PROPERTIES VERSION ${hyprutils_VERSION}
|
||||
SOVERSION 8)
|
||||
SOVERSION 9)
|
||||
target_link_libraries(hyprutils PkgConfig::deps)
|
||||
|
||||
# tests
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
hyprutils (0.9.0-0.1) unstable; urgency=medium
|
||||
hyprutils (0.10.0-0.1) unstable; urgency=medium
|
||||
|
||||
* Non-maintainer upload
|
||||
* New upstream release (Closes: #1117181)
|
||||
* Breaking ABI changes: updated soversion to 8
|
||||
* Breaking ABI changes: updated soversion to 9
|
||||
|
||||
-- Carl Keinath <carl.keinath@gmail.com> Sat, 04 Oct 2025 10:58:35 +0200
|
||||
-- Carl Keinath <carl.keinath@gmail.com> Mon, 06 Oct 2025 08:22:04 +0200
|
||||
|
||||
hyprutils (0.8.4-0.1) unstable; urgency=medium
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ Section: libdevel
|
|||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Depends:
|
||||
libhyprutils8 (= ${binary:Version}),
|
||||
libhyprutils9 (= ${binary:Version}),
|
||||
${misc:Depends},
|
||||
Description: Utilities used across the Hyprland ecosystem (development files)
|
||||
Hyprutils is a small C++ library for utilities used across the Hyprland
|
||||
|
|
@ -25,7 +25,7 @@ Description: Utilities used across the Hyprland ecosystem (development files)
|
|||
.
|
||||
This package contains the development files.
|
||||
|
||||
Package: libhyprutils8
|
||||
Package: libhyprutils9
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Depends:
|
||||
|
|
|
|||
|
|
@ -23,12 +23,11 @@
|
|||
|
||||
namespace Hyprutils::Memory {
|
||||
namespace Atomic_ {
|
||||
template <typename T>
|
||||
class impl : public Impl_::impl<T> {
|
||||
class impl : public Impl_::impl_base {
|
||||
std::recursive_mutex m_mutex;
|
||||
|
||||
public:
|
||||
impl(T* data, bool lock = true) noexcept : Impl_::impl<T>(data, lock) {
|
||||
impl(void* data, DeleteFn deleter) noexcept : Impl_::impl_base(data, deleter) {
|
||||
;
|
||||
}
|
||||
|
||||
|
|
@ -55,7 +54,13 @@ namespace Hyprutils::Memory {
|
|||
using validHierarchy = std::enable_if_t<std::is_assignable_v<CAtomicSharedPointer<T>&, X>, CAtomicSharedPointer&>;
|
||||
|
||||
public:
|
||||
explicit CAtomicSharedPointer(Impl_::impl_base* impl) noexcept : m_ptr(impl) {}
|
||||
explicit CAtomicSharedPointer(T* object) noexcept : m_ptr(new Atomic_::impl(sc<void*>(object), _delete)) {
|
||||
;
|
||||
}
|
||||
|
||||
CAtomicSharedPointer(Impl_::impl_base* impl) noexcept : m_ptr(impl) {
|
||||
;
|
||||
}
|
||||
|
||||
CAtomicSharedPointer(const CAtomicSharedPointer<T>& ref) {
|
||||
if (!ref.m_ptr.impl_)
|
||||
|
|
@ -141,7 +146,7 @@ namespace Hyprutils::Memory {
|
|||
// -> must unlock BEFORE reset
|
||||
// not last ref?
|
||||
// -> must unlock AFTER reset
|
||||
auto& mutex = sc<Atomic_::impl<T>*>(m_ptr.impl_)->getMutex();
|
||||
auto& mutex = sc<Atomic_::impl*>(m_ptr.impl_)->getMutex();
|
||||
mutex.lock();
|
||||
|
||||
if (m_ptr.impl_->ref() > 1) {
|
||||
|
|
@ -152,7 +157,11 @@ namespace Hyprutils::Memory {
|
|||
|
||||
if (m_ptr.impl_->wref() == 0) {
|
||||
mutex.unlock(); // Don't hold the mutex when destroying it
|
||||
m_ptr.reset();
|
||||
|
||||
m_ptr.impl_->destroy();
|
||||
delete sc<Atomic_::impl*>(m_ptr.impl_);
|
||||
m_ptr.impl_ = nullptr;
|
||||
|
||||
// mutex invalid
|
||||
return;
|
||||
} else {
|
||||
|
|
@ -163,12 +172,18 @@ namespace Hyprutils::Memory {
|
|||
// To avoid this altogether, keep a weak pointer here.
|
||||
// This guarantees that impl_ is still valid after the reset.
|
||||
CWeakPointer<T> guard = m_ptr;
|
||||
m_ptr.reset();
|
||||
m_ptr.reset(); // destroys the data
|
||||
|
||||
// Now we can safely check if guard is the last wref.
|
||||
if (guard.impl_->wref() == 1) {
|
||||
mutex.unlock();
|
||||
return; // ~guard destroys impl_ and mutex
|
||||
|
||||
// destroy impl_ (includes the mutex)
|
||||
delete sc<Atomic_::impl*>(guard.impl_);
|
||||
guard.impl_ = nullptr;
|
||||
|
||||
// mutex invalid
|
||||
return;
|
||||
}
|
||||
|
||||
guard.reset();
|
||||
|
|
@ -205,8 +220,12 @@ namespace Hyprutils::Memory {
|
|||
}
|
||||
|
||||
private:
|
||||
static void _delete(void* p) {
|
||||
std::default_delete<T>{}(sc<T*>(p));
|
||||
}
|
||||
|
||||
std::lock_guard<std::recursive_mutex> implLockGuard() const {
|
||||
return sc<Atomic_::impl<T>*>(m_ptr.impl_)->lockGuard();
|
||||
return sc<Atomic_::impl*>(m_ptr.impl_)->lockGuard();
|
||||
}
|
||||
|
||||
CSharedPointer<T> m_ptr;
|
||||
|
|
@ -312,11 +331,13 @@ namespace Hyprutils::Memory {
|
|||
// -> must unlock BEFORE reset
|
||||
// not last ref?
|
||||
// -> must unlock AFTER reset
|
||||
auto& mutex = sc<Atomic_::impl<T>*>(m_ptr.impl_)->getMutex();
|
||||
auto& mutex = sc<Atomic_::impl*>(m_ptr.impl_)->getMutex();
|
||||
mutex.lock();
|
||||
if (m_ptr.impl_->ref() == 0 && m_ptr.impl_->wref() == 1) {
|
||||
mutex.unlock();
|
||||
m_ptr.reset();
|
||||
|
||||
delete sc<Atomic_::impl*>(m_ptr.impl_);
|
||||
m_ptr.impl_ = nullptr;
|
||||
// mutex invalid
|
||||
return;
|
||||
}
|
||||
|
|
@ -375,7 +396,7 @@ namespace Hyprutils::Memory {
|
|||
|
||||
private:
|
||||
std::lock_guard<std::recursive_mutex> implLockGuard() const {
|
||||
return sc<Atomic_::impl<T>*>(m_ptr.impl_)->lockGuard();
|
||||
return sc<Atomic_::impl*>(m_ptr.impl_)->lockGuard();
|
||||
}
|
||||
|
||||
CWeakPointer<T> m_ptr;
|
||||
|
|
@ -387,7 +408,7 @@ namespace Hyprutils::Memory {
|
|||
};
|
||||
|
||||
template <typename U, typename... Args>
|
||||
static CAtomicSharedPointer<U> makeAtomicShared(Args&&... args) {
|
||||
return CAtomicSharedPointer<U>(new Atomic_::impl<U>(new U(std::forward<Args>(args)...)));
|
||||
[[nodiscard]] inline CAtomicSharedPointer<U> makeAtomicShared(Args&&... args) {
|
||||
return CAtomicSharedPointer<U>(new U(std::forward<Args>(args)...));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,44 +8,71 @@ namespace Hyprutils {
|
|||
namespace Impl_ {
|
||||
class impl_base {
|
||||
public:
|
||||
virtual ~impl_base() = default;
|
||||
using DeleteFn = void (*)(void*);
|
||||
|
||||
virtual void inc() noexcept = 0;
|
||||
virtual void dec() noexcept = 0;
|
||||
virtual void incWeak() noexcept = 0;
|
||||
virtual void decWeak() noexcept = 0;
|
||||
virtual unsigned int ref() noexcept = 0;
|
||||
virtual unsigned int wref() noexcept = 0;
|
||||
virtual void destroy() noexcept = 0;
|
||||
virtual bool destroying() noexcept = 0;
|
||||
virtual bool dataNonNull() noexcept = 0;
|
||||
virtual bool lockable() noexcept = 0;
|
||||
virtual void* getData() noexcept = 0;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class impl : public impl_base {
|
||||
public:
|
||||
impl(T* data, bool lock = true) noexcept : _lockable(lock), _data(data) {
|
||||
impl_base(void* data, DeleteFn deleter, bool lock = true) noexcept : _lockable(lock), _data(data), _deleter(deleter) {
|
||||
;
|
||||
}
|
||||
|
||||
void inc() noexcept {
|
||||
_ref++;
|
||||
}
|
||||
|
||||
void dec() noexcept {
|
||||
_ref--;
|
||||
}
|
||||
|
||||
void incWeak() noexcept {
|
||||
_weak++;
|
||||
}
|
||||
|
||||
void decWeak() noexcept {
|
||||
_weak--;
|
||||
}
|
||||
|
||||
unsigned int ref() noexcept {
|
||||
return _ref;
|
||||
}
|
||||
|
||||
unsigned int wref() noexcept {
|
||||
return _weak;
|
||||
}
|
||||
|
||||
void destroy() noexcept {
|
||||
_destroy();
|
||||
}
|
||||
|
||||
bool destroying() noexcept {
|
||||
return _destroying;
|
||||
}
|
||||
|
||||
bool lockable() noexcept {
|
||||
return _lockable;
|
||||
}
|
||||
|
||||
bool dataNonNull() noexcept {
|
||||
return _data != nullptr;
|
||||
}
|
||||
|
||||
void* getData() noexcept {
|
||||
return _data;
|
||||
}
|
||||
|
||||
~impl_base() {
|
||||
destroy();
|
||||
}
|
||||
|
||||
private:
|
||||
/* strong refcount */
|
||||
unsigned int _ref = 0;
|
||||
/* weak refcount */
|
||||
unsigned int _weak = 0;
|
||||
/* if this is lockable (shared) */
|
||||
bool _lockable = true;
|
||||
bool _lockable = true;
|
||||
|
||||
T* _data = nullptr;
|
||||
void* _data = nullptr;
|
||||
|
||||
friend void swap(impl*& a, impl*& b) {
|
||||
impl* tmp = a;
|
||||
a = b;
|
||||
b = tmp;
|
||||
}
|
||||
|
||||
/* if the destructor was called,
|
||||
/* if the destructor was called,
|
||||
creating shared_ptrs is no longer valid */
|
||||
bool _destroying = false;
|
||||
|
||||
|
|
@ -63,56 +90,7 @@ namespace Hyprutils {
|
|||
_destroying = false;
|
||||
}
|
||||
|
||||
std::default_delete<T> _deleter{};
|
||||
|
||||
//
|
||||
virtual void inc() noexcept {
|
||||
_ref++;
|
||||
}
|
||||
|
||||
virtual void dec() noexcept {
|
||||
_ref--;
|
||||
}
|
||||
|
||||
virtual void incWeak() noexcept {
|
||||
_weak++;
|
||||
}
|
||||
|
||||
virtual void decWeak() noexcept {
|
||||
_weak--;
|
||||
}
|
||||
|
||||
virtual unsigned int ref() noexcept {
|
||||
return _ref;
|
||||
}
|
||||
|
||||
virtual unsigned int wref() noexcept {
|
||||
return _weak;
|
||||
}
|
||||
|
||||
virtual void destroy() noexcept {
|
||||
_destroy();
|
||||
}
|
||||
|
||||
virtual bool destroying() noexcept {
|
||||
return _destroying;
|
||||
}
|
||||
|
||||
virtual bool lockable() noexcept {
|
||||
return _lockable;
|
||||
}
|
||||
|
||||
virtual bool dataNonNull() noexcept {
|
||||
return _data != nullptr;
|
||||
}
|
||||
|
||||
virtual void* getData() noexcept {
|
||||
return _data;
|
||||
}
|
||||
|
||||
virtual ~impl() {
|
||||
destroy();
|
||||
}
|
||||
DeleteFn _deleter = nullptr;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ namespace Hyprutils {
|
|||
|
||||
/* creates a new shared pointer managing a resource
|
||||
avoid calling. Could duplicate ownership. Prefer makeShared */
|
||||
explicit CSharedPointer(T* object) noexcept : impl_(new Impl_::impl<T>(object)) {
|
||||
explicit CSharedPointer(T* object) noexcept : impl_(new Impl_::impl_base(sc<void*>(object), _delete)) {
|
||||
increment();
|
||||
}
|
||||
|
||||
|
|
@ -140,6 +140,10 @@ namespace Hyprutils {
|
|||
Impl_::impl_base* impl_ = nullptr;
|
||||
|
||||
private:
|
||||
static void _delete(void* p) {
|
||||
std::default_delete<T>{}(sc<T*>(p));
|
||||
}
|
||||
|
||||
/*
|
||||
no-op if there is no impl_
|
||||
may delete the stored object if ref == 0
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ namespace Hyprutils {
|
|||
|
||||
/* creates a new unique pointer managing a resource
|
||||
avoid calling. Could duplicate ownership. Prefer makeUnique */
|
||||
explicit CUniquePointer(T* object) noexcept : impl_(new Impl_::impl<T>(object, false)) {
|
||||
explicit CUniquePointer(T* object) noexcept : impl_(new Impl_::impl_base(sc<void*>(object), [](void* p) { std::default_delete<T>{}(sc<T*>(p)); }, false)) {
|
||||
increment();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -145,16 +145,21 @@ CRegion& Hyprutils::Math::CRegion::scale(const Vector2D& scale) {
|
|||
if (scale == Vector2D{1, 1})
|
||||
return *this;
|
||||
|
||||
int rectsNum = 0;
|
||||
auto RECTSARR = pixman_region32_rectangles(&m_rRegion, &rectsNum);
|
||||
int rectsNum = 0;
|
||||
auto RECTSARR = pixman_region32_rectangles(&m_rRegion, &rectsNum);
|
||||
|
||||
std::vector<pixman_box32_t> boxes;
|
||||
boxes.resize(rectsNum);
|
||||
|
||||
for (int i = 0; i < rectsNum; ++i) {
|
||||
RECTSARR[i].x1 = std::floor(RECTSARR[i].x1 * scale.x);
|
||||
RECTSARR[i].x2 = std::ceil(RECTSARR[i].x2 * scale.x);
|
||||
RECTSARR[i].y1 = std::floor(RECTSARR[i].y1 * scale.y);
|
||||
RECTSARR[i].y2 = std::ceil(RECTSARR[i].y2 * scale.y);
|
||||
boxes[i].x1 = std::floor(RECTSARR[i].x1 * scale.x);
|
||||
boxes[i].x2 = std::ceil(RECTSARR[i].x2 * scale.x);
|
||||
boxes[i].y1 = std::floor(RECTSARR[i].y1 * scale.y);
|
||||
boxes[i].y2 = std::ceil(RECTSARR[i].y2 * scale.y);
|
||||
}
|
||||
|
||||
pixman_region32_fini(&m_rRegion);
|
||||
pixman_region32_init_rects(&m_rRegion, boxes.data(), boxes.size());
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -104,6 +104,20 @@ int main(int argc, char** argv, char** envp) {
|
|||
EXPECT(std::abs(expected.getMatrix().at(8) - matrixBox.getMatrix().at(8)) < 0.1, true);
|
||||
}
|
||||
|
||||
// Test Region Scaling
|
||||
{
|
||||
CRegion rg(CBox{{20, 20}, {40, 40}});
|
||||
|
||||
auto extents = rg.getExtents();
|
||||
EXPECT_VECTOR2D(extents.pos(), Vector2D(20, 20));
|
||||
EXPECT_VECTOR2D(extents.size(), Vector2D(40, 40));
|
||||
|
||||
rg.scale(2);
|
||||
extents = rg.getExtents();
|
||||
EXPECT_VECTOR2D(extents.pos(), Vector2D(40, 40));
|
||||
EXPECT_VECTOR2D(extents.size(), Vector2D(80, 80));
|
||||
}
|
||||
|
||||
{
|
||||
Vector2D original(30, 40);
|
||||
Vector2D monitorSize(100, 200);
|
||||
|
|
|
|||
|
|
@ -92,6 +92,38 @@ static int testAtomicImpl() {
|
|||
foo->bar = foo;
|
||||
}
|
||||
|
||||
{ // This tests destroying the data when storing the base class of a type
|
||||
class ITest {
|
||||
public:
|
||||
size_t num = 0;
|
||||
ITest() : num(1234) {};
|
||||
};
|
||||
|
||||
class CA : public ITest {
|
||||
public:
|
||||
size_t num2 = 0;
|
||||
CA() : ITest(), num2(4321) {};
|
||||
};
|
||||
|
||||
class CB : public ITest {
|
||||
public:
|
||||
int num2 = 0;
|
||||
CB() : ITest(), num2(-1) {};
|
||||
};
|
||||
|
||||
ASP<ITest> genericAtomic = nullptr;
|
||||
SP<ITest> genericNormal = nullptr;
|
||||
{
|
||||
auto derivedAtomic = makeAtomicShared<CA>();
|
||||
auto derivedNormal = makeShared<CA>();
|
||||
genericAtomic = derivedAtomic;
|
||||
genericNormal = derivedNormal;
|
||||
}
|
||||
|
||||
EXPECT(!!genericAtomic, true);
|
||||
EXPECT(!!genericNormal, true);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -113,6 +145,7 @@ int main(int argc, char** argv, char** envp) {
|
|||
EXPECT(intPtr.strongRef(), 1);
|
||||
EXPECT(*weak, 10);
|
||||
EXPECT(weak.expired(), false);
|
||||
EXPECT(!!weak.lock(), true);
|
||||
EXPECT(*weakUnique, 420);
|
||||
EXPECT(weakUnique.expired(), false);
|
||||
EXPECT(intUnique.impl_->wref(), 1);
|
||||
|
|
@ -149,6 +182,5 @@ int main(int argc, char** argv, char** envp) {
|
|||
EXPECT(*intPtr2, 10);
|
||||
|
||||
EXPECT(testAtomicImpl(), 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue