-
-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
17 changed files
with
1,025 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
--- | ||
Language: Cpp | ||
BasedOnStyle: LLVM | ||
|
||
AccessModifierOffset: -2 | ||
AlignAfterOpenBracket: Align | ||
AlignConsecutiveMacros: true | ||
AlignConsecutiveAssignments: true | ||
AlignEscapedNewlines: Right | ||
AlignOperands: false | ||
AlignTrailingComments: true | ||
AllowAllArgumentsOnNextLine: true | ||
AllowAllConstructorInitializersOnNextLine: true | ||
AllowAllParametersOfDeclarationOnNextLine: true | ||
AllowShortBlocksOnASingleLine: true | ||
AllowShortCaseLabelsOnASingleLine: true | ||
AllowShortFunctionsOnASingleLine: Empty | ||
AllowShortIfStatementsOnASingleLine: Never | ||
AllowShortLambdasOnASingleLine: All | ||
AllowShortLoopsOnASingleLine: false | ||
AlwaysBreakAfterDefinitionReturnType: None | ||
AlwaysBreakAfterReturnType: None | ||
AlwaysBreakBeforeMultilineStrings: false | ||
AlwaysBreakTemplateDeclarations: Yes | ||
BreakBeforeBraces: Attach | ||
BreakBeforeTernaryOperators: false | ||
BreakConstructorInitializers: AfterColon | ||
ColumnLimit: 180 | ||
CompactNamespaces: false | ||
ConstructorInitializerAllOnOneLineOrOnePerLine: false | ||
ExperimentalAutoDetectBinPacking: false | ||
FixNamespaceComments: false | ||
IncludeBlocks: Preserve | ||
IndentCaseLabels: true | ||
IndentWidth: 4 | ||
PointerAlignment: Left | ||
ReflowComments: false | ||
SortIncludes: false | ||
SortUsingDeclarations: false | ||
SpaceAfterCStyleCast: false | ||
SpaceAfterLogicalNot: false | ||
SpaceAfterTemplateKeyword: true | ||
SpaceBeforeCtorInitializerColon: true | ||
SpaceBeforeInheritanceColon: true | ||
SpaceBeforeParens: ControlStatements | ||
SpaceBeforeRangeBasedForLoopColon: true | ||
SpaceInEmptyParentheses: false | ||
SpacesBeforeTrailingComments: 1 | ||
SpacesInAngles: false | ||
SpacesInCStyleCastParentheses: false | ||
SpacesInContainerLiterals: false | ||
SpacesInParentheses: false | ||
SpacesInSquareBrackets: false | ||
Standard: Auto | ||
TabWidth: 4 | ||
UseTab: Never | ||
|
||
AllowShortEnumsOnASingleLine: false | ||
|
||
BraceWrapping: | ||
AfterEnum: false | ||
|
||
AlignConsecutiveDeclarations: AcrossEmptyLines | ||
|
||
NamespaceIndentation: All |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
name: Build & Test (Arch) | ||
|
||
on: [push, pull_request, workflow_dispatch] | ||
jobs: | ||
gcc: | ||
name: "Arch: Build and Test (gcc)" | ||
runs-on: ubuntu-latest | ||
container: | ||
image: archlinux | ||
steps: | ||
- name: Checkout repository actions | ||
uses: actions/checkout@v4 | ||
with: | ||
sparse-checkout: .github/actions | ||
|
||
- name: Get required pkgs | ||
run: | | ||
sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf | ||
pacman --noconfirm --noprogressbar -Syyu | ||
pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang libc++ | ||
- name: Build hyprutils with gcc | ||
run: | | ||
CC="/usr/bin/gcc" CXX="/usr/bin/g++" cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -S . -B ./build | ||
CC="/usr/bin/gcc" CXX="/usr/bin/g++" cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF` | ||
cmake --install ./build | ||
- name: Run tests | ||
run: | | ||
cd ./build && ctest --output-on-failure | ||
clang: | ||
name: "Arch: Build and Test (clang)" | ||
runs-on: ubuntu-latest | ||
container: | ||
image: archlinux | ||
steps: | ||
- name: Checkout repository actions | ||
uses: actions/checkout@v4 | ||
with: | ||
sparse-checkout: .github/actions | ||
|
||
- name: Get required pkgs | ||
run: | | ||
sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf | ||
pacman --noconfirm --noprogressbar -Syyu | ||
pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang libc++ | ||
- name: Build hyprutils with clang | ||
run: | | ||
CC="/usr/bin/clang" CXX="/usr/bin/clang++" cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -S . -B ./build | ||
CC="/usr/bin/clang" CXX="/usr/bin/clang++" cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF` | ||
cmake --install ./build | ||
- name: Run tests | ||
run: | | ||
cd ./build && ctest --output-on-failure |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,3 +30,7 @@ | |
*.exe | ||
*.out | ||
*.app | ||
|
||
build/ | ||
.vscode/ | ||
.cache/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
cmake_minimum_required(VERSION 3.19) | ||
|
||
set(HYPRUTILS_VERSION "0.1.0") | ||
add_compile_definitions(HYPRUTILS_VERSION="${HYPRUTILS_VERSION}") | ||
|
||
project(hyprutils | ||
VERSION ${HYPRUTILS_VERSION} | ||
DESCRIPTION "A library and toolkit for the Hyprland cursor format" | ||
) | ||
|
||
include(CTest) | ||
include(GNUInstallDirs) | ||
|
||
set(PREFIX ${CMAKE_INSTALL_PREFIX}) | ||
set(INCLUDE ${CMAKE_INSTALL_FULL_INCLUDEDIR}) | ||
set(LIBDIR ${CMAKE_INSTALL_FULL_LIBDIR}) | ||
|
||
configure_file(hyprutils.pc.in hyprutils.pc @ONLY) | ||
|
||
set(CMAKE_CXX_STANDARD 23) | ||
|
||
if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG) | ||
message(STATUS "Configuring hyprutils in Debug") | ||
add_compile_definitions(HYPRLAND_DEBUG) | ||
else() | ||
add_compile_options(-O3) | ||
message(STATUS "Configuring hyprutils in Release") | ||
endif() | ||
|
||
file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp" "include/*.hpp") | ||
|
||
add_library(hyprutils SHARED ${SRCFILES}) | ||
target_include_directories( hyprutils | ||
PUBLIC "./include" | ||
PRIVATE "./src" | ||
) | ||
set_target_properties(hyprutils PROPERTIES | ||
VERSION ${hyprutils_VERSION} | ||
SOVERSION 0 | ||
PUBLIC_HEADER include/hyprutils/hyprutils.hpp include/hyprutils/hyprutils.h include/hyprutils/shared.h | ||
) | ||
|
||
# tests | ||
add_custom_target(tests) | ||
|
||
add_executable(hyprutils_memory "tests/memory.cpp") | ||
target_link_libraries(hyprutils_memory PRIVATE hyprutils) | ||
add_test(NAME "Memory" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests COMMAND hyprutils_memory "memory") | ||
add_dependencies(tests hyprutils_memory) | ||
|
||
add_executable(hyprutils_string "tests/string.cpp") | ||
target_link_libraries(hyprutils_string PRIVATE hyprutils) | ||
add_test(NAME "String" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests COMMAND hyprutils_string "string") | ||
add_dependencies(tests hyprutils_string) | ||
|
||
add_executable(hyprutils_signal "tests/signal.cpp") | ||
target_link_libraries(hyprutils_signal PRIVATE hyprutils) | ||
add_test(NAME "Signal" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests COMMAND hyprutils_signal "signal") | ||
add_dependencies(tests hyprutils_signal) | ||
|
||
# Installation | ||
install(TARGETS hyprutils) | ||
install(DIRECTORY "include/hyprutils" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
prefix=@PREFIX@ | ||
includedir=@INCLUDE@ | ||
libdir=@LIBDIR@ | ||
|
||
Name: hyprutils | ||
URL: https://github.com/hyprwm/hyprutils | ||
Description: Hyprland utilities library used across the ecosystem | ||
Version: @HYPRUTILS_VERSION@ | ||
Cflags: -I${includedir} | ||
Libs: -L${libdir} -lhyprutils |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,302 @@ | ||
#pragma once | ||
|
||
#include <cstddef> | ||
#include <cstdint> | ||
#include <memory> | ||
|
||
/* | ||
This is a custom impl of std::shared_ptr. | ||
It is not thread-safe like the STL one, | ||
but Hyprland is single-threaded anyways. | ||
It differs a bit from how the STL one works, | ||
namely in the fact that it keeps the T* inside the | ||
control block, and that you can still make a CWeakPtr | ||
or deref an existing one inside the destructor. | ||
*/ | ||
|
||
namespace Hyprutils { | ||
namespace Memory { | ||
namespace CSharedPointer_ { | ||
class impl_base { | ||
public: | ||
virtual ~impl_base(){}; | ||
|
||
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; | ||
}; | ||
|
||
template <typename T> | ||
class impl : public impl_base { | ||
public: | ||
impl(T* data) noexcept : _data(data) { | ||
; | ||
} | ||
|
||
/* strong refcount */ | ||
unsigned int _ref = 0; | ||
/* weak refcount */ | ||
unsigned int _weak = 0; | ||
|
||
T* _data = nullptr; | ||
|
||
friend void swap(impl*& a, impl*& b) { | ||
impl* tmp = a; | ||
a = b; | ||
b = tmp; | ||
} | ||
|
||
/* if the destructor was called, | ||
creating shared_ptrs is no longer valid */ | ||
bool _destroying = false; | ||
|
||
void _destroy() { | ||
if (!_data || _destroying) | ||
return; | ||
|
||
// first, we destroy the data, but keep the pointer. | ||
// this way, weak pointers will still be able to | ||
// reference and use, but no longer create shared ones. | ||
_destroying = true; | ||
__deleter(_data); | ||
// now, we can reset the data and call it a day. | ||
_data = nullptr; | ||
_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 dataNonNull() noexcept { | ||
return _data; | ||
} | ||
|
||
virtual ~impl() { | ||
destroy(); | ||
} | ||
}; | ||
}; | ||
|
||
template <typename T> | ||
class CSharedPointer { | ||
public: | ||
template <typename X> | ||
using validHierarchy = typename std::enable_if<std::is_assignable<CSharedPointer<T>&, X>::value, CSharedPointer&>::type; | ||
template <typename X> | ||
using isConstructible = typename std::enable_if<std::is_constructible<T&, X&>::value>::type; | ||
|
||
/* creates a new shared pointer managing a resource | ||
avoid calling. Could duplicate ownership. Prefer makeShared */ | ||
explicit CSharedPointer(T* object) noexcept { | ||
impl_ = new CSharedPointer_::impl<T>(object); | ||
increment(); | ||
} | ||
|
||
/* creates a shared pointer from a reference */ | ||
template <typename U, typename = isConstructible<U>> | ||
CSharedPointer(const CSharedPointer<U>& ref) noexcept { | ||
impl_ = ref.impl_; | ||
increment(); | ||
} | ||
|
||
CSharedPointer(const CSharedPointer& ref) noexcept { | ||
impl_ = ref.impl_; | ||
increment(); | ||
} | ||
|
||
template <typename U, typename = isConstructible<U>> | ||
CSharedPointer(CSharedPointer<U>&& ref) noexcept { | ||
std::swap(impl_, ref.impl_); | ||
} | ||
|
||
CSharedPointer(CSharedPointer&& ref) noexcept { | ||
std::swap(impl_, ref.impl_); | ||
} | ||
|
||
/* allows weakPointer to create from an impl */ | ||
CSharedPointer(CSharedPointer_::impl_base* implementation) noexcept { | ||
impl_ = implementation; | ||
increment(); | ||
} | ||
|
||
/* creates an empty shared pointer with no implementation */ | ||
CSharedPointer() noexcept { | ||
; // empty | ||
} | ||
|
||
/* creates an empty shared pointer with no implementation */ | ||
CSharedPointer(std::nullptr_t) noexcept { | ||
; // empty | ||
} | ||
|
||
~CSharedPointer() { | ||
// we do not decrement here, | ||
// because we want to preserve the pointer | ||
// in case this is the last owner. | ||
if (impl_ && impl_->ref() == 1) | ||
destroyImpl(); | ||
else | ||
decrement(); | ||
} | ||
|
||
template <typename U> | ||
validHierarchy<const CSharedPointer<U>&> operator=(const CSharedPointer<U>& rhs) { | ||
if (impl_ == rhs.impl_) | ||
return *this; | ||
|
||
decrement(); | ||
impl_ = rhs.impl_; | ||
increment(); | ||
return *this; | ||
} | ||
|
||
CSharedPointer& operator=(const CSharedPointer& rhs) { | ||
if (impl_ == rhs.impl_) | ||
return *this; | ||
|
||
decrement(); | ||
impl_ = rhs.impl_; | ||
increment(); | ||
return *this; | ||
} | ||
|
||
template <typename U> | ||
validHierarchy<const CSharedPointer<U>&> operator=(CSharedPointer<U>&& rhs) { | ||
std::swap(impl_, rhs.impl_); | ||
return *this; | ||
} | ||
|
||
CSharedPointer& operator=(CSharedPointer&& rhs) { | ||
std::swap(impl_, rhs.impl_); | ||
return *this; | ||
} | ||
|
||
operator bool() const { | ||
return impl_ && impl_->dataNonNull(); | ||
} | ||
|
||
bool operator==(const CSharedPointer& rhs) const { | ||
return impl_ == rhs.impl_; | ||
} | ||
|
||
bool operator()(const CSharedPointer& lhs, const CSharedPointer& rhs) const { | ||
return (uintptr_t)lhs.impl_ < (uintptr_t)rhs.impl_; | ||
} | ||
|
||
bool operator<(const CSharedPointer& rhs) const { | ||
return (uintptr_t)impl_ < (uintptr_t)rhs.impl_; | ||
} | ||
|
||
T* operator->() const { | ||
return get(); | ||
} | ||
|
||
T& operator*() const { | ||
return *get(); | ||
} | ||
|
||
void reset() { | ||
decrement(); | ||
impl_ = nullptr; | ||
} | ||
|
||
T* get() const { | ||
return (T*)(impl_ ? static_cast<CSharedPointer_::impl<T>*>(impl_)->_data : nullptr); | ||
} | ||
|
||
unsigned int strongRef() const { | ||
return impl_ ? impl_->ref() : 0; | ||
} | ||
|
||
CSharedPointer_::impl_base* impl_ = nullptr; | ||
|
||
private: | ||
/* | ||
no-op if there is no impl_ | ||
may delete the stored object if ref == 0 | ||
may delete and reset impl_ if ref == 0 and weak == 0 | ||
*/ | ||
void decrement() { | ||
if (!impl_) | ||
return; | ||
|
||
impl_->dec(); | ||
|
||
// if ref == 0, we can destroy impl | ||
if (impl_->ref() == 0) | ||
destroyImpl(); | ||
} | ||
/* no-op if there is no impl_ */ | ||
void increment() { | ||
if (!impl_) | ||
return; | ||
|
||
impl_->inc(); | ||
} | ||
|
||
/* destroy the pointed-to object | ||
if able, will also destroy impl */ | ||
void destroyImpl() { | ||
// destroy the impl contents | ||
impl_->destroy(); | ||
|
||
// check for weak refs, if zero, we can also delete impl_ | ||
if (impl_->wref() == 0) { | ||
delete impl_; | ||
impl_ = nullptr; | ||
} | ||
} | ||
}; | ||
|
||
template <typename U, typename... Args> | ||
static CSharedPointer<U> makeShared(Args&&... args) { | ||
return CSharedPointer<U>(new U(std::forward<Args>(args)...)); | ||
} | ||
} | ||
} | ||
|
||
template <typename T> | ||
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_); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,192 @@ | ||
#pragma once | ||
|
||
#include "./SharedPtr.hpp" | ||
|
||
/* | ||
This is a Hyprland implementation of std::weak_ptr. | ||
See SharedPtr.hpp for more info on how it's different. | ||
*/ | ||
|
||
namespace Hyprutils { | ||
namespace Memory { | ||
template <typename T> | ||
class CWeakPointer { | ||
public: | ||
template <typename X> | ||
using validHierarchy = typename std::enable_if<std::is_assignable<CWeakPointer<T>&, X>::value, CWeakPointer&>::type; | ||
template <typename X> | ||
using isConstructible = typename std::enable_if<std::is_constructible<T&, X&>::value>::type; | ||
|
||
/* create a weak ptr from a reference */ | ||
template <typename U, typename = isConstructible<U>> | ||
CWeakPointer(const CSharedPointer<U>& ref) noexcept { | ||
if (!ref.impl_) | ||
return; | ||
|
||
impl_ = ref.impl_; | ||
incrementWeak(); | ||
} | ||
|
||
/* create a weak ptr from another weak ptr */ | ||
template <typename U, typename = isConstructible<U>> | ||
CWeakPointer(const CWeakPointer<U>& ref) noexcept { | ||
if (!ref.impl_) | ||
return; | ||
|
||
impl_ = ref.impl_; | ||
incrementWeak(); | ||
} | ||
|
||
CWeakPointer(const CWeakPointer& ref) noexcept { | ||
if (!ref.impl_) | ||
return; | ||
|
||
impl_ = ref.impl_; | ||
incrementWeak(); | ||
} | ||
|
||
template <typename U, typename = isConstructible<U>> | ||
CWeakPointer(CWeakPointer<U>&& ref) noexcept { | ||
std::swap(impl_, ref.impl_); | ||
} | ||
|
||
CWeakPointer(CWeakPointer&& ref) noexcept { | ||
std::swap(impl_, ref.impl_); | ||
} | ||
|
||
/* create a weak ptr from another weak ptr with assignment */ | ||
template <typename U> | ||
validHierarchy<const CWeakPointer<U>&> operator=(const CWeakPointer<U>& rhs) { | ||
if (impl_ == rhs.impl_) | ||
return *this; | ||
|
||
decrementWeak(); | ||
impl_ = rhs.impl_; | ||
incrementWeak(); | ||
return *this; | ||
} | ||
|
||
CWeakPointer<T>& operator=(const CWeakPointer& rhs) { | ||
if (impl_ == rhs.impl_) | ||
return *this; | ||
|
||
decrementWeak(); | ||
impl_ = rhs.impl_; | ||
incrementWeak(); | ||
return *this; | ||
} | ||
|
||
/* create a weak ptr from a shared ptr with assignment */ | ||
template <typename U> | ||
validHierarchy<const CWeakPointer<U>&> operator=(const CSharedPointer<U>& rhs) { | ||
if ((uintptr_t)impl_ == (uintptr_t)rhs.impl_) | ||
return *this; | ||
|
||
decrementWeak(); | ||
impl_ = rhs.impl_; | ||
incrementWeak(); | ||
return *this; | ||
} | ||
|
||
/* create an empty weak ptr */ | ||
CWeakPointer() { | ||
; | ||
} | ||
|
||
~CWeakPointer() { | ||
decrementWeak(); | ||
} | ||
|
||
/* expired MAY return true even if the pointer is still stored. | ||
the situation would be e.g. self-weak pointer in a destructor. | ||
for pointer validity, use valid() */ | ||
bool expired() const { | ||
return !impl_ || !impl_->dataNonNull() || impl_->destroying(); | ||
} | ||
|
||
/* this means the pointed-to object is not yet deleted and can still be | ||
referenced, but it might be in the process of being deleted. | ||
check !expired() if you want to check whether it's valid and | ||
assignable to a SP. */ | ||
bool valid() const { | ||
return impl_ && impl_->dataNonNull(); | ||
} | ||
|
||
void reset() { | ||
decrementWeak(); | ||
impl_ = nullptr; | ||
} | ||
|
||
CSharedPointer<T> lock() const { | ||
if (!impl_ || !impl_->dataNonNull() || impl_->destroying()) | ||
return {}; | ||
|
||
return CSharedPointer<T>(impl_); | ||
} | ||
|
||
/* this returns valid() */ | ||
operator bool() const { | ||
return valid(); | ||
} | ||
|
||
bool operator==(const CWeakPointer<T>& rhs) const { | ||
return impl_ == rhs.impl_; | ||
} | ||
|
||
bool operator==(const CSharedPointer<T>& rhs) const { | ||
return impl_ == rhs.impl_; | ||
} | ||
|
||
bool operator()(const CWeakPointer& lhs, const CWeakPointer& rhs) const { | ||
return (uintptr_t)lhs.impl_ < (uintptr_t)rhs.impl_; | ||
} | ||
|
||
bool operator<(const CWeakPointer& rhs) const { | ||
return (uintptr_t)impl_ < (uintptr_t)rhs.impl_; | ||
} | ||
|
||
T* get() const { | ||
return (T*)(impl_ ? static_cast<CSharedPointer_::impl<T>*>(impl_)->_data : nullptr); | ||
} | ||
|
||
T* operator->() const { | ||
return get(); | ||
} | ||
|
||
CSharedPointer_::impl_base* impl_ = nullptr; | ||
|
||
private: | ||
/* no-op if there is no impl_ */ | ||
void decrementWeak() { | ||
if (!impl_) | ||
return; | ||
|
||
impl_->decWeak(); | ||
|
||
// we need to check for ->destroying, | ||
// because otherwise we could destroy here | ||
// and have a shared_ptr destroy the same thing | ||
// later (in situations where we have a weak_ptr to self) | ||
if (impl_->wref() == 0 && impl_->ref() == 0 && !impl_->destroying()) { | ||
delete impl_; | ||
impl_ = nullptr; | ||
} | ||
} | ||
/* no-op if there is no impl_ */ | ||
void incrementWeak() { | ||
if (!impl_) | ||
return; | ||
|
||
impl_->incWeak(); | ||
} | ||
}; | ||
} | ||
} | ||
|
||
template <typename T> | ||
struct std::hash<Hyprutils::Memory::CWeakPointer<T>> { | ||
std::size_t operator()(const Hyprutils::Memory::CWeakPointer<T>& p) const noexcept { | ||
return std::hash<void*>{}(p.impl_); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
#pragma once | ||
|
||
#include <any> | ||
#include <functional> | ||
#include <hyprutils/memory/SharedPtr.hpp> | ||
|
||
namespace Hyprutils { | ||
namespace Signal { | ||
class CSignal; | ||
|
||
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); | ||
|
||
private: | ||
std::function<void(std::any)> m_fHandler; | ||
}; | ||
|
||
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; | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
#pragma once | ||
|
||
#include <functional> | ||
#include <any> | ||
#include <vector> | ||
#include <memory> | ||
#include <hyprutils/memory/WeakPtr.hpp> | ||
#include "./Listener.hpp" | ||
|
||
namespace Hyprutils { | ||
namespace Signal { | ||
class CSignal { | ||
public: | ||
void emit(std::any data = {}); | ||
|
||
// | ||
[[nodiscard("Listener is unregistered when the ptr is lost")]] CHyprSignalListener registerListener(std::function<void(std::any)> handler); | ||
|
||
// 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); | ||
|
||
private: | ||
std::vector<Hyprutils::Memory::CWeakPointer<CSignalListener>> m_vListeners; | ||
std::vector<std::unique_ptr<CStaticSignalListener>> m_vStaticListeners; | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
#pragma once | ||
#include <string> | ||
|
||
namespace Hyprutils { | ||
namespace String { | ||
// trims beginning and end of whitespace characters | ||
std::string trim(const std::string& in); | ||
bool isNumber(const std::string& str, bool allowfloat = false); | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
#include <hyprutils/signal/Listener.hpp> | ||
|
||
using namespace Hyprutils::Signal; | ||
|
||
Hyprutils::Signal::CSignalListener::CSignalListener(std::function<void(std::any)> handler) : m_fHandler(handler) { | ||
; | ||
} | ||
|
||
void Hyprutils::Signal::CSignalListener::emit(std::any 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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
#include <hyprutils/signal/Signal.hpp> | ||
#include <hyprutils/memory/WeakPtr.hpp> | ||
#include <algorithm> | ||
|
||
using namespace Hyprutils::Signal; | ||
using namespace Hyprutils::Memory; | ||
|
||
#define SP CSharedPointer | ||
#define WP CWeakPointer | ||
|
||
void Hyprutils::Signal::CSignal::emit(std::any data) { | ||
bool dirty = false; | ||
|
||
std::vector<SP<CSignalListener>> listeners; | ||
for (auto& l : m_vListeners) { | ||
if (l.expired()) { | ||
dirty = true; | ||
continue; | ||
} | ||
|
||
listeners.emplace_back(l.lock()); | ||
} | ||
|
||
std::vector<CStaticSignalListener*> statics; | ||
for (auto& l : m_vStaticListeners) { | ||
statics.emplace_back(l.get()); | ||
} | ||
|
||
for (auto& l : listeners) { | ||
// if there is only one lock, it means the event is only held by the listeners | ||
// vector and was removed during our iteration | ||
if (l.strongRef() == 1) { | ||
dirty = true; | ||
continue; | ||
} | ||
l->emit(data); | ||
} | ||
|
||
for (auto& l : statics) { | ||
l->emit(data); | ||
} | ||
|
||
// release SPs | ||
listeners.clear(); | ||
|
||
if (dirty) | ||
std::erase_if(m_vListeners, [](const auto& other) { return other.expired(); }); | ||
} | ||
|
||
CHyprSignalListener Hyprutils::Signal::CSignal::registerListener(std::function<void(std::any)> handler) { | ||
CHyprSignalListener listener = makeShared<CSignalListener>(handler); | ||
m_vListeners.emplace_back(listener); | ||
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)); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
#include <algorithm> | ||
#include <hyprutils/string/String.hpp> | ||
|
||
using namespace Hyprutils::String; | ||
|
||
std::string Hyprutils::String::trim(const std::string& in) { | ||
if (in.empty()) | ||
return in; | ||
|
||
int countBefore = 0; | ||
while (countBefore < in.length() && std::isspace(in.at(countBefore))) { | ||
countBefore++; | ||
} | ||
|
||
int countAfter = 0; | ||
while (countAfter < in.length() - countBefore && std::isspace(in.at(in.length() - countAfter - 1))) { | ||
countAfter++; | ||
} | ||
|
||
std::string result = in.substr(countBefore, in.length() - countBefore - countAfter); | ||
|
||
return result; | ||
} | ||
|
||
bool Hyprutils::String::isNumber(const std::string& str, bool allowfloat) { | ||
if (str.empty()) | ||
return false; | ||
|
||
for (size_t i = 0; i < str.length(); ++i) { | ||
const char& c = str.at(i); | ||
|
||
if (i == 0 && str.at(i) == '-') { | ||
// only place where we allow - | ||
continue; | ||
} | ||
|
||
if (!isdigit(c)) { | ||
if (!allowfloat) | ||
return false; | ||
|
||
if (c != '.') | ||
return false; | ||
|
||
if (i == 0) | ||
return false; | ||
|
||
if (str.at(0) == '-') | ||
return false; | ||
|
||
continue; | ||
} | ||
} | ||
|
||
if (str.back() == '.') | ||
return false; | ||
|
||
return true; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
#include <hyprutils/memory/WeakPtr.hpp> | ||
#include "shared.hpp" | ||
|
||
using namespace Hyprutils::Memory; | ||
|
||
#define SP CSharedPointer | ||
#define WP CWeakPointer | ||
|
||
int main(int argc, char** argv, char** envp) { | ||
SP<int> intPtr = makeShared<int>(10); | ||
|
||
int ret = 0; | ||
|
||
EXPECT(*intPtr, 10); | ||
EXPECT(intPtr.strongRef(), 1); | ||
|
||
WP<int> weak = intPtr; | ||
|
||
EXPECT(*intPtr, 10); | ||
EXPECT(intPtr.strongRef(), 1); | ||
EXPECT(*weak.get(), 10); | ||
EXPECT(weak.expired(), false); | ||
|
||
intPtr = {}; | ||
|
||
EXPECT(weak.expired(), true); | ||
|
||
return ret; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
#pragma once | ||
#include <iostream> | ||
|
||
namespace Colors { | ||
constexpr const char* RED = "\x1b[31m"; | ||
constexpr const char* GREEN = "\x1b[32m"; | ||
constexpr const char* YELLOW = "\x1b[33m"; | ||
constexpr const char* BLUE = "\x1b[34m"; | ||
constexpr const char* MAGENTA = "\x1b[35m"; | ||
constexpr const char* CYAN = "\x1b[36m"; | ||
constexpr const char* RESET = "\x1b[0m"; | ||
}; | ||
|
||
#define EXPECT(expr, val) \ | ||
if (const auto RESULT = expr; RESULT != (val)) { \ | ||
std::cout << Colors::RED << "Failed: " << Colors::RESET << #expr << ", expected " << #val << " but got " << RESULT << "\n"; \ | ||
ret = 1; \ | ||
} else { \ | ||
std::cout << Colors::GREEN << "Passed " << Colors::RESET << #expr << ". Got " << val << "\n"; \ | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
#include <hyprutils/signal/Signal.hpp> | ||
#include <hyprutils/memory/WeakPtr.hpp> | ||
#include "shared.hpp" | ||
|
||
using namespace Hyprutils::Signal; | ||
using namespace Hyprutils::Memory; | ||
|
||
int main(int argc, char** argv, char** envp) { | ||
int ret = 0; | ||
|
||
CSignal signal; | ||
int data = 0; | ||
auto listener = signal.registerListener([&] (std::any d) { | ||
data = 1; | ||
}); | ||
|
||
signal.emit(); | ||
|
||
EXPECT(data, 1); | ||
|
||
data = 0; | ||
|
||
listener.reset(); | ||
|
||
signal.emit(); | ||
|
||
EXPECT(data, 0); | ||
|
||
return ret; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
#include <hyprutils/string/String.hpp> | ||
#include "shared.hpp" | ||
|
||
using namespace Hyprutils::String; | ||
|
||
int main(int argc, char** argv, char** envp) { | ||
int ret = 0; | ||
|
||
EXPECT(trim(" a "), "a"); | ||
EXPECT(trim(" a a "), "a a"); | ||
EXPECT(trim("a"), "a"); | ||
EXPECT(trim(" "), ""); | ||
|
||
EXPECT(isNumber("99214123434"), true); | ||
EXPECT(isNumber("-35252345234"), true); | ||
EXPECT(isNumber("---3423--432"), false); | ||
EXPECT(isNumber("s---3423--432"), false); | ||
EXPECT(isNumber("---3423--432s"), false); | ||
EXPECT(isNumber("1s"), false); | ||
EXPECT(isNumber(""), false); | ||
EXPECT(isNumber("--0"), false); | ||
EXPECT(isNumber("abc"), false); | ||
EXPECT(isNumber("0.0", true), true); | ||
EXPECT(isNumber("0.2", true), true); | ||
EXPECT(isNumber("0.", true), false); | ||
EXPECT(isNumber(".0", true), false); | ||
EXPECT(isNumber("", true), false); | ||
EXPECT(isNumber("vvss", true), false); | ||
EXPECT(isNumber("0.9999s", true), false); | ||
EXPECT(isNumber("s0.9999", true), false); | ||
|
||
return ret; | ||
} |