New upstream version 0.1.2
This commit is contained in:
commit
1bd6d73aa6
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
name: Build & Test
|
||||
|
||||
on: [push, pull_request, workflow_dispatch]
|
||||
jobs:
|
||||
nix:
|
||||
strategy:
|
||||
matrix:
|
||||
package:
|
||||
- hyprutils
|
||||
- hyprutils-with-tests
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: cachix/install-nix-action@v26
|
||||
- uses: DeterminateSystems/magic-nix-cache-action@main
|
||||
|
||||
# not needed (yet)
|
||||
# - uses: cachix/cachix-action@v12
|
||||
# with:
|
||||
# name: hyprland
|
||||
# authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||
|
||||
- name: Build & Test
|
||||
run: nix build .#${{ matrix.package }} --print-build-logs
|
||||
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
|
||||
# Fortran module files
|
||||
*.mod
|
||||
*.smod
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
|
||||
build/
|
||||
.vscode/
|
||||
.cache/
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
cmake_minimum_required(VERSION 3.19)
|
||||
|
||||
set(HYPRUTILS_VERSION "0.1.2")
|
||||
add_compile_definitions(HYPRUTILS_VERSION="${HYPRUTILS_VERSION}")
|
||||
|
||||
project(hyprutils
|
||||
VERSION ${HYPRUTILS_VERSION}
|
||||
DESCRIPTION "Small C++ library for utilities used across the Hypr* ecosystem"
|
||||
)
|
||||
|
||||
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")
|
||||
file(GLOB_RECURSE PUBLIC_HEADERS CONFIGURE_DEPENDS "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
|
||||
)
|
||||
|
||||
# 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})
|
||||
install(FILES ${CMAKE_BINARY_DIR}/hyprutils.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2024, Hypr Development
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
# hyprutils
|
||||
|
||||
Hyprutils is a small C++ library for utilities used across the Hypr* ecosystem.
|
||||
|
||||
## Stability
|
||||
|
||||
Hyprutils depends on the ABI stability of the stdlib implementation of your compiler. Sover bumps will be done only for hyprutils ABI breaks, not stdlib.
|
||||
|
||||
## Building
|
||||
|
||||
```sh
|
||||
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -S . -B ./build
|
||||
cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf _NPROCESSORS_CONF`
|
||||
```
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1717602782,
|
||||
"narHash": "sha256-pL9jeus5QpX5R+9rsp3hhZ+uplVHscNJh8n8VpqscM0=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "e8057b67ebf307f01bdcc8fba94d94f75039d1f6",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs",
|
||||
"systems": "systems"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1689347949,
|
||||
"narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default-linux",
|
||||
"rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default-linux",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
{
|
||||
description = "Small C++ library for utilities used across the Hypr* ecosystem";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
systems.url = "github:nix-systems/default-linux";
|
||||
};
|
||||
|
||||
outputs = {
|
||||
self,
|
||||
nixpkgs,
|
||||
systems,
|
||||
}: let
|
||||
inherit (nixpkgs) lib;
|
||||
eachSystem = lib.genAttrs (import systems);
|
||||
pkgsFor = eachSystem (system:
|
||||
import nixpkgs {
|
||||
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)
|
||||
]);
|
||||
in {
|
||||
overlays = {
|
||||
default = self.overlays.hyprutils;
|
||||
hyprutils = final: prev: {
|
||||
hyprutils = final.callPackage ./nix/default.nix {
|
||||
stdenv = final.gcc13Stdenv;
|
||||
version = "0.pre" + "+date=" + (mkDate (self.lastModifiedDate or "19700101")) + "_" + (self.shortRev or "dirty");
|
||||
};
|
||||
hyprutils-with-tests = final.hyprutils.override {doCheck = true;};
|
||||
};
|
||||
};
|
||||
|
||||
packages = eachSystem (system: {
|
||||
default = self.packages.${system}.hyprutils;
|
||||
inherit (pkgsFor.${system}) hyprutils hyprutils-with-tests;
|
||||
});
|
||||
|
||||
formatter = eachSystem (system: pkgsFor.${system}.alejandra);
|
||||
};
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,296 @@
|
|||
#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() {
|
||||
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_);
|
||||
}
|
||||
};
|
||||
|
|
@ -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_);
|
||||
}
|
||||
};
|
||||
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
#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);
|
||||
void replaceInString(std::string& string, const std::string& what, const std::string& to);
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
#pragma once
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace Hyprutils {
|
||||
namespace String {
|
||||
class CVarList {
|
||||
public:
|
||||
/** Split string into 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
|
||||
*/
|
||||
CVarList(const std::string& in, const size_t maxSize = 0, const char delim = ',', const bool removeEmpty = false);
|
||||
|
||||
~CVarList() = default;
|
||||
|
||||
size_t size() const {
|
||||
return m_vArgs.size();
|
||||
}
|
||||
|
||||
std::string join(const std::string& joiner, size_t from = 0, size_t to = 0) const;
|
||||
|
||||
void map(std::function<void(std::string&)> func) {
|
||||
for (auto& s : m_vArgs)
|
||||
func(s);
|
||||
}
|
||||
|
||||
void append(const std::string arg) {
|
||||
m_vArgs.emplace_back(arg);
|
||||
}
|
||||
|
||||
std::string operator[](const size_t& idx) const {
|
||||
if (idx >= m_vArgs.size())
|
||||
return "";
|
||||
return m_vArgs[idx];
|
||||
}
|
||||
|
||||
// for range-based loops
|
||||
std::vector<std::string>::iterator begin() {
|
||||
return m_vArgs.begin();
|
||||
}
|
||||
std::vector<std::string>::const_iterator begin() const {
|
||||
return m_vArgs.begin();
|
||||
}
|
||||
std::vector<std::string>::iterator end() {
|
||||
return m_vArgs.end();
|
||||
}
|
||||
std::vector<std::string>::const_iterator end() const {
|
||||
return m_vArgs.end();
|
||||
}
|
||||
|
||||
bool contains(const std::string& el) {
|
||||
for (auto& a : m_vArgs) {
|
||||
if (a == el)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::string> m_vArgs;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
lib,
|
||||
stdenv,
|
||||
cmake,
|
||||
version ? "git",
|
||||
doCheck ? false,
|
||||
}:
|
||||
stdenv.mkDerivation {
|
||||
pname = "hyprutils";
|
||||
inherit version doCheck;
|
||||
src = ../.;
|
||||
|
||||
nativeBuildInputs = [cmake];
|
||||
|
||||
outputs = ["out" "dev"];
|
||||
|
||||
meta = with lib; {
|
||||
homepage = "https://github.com/hyprwm/hyprutils";
|
||||
description = "Small C++ library for utilities used across the Hypr* ecosystem";
|
||||
license = licenses.bsd3;
|
||||
platforms = platforms.linux;
|
||||
};
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
#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;
|
||||
}
|
||||
|
||||
void Hyprutils::String::replaceInString(std::string& string, const std::string& what, const std::string& to) {
|
||||
if (string.empty())
|
||||
return;
|
||||
size_t pos = 0;
|
||||
while ((pos = string.find(what, pos)) != std::string::npos) {
|
||||
string.replace(pos, what.length(), to);
|
||||
pos += to.length();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
#include <ranges>
|
||||
#include <algorithm>
|
||||
#include <hyprutils/string/VarList.hpp>
|
||||
#include <hyprutils/string/String.hpp>
|
||||
|
||||
using namespace Hyprutils::String;
|
||||
|
||||
Hyprutils::String::CVarList::CVarList(const std::string& in, const size_t lastArgNo, const char delim, const bool removeEmpty) {
|
||||
if (in.empty())
|
||||
m_vArgs.emplace_back("");
|
||||
|
||||
std::string args{in};
|
||||
size_t idx = 0;
|
||||
size_t pos = 0;
|
||||
std::ranges::replace_if(
|
||||
args, [&](const char& c) { return delim == 's' ? std::isspace(c) : c == delim; }, 0);
|
||||
|
||||
for (const auto& s : args | std::views::split(0)) {
|
||||
if (removeEmpty && s.empty())
|
||||
continue;
|
||||
if (++idx == lastArgNo) {
|
||||
m_vArgs.emplace_back(trim(in.substr(pos)));
|
||||
break;
|
||||
}
|
||||
pos += s.size() + 1;
|
||||
m_vArgs.emplace_back(trim(std::string_view{s}.data()));
|
||||
}
|
||||
}
|
||||
|
||||
std::string Hyprutils::String::CVarList::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) {
|
||||
rolling += m_vArgs[i] + (i + 1 < last ? joiner : "");
|
||||
}
|
||||
|
||||
return rolling;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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"; \
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
#include <hyprutils/string/String.hpp>
|
||||
#include <hyprutils/string/VarList.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);
|
||||
|
||||
CVarList list("hello world!", 0, 's', true);
|
||||
EXPECT(list[0], "hello");
|
||||
EXPECT(list[1], "world!");
|
||||
|
||||
std::string hello = "hello world!";
|
||||
replaceInString(hello, "hello", "hi");
|
||||
EXPECT(hello, "hi world!");
|
||||
|
||||
return ret;
|
||||
}
|
||||
Loading…
Reference in New Issue