Merge branch 'debian/latest' into 'debian/latest'
new upstream version 0.8.4 See merge request NyxTrail/hyprutils!3
This commit is contained in:
commit
3ea49697a9
|
|
@ -0,0 +1,11 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.{cmake,nix,yml,yaml},CMakeLists.txt]
|
||||
indent_size = 2
|
||||
|
|
@ -13,7 +13,35 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: cachix/install-nix-action@v26
|
||||
- name: Install Nix
|
||||
uses: nixbuild/nix-quick-install-action@v31
|
||||
with:
|
||||
nix_conf: |
|
||||
keep-env-derivations = true
|
||||
keep-outputs = true
|
||||
|
||||
- name: Restore and save Nix store
|
||||
uses: nix-community/cache-nix-action@v6
|
||||
with:
|
||||
# restore and save a cache using this key
|
||||
primary-key: nix-${{ runner.os }}-${{ hashFiles('**/*.nix', '**/flake.lock') }}
|
||||
# if there's no cache hit, restore a cache by this prefix
|
||||
restore-prefixes-first-match: nix-${{ runner.os }}-
|
||||
# collect garbage until the Nix store size (in bytes) is at most this number
|
||||
# before trying to save a new cache
|
||||
# 1G = 1073741824
|
||||
gc-max-store-size-linux: 1G
|
||||
# do purge caches
|
||||
purge: true
|
||||
# purge all versions of the cache
|
||||
purge-prefixes: nix-${{ runner.os }}-
|
||||
# created more than this number of seconds ago
|
||||
purge-created: 0
|
||||
# or, last accessed more than this number of seconds ago
|
||||
# relative to the start of the `Post Restore and save Nix store` phase
|
||||
purge-last-accessed: 0
|
||||
# except any version with the key that is the same as the `primary-key`
|
||||
purge-primary-key: never
|
||||
|
||||
# not needed (yet)
|
||||
# - uses: cachix/cachix-action@v12
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
build/
|
||||
.vscode/
|
||||
.cache/
|
||||
.direnv/
|
||||
|
||||
.cmake/
|
||||
CMakeCache.txt
|
||||
|
|
@ -44,3 +45,4 @@ Makefile
|
|||
cmake_install.cmake
|
||||
compile_commands.json
|
||||
hyprutils.pc
|
||||
.envrc
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ target_include_directories(
|
|||
PUBLIC "./include"
|
||||
PRIVATE "./src")
|
||||
set_target_properties(hyprutils PROPERTIES VERSION ${hyprutils_VERSION}
|
||||
SOVERSION 6)
|
||||
SOVERSION 7)
|
||||
target_link_libraries(hyprutils PkgConfig::deps)
|
||||
|
||||
# tests
|
||||
|
|
|
|||
|
|
@ -1,3 +1,13 @@
|
|||
hyprutils (0.8.4-0.1) unstable; urgency=medium
|
||||
|
||||
* Non-maintainer upload.
|
||||
* New upstream version 0.8.4. (Closes: #1090839)
|
||||
* Breaking ABI changes: updated soversion to 7.
|
||||
* Added autopkgtests to run upstream tests.
|
||||
* d/watch: migrated to Version 5.
|
||||
|
||||
-- Carl Keinath <carl.keinath@gmail.com> Wed, 17 Sep 2025 23:04:34 +0200
|
||||
|
||||
hyprutils (0.7.0-1) unstable; urgency=medium
|
||||
|
||||
[ alan (NyxTrail) ]
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ Source: hyprutils
|
|||
Section: libs
|
||||
Priority: optional
|
||||
Maintainer: Alan M Varghese (NyxTrail) <alan@digistorm.in>
|
||||
Rules-Requires-Root: no
|
||||
Build-Depends:
|
||||
debhelper-compat (= 13),
|
||||
cmake,
|
||||
|
|
@ -18,7 +17,7 @@ Section: libdevel
|
|||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Depends:
|
||||
libhyprutils6 (= ${binary:Version}),
|
||||
libhyprutils7 (= ${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
|
||||
|
|
@ -26,7 +25,7 @@ Description: Utilities used across the Hyprland ecosystem (development files)
|
|||
.
|
||||
This package contains the development files.
|
||||
|
||||
Package: libhyprutils6
|
||||
Package: libhyprutils7
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Depends:
|
||||
|
|
|
|||
|
|
@ -2,9 +2,5 @@
|
|||
|
||||
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
|
||||
|
||||
ifeq ($(DEB_HOST_ARCH),i386)
|
||||
export DEB_CXXFLAGS_MAINT_APPEND += -ffloat-store
|
||||
endif
|
||||
|
||||
%:
|
||||
dh $@
|
||||
|
|
|
|||
|
|
@ -0,0 +1,74 @@
|
|||
cmake_minimum_required(VERSION 3.19)
|
||||
project(hyprutils)
|
||||
|
||||
include(CTest)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
add_compile_options(
|
||||
-Wall
|
||||
-Wextra
|
||||
-Wpedantic
|
||||
-Wno-unused-parameter
|
||||
-Wno-unused-value
|
||||
-Wno-missing-field-initializers
|
||||
-Wno-narrowing
|
||||
-Wno-pointer-arith
|
||||
)
|
||||
|
||||
enable_testing()
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(deps REQUIRED IMPORTED_TARGET pixman-1)
|
||||
|
||||
find_library(HYPRUTILS_LIB hyprutils REQUIRED)
|
||||
|
||||
|
||||
add_executable(hyprutils_memory "tests/memory.cpp")
|
||||
target_link_libraries(hyprutils_memory PRIVATE ${HYPRUTILS_LIB} PkgConfig::deps)
|
||||
add_test(
|
||||
NAME "Memory"
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
|
||||
COMMAND hyprutils_memory "memory")
|
||||
|
||||
add_executable(hyprutils_string "tests/string.cpp")
|
||||
target_link_libraries(hyprutils_string PRIVATE ${HYPRUTILS_LIB} PkgConfig::deps)
|
||||
add_test(
|
||||
NAME "String"
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
|
||||
COMMAND hyprutils_string "string")
|
||||
|
||||
add_executable(hyprutils_signal "tests/signal.cpp")
|
||||
target_link_libraries(hyprutils_signal PRIVATE ${HYPRUTILS_LIB} PkgConfig::deps)
|
||||
add_test(
|
||||
NAME "Signal"
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
|
||||
COMMAND hyprutils_signal "signal")
|
||||
|
||||
add_executable(hyprutils_math "tests/math.cpp")
|
||||
target_link_libraries(hyprutils_math PRIVATE ${HYPRUTILS_LIB} PkgConfig::deps)
|
||||
add_test(
|
||||
NAME "Math"
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
|
||||
COMMAND hyprutils_math "math")
|
||||
|
||||
add_executable(hyprutils_os "tests/os.cpp")
|
||||
target_link_libraries(hyprutils_os PRIVATE ${HYPRUTILS_LIB} PkgConfig::deps)
|
||||
add_test(
|
||||
NAME "OS"
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
|
||||
COMMAND hyprutils_os "os")
|
||||
|
||||
add_executable(hyprutils_filedescriptor "tests/filedescriptor.cpp")
|
||||
target_link_libraries(hyprutils_filedescriptor PRIVATE ${HYPRUTILS_LIB} PkgConfig::deps)
|
||||
add_test(
|
||||
NAME "Filedescriptor"
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
|
||||
COMMAND hyprutils_filedescriptor "filedescriptor")
|
||||
|
||||
add_executable(hyprutils_animation "tests/animation.cpp")
|
||||
target_link_libraries(hyprutils_animation PRIVATE ${HYPRUTILS_LIB} PkgConfig::deps)
|
||||
add_test(
|
||||
NAME "Animation"
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
|
||||
COMMAND hyprutils_animation "utils")
|
||||
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
Tests: upstream-tests
|
||||
Depends: @, @builddeps@
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
#!/bin/sh
|
||||
|
||||
set -eu
|
||||
|
||||
mkdir -p "$AUTOPKGTEST_TMP"
|
||||
cp -r tests "$AUTOPKGTEST_TMP"/tests
|
||||
cp debian/tests/CMakeLists.txt "$AUTOPKGTEST_TMP"/CMakeLists.txt
|
||||
|
||||
mkdir -p "$AUTOPKGTEST_TMP"/build
|
||||
cd "$AUTOPKGTEST_TMP"/build
|
||||
|
||||
cmake ..
|
||||
cmake --build .
|
||||
ctest --output-on-failure
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
version=4
|
||||
opts="filenamemangle=s%(?:.*?)?v?(\d[\d.]*)\.tar\.gz%@PACKAGE@-$1.tar.gz%" \
|
||||
https://github.com/hyprwm/hyprutils/tags \
|
||||
(?:.*?/)?v?(\d[\d.]*)\.tar\.gz
|
||||
Version: 5
|
||||
|
||||
Template: Github
|
||||
Owner: hyprwm
|
||||
Project: hyprutils
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@
|
|||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1734119587,
|
||||
"narHash": "sha256-AKU6qqskl0yf2+JdRdD0cfxX4b9x3KKV5RqA6wijmPM=",
|
||||
"lastModified": 1748929857,
|
||||
"narHash": "sha256-lcZQ8RhsmhsK8u7LIFsJhsLh/pzR9yZ8yqpTzyGdj+Q=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "3566ab7246670a43abd2ffa913cc62dad9cdf7d5",
|
||||
"rev": "c2a03962b8e24e669fb37b7df10e7c79531ff1a4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
|
|||
20
flake.nix
20
flake.nix
|
|
@ -18,28 +18,12 @@
|
|||
localSystem.system = system;
|
||||
overlays = with self.overlays; [hyprutils];
|
||||
});
|
||||
mkDate = longDate: (lib.concatStringsSep "-" [
|
||||
(builtins.substring 0 4 longDate)
|
||||
(builtins.substring 4 2 longDate)
|
||||
(builtins.substring 6 2 longDate)
|
||||
]);
|
||||
|
||||
version = lib.removeSuffix "\n" (builtins.readFile ./VERSION);
|
||||
in {
|
||||
overlays = {
|
||||
default = self.overlays.hyprutils;
|
||||
hyprutils = final: prev: {
|
||||
hyprutils = final.callPackage ./nix/default.nix {
|
||||
stdenv = final.gcc14Stdenv;
|
||||
version = version + "+date=" + (mkDate (self.lastModifiedDate or "19700101")) + "_" + (self.shortRev or "dirty");
|
||||
};
|
||||
hyprutils-with-tests = final.hyprutils.override {doCheck = true;};
|
||||
};
|
||||
};
|
||||
overlays = import ./nix/overlays.nix {inherit self lib;};
|
||||
|
||||
packages = eachSystem (system: {
|
||||
default = self.packages.${system}.hyprutils;
|
||||
inherit (pkgsFor.${system}) hyprutils hyprutils-with-tests;
|
||||
inherit (pkgsFor.${system}) hyprutils hyprutils-debug hyprutils-with-tests;
|
||||
});
|
||||
|
||||
formatter = eachSystem (system: pkgsFor.${system}.alejandra);
|
||||
|
|
|
|||
|
|
@ -35,8 +35,8 @@ namespace Hyprutils {
|
|||
const std::unordered_map<std::string, Memory::CSharedPointer<CBezierCurve>>& getAllBeziers();
|
||||
|
||||
struct SAnimationManagerSignals {
|
||||
Signal::CSignal connect; // WP<CBaseAnimatedVariable>
|
||||
Signal::CSignal disconnect; // WP<CBaseAnimatedVariable>
|
||||
Signal::CSignalT<Memory::CWeakPointer<CBaseAnimatedVariable>> connect;
|
||||
Signal::CSignalT<Memory::CWeakPointer<CBaseAnimatedVariable>> disconnect;
|
||||
};
|
||||
|
||||
Memory::CWeakPointer<SAnimationManagerSignals> getSignals() const;
|
||||
|
|
@ -48,9 +48,6 @@ namespace Hyprutils {
|
|||
|
||||
bool m_bTickScheduled = false;
|
||||
|
||||
void onConnect(std::any data);
|
||||
void onDisconnect(std::any data);
|
||||
|
||||
struct SAnimVarListeners {
|
||||
Signal::CHyprSignalListener connect;
|
||||
Signal::CHyprSignalListener disconnect;
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@ namespace Hyprutils {
|
|||
float getXForT(float const& t) const;
|
||||
float getYForPoint(float const& x) const;
|
||||
|
||||
/* this INCLUDES the 0,0 and 1,1 points. */
|
||||
const std::vector<Hyprutils::Math::Vector2D>& getControlPoints() const;
|
||||
|
||||
private:
|
||||
/* this INCLUDES the 0,0 and 1,1 points. */
|
||||
std::vector<Hyprutils::Math::Vector2D> m_vPoints;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
#pragma once
|
||||
#include "hyprutils/memory/Casts.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace Hyprutils::Math {
|
||||
|
|
@ -18,7 +20,7 @@ namespace Hyprutils::Math {
|
|||
|
||||
CEdges() = default;
|
||||
CEdges(eEdges edges) : m_edges(edges) {}
|
||||
CEdges(uint8_t edges) : m_edges(static_cast<eEdges>(edges)) {}
|
||||
CEdges(uint8_t edges) : m_edges(Memory::sc<eEdges>(edges)) {}
|
||||
|
||||
bool operator==(const CEdges& other) {
|
||||
return m_edges == other.m_edges;
|
||||
|
|
@ -80,28 +82,28 @@ namespace Hyprutils::Math {
|
|||
* @param top The state the top edge should be set to.
|
||||
*/
|
||||
void setTop(bool top) {
|
||||
m_edges = static_cast<eEdges>((m_edges & ~TOP) | (TOP * top));
|
||||
m_edges = Memory::sc<eEdges>((m_edges & ~TOP) | (TOP * top));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param left The state the left edge should be set to.
|
||||
*/
|
||||
void setLeft(bool left) {
|
||||
m_edges = static_cast<eEdges>((m_edges & ~LEFT) | (LEFT * left));
|
||||
m_edges = Memory::sc<eEdges>((m_edges & ~LEFT) | (LEFT * left));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bottom The state the bottom edge should be set to.
|
||||
*/
|
||||
void setBottom(bool bottom) {
|
||||
m_edges = static_cast<eEdges>((m_edges & ~BOTTOM) | (BOTTOM * bottom));
|
||||
m_edges = Memory::sc<eEdges>((m_edges & ~BOTTOM) | (BOTTOM * bottom));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param right The state the right edge should be set to.
|
||||
*/
|
||||
void setRight(bool right) {
|
||||
m_edges = static_cast<eEdges>((m_edges & ~RIGHT) | (RIGHT * right));
|
||||
m_edges = Memory::sc<eEdges>((m_edges & ~RIGHT) | (RIGHT * right));
|
||||
}
|
||||
|
||||
eEdges m_edges = NONE;
|
||||
|
|
|
|||
|
|
@ -21,17 +21,21 @@ namespace Hyprutils {
|
|||
CRegion(pixman_box32_t* box);
|
||||
|
||||
CRegion(const CRegion&);
|
||||
CRegion(CRegion&&);
|
||||
CRegion(CRegion&&) noexcept;
|
||||
|
||||
~CRegion();
|
||||
|
||||
CRegion& operator=(CRegion&& other) {
|
||||
pixman_region32_copy(&m_rRegion, other.pixman());
|
||||
CRegion& operator=(CRegion&& other) noexcept {
|
||||
if (this != &other)
|
||||
pixman_region32_copy(&m_rRegion, other.pixman());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
CRegion& operator=(CRegion& other) {
|
||||
pixman_region32_copy(&m_rRegion, other.pixman());
|
||||
CRegion& operator=(const CRegion& other) {
|
||||
if (this != &other)
|
||||
pixman_region32_copy(&m_rRegion, other.pixman());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
@ -58,12 +62,24 @@ namespace Hyprutils {
|
|||
CRegion copy() const;
|
||||
|
||||
std::vector<pixman_box32_t> getRects() const;
|
||||
template <typename T>
|
||||
void forEachRect(T&& cb) const {
|
||||
int rectsNum = 0;
|
||||
const auto* rects = pixman_region32_rectangles(&m_rRegion, &rectsNum);
|
||||
for (int i = 0; i < rectsNum; ++i) {
|
||||
std::forward<T>(cb)(rects[i]);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
pixman_region32_t* pixman() {
|
||||
return &m_rRegion;
|
||||
}
|
||||
|
||||
const pixman_region32_t* pixman() const {
|
||||
return &m_rRegion;
|
||||
}
|
||||
|
||||
private:
|
||||
pixman_region32_t m_rRegion;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <hyprutils/math/Misc.hpp>
|
||||
|
||||
#include <format>
|
||||
#include <string>
|
||||
|
||||
|
|
@ -97,6 +99,8 @@ namespace Hyprutils {
|
|||
Vector2D round() const;
|
||||
|
||||
Vector2D getComponentMax(const Vector2D& other) const;
|
||||
|
||||
Vector2D transform(eTransform transform, const Vector2D& monitorSize) const;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,393 @@
|
|||
#pragma once
|
||||
|
||||
#include "./ImplBase.hpp"
|
||||
#include "./SharedPtr.hpp"
|
||||
#include "./WeakPtr.hpp"
|
||||
#include <mutex>
|
||||
|
||||
/*
|
||||
This header provides a thread-safe wrapper for Hyprutils shared pointer implementations.
|
||||
Like with STL shared pointers, that does not mean that individual SP/WP objects can be shared across threads without synchronization.
|
||||
It only means that the refcounting of the data is thread-safe.
|
||||
|
||||
Should an Atomic SP/WP be shared across threads, calling a non-const member leads to a data race.
|
||||
To avoid that, each thread should have thread-local SP/WP objects.
|
||||
|
||||
Example:
|
||||
We have a CAtomicSharedPointer member in a class. Suppose this member is accessed by multiple threads and is not constant.
|
||||
In such a case we need external synchronization to ensure valid data access.
|
||||
However, if we create a copy of this CAtomicWeakPointer member for each thread that accesses it,
|
||||
then the references to the object will be counted in a thread-safe manner and it will be safe to lock a WP and to access the data in case of an SP.
|
||||
In such an example, the inner data would need its own synchronization mechanism if it isn't constant itself.
|
||||
*/
|
||||
|
||||
namespace Hyprutils::Memory {
|
||||
namespace Atomic_ {
|
||||
template <typename T>
|
||||
class impl : public Impl_::impl<T> {
|
||||
std::recursive_mutex m_mutex;
|
||||
|
||||
public:
|
||||
impl(T* data, bool lock = true) noexcept : Impl_::impl<T>(data, lock) {
|
||||
;
|
||||
}
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lockGuard() {
|
||||
return std::lock_guard<std::recursive_mutex>(m_mutex);
|
||||
}
|
||||
|
||||
// Needed when unlock order or mutex lifetime matters.
|
||||
std::recursive_mutex& getMutex() {
|
||||
return m_mutex;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Forward declaration for friend
|
||||
template <typename T>
|
||||
class CAtomicWeakPointer;
|
||||
|
||||
template <typename T>
|
||||
class CAtomicSharedPointer {
|
||||
template <typename X>
|
||||
using isConstructible = typename std::enable_if<std::is_constructible<T&, X&>::value>::type;
|
||||
template <typename X>
|
||||
using validHierarchy = typename std::enable_if<std::is_assignable<CAtomicSharedPointer<T>&, X>::value, CAtomicSharedPointer&>::type;
|
||||
|
||||
public:
|
||||
explicit CAtomicSharedPointer(Impl_::impl_base* impl) noexcept : m_ptr(impl) {}
|
||||
|
||||
CAtomicSharedPointer(const CAtomicSharedPointer<T>& ref) {
|
||||
if (!ref.m_ptr.impl_)
|
||||
return;
|
||||
|
||||
auto lg = ref.implLockGuard();
|
||||
m_ptr = ref.m_ptr;
|
||||
}
|
||||
|
||||
template <typename U, typename = isConstructible<U>>
|
||||
CAtomicSharedPointer(const CAtomicSharedPointer<U>& ref) {
|
||||
if (!ref.m_ptr.impl_)
|
||||
return;
|
||||
|
||||
auto lg = ref.implLockGuard();
|
||||
m_ptr = ref.m_ptr;
|
||||
}
|
||||
|
||||
template <typename U, typename = isConstructible<U>>
|
||||
CAtomicSharedPointer(CAtomicSharedPointer<U>&& ref) noexcept {
|
||||
std::swap(m_ptr, ref.m_ptr);
|
||||
}
|
||||
|
||||
CAtomicSharedPointer(CAtomicSharedPointer&& ref) noexcept {
|
||||
std::swap(m_ptr, ref.m_ptr);
|
||||
}
|
||||
|
||||
CAtomicSharedPointer() noexcept = default;
|
||||
|
||||
CAtomicSharedPointer(std::nullptr_t) noexcept {
|
||||
; // empty
|
||||
}
|
||||
|
||||
~CAtomicSharedPointer() {
|
||||
reset();
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
validHierarchy<const CAtomicSharedPointer<U>&> operator=(const CAtomicSharedPointer<U>& rhs) {
|
||||
reset();
|
||||
|
||||
if (!rhs.m_ptr.impl_)
|
||||
return *this;
|
||||
|
||||
auto lg = rhs.implLockGuard();
|
||||
m_ptr = rhs.m_ptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
CAtomicSharedPointer& operator=(const CAtomicSharedPointer& rhs) {
|
||||
if (this == &rhs)
|
||||
return *this;
|
||||
|
||||
reset();
|
||||
|
||||
if (!rhs.m_ptr.impl_)
|
||||
return *this;
|
||||
|
||||
auto lg = rhs.implLockGuard();
|
||||
m_ptr = rhs.m_ptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
validHierarchy<const CAtomicSharedPointer<U>&> operator=(CAtomicSharedPointer<U>&& rhs) noexcept {
|
||||
std::swap(m_ptr, rhs.m_ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
CAtomicSharedPointer& operator=(CAtomicSharedPointer&& rhs) noexcept {
|
||||
if (this == &rhs)
|
||||
return *this;
|
||||
|
||||
std::swap(m_ptr, rhs.m_ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
if (!m_ptr.impl_)
|
||||
return;
|
||||
|
||||
// last ref and last wref?
|
||||
// -> must unlock BEFORE reset
|
||||
// not last ref?
|
||||
// -> must unlock AFTER reset
|
||||
auto& mutex = sc<Atomic_::impl<T>*>(m_ptr.impl_)->getMutex();
|
||||
mutex.lock();
|
||||
|
||||
if (m_ptr.impl_->ref() > 1) {
|
||||
m_ptr.reset();
|
||||
mutex.unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_ptr.impl_->wref() == 0) {
|
||||
mutex.unlock(); // Don't hold the mutex when destroying it
|
||||
m_ptr.reset();
|
||||
// mutex invalid
|
||||
return;
|
||||
} else {
|
||||
// When the control block gets destroyed, the mutex is destroyed with it.
|
||||
// Thus we must avoid attempting an unlock after impl_ has been destroyed.
|
||||
// Without the workaround is no safe way of checking whether it has been destroyed or not.
|
||||
//
|
||||
// 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();
|
||||
|
||||
// Now we can safely check if guard is the last wref.
|
||||
if (guard.impl_->wref() == 1) {
|
||||
mutex.unlock();
|
||||
return; // ~guard destroys impl_ and mutex
|
||||
}
|
||||
|
||||
guard.reset();
|
||||
mutex.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
T& operator*() const {
|
||||
return *m_ptr;
|
||||
}
|
||||
|
||||
T* operator->() const {
|
||||
return m_ptr.get();
|
||||
}
|
||||
|
||||
T* get() const {
|
||||
return m_ptr.get();
|
||||
}
|
||||
|
||||
operator bool() const {
|
||||
return m_ptr;
|
||||
}
|
||||
|
||||
bool operator==(const CAtomicSharedPointer& rhs) const {
|
||||
return m_ptr == rhs.m_ptr;
|
||||
}
|
||||
|
||||
bool operator()(const CAtomicSharedPointer& lhs, const CAtomicSharedPointer& rhs) const {
|
||||
return lhs.m_ptr == rhs.m_ptr;
|
||||
}
|
||||
|
||||
unsigned int strongRef() const {
|
||||
return m_ptr.impl_ ? m_ptr.impl_->ref() : 0;
|
||||
}
|
||||
|
||||
private:
|
||||
std::lock_guard<std::recursive_mutex> implLockGuard() const {
|
||||
return sc<Atomic_::impl<T>*>(m_ptr.impl_)->lockGuard();
|
||||
}
|
||||
|
||||
CSharedPointer<T> m_ptr;
|
||||
|
||||
template <typename U>
|
||||
friend class CAtomicWeakPointer;
|
||||
template <typename U>
|
||||
friend class CAtomicSharedPointer;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class CAtomicWeakPointer {
|
||||
|
||||
template <typename X>
|
||||
using isConstructible = typename std::enable_if<std::is_constructible<T&, X&>::value>::type;
|
||||
template <typename X>
|
||||
using validHierarchy = typename std::enable_if<std::is_assignable<CAtomicWeakPointer<T>&, X>::value, CAtomicWeakPointer&>::type;
|
||||
|
||||
public:
|
||||
CAtomicWeakPointer(const CAtomicWeakPointer<T>& ref) {
|
||||
if (!ref.m_ptr.impl_)
|
||||
return;
|
||||
|
||||
auto lg = ref.implLockGuard();
|
||||
m_ptr = ref.m_ptr;
|
||||
}
|
||||
|
||||
template <typename U, typename = isConstructible<U>>
|
||||
CAtomicWeakPointer(const CAtomicWeakPointer<U>& ref) {
|
||||
if (!ref.m_ptr.impl_)
|
||||
return;
|
||||
|
||||
auto lg = ref.implLockGuard();
|
||||
m_ptr = ref.m_ptr;
|
||||
}
|
||||
|
||||
template <typename U, typename = isConstructible<U>>
|
||||
CAtomicWeakPointer(CAtomicWeakPointer<U>&& ref) noexcept {
|
||||
std::swap(m_ptr, ref.m_ptr);
|
||||
}
|
||||
|
||||
CAtomicWeakPointer(CAtomicWeakPointer&& ref) noexcept {
|
||||
std::swap(m_ptr, ref.m_ptr);
|
||||
}
|
||||
|
||||
CAtomicWeakPointer(const CAtomicSharedPointer<T>& ref) {
|
||||
if (!ref.m_ptr.impl_)
|
||||
return;
|
||||
|
||||
auto lg = ref.implLockGuard();
|
||||
m_ptr = ref.m_ptr;
|
||||
}
|
||||
|
||||
CAtomicWeakPointer() noexcept = default;
|
||||
|
||||
CAtomicWeakPointer(std::nullptr_t) noexcept {
|
||||
; // empty
|
||||
}
|
||||
|
||||
~CAtomicWeakPointer() {
|
||||
reset();
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
validHierarchy<const CAtomicWeakPointer<U>&> operator=(const CAtomicWeakPointer<U>& rhs) {
|
||||
reset();
|
||||
|
||||
auto lg = rhs.implLockGuard();
|
||||
m_ptr = rhs.m_ptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
CAtomicWeakPointer& operator=(const CAtomicWeakPointer& rhs) {
|
||||
if (this == &rhs)
|
||||
return *this;
|
||||
|
||||
reset();
|
||||
|
||||
auto lg = rhs.implLockGuard();
|
||||
m_ptr = rhs.m_ptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
validHierarchy<const CAtomicWeakPointer<U>&> operator=(CAtomicWeakPointer<U>&& rhs) noexcept {
|
||||
std::swap(m_ptr, rhs.m_ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
CAtomicWeakPointer& operator=(CAtomicWeakPointer&& rhs) noexcept {
|
||||
if (this == &rhs)
|
||||
return *this;
|
||||
|
||||
std::swap(m_ptr, rhs.m_ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
if (!m_ptr.impl_)
|
||||
return;
|
||||
|
||||
// last ref and last wref?
|
||||
// -> must unlock BEFORE reset
|
||||
// not last ref?
|
||||
// -> must unlock AFTER reset
|
||||
auto& mutex = sc<Atomic_::impl<T>*>(m_ptr.impl_)->getMutex();
|
||||
mutex.lock();
|
||||
if (m_ptr.impl_->ref() == 0 && m_ptr.impl_->wref() == 1) {
|
||||
mutex.unlock();
|
||||
m_ptr.reset();
|
||||
// mutex invalid
|
||||
return;
|
||||
}
|
||||
|
||||
m_ptr.reset();
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
T& operator*() const {
|
||||
return *m_ptr;
|
||||
}
|
||||
|
||||
T* operator->() const {
|
||||
return m_ptr.get();
|
||||
}
|
||||
|
||||
T* get() const {
|
||||
return m_ptr.get();
|
||||
}
|
||||
|
||||
operator bool() const {
|
||||
return m_ptr;
|
||||
}
|
||||
|
||||
bool operator==(const CAtomicWeakPointer& rhs) const {
|
||||
return m_ptr == rhs.m_ptr;
|
||||
}
|
||||
|
||||
bool operator==(const CAtomicSharedPointer<T>& rhs) const {
|
||||
return m_ptr == rhs.m_ptr;
|
||||
}
|
||||
|
||||
bool operator()(const CAtomicWeakPointer& lhs, const CAtomicWeakPointer& rhs) const {
|
||||
return lhs.m_ptr == rhs.m_ptr;
|
||||
}
|
||||
|
||||
bool expired() {
|
||||
return m_ptr.expired();
|
||||
}
|
||||
|
||||
bool valid() {
|
||||
return m_ptr.valid();
|
||||
}
|
||||
|
||||
CAtomicSharedPointer<T> lock() const {
|
||||
if (!m_ptr.impl_)
|
||||
return {};
|
||||
|
||||
auto lg = implLockGuard();
|
||||
|
||||
if (!m_ptr.impl_->dataNonNull() || m_ptr.impl_->destroying() || !m_ptr.impl_->lockable())
|
||||
return {};
|
||||
|
||||
return CAtomicSharedPointer<T>(m_ptr.impl_);
|
||||
}
|
||||
|
||||
private:
|
||||
std::lock_guard<std::recursive_mutex> implLockGuard() const {
|
||||
return sc<Atomic_::impl<T>*>(m_ptr.impl_)->lockGuard();
|
||||
}
|
||||
|
||||
CWeakPointer<T> m_ptr;
|
||||
|
||||
template <typename U>
|
||||
friend class CAtomicWeakPointer;
|
||||
template <typename U>
|
||||
friend class CAtomicSharedPointer;
|
||||
};
|
||||
|
||||
template <typename U, typename... Args>
|
||||
static CAtomicSharedPointer<U> makeAtomicShared(Args&&... args) {
|
||||
return CAtomicSharedPointer<U>(new Atomic_::impl<U>(new U(std::forward<Args>(args)...)));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
#pragma once
|
||||
#include <bit>
|
||||
#include <utility>
|
||||
namespace Hyprutils::Memory {
|
||||
template <typename To, typename From>
|
||||
constexpr To sc(From&& from) noexcept {
|
||||
return static_cast<To>(std::forward<From>(from));
|
||||
}
|
||||
|
||||
template <typename To, typename From>
|
||||
constexpr To cc(From&& from) noexcept {
|
||||
return const_cast<To>(std::forward<From>(from));
|
||||
}
|
||||
|
||||
template <typename To, typename From>
|
||||
constexpr To rc(From&& from) noexcept {
|
||||
return reinterpret_cast<To>(std::forward<From>(from));
|
||||
}
|
||||
|
||||
template <typename To, typename From>
|
||||
constexpr To dc(From&& from) {
|
||||
return dynamic_cast<To>(std::forward<From>(from));
|
||||
}
|
||||
|
||||
template <typename To, typename From>
|
||||
constexpr To bc(const From& from) noexcept {
|
||||
return std::bit_cast<To>(from);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
namespace Hyprutils {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
#include "ImplBase.hpp"
|
||||
#include "Casts.hpp"
|
||||
|
||||
/*
|
||||
This is a custom impl of std::shared_ptr.
|
||||
|
|
@ -59,9 +61,7 @@ namespace Hyprutils {
|
|||
}
|
||||
|
||||
/* creates an empty shared pointer with no implementation */
|
||||
CSharedPointer() noexcept {
|
||||
; // empty
|
||||
}
|
||||
CSharedPointer() noexcept = default;
|
||||
|
||||
/* creates an empty shared pointer with no implementation */
|
||||
CSharedPointer(std::nullptr_t) noexcept {
|
||||
|
|
@ -113,11 +113,11 @@ namespace Hyprutils {
|
|||
}
|
||||
|
||||
bool operator()(const CSharedPointer& lhs, const CSharedPointer& rhs) const {
|
||||
return reinterpret_cast<uintptr_t>(lhs.impl_) < reinterpret_cast<uintptr_t>(rhs.impl_);
|
||||
return rc<uintptr_t>(lhs.impl_) < rc<uintptr_t>(rhs.impl_);
|
||||
}
|
||||
|
||||
bool operator<(const CSharedPointer& rhs) const {
|
||||
return reinterpret_cast<uintptr_t>(impl_) < reinterpret_cast<uintptr_t>(rhs.impl_);
|
||||
return rc<uintptr_t>(impl_) < rc<uintptr_t>(rhs.impl_);
|
||||
}
|
||||
|
||||
T* operator->() const {
|
||||
|
|
@ -134,7 +134,7 @@ namespace Hyprutils {
|
|||
}
|
||||
|
||||
T* get() const {
|
||||
return impl_ ? static_cast<T*>(impl_->getData()) : nullptr;
|
||||
return impl_ ? sc<T*>(impl_->getData()) : nullptr;
|
||||
}
|
||||
|
||||
unsigned int strongRef() const {
|
||||
|
|
@ -182,7 +182,7 @@ namespace Hyprutils {
|
|||
};
|
||||
|
||||
template <typename U, typename... Args>
|
||||
static CSharedPointer<U> makeShared(Args&&... args) {
|
||||
[[nodiscard]] inline CSharedPointer<U> makeShared(Args&&... args) {
|
||||
return CSharedPointer<U>(new U(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
|
|
@ -198,4 +198,4 @@ struct std::hash<Hyprutils::Memory::CSharedPointer<T>> {
|
|||
std::size_t operator()(const Hyprutils::Memory::CSharedPointer<T>& p) const noexcept {
|
||||
return std::hash<void*>{}(p.impl_);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "ImplBase.hpp"
|
||||
#include "Casts.hpp"
|
||||
|
||||
/*
|
||||
This is a custom impl of std::unique_ptr.
|
||||
|
|
@ -41,9 +42,7 @@ namespace Hyprutils {
|
|||
}
|
||||
|
||||
/* creates an empty unique pointer with no implementation */
|
||||
CUniquePointer() noexcept {
|
||||
; // empty
|
||||
}
|
||||
CUniquePointer() noexcept = default;
|
||||
|
||||
/* creates an empty unique pointer with no implementation */
|
||||
CUniquePointer(std::nullptr_t) noexcept {
|
||||
|
|
@ -74,7 +73,7 @@ namespace Hyprutils {
|
|||
}
|
||||
|
||||
bool operator()(const CUniquePointer& lhs, const CUniquePointer& rhs) const {
|
||||
return reinterpret_cast<uintptr_t>(lhs.impl_) < reinterpret_cast<uintptr_t>(rhs.impl_);
|
||||
return rc<uintptr_t>(lhs.impl_) < rc<uintptr_t>(rhs.impl_);
|
||||
}
|
||||
|
||||
T* operator->() const {
|
||||
|
|
@ -91,7 +90,7 @@ namespace Hyprutils {
|
|||
}
|
||||
|
||||
T* get() const {
|
||||
return impl_ ? static_cast<T*>(impl_->getData()) : nullptr;
|
||||
return impl_ ? sc<T*>(impl_->getData()) : nullptr;
|
||||
}
|
||||
|
||||
Impl_::impl_base* impl_ = nullptr;
|
||||
|
|
@ -135,7 +134,7 @@ namespace Hyprutils {
|
|||
};
|
||||
|
||||
template <typename U, typename... Args>
|
||||
static CUniquePointer<U> makeUnique(Args&&... args) {
|
||||
[[nodiscard]] inline CUniquePointer<U> makeUnique(Args&&... args) {
|
||||
return CUniquePointer<U>(new U(std::forward<Args>(args)...));
|
||||
}
|
||||
}
|
||||
|
|
@ -146,4 +145,4 @@ struct std::hash<Hyprutils::Memory::CUniquePointer<T>> {
|
|||
std::size_t operator()(const Hyprutils::Memory::CUniquePointer<T>& p) const noexcept {
|
||||
return std::hash<void*>{}(p.impl_);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "./SharedPtr.hpp"
|
||||
#include "./UniquePtr.hpp"
|
||||
#include "./Casts.hpp"
|
||||
|
||||
/*
|
||||
This is a Hyprland implementation of std::weak_ptr.
|
||||
|
|
@ -91,7 +92,7 @@ namespace Hyprutils {
|
|||
/* create a weak ptr from a shared ptr with assignment */
|
||||
template <typename U>
|
||||
validHierarchy<const CWeakPointer<U>&> operator=(const CSharedPointer<U>& rhs) {
|
||||
if (reinterpret_cast<uintptr_t>(impl_) == reinterpret_cast<uintptr_t>(rhs.impl_))
|
||||
if (rc<uintptr_t>(impl_) == rc<uintptr_t>(rhs.impl_))
|
||||
return *this;
|
||||
|
||||
decrementWeak();
|
||||
|
|
@ -101,9 +102,7 @@ namespace Hyprutils {
|
|||
}
|
||||
|
||||
/* create an empty weak ptr */
|
||||
CWeakPointer() {
|
||||
;
|
||||
}
|
||||
CWeakPointer() noexcept = default;
|
||||
|
||||
~CWeakPointer() {
|
||||
decrementWeak();
|
||||
|
|
@ -162,15 +161,15 @@ namespace Hyprutils {
|
|||
}
|
||||
|
||||
bool operator()(const CWeakPointer& lhs, const CWeakPointer& rhs) const {
|
||||
return reinterpret_cast<uintptr_t>(lhs.impl_) < reinterpret_cast<uintptr_t>(rhs.impl_);
|
||||
return rc<uintptr_t>(lhs.impl_) < rc<uintptr_t>(rhs.impl_);
|
||||
}
|
||||
|
||||
bool operator<(const CWeakPointer& rhs) const {
|
||||
return reinterpret_cast<uintptr_t>(impl_) < reinterpret_cast<uintptr_t>(rhs.impl_);
|
||||
return rc<uintptr_t>(impl_) < rc<uintptr_t>(rhs.impl_);
|
||||
}
|
||||
|
||||
T* get() const {
|
||||
return impl_ ? static_cast<T*>(impl_->getData()) : nullptr;
|
||||
return impl_ ? sc<T*>(impl_->getData()) : nullptr;
|
||||
}
|
||||
|
||||
T* operator->() const {
|
||||
|
|
|
|||
|
|
@ -13,8 +13,17 @@ namespace Hyprutils {
|
|||
CProcess(const std::string& binary_, const std::vector<std::string>& args_);
|
||||
~CProcess();
|
||||
|
||||
void addEnv(const std::string& name, const std::string& value);
|
||||
CProcess(CProcess&) = delete;
|
||||
CProcess(CProcess&&) = delete;
|
||||
CProcess(const CProcess&&) = delete;
|
||||
CProcess(const CProcess&) = delete;
|
||||
CProcess& operator=(const CProcess&) = delete;
|
||||
CProcess& operator=(CProcess&&) = delete;
|
||||
|
||||
void addEnv(const std::string& name, const std::string& value);
|
||||
|
||||
// only for async, sync doesn't make sense
|
||||
void setStdinFD(int fd);
|
||||
// only for async, sync doesn't make sense
|
||||
void setStdoutFD(int fd);
|
||||
// only for async, sync doesn't make sense
|
||||
|
|
@ -40,4 +49,4 @@ namespace Hyprutils {
|
|||
impl* m_impl;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,39 +6,27 @@
|
|||
|
||||
namespace Hyprutils {
|
||||
namespace Signal {
|
||||
class CSignal;
|
||||
class CSignalBase;
|
||||
|
||||
class CSignalListener {
|
||||
public:
|
||||
CSignalListener(std::function<void(std::any)> handler);
|
||||
|
||||
CSignalListener(CSignalListener&&) = delete;
|
||||
CSignalListener(CSignalListener&) = delete;
|
||||
CSignalListener(const CSignalListener&) = delete;
|
||||
CSignalListener(const CSignalListener&&) = delete;
|
||||
|
||||
void emit(std::any data);
|
||||
[[deprecated("Relic of the legacy untyped signal API. Using this with CSignalT is undefined behavior.")]] void emit(std::any data);
|
||||
|
||||
private:
|
||||
std::function<void(std::any)> m_fHandler;
|
||||
CSignalListener(std::function<void(void*)> handler);
|
||||
|
||||
void emitInternal(void* args);
|
||||
|
||||
std::function<void(void*)> m_fHandler;
|
||||
|
||||
friend class CSignalBase;
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,27 +2,109 @@
|
|||
|
||||
#include <functional>
|
||||
#include <any>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
#include <hyprutils/memory/SharedPtr.hpp>
|
||||
#include <hyprutils/memory/WeakPtr.hpp>
|
||||
#include "./Listener.hpp"
|
||||
|
||||
namespace Hyprutils {
|
||||
namespace Signal {
|
||||
class CSignal {
|
||||
public:
|
||||
void emit(std::any data = {});
|
||||
class CSignalBase {
|
||||
protected:
|
||||
CHyprSignalListener registerListenerInternal(std::function<void(void*)> handler);
|
||||
void registerStaticListenerInternal(std::function<void(void*)> handler);
|
||||
void emitInternal(void* args);
|
||||
|
||||
//
|
||||
[[nodiscard("Listener is unregistered when the ptr is lost")]] CHyprSignalListener registerListener(std::function<void(std::any)> handler);
|
||||
std::vector<Hyprutils::Memory::CWeakPointer<CSignalListener>> m_vListeners;
|
||||
std::vector<Hyprutils::Memory::CSharedPointer<CSignalListener>> m_vStaticListeners;
|
||||
};
|
||||
|
||||
template <typename... Args>
|
||||
class CSignalT : public CSignalBase {
|
||||
template <typename T>
|
||||
using RefArg = std::conditional_t<std::is_reference_v<T> || std::is_arithmetic_v<T>, T, const T&>;
|
||||
|
||||
public:
|
||||
void emit(RefArg<Args>... args) {
|
||||
if constexpr (sizeof...(Args) == 0)
|
||||
emitInternal(nullptr);
|
||||
else {
|
||||
auto argsTuple = std::tuple<RefArg<Args>...>(args...);
|
||||
|
||||
if constexpr (sizeof...(Args) == 1)
|
||||
// NOLINTNEXTLINE: const is reapplied by handler invocation if required
|
||||
emitInternal(Memory::cc<void*>(Memory::sc<const void*>(&std::get<0>(argsTuple))));
|
||||
else
|
||||
emitInternal(&argsTuple);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard("Listener is unregistered when the ptr is lost")]] CHyprSignalListener listen(std::function<void(RefArg<Args>...)> handler) {
|
||||
return registerListenerInternal(mkHandler(handler));
|
||||
}
|
||||
|
||||
[[nodiscard("Listener is unregistered when the ptr is lost")]] CHyprSignalListener listen(std::function<void()> handler)
|
||||
requires(sizeof...(Args) != 0)
|
||||
{
|
||||
return listen([handler](RefArg<Args>... args) { handler(); });
|
||||
}
|
||||
|
||||
template <typename... OtherArgs>
|
||||
[[nodiscard("Listener is unregistered when the ptr is lost")]] CHyprSignalListener forward(CSignalT<OtherArgs...>& signal) {
|
||||
if constexpr (sizeof...(OtherArgs) == 0)
|
||||
return listen([&signal](RefArg<Args>... args) { signal.emit(); });
|
||||
else
|
||||
return listen([&signal](RefArg<Args>... args) { signal.emit(args...); });
|
||||
}
|
||||
|
||||
// deprecated, use listen()
|
||||
CHyprSignalListener registerListener(std::function<void(std::any d)> handler) {
|
||||
return listen([handler](const Args&... args) {
|
||||
constexpr auto mkAny = [](std::any d = {}) { return d; };
|
||||
handler(mkAny(args...));
|
||||
});
|
||||
}
|
||||
|
||||
// this is for static listeners. They die with this signal.
|
||||
// TODO: can we somehow rid of the void* data and make it a custom this?
|
||||
void registerStaticListener(std::function<void(void*, std::any)> handler, void* owner);
|
||||
void listenStatic(std::function<void(RefArg<Args>...)> handler) {
|
||||
registerStaticListenerInternal(mkHandler(handler));
|
||||
}
|
||||
|
||||
void listenStatic(std::function<void()> handler)
|
||||
requires(sizeof...(Args) != 0)
|
||||
{
|
||||
return listenStatic([handler](RefArg<Args>... args) { handler(); });
|
||||
}
|
||||
|
||||
// Deprecated: use listenStatic()
|
||||
void registerStaticListener(std::function<void(void*, std::any)> handler, void* owner) {
|
||||
return listenStatic([handler, owner](const RefArg<Args>&... args) {
|
||||
constexpr auto mkAny = [](std::any d = {}) { return d; };
|
||||
handler(owner, mkAny(args...));
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<Hyprutils::Memory::CWeakPointer<CSignalListener>> m_vListeners;
|
||||
std::vector<std::unique_ptr<CStaticSignalListener>> m_vStaticListeners;
|
||||
std::function<void(void*)> mkHandler(std::function<void(RefArg<Args>...)> handler) {
|
||||
return [handler](void* args) {
|
||||
if constexpr (sizeof...(Args) == 0)
|
||||
handler();
|
||||
else if constexpr (sizeof...(Args) == 1)
|
||||
handler(*Memory::sc<std::remove_reference_t<std::tuple_element_t<0, std::tuple<RefArg<Args>...>>>*>(args));
|
||||
else
|
||||
std::apply(handler, *Memory::sc<std::tuple<RefArg<Args>...>*>(args));
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// compat. Deprecated.
|
||||
class CSignal : public CSignalT<std::any> {
|
||||
public:
|
||||
void emit(std::any data = {});
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,64 @@
|
|||
#pragma once
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace Hyprutils {
|
||||
namespace String {
|
||||
class CConstVarList {
|
||||
public:
|
||||
/** Split string into an immutable arg list
|
||||
@param lastArgNo stop splitting after argv reaches maximum size, last arg will contain rest of unsplit args
|
||||
@param delim if delimiter is 's', use std::isspace
|
||||
@param removeEmpty remove empty args from argv
|
||||
*/
|
||||
CConstVarList(const std::string& in, const size_t lastArgNo = 0, const char delim = ',', const bool removeEmpty = false);
|
||||
|
||||
~CConstVarList() = default;
|
||||
|
||||
size_t size() const {
|
||||
return m_args.size();
|
||||
}
|
||||
|
||||
std::string join(const std::string& joiner, size_t from = 0, size_t to = 0) const;
|
||||
|
||||
void map(std::function<void(const std::string_view&)> func) {
|
||||
for (auto& s : m_args)
|
||||
func(s);
|
||||
}
|
||||
|
||||
std::string_view operator[](const size_t& idx) const {
|
||||
if (idx >= m_args.size())
|
||||
return "";
|
||||
return m_args[idx];
|
||||
}
|
||||
|
||||
// for range-based loops
|
||||
std::vector<std::string_view>::iterator begin() {
|
||||
return m_args.begin();
|
||||
}
|
||||
std::vector<std::string_view>::const_iterator begin() const {
|
||||
return m_args.begin();
|
||||
}
|
||||
std::vector<std::string_view>::iterator end() {
|
||||
return m_args.end();
|
||||
}
|
||||
std::vector<std::string_view>::const_iterator end() const {
|
||||
return m_args.end();
|
||||
}
|
||||
|
||||
bool contains(const std::string_view& el) {
|
||||
for (auto& a : m_args) {
|
||||
if (a == el)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_str;
|
||||
std::vector<std::string_view> m_args;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -8,19 +8,23 @@
|
|||
version ? "git",
|
||||
doCheck ? false,
|
||||
debug ? false,
|
||||
# whether to use the mold linker
|
||||
# disable this for older machines without SSE4_2 and AVX2 support
|
||||
withMold ? true,
|
||||
}: let
|
||||
inherit (builtins) foldl';
|
||||
inherit (lib.lists) flatten;
|
||||
inherit (lib.strings) optionalString;
|
||||
|
||||
adapters = flatten [
|
||||
stdenvAdapters.useMoldLinker
|
||||
(lib.optional withMold stdenvAdapters.useMoldLinker)
|
||||
(lib.optional debug stdenvAdapters.keepDebugInfo)
|
||||
];
|
||||
|
||||
customStdenv = foldl' (acc: adapter: adapter acc) stdenv adapters;
|
||||
in
|
||||
customStdenv.mkDerivation {
|
||||
pname = "hyprutils";
|
||||
pname = "hyprutils" + optionalString debug "-debug";
|
||||
inherit version doCheck;
|
||||
src = ../.;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
self,
|
||||
lib,
|
||||
}: let
|
||||
mkDate = longDate: (lib.concatStringsSep "-" [
|
||||
(builtins.substring 0 4 longDate)
|
||||
(builtins.substring 4 2 longDate)
|
||||
(builtins.substring 6 2 longDate)
|
||||
]);
|
||||
|
||||
ver = lib.removeSuffix "\n" (builtins.readFile ../VERSION);
|
||||
version = ver + "+date=" + (mkDate (self.lastModifiedDate or "19700101")) + "_" + (self.shortRev or "dirty");
|
||||
in {
|
||||
default = self.overlays.hyprutils;
|
||||
hyprutils = final: prev: {
|
||||
hyprutils = final.callPackage ./default.nix {
|
||||
stdenv = final.gcc15Stdenv;
|
||||
inherit version;
|
||||
};
|
||||
hyprutils-debug = final.hyprutils.override {debug = true;};
|
||||
hyprutils-with-tests = final.hyprutils.override {doCheck = true;};
|
||||
};
|
||||
}
|
||||
|
|
@ -20,31 +20,18 @@ CAnimationManager::CAnimationManager() {
|
|||
m_events = makeUnique<SAnimationManagerSignals>();
|
||||
m_listeners = makeUnique<SAnimVarListeners>();
|
||||
|
||||
m_listeners->connect = m_events->connect.registerListener([this](std::any data) { onConnect(data); });
|
||||
m_listeners->disconnect = m_events->disconnect.registerListener([this](std::any data) { onDisconnect(data); });
|
||||
}
|
||||
m_listeners->connect = m_events->connect.listen([this](const WP<CBaseAnimatedVariable>& animVar) {
|
||||
if (!m_bTickScheduled)
|
||||
scheduleTick();
|
||||
|
||||
void CAnimationManager::onConnect(std::any data) {
|
||||
if (!m_bTickScheduled)
|
||||
scheduleTick();
|
||||
if (animVar)
|
||||
m_vActiveAnimatedVariables.emplace_back(animVar);
|
||||
});
|
||||
|
||||
try {
|
||||
const auto PAV = std::any_cast<WP<CBaseAnimatedVariable>>(data);
|
||||
if (!PAV)
|
||||
return;
|
||||
|
||||
m_vActiveAnimatedVariables.emplace_back(PAV);
|
||||
} catch (const std::bad_any_cast&) { return; }
|
||||
}
|
||||
|
||||
void CAnimationManager::onDisconnect(std::any data) {
|
||||
try {
|
||||
const auto PAV = std::any_cast<WP<CBaseAnimatedVariable>>(data);
|
||||
if (!PAV)
|
||||
return;
|
||||
|
||||
std::erase_if(m_vActiveAnimatedVariables, [&](const auto& other) { return !other || other == PAV; });
|
||||
} catch (const std::bad_any_cast&) { return; }
|
||||
m_listeners->disconnect = m_events->disconnect.listen([this](const WP<CBaseAnimatedVariable>& animVar) {
|
||||
if (animVar)
|
||||
std::erase_if(m_vActiveAnimatedVariables, [&](const auto& other) { return !other || other == animVar; });
|
||||
});
|
||||
}
|
||||
|
||||
void CAnimationManager::removeAllBeziers() {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
#include <hyprutils/animation/BezierCurve.hpp>
|
||||
#include <hyprutils/memory/Casts.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
|
||||
using namespace Hyprutils::Animation;
|
||||
using namespace Hyprutils::Math;
|
||||
using namespace Hyprutils::Memory;
|
||||
|
||||
void CBezierCurve::setup(const std::array<Vector2D, 2>& pVec) {
|
||||
// Avoid reallocations by reserving enough memory upfront
|
||||
|
|
@ -21,7 +23,7 @@ void CBezierCurve::setup(const std::array<Vector2D, 2>& pVec) {
|
|||
// bake BAKEDPOINTS points for faster lookups
|
||||
// T -> X ( / BAKEDPOINTS )
|
||||
for (int i = 0; i < BAKEDPOINTS; ++i) {
|
||||
float const t = (i + 1) / (float)BAKEDPOINTS;
|
||||
float const t = (i + 1) / sc<float>(BAKEDPOINTS);
|
||||
m_aPointsBaked[i] = Vector2D(getXForT(t), getYForT(t));
|
||||
}
|
||||
|
||||
|
|
@ -76,3 +78,7 @@ float CBezierCurve::getYForPoint(float const& x) const {
|
|||
|
||||
return LOWERPOINT->y + ((UPPERPOINT->y - LOWERPOINT->y) * PERCINDELTA);
|
||||
}
|
||||
|
||||
const std::vector<Hyprutils::Math::Vector2D>& CBezierCurve::getControlPoints() const {
|
||||
return m_vPoints;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
#include <hyprutils/math/Mat3x3.hpp>
|
||||
#include <hyprutils/math/Vector2D.hpp>
|
||||
#include <hyprutils/math/Box.hpp>
|
||||
#include <hyprutils/memory/Casts.hpp>
|
||||
#include <cmath>
|
||||
#include <unordered_map>
|
||||
#include <format>
|
||||
|
||||
using namespace Hyprutils::Math;
|
||||
using namespace Hyprutils::Memory;
|
||||
|
||||
static std::unordered_map<eTransform, Mat3x3> transforms = {
|
||||
{HYPRUTILS_TRANSFORM_NORMAL, std::array<float, 9>{1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f}},
|
||||
|
|
@ -93,12 +95,12 @@ Mat3x3& Mat3x3::transform(eTransform transform) {
|
|||
}
|
||||
|
||||
Mat3x3& Mat3x3::rotate(float rot) {
|
||||
multiply(std::array<float, 9>{(float)cos(rot), (float)-sin(rot), 0.0f, (float)sin(rot), (float)cos(rot), 0.0f, 0.0f, 0.0f, 1.0f});
|
||||
multiply(std::array<float, 9>{cosf(rot), -sinf(rot), 0.0f, sinf(rot), cosf(rot), 0.0f, 0.0f, 0.0f, 1.0f});
|
||||
return *this;
|
||||
}
|
||||
|
||||
Mat3x3& Mat3x3::scale(const Vector2D& scale_) {
|
||||
multiply(std::array<float, 9>{(float)scale_.x, 0.0f, 0.0f, 0.0f, (float)scale_.y, 0.0f, 0.0f, 0.0f, 1.0f});
|
||||
multiply(std::array<float, 9>{sc<float>(scale_.x), 0.0f, 0.0f, 0.0f, sc<float>(scale_.y), 0.0f, 0.0f, 0.0f, 1.0f});
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
@ -107,7 +109,7 @@ Mat3x3& Mat3x3::scale(const float scale_) {
|
|||
}
|
||||
|
||||
Mat3x3& Mat3x3::translate(const Vector2D& offset) {
|
||||
multiply(std::array<float, 9>{1.0f, 0.0f, (float)offset.x, 0.0f, 1.0f, (float)offset.y, 0.0f, 0.0f, 1.0f});
|
||||
multiply(std::array<float, 9>{1.0f, 0.0f, sc<float>(offset.x), 0.0f, 1.0f, sc<float>(offset.y), 0.0f, 0.0f, 1.0f});
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
@ -117,19 +119,22 @@ Mat3x3& Mat3x3::transpose() {
|
|||
}
|
||||
|
||||
Mat3x3& Mat3x3::multiply(const Mat3x3& other) {
|
||||
const float* m1 = matrix.data(); // Pointer to current matrix
|
||||
const float* m2 = other.matrix.data(); // Pointer to the other matrix
|
||||
|
||||
std::array<float, 9> product;
|
||||
|
||||
product[0] = matrix[0] * other.matrix[0] + matrix[1] * other.matrix[3] + matrix[2] * other.matrix[6];
|
||||
product[1] = matrix[0] * other.matrix[1] + matrix[1] * other.matrix[4] + matrix[2] * other.matrix[7];
|
||||
product[2] = matrix[0] * other.matrix[2] + matrix[1] * other.matrix[5] + matrix[2] * other.matrix[8];
|
||||
product[0] = m1[0] * m2[0] + m1[1] * m2[3] + m1[2] * m2[6];
|
||||
product[1] = m1[0] * m2[1] + m1[1] * m2[4] + m1[2] * m2[7];
|
||||
product[2] = m1[0] * m2[2] + m1[1] * m2[5] + m1[2] * m2[8];
|
||||
|
||||
product[3] = matrix[3] * other.matrix[0] + matrix[4] * other.matrix[3] + matrix[5] * other.matrix[6];
|
||||
product[4] = matrix[3] * other.matrix[1] + matrix[4] * other.matrix[4] + matrix[5] * other.matrix[7];
|
||||
product[5] = matrix[3] * other.matrix[2] + matrix[4] * other.matrix[5] + matrix[5] * other.matrix[8];
|
||||
product[3] = m1[3] * m2[0] + m1[4] * m2[3] + m1[5] * m2[6];
|
||||
product[4] = m1[3] * m2[1] + m1[4] * m2[4] + m1[5] * m2[7];
|
||||
product[5] = m1[3] * m2[2] + m1[4] * m2[5] + m1[5] * m2[8];
|
||||
|
||||
product[6] = matrix[6] * other.matrix[0] + matrix[7] * other.matrix[3] + matrix[8] * other.matrix[6];
|
||||
product[7] = matrix[6] * other.matrix[1] + matrix[7] * other.matrix[4] + matrix[8] * other.matrix[7];
|
||||
product[8] = matrix[6] * other.matrix[2] + matrix[7] * other.matrix[5] + matrix[8] * other.matrix[8];
|
||||
product[6] = m1[6] * m2[0] + m1[7] * m2[3] + m1[8] * m2[6];
|
||||
product[7] = m1[6] * m2[1] + m1[7] * m2[4] + m1[8] * m2[7];
|
||||
product[8] = m1[6] * m2[2] + m1[7] * m2[5] + m1[8] * m2[8];
|
||||
|
||||
matrix = product;
|
||||
return *this;
|
||||
|
|
@ -140,6 +145,11 @@ Mat3x3 Mat3x3::copy() const {
|
|||
}
|
||||
|
||||
std::string Mat3x3::toString() const {
|
||||
for (const auto& m : matrix) {
|
||||
if (!std::isfinite(m))
|
||||
return "[mat3x3: invalid values]";
|
||||
}
|
||||
|
||||
return std::format("[mat3x3: {}, {}, {}, {}, {}, {}, {}, {}, {}]", matrix.at(0), matrix.at(1), matrix.at(2), matrix.at(3), matrix.at(4), matrix.at(5), matrix.at(6),
|
||||
matrix.at(7), matrix.at(8));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
#include "hyprutils/memory/Casts.hpp"
|
||||
#include <hyprutils/math/Region.hpp>
|
||||
#include <cmath>
|
||||
|
||||
using namespace Hyprutils::Math;
|
||||
using namespace Hyprutils::Memory;
|
||||
|
||||
constexpr const int64_t MAX_REGION_SIDE = 10000000;
|
||||
|
||||
|
|
@ -28,10 +30,10 @@ Hyprutils::Math::CRegion::CRegion(pixman_box32_t* box) {
|
|||
|
||||
Hyprutils::Math::CRegion::CRegion(const CRegion& other) {
|
||||
pixman_region32_init(&m_rRegion);
|
||||
pixman_region32_copy(&m_rRegion, const_cast<CRegion*>(&other)->pixman());
|
||||
pixman_region32_copy(&m_rRegion, other.pixman());
|
||||
}
|
||||
|
||||
Hyprutils::Math::CRegion::CRegion(CRegion&& other) {
|
||||
Hyprutils::Math::CRegion::CRegion(CRegion&& other) noexcept {
|
||||
pixman_region32_init(&m_rRegion);
|
||||
pixman_region32_copy(&m_rRegion, other.pixman());
|
||||
}
|
||||
|
|
@ -46,12 +48,12 @@ CRegion& Hyprutils::Math::CRegion::clear() {
|
|||
}
|
||||
|
||||
CRegion& Hyprutils::Math::CRegion::set(const CRegion& other) {
|
||||
pixman_region32_copy(&m_rRegion, const_cast<CRegion*>(&other)->pixman());
|
||||
pixman_region32_copy(&m_rRegion, other.pixman());
|
||||
return *this;
|
||||
}
|
||||
|
||||
CRegion& Hyprutils::Math::CRegion::add(const CRegion& other) {
|
||||
pixman_region32_union(&m_rRegion, &m_rRegion, const_cast<CRegion*>(&other)->pixman());
|
||||
pixman_region32_union(&m_rRegion, &m_rRegion, other.pixman());
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
@ -66,12 +68,12 @@ CRegion& Hyprutils::Math::CRegion::add(const CBox& other) {
|
|||
}
|
||||
|
||||
CRegion& Hyprutils::Math::CRegion::subtract(const CRegion& other) {
|
||||
pixman_region32_subtract(&m_rRegion, &m_rRegion, const_cast<CRegion*>(&other)->pixman());
|
||||
pixman_region32_subtract(&m_rRegion, &m_rRegion, other.pixman());
|
||||
return *this;
|
||||
}
|
||||
|
||||
CRegion& Hyprutils::Math::CRegion::intersect(const CRegion& other) {
|
||||
pixman_region32_intersect(&m_rRegion, &m_rRegion, const_cast<CRegion*>(&other)->pixman());
|
||||
pixman_region32_intersect(&m_rRegion, &m_rRegion, other.pixman());
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
@ -86,7 +88,7 @@ CRegion& Hyprutils::Math::CRegion::invert(pixman_box32_t* box) {
|
|||
}
|
||||
|
||||
CRegion& Hyprutils::Math::CRegion::invert(const CBox& box) {
|
||||
pixman_box32 pixmanBox = {.x1 = (int32_t)box.x, .y1 = (int32_t)box.y, .x2 = (int32_t)box.w + (int32_t)box.x, .y2 = (int32_t)box.h + (int32_t)box.y};
|
||||
pixman_box32 pixmanBox = {.x1 = sc<int32_t>(box.x), .y1 = sc<int32_t>(box.y), .x2 = sc<int32_t>(box.w) + sc<int32_t>(box.x), .y2 = sc<int32_t>(box.h) + sc<int32_t>(box.y)};
|
||||
return this->invert(&pixmanBox);
|
||||
}
|
||||
|
||||
|
|
@ -104,7 +106,7 @@ CRegion& Hyprutils::Math::CRegion::transform(const eTransform t, double w, doubl
|
|||
clear();
|
||||
|
||||
for (auto& r : rects) {
|
||||
CBox xfmd{(double)r.x1, (double)r.y1, (double)r.x2 - r.x1, (double)r.y2 - r.y1};
|
||||
CBox xfmd{r.x1, r.y1, r.x2 - r.x1, r.y2 - r.y1};
|
||||
xfmd.transform(t, w, h);
|
||||
add(xfmd);
|
||||
}
|
||||
|
|
@ -118,7 +120,7 @@ CRegion& Hyprutils::Math::CRegion::expand(double units) {
|
|||
clear();
|
||||
|
||||
for (auto& r : rects) {
|
||||
CBox b{(double)r.x1 - units, (double)r.y1 - units, (double)r.x2 - r.x1 + (units * 2), (double)r.y2 - r.y1 + (units * 2)};
|
||||
CBox b{sc<double>(r.x1) - units, sc<double>(r.y1) - units, sc<double>(r.x2) - r.x1 + (units * 2), sc<double>(r.y2) - r.y1 + (units * 2)};
|
||||
add(b);
|
||||
}
|
||||
|
||||
|
|
@ -171,7 +173,7 @@ std::vector<pixman_box32_t> Hyprutils::Math::CRegion::getRects() const {
|
|||
|
||||
CBox Hyprutils::Math::CRegion::getExtents() {
|
||||
pixman_box32_t* box = pixman_region32_extents(&m_rRegion);
|
||||
return {(double)box->x1, (double)box->y1, (double)box->x2 - box->x1, (double)box->y2 - box->y1};
|
||||
return {sc<double>(box->x1), sc<double>(box->y1), sc<double>(box->x2) - box->x1, sc<double>(box->y2) - box->y1};
|
||||
}
|
||||
|
||||
bool Hyprutils::Math::CRegion::containsPoint(const Vector2D& vec) const {
|
||||
|
|
|
|||
|
|
@ -1,14 +1,17 @@
|
|||
#include <hyprutils/math/Vector2D.hpp>
|
||||
#include <hyprutils/memory/Casts.hpp>
|
||||
#include <hyprutils/math/Misc.hpp>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
using namespace Hyprutils::Math;
|
||||
using namespace Hyprutils::Memory;
|
||||
|
||||
Hyprutils::Math::Vector2D::Vector2D(double xx, double yy) : x(xx), y(yy) {
|
||||
;
|
||||
}
|
||||
|
||||
Hyprutils::Math::Vector2D::Vector2D(int xx, int yy) : x((double)xx), y((double)yy) {
|
||||
Hyprutils::Math::Vector2D::Vector2D(int xx, int yy) : x(sc<double>(xx)), y(sc<double>(yy)) {
|
||||
;
|
||||
}
|
||||
|
||||
|
|
@ -55,3 +58,17 @@ double Hyprutils::Math::Vector2D::size() const {
|
|||
Vector2D Hyprutils::Math::Vector2D::getComponentMax(const Vector2D& other) const {
|
||||
return Vector2D(std::max(this->x, other.x), std::max(this->y, other.y));
|
||||
}
|
||||
|
||||
Vector2D Hyprutils::Math::Vector2D::transform(eTransform transform, const Vector2D& monitorSize) const {
|
||||
switch (transform) {
|
||||
case HYPRUTILS_TRANSFORM_NORMAL: return *this;
|
||||
case HYPRUTILS_TRANSFORM_90: return Vector2D(y, monitorSize.y - x);
|
||||
case HYPRUTILS_TRANSFORM_180: return Vector2D(monitorSize.x - x, monitorSize.y - y);
|
||||
case HYPRUTILS_TRANSFORM_270: return Vector2D(monitorSize.x - y, x);
|
||||
case HYPRUTILS_TRANSFORM_FLIPPED: return Vector2D(monitorSize.x - x, y);
|
||||
case HYPRUTILS_TRANSFORM_FLIPPED_90: return Vector2D(y, x);
|
||||
case HYPRUTILS_TRANSFORM_FLIPPED_180: return Vector2D(x, monitorSize.y - y);
|
||||
case HYPRUTILS_TRANSFORM_FLIPPED_270: return Vector2D(monitorSize.x - y, monitorSize.y - x);
|
||||
default: return *this;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
#include <hyprutils/os/Process.hpp>
|
||||
#include <hyprutils/memory/Casts.hpp>
|
||||
using namespace Hyprutils::OS;
|
||||
using namespace Hyprutils::Memory;
|
||||
|
||||
#include <csignal>
|
||||
#include <cstdio>
|
||||
|
|
@ -17,7 +19,7 @@ struct Hyprutils::OS::CProcess::impl {
|
|||
std::vector<std::string> args;
|
||||
std::vector<std::pair<std::string, std::string>> env;
|
||||
pid_t grandchildPid = 0;
|
||||
int stdoutFD = -1, stderrFD = -1, exitCode = 0;
|
||||
int stdoutFD = -1, stderrFD = -1, exitCode = 0, stdinFD = -1;
|
||||
};
|
||||
|
||||
Hyprutils::OS::CProcess::CProcess(const std::string& binary, const std::vector<std::string>& args) : m_impl(new impl()) {
|
||||
|
|
@ -61,7 +63,7 @@ bool Hyprutils::OS::CProcess::runSync() {
|
|||
dup2(errPipe[1], 2 /* stderr */);
|
||||
|
||||
// build argv
|
||||
std::vector<const char*> argsC;
|
||||
std::vector<char*> argsC;
|
||||
argsC.emplace_back(strdup(m_impl->binary.c_str()));
|
||||
for (auto& arg : m_impl->args) {
|
||||
// TODO: does this leak? Can we just pipe c_str() as the strings won't be realloc'd?
|
||||
|
|
@ -75,7 +77,7 @@ bool Hyprutils::OS::CProcess::runSync() {
|
|||
setenv(n.c_str(), v.c_str(), 1);
|
||||
}
|
||||
|
||||
execvp(m_impl->binary.c_str(), (char* const*)argsC.data());
|
||||
execvp(m_impl->binary.c_str(), argsC.data());
|
||||
exit(1);
|
||||
} else {
|
||||
// parent
|
||||
|
|
@ -129,7 +131,7 @@ bool Hyprutils::OS::CProcess::runSync() {
|
|||
|
||||
if (pollfds[0].revents & POLLIN) {
|
||||
while ((ret = read(outPipe[0], buf.data(), 1023)) > 0) {
|
||||
m_impl->out += std::string_view{(char*)buf.data(), (size_t)ret};
|
||||
m_impl->out += std::string_view{buf.data(), sc<size_t>(ret)};
|
||||
}
|
||||
|
||||
buf.fill(0);
|
||||
|
|
@ -137,7 +139,7 @@ bool Hyprutils::OS::CProcess::runSync() {
|
|||
|
||||
if (pollfds[1].revents & POLLIN) {
|
||||
while ((ret = read(errPipe[0], buf.data(), 1023)) > 0) {
|
||||
m_impl->err += std::string_view{(char*)buf.data(), (size_t)ret};
|
||||
m_impl->err += std::string_view{buf.data(), sc<size_t>(ret)};
|
||||
}
|
||||
|
||||
buf.fill(0);
|
||||
|
|
@ -146,13 +148,13 @@ bool Hyprutils::OS::CProcess::runSync() {
|
|||
|
||||
// Final reads. Nonblock, so its ok.
|
||||
while ((ret = read(outPipe[0], buf.data(), 1023)) > 0) {
|
||||
m_impl->out += std::string_view{(char*)buf.data(), (size_t)ret};
|
||||
m_impl->out += std::string_view{buf.data(), sc<size_t>(ret)};
|
||||
}
|
||||
|
||||
buf.fill(0);
|
||||
|
||||
while ((ret = read(errPipe[0], buf.data(), 1023)) > 0) {
|
||||
m_impl->err += std::string_view{(char*)buf.data(), (size_t)ret};
|
||||
m_impl->err += std::string_view{buf.data(), sc<size_t>(ret)};
|
||||
}
|
||||
|
||||
buf.fill(0);
|
||||
|
|
@ -198,7 +200,7 @@ bool Hyprutils::OS::CProcess::runAsync() {
|
|||
close(socket[0]);
|
||||
close(socket[1]);
|
||||
// build argv
|
||||
std::vector<const char*> argsC;
|
||||
std::vector<char*> argsC;
|
||||
argsC.emplace_back(strdup(m_impl->binary.c_str()));
|
||||
for (auto& arg : m_impl->args) {
|
||||
argsC.emplace_back(strdup(arg.c_str()));
|
||||
|
|
@ -206,12 +208,25 @@ bool Hyprutils::OS::CProcess::runAsync() {
|
|||
|
||||
argsC.emplace_back(nullptr);
|
||||
|
||||
if (m_impl->stdoutFD != -1)
|
||||
dup2(m_impl->stdoutFD, 1);
|
||||
if (m_impl->stderrFD != -1)
|
||||
dup2(m_impl->stderrFD, 2);
|
||||
// pass env
|
||||
for (auto& [n, v] : m_impl->env) {
|
||||
setenv(n.c_str(), v.c_str(), 1);
|
||||
}
|
||||
|
||||
execvp(m_impl->binary.c_str(), (char* const*)argsC.data());
|
||||
if (m_impl->stdinFD != -1) {
|
||||
dup2(m_impl->stdinFD, STDIN_FILENO);
|
||||
close(m_impl->stdinFD);
|
||||
}
|
||||
if (m_impl->stdoutFD != -1) {
|
||||
dup2(m_impl->stdoutFD, STDOUT_FILENO);
|
||||
close(m_impl->stdoutFD);
|
||||
}
|
||||
if (m_impl->stderrFD != -1) {
|
||||
dup2(m_impl->stderrFD, STDERR_FILENO);
|
||||
close(m_impl->stderrFD);
|
||||
}
|
||||
|
||||
execvp(m_impl->binary.c_str(), argsC.data());
|
||||
_exit(0);
|
||||
}
|
||||
close(socket[0]);
|
||||
|
|
@ -257,6 +272,10 @@ int Hyprutils::OS::CProcess::exitCode() {
|
|||
return m_impl->exitCode;
|
||||
}
|
||||
|
||||
void Hyprutils::OS::CProcess::setStdinFD(int fd) {
|
||||
m_impl->stdinFD = fd;
|
||||
}
|
||||
|
||||
void Hyprutils::OS::CProcess::setStdoutFD(int fd) {
|
||||
m_impl->stdoutFD = fd;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,20 @@
|
|||
#include <hyprutils/signal/Listener.hpp>
|
||||
#include <tuple>
|
||||
|
||||
using namespace Hyprutils::Signal;
|
||||
|
||||
Hyprutils::Signal::CSignalListener::CSignalListener(std::function<void(std::any)> handler) : m_fHandler(handler) {
|
||||
Hyprutils::Signal::CSignalListener::CSignalListener(std::function<void(void*)> handler) : m_fHandler(handler) {
|
||||
;
|
||||
}
|
||||
|
||||
void Hyprutils::Signal::CSignalListener::emit(std::any data) {
|
||||
void Hyprutils::Signal::CSignalListener::emitInternal(void* data) {
|
||||
if (!m_fHandler)
|
||||
return;
|
||||
|
||||
m_fHandler(data);
|
||||
}
|
||||
|
||||
Hyprutils::Signal::CStaticSignalListener::CStaticSignalListener(std::function<void(void*, std::any)> handler, void* owner) : m_pOwner(owner), m_fHandler(handler) {
|
||||
;
|
||||
}
|
||||
|
||||
void Hyprutils::Signal::CStaticSignalListener::emit(std::any data) {
|
||||
m_fHandler(m_pOwner, data);
|
||||
void Hyprutils::Signal::CSignalListener::emit(std::any data) {
|
||||
auto dataTuple = std::tuple<std::any>(data);
|
||||
emitInternal(&dataTuple);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#include "hyprutils/memory/SharedPtr.hpp"
|
||||
#include <hyprutils/signal/Signal.hpp>
|
||||
#include <hyprutils/memory/WeakPtr.hpp>
|
||||
#include <algorithm>
|
||||
|
|
@ -8,8 +9,9 @@ using namespace Hyprutils::Memory;
|
|||
#define SP CSharedPointer
|
||||
#define WP CWeakPointer
|
||||
|
||||
void Hyprutils::Signal::CSignal::emit(std::any data) {
|
||||
void Hyprutils::Signal::CSignalBase::emitInternal(void* args) {
|
||||
std::vector<SP<CSignalListener>> listeners;
|
||||
listeners.reserve(m_vListeners.size());
|
||||
for (auto& l : m_vListeners) {
|
||||
if (l.expired())
|
||||
continue;
|
||||
|
|
@ -17,11 +19,7 @@ void Hyprutils::Signal::CSignal::emit(std::any data) {
|
|||
listeners.emplace_back(l.lock());
|
||||
}
|
||||
|
||||
std::vector<CStaticSignalListener*> statics;
|
||||
statics.reserve(m_vStaticListeners.size());
|
||||
for (auto& l : m_vStaticListeners) {
|
||||
statics.emplace_back(l.get());
|
||||
}
|
||||
auto statics = m_vStaticListeners;
|
||||
|
||||
for (auto& l : listeners) {
|
||||
// if there is only one lock, it means the event is only held by the listeners
|
||||
|
|
@ -29,11 +27,11 @@ void Hyprutils::Signal::CSignal::emit(std::any data) {
|
|||
if (l.strongRef() == 1)
|
||||
continue;
|
||||
|
||||
l->emit(data);
|
||||
l->emitInternal(args);
|
||||
}
|
||||
|
||||
for (auto& l : statics) {
|
||||
l->emit(data);
|
||||
l->emitInternal(args);
|
||||
}
|
||||
|
||||
// release SPs
|
||||
|
|
@ -43,8 +41,8 @@ void Hyprutils::Signal::CSignal::emit(std::any data) {
|
|||
// as such we'd be doing a UAF
|
||||
}
|
||||
|
||||
CHyprSignalListener Hyprutils::Signal::CSignal::registerListener(std::function<void(std::any)> handler) {
|
||||
CHyprSignalListener listener = makeShared<CSignalListener>(handler);
|
||||
CHyprSignalListener Hyprutils::Signal::CSignalBase::registerListenerInternal(std::function<void(void*)> handler) {
|
||||
CHyprSignalListener listener = SP<CSignalListener>(new CSignalListener(handler));
|
||||
m_vListeners.emplace_back(listener);
|
||||
|
||||
// housekeeping: remove any stale listeners
|
||||
|
|
@ -53,6 +51,10 @@ CHyprSignalListener Hyprutils::Signal::CSignal::registerListener(std::function<v
|
|||
return listener;
|
||||
}
|
||||
|
||||
void Hyprutils::Signal::CSignal::registerStaticListener(std::function<void(void*, std::any)> handler, void* owner) {
|
||||
m_vStaticListeners.emplace_back(std::make_unique<CStaticSignalListener>(handler, owner));
|
||||
void Hyprutils::Signal::CSignalBase::registerStaticListenerInternal(std::function<void(void*)> handler) {
|
||||
m_vStaticListeners.emplace_back(SP<CSignalListener>(new CSignalListener(handler)));
|
||||
}
|
||||
|
||||
void Hyprutils::Signal::CSignal::emit(std::any data) {
|
||||
CSignalT::emit(data);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
#include <ranges>
|
||||
#include <algorithm>
|
||||
#include <hyprutils/string/ConstVarList.hpp>
|
||||
|
||||
using namespace Hyprutils::String;
|
||||
|
||||
static std::string_view trim(const std::string_view& sv) {
|
||||
if (sv.empty())
|
||||
return sv;
|
||||
|
||||
size_t countBefore = 0;
|
||||
while (countBefore < sv.length() && std::isspace(sv.at(countBefore))) {
|
||||
countBefore++;
|
||||
}
|
||||
|
||||
size_t countAfter = 0;
|
||||
while (countAfter < sv.length() - countBefore && std::isspace(sv.at(sv.length() - countAfter - 1))) {
|
||||
countAfter++;
|
||||
}
|
||||
|
||||
return sv.substr(countBefore, sv.length() - countBefore - countAfter);
|
||||
}
|
||||
|
||||
CConstVarList::CConstVarList(const std::string& in, const size_t lastArgNo, const char delim, const bool removeEmpty) : m_str(in) {
|
||||
if (in.empty())
|
||||
return;
|
||||
|
||||
size_t idx = 0;
|
||||
size_t pos = 0;
|
||||
std::ranges::replace_if(m_str, [&](const char& c) { return delim == 's' ? std::isspace(c) : c == delim; }, 0);
|
||||
|
||||
for (const auto& s : m_str | std::views::split(0)) {
|
||||
if (removeEmpty && s.empty())
|
||||
continue;
|
||||
if (++idx == lastArgNo) {
|
||||
m_args.emplace_back(trim(in.substr(pos)));
|
||||
break;
|
||||
}
|
||||
pos += s.size() + 1;
|
||||
m_args.emplace_back(trim(s.data()));
|
||||
}
|
||||
}
|
||||
|
||||
std::string CConstVarList::join(const std::string& joiner, size_t from, size_t to) const {
|
||||
size_t last = to == 0 ? size() : to;
|
||||
|
||||
std::string rolling;
|
||||
for (size_t i = from; i < last; ++i) {
|
||||
// cast can be removed once C++26's change to allow this is supported
|
||||
rolling += std::string{m_args[i]} + (i + 1 < last ? joiner : "");
|
||||
}
|
||||
|
||||
return rolling;
|
||||
}
|
||||
|
|
@ -62,7 +62,7 @@ class CMyAnimationManager : public CAnimationManager {
|
|||
|
||||
switch (PAV->m_Type) {
|
||||
case eAVTypes::INT: {
|
||||
auto avInt = dynamic_cast<CAnimatedVariable<int>*>(PAV.get());
|
||||
auto avInt = dc<CAnimatedVariable<int>*>(PAV.get());
|
||||
if (!avInt)
|
||||
std::cout << Colors::RED << "Dynamic cast upcast failed" << Colors::RESET;
|
||||
|
||||
|
|
@ -70,7 +70,7 @@ class CMyAnimationManager : public CAnimationManager {
|
|||
avInt->value() = avInt->begun() + (DELTA * POINTY);
|
||||
} break;
|
||||
case eAVTypes::TEST: {
|
||||
auto avCustom = dynamic_cast<CAnimatedVariable<SomeTestType>*>(PAV.get());
|
||||
auto avCustom = dc<CAnimatedVariable<SomeTestType>*>(PAV.get());
|
||||
if (!avCustom)
|
||||
std::cout << Colors::RED << "Dynamic cast upcast failed" << Colors::RESET;
|
||||
|
||||
|
|
@ -93,7 +93,7 @@ class CMyAnimationManager : public CAnimationManager {
|
|||
constexpr const eAVTypes EAVTYPE = std::is_same_v<VarType, int> ? eAVTypes::INT : eAVTypes::TEST;
|
||||
const auto PAV = makeShared<CGenericAnimatedVariable<VarType, EmtpyContext>>();
|
||||
|
||||
PAV->create(EAVTYPE, static_cast<CAnimationManager*>(this), PAV, v);
|
||||
PAV->create(EAVTYPE, sc<CAnimationManager*>(this), PAV, v);
|
||||
PAV->setConfig(animationTree.getConfig(animationConfigName));
|
||||
av = std::move(PAV);
|
||||
}
|
||||
|
|
@ -348,7 +348,7 @@ int main(int argc, char** argv, char** envp) {
|
|||
*s.m_iA = 5;
|
||||
s.m_iA->setCallbackOnEnd([&endCallbackRan](WP<CBaseAnimatedVariable> v) {
|
||||
endCallbackRan++;
|
||||
const auto PAV = dynamic_cast<CAnimatedVariable<int>*>(v.lock().get());
|
||||
const auto PAV = dc<CAnimatedVariable<int>*>(v.lock().get());
|
||||
|
||||
*PAV = 10;
|
||||
PAV->setCallbackOnEnd([&endCallbackRan](WP<CBaseAnimatedVariable> v) { endCallbackRan++; });
|
||||
|
|
|
|||
|
|
@ -92,8 +92,31 @@ int main(int argc, char** argv, char** envp) {
|
|||
Mat3x3 matrixBox = jeremy.projectBox(CBox{10, 10, 200, 200}, HYPRUTILS_TRANSFORM_NORMAL).translate({100, 100}).scale({1.25F, 1.5F}).transpose();
|
||||
|
||||
Mat3x3 expected = std::array<float, 9>{0, 0.46296296, 0, 0.3125, 0, 0, 19.84375, 36.055557, 1};
|
||||
EXPECT(matrixBox, expected);
|
||||
// we need to do this to avoid precision errors on 32-bit archs
|
||||
EXPECT(std::abs(expected.getMatrix().at(0) - matrixBox.getMatrix().at(0)) < 0.1, true);
|
||||
EXPECT(std::abs(expected.getMatrix().at(1) - matrixBox.getMatrix().at(1)) < 0.1, true);
|
||||
EXPECT(std::abs(expected.getMatrix().at(2) - matrixBox.getMatrix().at(2)) < 0.1, true);
|
||||
EXPECT(std::abs(expected.getMatrix().at(3) - matrixBox.getMatrix().at(3)) < 0.1, true);
|
||||
EXPECT(std::abs(expected.getMatrix().at(4) - matrixBox.getMatrix().at(4)) < 0.1, true);
|
||||
EXPECT(std::abs(expected.getMatrix().at(5) - matrixBox.getMatrix().at(5)) < 0.1, true);
|
||||
EXPECT(std::abs(expected.getMatrix().at(6) - matrixBox.getMatrix().at(6)) < 0.1, true);
|
||||
EXPECT(std::abs(expected.getMatrix().at(7) - matrixBox.getMatrix().at(7)) < 0.1, true);
|
||||
EXPECT(std::abs(expected.getMatrix().at(8) - matrixBox.getMatrix().at(8)) < 0.1, true);
|
||||
}
|
||||
|
||||
{
|
||||
Vector2D original(30, 40);
|
||||
Vector2D monitorSize(100, 200);
|
||||
|
||||
EXPECT_VECTOR2D(original.transform(HYPRUTILS_TRANSFORM_NORMAL, monitorSize), Vector2D(30, 40 ));
|
||||
EXPECT_VECTOR2D(original.transform(HYPRUTILS_TRANSFORM_90, monitorSize), Vector2D(40, 200 - 30));
|
||||
EXPECT_VECTOR2D(original.transform(HYPRUTILS_TRANSFORM_180, monitorSize), Vector2D(100 - 30, 200 - 40));
|
||||
EXPECT_VECTOR2D(original.transform(HYPRUTILS_TRANSFORM_270, monitorSize), Vector2D(100 - 40, 30 ));
|
||||
EXPECT_VECTOR2D(original.transform(HYPRUTILS_TRANSFORM_FLIPPED, monitorSize), Vector2D(100 - 30, 40 ));
|
||||
EXPECT_VECTOR2D(original.transform(HYPRUTILS_TRANSFORM_FLIPPED_90, monitorSize), Vector2D(40, 30 ));
|
||||
EXPECT_VECTOR2D(original.transform(HYPRUTILS_TRANSFORM_FLIPPED_180, monitorSize), Vector2D(30, 200 - 40));
|
||||
EXPECT_VECTOR2D(original.transform(HYPRUTILS_TRANSFORM_FLIPPED_270, monitorSize), Vector2D(100 - 40, 200 - 30));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
#include <hyprutils/memory/WeakPtr.hpp>
|
||||
#include <hyprutils/memory/UniquePtr.hpp>
|
||||
#include <hyprutils/memory/SharedPtr.hpp>
|
||||
#include <hyprutils/memory/WeakPtr.hpp>
|
||||
#include <hyprutils/memory/Atomic.hpp>
|
||||
#include "shared.hpp"
|
||||
#include <chrono>
|
||||
#include <print>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
using namespace Hyprutils::Memory;
|
||||
|
|
@ -9,6 +14,87 @@ using namespace Hyprutils::Memory;
|
|||
#define WP CWeakPointer
|
||||
#define UP CUniquePointer
|
||||
|
||||
#define ASP CAtomicSharedPointer
|
||||
#define AWP CAtomicWeakPointer
|
||||
#define NTHREADS 8
|
||||
#define ITERATIONS 10000
|
||||
|
||||
static int testAtomicImpl() {
|
||||
int ret = 0;
|
||||
|
||||
{
|
||||
// Using makeShared here could lead to invalid refcounts.
|
||||
ASP<int> shared = makeAtomicShared<int>(0);
|
||||
std::vector<std::thread> threads;
|
||||
|
||||
threads.reserve(NTHREADS);
|
||||
for (size_t i = 0; i < NTHREADS; i++) {
|
||||
threads.emplace_back([shared]() {
|
||||
for (size_t j = 0; j < ITERATIONS; j++) {
|
||||
ASP<int> strongRef = shared;
|
||||
(*shared)++;
|
||||
strongRef.reset();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (auto& thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
|
||||
// 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(shared, false);
|
||||
}
|
||||
|
||||
{
|
||||
ASP<int> shared = makeAtomicShared<int>(0);
|
||||
AWP<int> weak = shared;
|
||||
std::vector<std::thread> threads;
|
||||
|
||||
threads.reserve(NTHREADS);
|
||||
for (size_t i = 0; i < NTHREADS; i++) {
|
||||
threads.emplace_back([weak]() {
|
||||
for (size_t j = 0; j < ITERATIONS; j++) {
|
||||
if (auto s = weak.lock(); s) {
|
||||
(*s)++;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
shared.reset();
|
||||
|
||||
for (auto& thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
|
||||
EXPECT(shared.strongRef(), 0);
|
||||
EXPECT(weak.valid(), false);
|
||||
|
||||
auto shared2 = weak.lock();
|
||||
EXPECT(shared, false);
|
||||
EXPECT(shared2.get(), nullptr);
|
||||
EXPECT(shared.strongRef(), 0);
|
||||
EXPECT(weak.valid(), false);
|
||||
EXPECT(weak.expired(), true);
|
||||
}
|
||||
|
||||
{ // This tests recursive deletion. When foo will be deleted, bar will be deleted within the foo dtor.
|
||||
class CFoo {
|
||||
public:
|
||||
AWP<CFoo> bar;
|
||||
};
|
||||
|
||||
ASP<CFoo> foo = makeAtomicShared<CFoo>();
|
||||
foo->bar = foo;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv, char** envp) {
|
||||
SP<int> intPtr = makeShared<int>(10);
|
||||
SP<int> intPtr2 = makeShared<int>(-1337);
|
||||
|
|
@ -62,5 +148,7 @@ int main(int argc, char** argv, char** envp) {
|
|||
EXPECT(*intPtr2AsUint, 10);
|
||||
EXPECT(*intPtr2, 10);
|
||||
|
||||
EXPECT(testAtomicImpl(), 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
348
tests/signal.cpp
348
tests/signal.cpp
|
|
@ -1,13 +1,19 @@
|
|||
#include <any>
|
||||
#include <hyprutils/signal/Signal.hpp>
|
||||
#include <hyprutils/memory/WeakPtr.hpp>
|
||||
#include <memory>
|
||||
#include "hyprutils/memory/SharedPtr.hpp"
|
||||
#include "hyprutils/signal/Listener.hpp"
|
||||
#include "shared.hpp"
|
||||
|
||||
using namespace Hyprutils::Signal;
|
||||
using namespace Hyprutils::Memory;
|
||||
|
||||
int main(int argc, char** argv, char** envp) {
|
||||
int ret = 0;
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
//
|
||||
|
||||
void legacy(int& ret) {
|
||||
CSignal signal;
|
||||
int data = 0;
|
||||
auto listener = signal.registerListener([&]([[maybe_unused]] std::any d) { data = 1; });
|
||||
|
|
@ -23,6 +29,342 @@ int main(int argc, char** argv, char** envp) {
|
|||
signal.emit();
|
||||
|
||||
EXPECT(data, 0);
|
||||
}
|
||||
|
||||
void legacyListenerEmit(int& ret) {
|
||||
int data = 0;
|
||||
CSignal signal;
|
||||
auto listener = signal.registerListener([&](std::any d) { data = std::any_cast<int>(d); });
|
||||
|
||||
listener->emit(1); // not a typo
|
||||
EXPECT(data, 1);
|
||||
}
|
||||
|
||||
void legacyListeners(int& ret) {
|
||||
int data = 0;
|
||||
|
||||
CSignalT<> signal0;
|
||||
CSignalT<int> signal1;
|
||||
|
||||
auto listener0 = signal0.registerListener([&](std::any d) { data += 1; });
|
||||
auto listener1 = signal1.registerListener([&](std::any d) { data += std::any_cast<int>(d); });
|
||||
|
||||
signal0.registerStaticListener([&](void* o, std::any d) { data += 10; }, nullptr);
|
||||
signal1.registerStaticListener([&](void* o, std::any d) { data += std::any_cast<int>(d) * 10; }, nullptr);
|
||||
|
||||
signal0.emit();
|
||||
signal1.emit(2);
|
||||
|
||||
EXPECT(data, 33);
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
//
|
||||
|
||||
void empty(int& ret) {
|
||||
int data = 0;
|
||||
|
||||
CSignalT<> signal;
|
||||
auto listener = signal.listen([&] { 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.listen([&](int newData) { data = newData; });
|
||||
|
||||
signal.emit(1);
|
||||
EXPECT(data, 1);
|
||||
}
|
||||
|
||||
void ignoreParams(int& ret) {
|
||||
int data = 0;
|
||||
|
||||
CSignalT<int> signal;
|
||||
auto listener = signal.listen([&] { data += 1; });
|
||||
|
||||
signal.listenStatic([&] { data += 1; });
|
||||
|
||||
signal.emit(2);
|
||||
EXPECT(data, 2);
|
||||
}
|
||||
|
||||
void typedMany(int& ret) {
|
||||
int data1 = 0;
|
||||
int data2 = 0;
|
||||
int data3 = 0;
|
||||
|
||||
CSignalT<int, int, int> signal;
|
||||
auto listener = signal.listen([&](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 ref(int& ret) {
|
||||
int count = 0;
|
||||
int data = 0;
|
||||
|
||||
CSignalT<int&> signal;
|
||||
auto l1 = signal.listen([&](int& v) { v += 1; });
|
||||
auto l2 = signal.listen([&](int v) { count += v; });
|
||||
signal.emit(data);
|
||||
|
||||
CSignalT<const int&> constSignal;
|
||||
auto l3 = constSignal.listen([&](const int& v) { count += v; });
|
||||
auto l4 = constSignal.listen([&](int v) { count += v; });
|
||||
constSignal.emit(data);
|
||||
|
||||
EXPECT(data, 1);
|
||||
EXPECT(count, 3);
|
||||
}
|
||||
|
||||
void refMany(int& ret) {
|
||||
int count = 0;
|
||||
int data1 = 0;
|
||||
int data2 = 10;
|
||||
|
||||
CSignalT<int&, const int&> signal;
|
||||
auto l1 = signal.listen([&](int& v, const int&) { v += 1; });
|
||||
auto l2 = signal.listen([&](int v1, int v2) { count += v1 + v2; });
|
||||
|
||||
signal.emit(data1, data2);
|
||||
EXPECT(data1, 1);
|
||||
EXPECT(count, 11);
|
||||
}
|
||||
|
||||
void autoRefTypes(int& ret) {
|
||||
class CCopyCounter {
|
||||
public:
|
||||
CCopyCounter(int& createCount, int& destroyCount) : createCount(createCount), destroyCount(destroyCount) {
|
||||
createCount += 1;
|
||||
}
|
||||
|
||||
CCopyCounter(CCopyCounter&& other) noexcept : CCopyCounter(other.createCount, other.destroyCount) {}
|
||||
CCopyCounter(const CCopyCounter& other) noexcept : CCopyCounter(other.createCount, other.destroyCount) {}
|
||||
|
||||
~CCopyCounter() {
|
||||
destroyCount += 1;
|
||||
}
|
||||
|
||||
private:
|
||||
int& createCount;
|
||||
int& destroyCount;
|
||||
};
|
||||
|
||||
auto createCount = 0;
|
||||
auto destroyCount = 0;
|
||||
|
||||
CSignalT<CCopyCounter> signal;
|
||||
auto listener = signal.listen([](const CCopyCounter& counter) {});
|
||||
|
||||
signal.emit(CCopyCounter(createCount, destroyCount));
|
||||
EXPECT(createCount, 1);
|
||||
EXPECT(destroyCount, 1);
|
||||
}
|
||||
|
||||
void forward(int& ret) {
|
||||
int count = 0;
|
||||
|
||||
CSignalT<int> sig;
|
||||
CSignalT<int> connected1;
|
||||
CSignalT<> connected2;
|
||||
|
||||
auto conn1 = sig.forward(connected1);
|
||||
auto conn2 = sig.forward(connected2);
|
||||
|
||||
auto listener1 = connected1.listen([&](int v) { count += v; });
|
||||
auto listener2 = connected2.listen([&] { count += 1; });
|
||||
|
||||
sig.emit(2);
|
||||
|
||||
EXPECT(count, 3);
|
||||
}
|
||||
|
||||
void listenerAdded(int& ret) {
|
||||
int count = 0;
|
||||
|
||||
CSignalT<> signal;
|
||||
CHyprSignalListener secondListener;
|
||||
|
||||
auto listener = signal.listen([&] {
|
||||
count += 1;
|
||||
|
||||
if (!secondListener)
|
||||
secondListener = signal.listen([&] { count += 1; });
|
||||
});
|
||||
|
||||
signal.emit();
|
||||
EXPECT(count, 1); // second should NOT be invoked as it was registed during emit
|
||||
|
||||
signal.emit();
|
||||
EXPECT(count, 3); // second should be invoked
|
||||
}
|
||||
|
||||
void lastListenerSwapped(int& ret) {
|
||||
int count = 0;
|
||||
|
||||
CSignalT<> signal;
|
||||
CHyprSignalListener removedListener;
|
||||
CHyprSignalListener addedListener;
|
||||
|
||||
auto firstListener = signal.listen([&] {
|
||||
removedListener.reset(); // dropped and should NOT be invoked
|
||||
|
||||
if (!addedListener)
|
||||
addedListener = signal.listen([&] { count += 2; });
|
||||
});
|
||||
|
||||
removedListener = signal.listen([&] { count += 1; });
|
||||
|
||||
signal.emit();
|
||||
EXPECT(count, 0); // neither the removed nor added listeners should fire
|
||||
|
||||
signal.emit();
|
||||
EXPECT(count, 2); // only the new listener should fire
|
||||
}
|
||||
|
||||
void signalDestroyed(int& ret) {
|
||||
int count = 0;
|
||||
|
||||
auto signal = std::make_unique<CSignalT<>>();
|
||||
|
||||
// This ensures a destructor of a listener called before signal reset is safe.
|
||||
auto preListener = signal->listen([&] { count += 1; });
|
||||
|
||||
auto listener = signal->listen([&] { signal.reset(); });
|
||||
|
||||
// This ensures a destructor of a listener called after signal reset is safe
|
||||
// and gets called.
|
||||
auto postListener = signal->listen([&] { count += 1; });
|
||||
|
||||
signal->emit();
|
||||
EXPECT(count, 2); // all listeners should fire regardless of signal deletion
|
||||
}
|
||||
|
||||
// purely an asan test
|
||||
void signalDestroyedBeforeListener() {
|
||||
CHyprSignalListener listener1;
|
||||
CHyprSignalListener listener2;
|
||||
|
||||
CSignalT<> signal;
|
||||
|
||||
listener1 = signal.listen([] {});
|
||||
listener2 = signal.listen([] {});
|
||||
}
|
||||
|
||||
void signalDestroyedWithAddedListener(int& ret) {
|
||||
int count = 0;
|
||||
|
||||
auto signal = std::make_unique<CSignalT<>>();
|
||||
CHyprSignalListener shouldNotRun;
|
||||
|
||||
auto listener = signal->listen([&] {
|
||||
shouldNotRun = signal->listen([&] { count += 2; });
|
||||
signal.reset();
|
||||
});
|
||||
|
||||
signal->emit();
|
||||
EXPECT(count, 0);
|
||||
}
|
||||
|
||||
void signalDestroyedWithRemovedAndAddedListener(int& ret) {
|
||||
int count = 0;
|
||||
|
||||
auto signal = std::make_unique<CSignalT<>>();
|
||||
CHyprSignalListener removed;
|
||||
CHyprSignalListener shouldNotRun;
|
||||
|
||||
auto listener = signal->listen([&] {
|
||||
removed.reset();
|
||||
shouldNotRun = signal->listen([&] { count += 2; });
|
||||
signal.reset();
|
||||
});
|
||||
|
||||
removed = signal->listen([&] { count += 1; });
|
||||
|
||||
signal->emit();
|
||||
EXPECT(count, 0);
|
||||
}
|
||||
|
||||
void staticListener(int& ret) {
|
||||
int data = 0;
|
||||
|
||||
CSignalT<int> signal;
|
||||
signal.listenStatic([&](int newData) { data = newData; });
|
||||
|
||||
signal.emit(1);
|
||||
EXPECT(data, 1);
|
||||
}
|
||||
|
||||
void staticListenerDestroy(int& ret) {
|
||||
int count = 0;
|
||||
|
||||
auto signal = makeShared<CSignalT<>>();
|
||||
signal->listenStatic([&] { count += 1; });
|
||||
|
||||
signal->listenStatic([&] {
|
||||
// should not fire but SHOULD be freed
|
||||
signal->listenStatic([&] { count += 3; });
|
||||
|
||||
signal.reset();
|
||||
});
|
||||
|
||||
signal->listenStatic([&] { count += 1; });
|
||||
|
||||
signal->emit();
|
||||
EXPECT(count, 2);
|
||||
}
|
||||
|
||||
// purely an asan test
|
||||
void listenerDestroysSelf() {
|
||||
CSignalT<> signal;
|
||||
|
||||
CHyprSignalListener listener;
|
||||
listener = signal.listen([&] { listener.reset(); });
|
||||
|
||||
// the static signal case is taken care of above
|
||||
|
||||
signal.emit();
|
||||
}
|
||||
|
||||
int main(int argc, char** argv, char** envp) {
|
||||
int ret = 0;
|
||||
legacy(ret);
|
||||
legacyListenerEmit(ret);
|
||||
legacyListeners(ret);
|
||||
empty(ret);
|
||||
typed(ret);
|
||||
ignoreParams(ret);
|
||||
typedMany(ret);
|
||||
ref(ret);
|
||||
refMany(ret);
|
||||
autoRefTypes(ret);
|
||||
forward(ret);
|
||||
listenerAdded(ret);
|
||||
lastListenerSwapped(ret);
|
||||
signalDestroyed(ret);
|
||||
signalDestroyedBeforeListener();
|
||||
signalDestroyedWithAddedListener(ret);
|
||||
signalDestroyedWithRemovedAndAddedListener(ret);
|
||||
staticListener(ret);
|
||||
staticListenerDestroy(ret);
|
||||
signalDestroyed(ret);
|
||||
listenerDestroysSelf();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include <hyprutils/string/String.hpp>
|
||||
#include <hyprutils/string/VarList.hpp>
|
||||
#include <hyprutils/string/ConstVarList.hpp>
|
||||
#include "shared.hpp"
|
||||
|
||||
using namespace Hyprutils::String;
|
||||
|
|
@ -38,6 +39,10 @@ int main(int argc, char** argv, char** envp) {
|
|||
EXPECT(list[0], "hello");
|
||||
EXPECT(list[1], "world!");
|
||||
|
||||
CConstVarList listConst("hello world!", 0, 's', true);
|
||||
EXPECT(listConst[0], "hello");
|
||||
EXPECT(listConst[1], "world!");
|
||||
|
||||
std::string hello = "hello world!";
|
||||
replaceInString(hello, "hello", "hi");
|
||||
EXPECT(hello, "hi world!");
|
||||
|
|
|
|||
Loading…
Reference in New Issue