pax_global_header00006660000000000000000000000064151110640020014500gustar00rootroot0000000000000052 comment=bc9803c4b8ba1e61769d79d9590d5d80729d5613 hyprwm-hyprutils-bc9803c/000077500000000000000000000000001511106400200155045ustar00rootroot00000000000000hyprwm-hyprutils-bc9803c/.clang-format000066400000000000000000000034161511106400200200630ustar00rootroot00000000000000--- 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 hyprwm-hyprutils-bc9803c/.clang-tidy000066400000000000000000000072701511106400200175460ustar00rootroot00000000000000WarningsAsErrors: '*' HeaderFilterRegex: '.*\.hpp' FormatStyle: file Checks: > -*, bugprone-*, -bugprone-easily-swappable-parameters, -bugprone-forward-declararion-namespace, -bugprone-forward-declararion-namespace, -bugprone-macro-parentheses, -bugprone-narrowing-conversions, -bugprone-branch-clone, -bugprone-assignment-in-if-condition, concurrency-*, -concurrency-mt-unsafe, cppcoreguidelines-*, -cppcoreguidelines-owning-memory, -cppcoreguidelines-avoid-magic-numbers, -cppcoreguidelines-pro-bounds-constant-array-index, -cppcoreguidelines-avoid-const-or-ref-data-members, -cppcoreguidelines-non-private-member-variables-in-classes, -cppcoreguidelines-avoid-goto, -cppcoreguidelines-pro-bounds-array-to-pointer-decay, -cppcoreguidelines-avoid-do-while, -cppcoreguidelines-avoid-non-const-global-variables, -cppcoreguidelines-special-member-functions, -cppcoreguidelines-explicit-virtual-functions, -cppcoreguidelines-avoid-c-arrays, -cppcoreguidelines-pro-bounds-pointer-arithmetic, -cppcoreguidelines-narrowing-conversions, -cppcoreguidelines-pro-type-union-access, -cppcoreguidelines-pro-type-member-init, -cppcoreguidelines-macro-usage, -cppcoreguidelines-macro-to-enum, -cppcoreguidelines-init-variables, -cppcoreguidelines-pro-type-cstyle-cast, -cppcoreguidelines-pro-type-vararg, -cppcoreguidelines-pro-type-reinterpret-cast, google-global-names-in-headers, -google-readability-casting, google-runtime-operator, misc-*, -misc-unused-parameters, -misc-no-recursion, -misc-non-private-member-variables-in-classes, -misc-include-cleaner, -misc-use-anonymous-namespace, -misc-const-correctness, modernize-*, -modernize-return-braced-init-list, -modernize-use-trailing-return-type, -modernize-use-using, -modernize-use-override, -modernize-avoid-c-arrays, -modernize-macro-to-enum, -modernize-loop-convert, -modernize-use-nodiscard, -modernize-pass-by-value, -modernize-use-auto, performance-*, -performance-avoid-endl, -performance-unnecessary-value-param, portability-std-allocator-const, readability-*, -readability-function-cognitive-complexity, -readability-function-size, -readability-identifier-length, -readability-magic-numbers, -readability-uppercase-literal-suffix, -readability-braces-around-statements, -readability-redundant-access-specifiers, -readability-else-after-return, -readability-container-data-pointer, -readability-implicit-bool-conversion, -readability-avoid-nested-conditional-operator, -readability-redundant-member-init, -readability-redundant-string-init, -readability-avoid-const-params-in-decls, -readability-named-parameter, -readability-convert-member-functions-to-static, -readability-qualified-auto, -readability-make-member-function-const, -readability-isolate-declaration, -readability-inconsistent-declaration-parameter-name, -clang-diagnostic-error, CheckOptions: performance-for-range-copy.WarnOnAllAutoCopies: true performance-inefficient-string-concatenation.StrictMode: true readability-braces-around-statements.ShortStatementLines: 0 readability-identifier-naming.ClassCase: CamelCase readability-identifier-naming.ClassIgnoredRegexp: I.* readability-identifier-naming.ClassPrefix: C # We can't use regex here?!?!?!? readability-identifier-naming.EnumCase: CamelCase readability-identifier-naming.EnumPrefix: e readability-identifier-naming.EnumConstantCase: UPPER_CASE readability-identifier-naming.FunctionCase: camelBack readability-identifier-naming.NamespaceCase: CamelCase readability-identifier-naming.NamespacePrefix: N readability-identifier-naming.StructPrefix: S readability-identifier-naming.StructCase: CamelCase hyprwm-hyprutils-bc9803c/.editorconfig000066400000000000000000000002531511106400200201610ustar00rootroot00000000000000root = 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 = 2hyprwm-hyprutils-bc9803c/.github/000077500000000000000000000000001511106400200170445ustar00rootroot00000000000000hyprwm-hyprutils-bc9803c/.github/workflows/000077500000000000000000000000001511106400200211015ustar00rootroot00000000000000hyprwm-hyprutils-bc9803c/.github/workflows/arch.yml000066400000000000000000000042141511106400200225420ustar00rootroot00000000000000name: 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 gtest base-devel cmake clang libc++ pixman - 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 gtest base-devel cmake clang libc++ pixman - 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 hyprwm-hyprutils-bc9803c/.github/workflows/nix.yml000066400000000000000000000032771511106400200224330ustar00rootroot00000000000000name: 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 - 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 # with: # name: hyprland # authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - name: Build & Test run: nix build .#${{ matrix.package }} --print-build-logs hyprwm-hyprutils-bc9803c/.gitignore000066400000000000000000000007051511106400200174760ustar00rootroot00000000000000# 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/ .direnv/ .cmake/ CMakeCache.txt CMakeFiles/ CTestTestfile.cmake DartConfiguration.tcl Makefile cmake_install.cmake compile_commands.json hyprutils.pc .envrc hyprwm-hyprutils-bc9803c/CMakeLists.txt000066400000000000000000000051531511106400200202500ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.19) file(READ "${CMAKE_SOURCE_DIR}/VERSION" VER_RAW) string(STRIP ${VER_RAW} HYPRUTILS_VERSION) 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) add_compile_options( -Wall -Wextra -Wpedantic -Wno-unused-parameter -Wno-unused-value -Wno-missing-field-initializers -Wno-narrowing -Wno-pointer-arith) set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE) if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG) message(STATUS "Configuring hyprutils in Debug") add_compile_definitions(HYPRLAND_DEBUG) set(BUILD_TESTING ON) else() add_compile_options(-O3) message(STATUS "Configuring hyprutils in Release") set(BUILD_TESTING OFF) endif() file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp" "include/*.hpp") file(GLOB_RECURSE PUBLIC_HEADERS CONFIGURE_DEPENDS "include/*.hpp") find_package(PkgConfig REQUIRED) pkg_check_modules(deps REQUIRED IMPORTED_TARGET pixman-1) add_library(hyprutils SHARED ${SRCFILES}) target_include_directories( hyprutils PUBLIC "./include" PRIVATE "./src") set_target_properties(hyprutils PROPERTIES VERSION ${hyprutils_VERSION} SOVERSION 9) target_link_libraries(hyprutils PkgConfig::deps) if(BUILD_TESTING) # GTest find_package(GTest CONFIG REQUIRED) include(GoogleTest) file(GLOB_RECURSE TESTFILES CONFIGURE_DEPENDS "tests/*.cpp") add_executable(hyprutils_tests ${TESTFILES}) target_compile_options(hyprutils_tests PRIVATE --coverage) target_link_options(hyprutils_tests PRIVATE --coverage) target_include_directories( hyprutils_tests PUBLIC "./include" PRIVATE "./src" "./src/include" "./protocols" "${CMAKE_BINARY_DIR}") target_link_libraries(hyprutils_tests PRIVATE hyprutils GTest::gtest_main PkgConfig::deps) gtest_discover_tests(hyprutils_tests) # Add coverage to hyprutils for test builds target_compile_options(hyprutils PRIVATE --coverage) target_link_options(hyprutils PRIVATE --coverage) endif() # Installation install(TARGETS hyprutils) install(DIRECTORY "include/hyprutils" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) install(FILES ${CMAKE_BINARY_DIR}/hyprutils.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) hyprwm-hyprutils-bc9803c/LICENSE000066400000000000000000000027371511106400200165220ustar00rootroot00000000000000BSD 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. hyprwm-hyprutils-bc9803c/README.md000066400000000000000000000011151511106400200167610ustar00rootroot00000000000000# 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 git clone https://github.com/hyprwm/hyprutils.git cd hyprutils/ 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` sudo cmake --install build ``` hyprwm-hyprutils-bc9803c/VERSION000066400000000000000000000000071511106400200165510ustar00rootroot000000000000000.10.4 hyprwm-hyprutils-bc9803c/flake.lock000066400000000000000000000020011511106400200174310ustar00rootroot00000000000000{ "nodes": { "nixpkgs": { "locked": { "lastModified": 1748929857, "narHash": "sha256-lcZQ8RhsmhsK8u7LIFsJhsLh/pzR9yZ8yqpTzyGdj+Q=", "owner": "NixOS", "repo": "nixpkgs", "rev": "c2a03962b8e24e669fb37b7df10e7c79531ff1a4", "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 } hyprwm-hyprutils-bc9803c/flake.nix000066400000000000000000000014741511106400200173140ustar00rootroot00000000000000{ 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]; }); in { overlays = import ./nix/overlays.nix {inherit self lib;}; packages = eachSystem (system: { default = self.packages.${system}.hyprutils; inherit (pkgsFor.${system}) hyprutils hyprutils-debug hyprutils-with-tests; }); formatter = eachSystem (system: pkgsFor.${system}.alejandra); }; } hyprwm-hyprutils-bc9803c/hyprutils.pc.in000066400000000000000000000004051511106400200204770ustar00rootroot00000000000000prefix=@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 hyprwm-hyprutils-bc9803c/include/000077500000000000000000000000001511106400200171275ustar00rootroot00000000000000hyprwm-hyprutils-bc9803c/include/hyprutils/000077500000000000000000000000001511106400200211725ustar00rootroot00000000000000hyprwm-hyprutils-bc9803c/include/hyprutils/animation/000077500000000000000000000000001511106400200231515ustar00rootroot00000000000000hyprwm-hyprutils-bc9803c/include/hyprutils/animation/AnimatedVariable.hpp000066400000000000000000000200201511106400200270440ustar00rootroot00000000000000#pragma once #include "AnimationConfig.hpp" #include "../memory/WeakPtr.hpp" #include "../memory/SharedPtr.hpp" #include "../signal/Signal.hpp" #include "AnimationManager.hpp" #include #include namespace Hyprutils { namespace Animation { /* A base class for animated variables. */ class CBaseAnimatedVariable { public: using CallbackFun = std::function thisptr)>; CBaseAnimatedVariable() { ; // m_bDummy = true; }; void create(CAnimationManager*, int, Memory::CSharedPointer); void connectToActive(); void disconnectFromActive(); /* Needs to call disconnectFromActive to remove `m_pSelf` from the active animation list */ virtual ~CBaseAnimatedVariable() { disconnectFromActive(); }; virtual void warp(bool endCallback = true, bool forceDisconnect = true) = 0; CBaseAnimatedVariable(const CBaseAnimatedVariable&) = delete; CBaseAnimatedVariable(CBaseAnimatedVariable&&) = delete; CBaseAnimatedVariable& operator=(const CBaseAnimatedVariable&) = delete; CBaseAnimatedVariable& operator=(CBaseAnimatedVariable&&) = delete; // void setConfig(Memory::CSharedPointer pConfig) { m_pConfig = pConfig; } Memory::CWeakPointer getConfig() const { return m_pConfig; } bool enabled() const; const std::string& getBezierName() const; const std::string& getStyle() const; /* returns the spent (completion) % */ float getPercent() const; /* returns the current curve value. */ float getCurveValue() const; /* checks if an animation is in progress */ bool isBeingAnimated() const { return m_bIsBeingAnimated; } /* checks m_bDummy and m_pAnimationManager */ bool ok() const; /* calls the update callback */ void onUpdate(); /* sets a function to be ran when an animation ended. if "remove" is set to true, it will remove the callback when ran. */ void setCallbackOnEnd(CallbackFun func, bool remove = true); /* sets a function to be ran when an animation is started. if "remove" is set to true, it will remove the callback when ran. */ void setCallbackOnBegin(CallbackFun func, bool remove = true); /* sets the update callback, called every time the value is animated and a step is done Warning: calling unregisterVar/registerVar in this handler will cause UB */ void setUpdateCallback(CallbackFun func); /* resets all callbacks. Does not call any. */ void resetAllCallbacks(); void onAnimationEnd(); void onAnimationBegin(); /* returns whether the parent CAnimationManager is dead */ bool isAnimationManagerDead() const; int m_Type = -1; protected: friend class CAnimationManager; CAnimationManager* m_pAnimationManager = nullptr; bool m_bIsConnectedToActive = false; bool m_bIsBeingAnimated = false; Memory::CWeakPointer m_pSelf; Memory::CWeakPointer m_pSignals; private: Memory::CWeakPointer m_pConfig; std::chrono::steady_clock::time_point animationBegin; bool m_bDummy = true; bool m_bRemoveEndAfterRan = true; bool m_bRemoveBeginAfterRan = true; CallbackFun m_fEndCallback; CallbackFun m_fBeginCallback; CallbackFun m_fUpdateCallback; }; /* This concept represents the minimum requirement for a type to be used with CGenericAnimatedVariable */ template concept AnimatedType = requires(ValueImpl val) { requires std::is_copy_constructible_v; { val == val } -> std::same_as; // requires operator== { val = val }; // requires operator= }; /* A generic class for variables. VarType is the type of the variable to be animated. AnimationContext is there to attach additional data to the animation. In Hyprland that struct would contain a reference to window, workspace or layer for example. */ template class CGenericAnimatedVariable : public CBaseAnimatedVariable { public: CGenericAnimatedVariable() = default; void create(const int typeInfo, CAnimationManager* pAnimationManager, Memory::CSharedPointer> pSelf, const VarType& initialValue) { m_Begun = initialValue; m_Value = initialValue; m_Goal = initialValue; CBaseAnimatedVariable::create(pAnimationManager, typeInfo, pSelf); } CGenericAnimatedVariable(const CGenericAnimatedVariable&) = delete; CGenericAnimatedVariable(CGenericAnimatedVariable&&) = delete; CGenericAnimatedVariable& operator=(const CGenericAnimatedVariable&) = delete; CGenericAnimatedVariable& operator=(CGenericAnimatedVariable&&) = delete; virtual void warp(bool endCallback = true, bool forceDisconnect = true) { if (!m_bIsBeingAnimated) return; m_Value = m_Goal; onUpdate(); m_bIsBeingAnimated = false; if (forceDisconnect) disconnectFromActive(); if (endCallback) onAnimationEnd(); } const VarType& value() const { return m_Value; } /* used to update the value each tick via the AnimationManager */ VarType& value() { return m_Value; } const VarType& goal() const { return m_Goal; } const VarType& begun() const { return m_Begun; } CGenericAnimatedVariable& operator=(const VarType& v) { if (v == m_Goal) return *this; m_Goal = v; m_Begun = m_Value; onAnimationBegin(); return *this; } /* Sets the actual stored value, without affecting the goal, but resets the timer*/ void setValue(const VarType& v) { if (v == m_Value) return; m_Value = v; m_Begun = m_Value; onAnimationBegin(); } /* Sets the actual value and goal*/ void setValueAndWarp(const VarType& v) { m_Goal = v; m_bIsBeingAnimated = true; warp(); } AnimationContext m_Context; private: VarType m_Value{}; VarType m_Goal{}; VarType m_Begun{}; }; } } hyprwm-hyprutils-bc9803c/include/hyprutils/animation/AnimationConfig.hpp000066400000000000000000000053351511106400200267350ustar00rootroot00000000000000#pragma once #include "../memory/WeakPtr.hpp" #include #include namespace Hyprutils { namespace Animation { /* Structure for animation properties. Config properties need to have a static lifetime to allow for config reload. */ struct SAnimationPropertyConfig { bool overridden = false; std::string internalBezier = ""; std::string internalStyle = ""; float internalSpeed = 0.f; int internalEnabled = -1; Memory::CWeakPointer pValues; Memory::CWeakPointer pParentAnimation; }; /* A class to manage SAnimationPropertyConfig objects in a tree structure */ class CAnimationConfigTree { public: CAnimationConfigTree() = default; ~CAnimationConfigTree() = default; /* Add a new animation node inheriting from a parent. If parent is empty, a root node will be created that references it's own values. Make sure the parent node has already been created through this interface. */ void createNode(const std::string& nodeName, const std::string& parent = ""); /* check if a node name has been created using createNode */ bool nodeExists(const std::string& nodeName) const; /* Override the values of a node. The root node can also be overriden. */ void setConfigForNode(const std::string& nodeName, int enabled, float speed, const std::string& bezier, const std::string& style = ""); Memory::CSharedPointer getConfig(const std::string& name) const; const std::unordered_map>& getFullConfig() const; CAnimationConfigTree(const CAnimationConfigTree&) = delete; CAnimationConfigTree(CAnimationConfigTree&&) = delete; CAnimationConfigTree& operator=(const CAnimationConfigTree&) = delete; CAnimationConfigTree& operator=(CAnimationConfigTree&&) = delete; private: void setAnimForChildren(Memory::CSharedPointer PANIM); std::unordered_map> m_mAnimationConfig; }; } } hyprwm-hyprutils-bc9803c/include/hyprutils/animation/AnimationManager.hpp000066400000000000000000000051471511106400200271030ustar00rootroot00000000000000#pragma once #include "./BezierCurve.hpp" #include "../math/Vector2D.hpp" #include "../memory/WeakPtr.hpp" #include "../signal/Signal.hpp" #include #include #include namespace Hyprutils { namespace Animation { class CBaseAnimatedVariable; /* A class for managing bezier curves and variables that are being animated. */ class CAnimationManager { public: CAnimationManager(); virtual ~CAnimationManager() = default; void tickDone(); void rotateActive(); bool shouldTickForNext(); virtual void scheduleTick() = 0; virtual void onTicked() = 0; void addBezierWithName(std::string, const Math::Vector2D&, const Math::Vector2D&); void removeAllBeziers(); bool bezierExists(const std::string&); Memory::CSharedPointer getBezier(const std::string&); const std::unordered_map>& getAllBeziers(); struct SAnimationManagerSignals { Signal::CSignalT> connect; Signal::CSignalT> disconnect; }; Memory::CWeakPointer getSignals() const; std::vector> m_vActiveAnimatedVariables; private: std::unordered_map> m_mBezierCurves; bool m_bTickScheduled = false; struct SAnimVarListeners { Signal::CHyprSignalListener connect; Signal::CHyprSignalListener disconnect; }; Memory::CUniquePointer m_listeners; Memory::CUniquePointer m_events; }; } } hyprwm-hyprutils-bc9803c/include/hyprutils/animation/BezierCurve.hpp000066400000000000000000000023251511106400200261110ustar00rootroot00000000000000#pragma once #include #include #include "../math/Vector2D.hpp" namespace Hyprutils { namespace Animation { constexpr int BAKEDPOINTS = 255; constexpr float INVBAKEDPOINTS = 1.f / BAKEDPOINTS; /* An implementation of a cubic bezier curve. */ class CBezierCurve { public: /* Calculates a cubic bezier curve based on 2 control points (EXCLUDES the 0,0 and 1,1 points). */ void setup(const std::array& points); /* Calculates a cubic bezier curve based on 4 control points. */ void setup4(const std::array& points); float getYForT(float const& t) const; float getXForT(float const& t) const; float getYForPoint(float const& x) const; /* this INCLUDES the 0,0 and 1,1 points. */ const std::vector& getControlPoints() const; private: /* this INCLUDES the 0,0 and 1,1 points. */ std::vector m_vPoints; std::array m_aPointsBaked; }; } } hyprwm-hyprutils-bc9803c/include/hyprutils/cli/000077500000000000000000000000001511106400200217415ustar00rootroot00000000000000hyprwm-hyprutils-bc9803c/include/hyprutils/cli/ArgumentParser.hpp000066400000000000000000000031321511106400200254100ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include "../memory/UniquePtr.hpp" namespace Hyprutils::CLI { class CArgumentParserImpl; class CArgumentParser { public: CArgumentParser(const std::span& args); ~CArgumentParser() = default; std::expected registerBoolOption(const std::string_view& name, const std::string_view& abbrev, const std::string_view& description); std::expected registerIntOption(const std::string_view& name, const std::string_view& abbrev, const std::string_view& description); std::expected registerFloatOption(const std::string_view& name, const std::string_view& abbrev, const std::string_view& description); std::expected registerStringOption(const std::string_view& name, const std::string_view& abbrev, const std::string_view& description); std::optional getBool(const std::string_view& name); std::optional getInt(const std::string_view& name); std::optional getFloat(const std::string_view& name); std::optional getString(const std::string_view& name); // commence the parsing after registering std::expected parse(); std::string getDescription(const std::string_view& header, std::optional maxWidth = {}); private: Memory::CUniquePointer m_impl; }; };hyprwm-hyprutils-bc9803c/include/hyprutils/cli/Logger.hpp000066400000000000000000000072471511106400200237030ustar00rootroot00000000000000#pragma once #include #include #include #include "../memory/UniquePtr.hpp" #include "../memory/WeakPtr.hpp" namespace Hyprutils::CLI { class CLoggerImpl; enum eLogLevel : uint8_t { LOG_TRACE = 0, LOG_DEBUG, LOG_WARN, LOG_ERR, LOG_CRIT, }; // CLogger is a thread-safe, general purpose logger. // the logger's stdout is enabled by default. // color is enabled by default, it's only for stdout. // everything else is disabled. class CLogger { public: CLogger(); ~CLogger(); CLogger(const CLogger&) = delete; CLogger(CLogger&) = delete; CLogger(CLogger&&) = delete; void setLogLevel(eLogLevel level); void setTime(bool enabled); void setEnableStdout(bool enabled); void setEnableColor(bool enabled); void setEnableRolling(bool enabled); std::expected setOutputFile(const std::string_view& file); const std::string& rollingLog(); void log(eLogLevel level, const std::string_view& msg); template // NOLINTNEXTLINE void log(eLogLevel level, std::format_string fmt, Args&&... args) { if (!m_shouldLogAtAll) return; if (level < m_logLevel) return; std::string logMsg = std::vformat(fmt.get(), std::make_format_args(args...)); log(level, logMsg); } private: Memory::CUniquePointer m_impl; // this has to be here as part of important optimization of trace logs eLogLevel m_logLevel = LOG_DEBUG; // this has to be here as part of important optimization of disabled logging bool m_shouldLogAtAll = false; friend class CLoggerImpl; friend class CLoggerConnection; }; // CLoggerConnection is a "handle" to a logger, that can be created from a logger and // allows to send messages to a logger via a proxy // this does not allow for any changes to the logger itself, only sending logs. // Logger connections keep their own logLevel. They inherit it at creation, but can be changed class CLoggerConnection { public: CLoggerConnection(CLogger& logger); ~CLoggerConnection(); CLoggerConnection(const CLoggerConnection&) = delete; CLoggerConnection(CLoggerConnection&) = delete; // Allow move CLoggerConnection(CLoggerConnection&&) = default; void setName(const std::string_view& name); void setLogLevel(eLogLevel level); void log(eLogLevel level, const std::string_view& msg); CLogger* getLogger(); void redirect(CLogger& logger); template // NOLINTNEXTLINE void log(eLogLevel level, std::format_string fmt, Args&&... args) { if (!m_impl || !m_logger) return; if (!m_logger->m_shouldLogAtAll) return; if (level < m_logLevel) return; std::string logMsg = std::vformat(fmt.get(), std::make_format_args(args...)); log(level, logMsg); } private: Memory::CWeakPointer m_impl; CLogger* m_logger = nullptr; eLogLevel m_logLevel = LOG_DEBUG; std::string m_name = ""; }; };hyprwm-hyprutils-bc9803c/include/hyprutils/i18n/000077500000000000000000000000001511106400200217515ustar00rootroot00000000000000hyprwm-hyprutils-bc9803c/include/hyprutils/i18n/I18nEngine.hpp000066400000000000000000000030661511106400200243340ustar00rootroot00000000000000#pragma once #include "../memory/UniquePtr.hpp" #include #include #include #include namespace Hyprutils::I18n { struct SI18nEngineImpl; typedef std::unordered_map translationVarMap; typedef std::function translationFn; class CI18nLocale { public: ~CI18nLocale() = default; std::string locale(); std::string stem(); std::string full(); private: CI18nLocale(std::string fullLocale); std::string m_locale, m_rawFullLocale; friend class CI18nEngine; }; class CI18nEngine { public: CI18nEngine(); ~CI18nEngine(); /* Register a translation entry. The internal translation db is kept as a vector, so make sure your keys are linear, don't use e.g. 2 billion as that will call .resize() on the vec to 2 billion. If you pass a Fn, you can do logic, e.g. "1 point" vs "2 points". */ void registerEntry(const std::string& locale, uint64_t key, std::string&& translationUTF8); void registerEntry(const std::string& locale, uint64_t key, translationFn&& translationFn); void setFallbackLocale(const std::string& locale); std::string localizeEntry(const std::string& locale, uint64_t key, const translationVarMap& map); CI18nLocale getSystemLocale(); private: Memory::CUniquePointer m_impl; }; }hyprwm-hyprutils-bc9803c/include/hyprutils/math/000077500000000000000000000000001511106400200221235ustar00rootroot00000000000000hyprwm-hyprutils-bc9803c/include/hyprutils/math/Box.hpp000066400000000000000000000141361511106400200233710ustar00rootroot00000000000000#pragma once #include "./Vector2D.hpp" #include "./Misc.hpp" namespace Hyprutils::Math { /** * @brief Represents the extents of a bounding box. */ struct SBoxExtents { Vector2D topLeft; Vector2D bottomRight; /** * @brief Scales the extents by a given factor. * @param scale The scaling factor. * @return Scaled SBoxExtents. */ SBoxExtents operator*(const double& scale) const { return SBoxExtents{topLeft * scale, bottomRight * scale}; } /** * @brief Rounds the coordinates of the extents. * @return Rounded SBoxExtents. */ SBoxExtents round() { return {topLeft.round(), bottomRight.round()}; } /** * @brief Checks equality between two SBoxExtents objects. * @param other Another SBoxExtents object to compare. * @return True if both SBoxExtents are equal, false otherwise. */ bool operator==(const SBoxExtents& other) const { return topLeft == other.topLeft && bottomRight == other.bottomRight; } /** * @brief Adjusts the extents to encompass another SBoxExtents. * @param other Another SBoxExtents to add to this one. */ void addExtents(const SBoxExtents& other) { topLeft = topLeft.getComponentMax(other.topLeft); bottomRight = bottomRight.getComponentMax(other.bottomRight); } }; /** * @brief Represents a 2D bounding box. */ class CBox { public: /** * @brief Constructs a CBox with specified position and dimensions. * @param x_ X-coordinate of the top-left corner. * @param y_ Y-coordinate of the top-left corner. * @param w_ Width of the box. * @param h_ Height of the box. */ CBox(double x_, double y_, double w_, double h_) { x = x_; y = y_; w = w_; h = h_; } /** * @brief Default constructor. Initializes an empty box (0 width, 0 height). */ CBox() { w = 0; h = 0; } /** * @brief Constructs a CBox with uniform dimensions. * @param d Dimensions to apply uniformly (x, y, width, height). */ CBox(const double d) { x = d; y = d; w = d; h = d; } /** * @brief Constructs a CBox from a position and size vector. * @param pos Position vector representing the top-left corner. * @param size Size vector representing width and height. */ CBox(const Vector2D& pos, const Vector2D& size) { x = pos.x; y = pos.y; w = size.x; h = size.y; } // Geometric operations CBox& applyFromWlr(); CBox& scale(double scale); CBox& scaleFromCenter(double scale); CBox& scale(const Vector2D& scale); CBox& translate(const Vector2D& vec); CBox& round(); CBox& transform(const eTransform t, double w, double h); CBox& addExtents(const SBoxExtents& e); CBox& expand(const double& value); CBox& noNegativeSize(); CBox copy() const; CBox intersection(const CBox& other) const; bool overlaps(const CBox& other) const; bool inside(const CBox& bound) const; /** * @brief Computes the extents of the box relative to another box. * @param small Another CBox to compare against. * @return SBoxExtents representing the extents of the box relative to 'small'. */ SBoxExtents extentsFrom(const CBox&); // this is the big box /** * @brief Calculates the middle point of the box. * @return Vector2D representing the middle point. */ Vector2D middle() const; /** * @brief Retrieves the position of the top-left corner of the box. * @return Vector2D representing the position. */ Vector2D pos() const; /** * @brief Retrieves the size (width and height) of the box. * @return Vector2D representing the size. */ Vector2D size() const; /** * @brief Retrieves the size of the box offset by its position. * @return Vector2D representing the bottom right extent of the box. */ Vector2D extent() const; /** * @brief Finds the closest point within the box to a given vector. * @param vec Vector from which to find the closest point. * @return Vector2D representing the closest point within the box. */ Vector2D closestPoint(const Vector2D& vec) const; /** * @brief Checks if a given point is inside the box. * @param vec Vector representing the point to check. * @return True if the point is inside the box, false otherwise. */ bool containsPoint(const Vector2D& vec) const; /** * @brief Checks if the box is empty (zero width or height). * @return True if the box is empty, false otherwise. */ bool empty() const; double x = 0, y = 0; // Position of the top-left corner of the box. union { double w; double width; }; union { double h; double height; }; double rot = 0; //< Rotation angle of the box in radians (counterclockwise). /** * @brief Checks equality between two CBox objects. * @param rhs Another CBox object to compare. * @return True if both CBox objects are equal, false otherwise. */ bool operator==(const CBox& rhs) const { return x == rhs.x && y == rhs.y && w == rhs.w && h == rhs.h; } private: CBox roundInternal(); }; } hyprwm-hyprutils-bc9803c/include/hyprutils/math/Edges.hpp000066400000000000000000000053441511106400200236710ustar00rootroot00000000000000#pragma once #include "hyprutils/memory/Casts.hpp" #include namespace Hyprutils::Math { /** * @brief Flag set of box edges */ class CEdges { public: enum eEdges : uint8_t { NONE = 0, TOP = 1, LEFT = 2, BOTTOM = 4, RIGHT = 8, }; CEdges() = default; CEdges(eEdges edges) : m_edges(edges) {} CEdges(uint8_t edges) : m_edges(Memory::sc(edges)) {} bool operator==(const CEdges& other) { return m_edges == other.m_edges; } CEdges operator|(const CEdges& other) { return m_edges | other.m_edges; } CEdges operator&(const CEdges& other) { return m_edges & other.m_edges; } CEdges operator^(const CEdges& other) { return m_edges ^ other.m_edges; } void operator|=(const CEdges& other) { m_edges = (*this | other).m_edges; } void operator&=(const CEdges& other) { m_edges = (*this & other).m_edges; } void operator^=(const CEdges& other) { m_edges = (*this ^ other).m_edges; } /** * @return if the edge set contains the top edge. */ bool top() { return m_edges & TOP; } /** * @return if the edge set contains the left edge. */ bool left() { return m_edges & LEFT; } /** * @return if the edge set contains the bottom edge. */ bool bottom() { return m_edges & BOTTOM; } /** * @return if the edge set contains the right edge. */ bool right() { return m_edges & RIGHT; } /** * @param top The state the top edge should be set to. */ void setTop(bool top) { m_edges = Memory::sc((m_edges & ~TOP) | (TOP * top)); } /** * @param left The state the left edge should be set to. */ void setLeft(bool left) { m_edges = Memory::sc((m_edges & ~LEFT) | (LEFT * left)); } /** * @param bottom The state the bottom edge should be set to. */ void setBottom(bool bottom) { m_edges = Memory::sc((m_edges & ~BOTTOM) | (BOTTOM * bottom)); } /** * @param right The state the right edge should be set to. */ void setRight(bool right) { m_edges = Memory::sc((m_edges & ~RIGHT) | (RIGHT * right)); } eEdges m_edges = NONE; }; } hyprwm-hyprutils-bc9803c/include/hyprutils/math/Mat3x3.hpp000066400000000000000000000032551511106400200237200ustar00rootroot00000000000000#pragma once #include #include #include #include #include "./Misc.hpp" namespace Hyprutils { namespace Math { class CBox; class Vector2D; class Mat3x3 { public: Mat3x3(); Mat3x3(std::array); Mat3x3(std::vector); /* create an identity 3x3 matrix */ static Mat3x3 identity(); /* create an output projection matrix */ static Mat3x3 outputProjection(const Vector2D& size, eTransform transform); /* get the matrix as an array, in a row-major order. */ std::array getMatrix() const; /* create a box projection matrix */ Mat3x3 projectBox(const CBox& box, eTransform transform, float rot = 0.F /* rad, CCW */) const; /* in-place functions */ Mat3x3& transform(eTransform transform); Mat3x3& rotate(float rot /* rad, CCW */); Mat3x3& scale(const Vector2D& scale); Mat3x3& scale(const float scale); Mat3x3& translate(const Vector2D& offset); Mat3x3& transpose(); Mat3x3& multiply(const Mat3x3& other); /* misc utils */ Mat3x3 copy() const; std::string toString() const; bool operator==(const Mat3x3& other) const { return other.matrix == matrix; } friend std::ostream& operator<<(std::ostream& os, const Mat3x3& mat) { os << mat.toString(); return os; } private: std::array matrix; }; } }hyprwm-hyprutils-bc9803c/include/hyprutils/math/Misc.hpp000066400000000000000000000007551511106400200235360ustar00rootroot00000000000000#pragma once namespace Hyprutils { namespace Math { enum eTransform { HYPRUTILS_TRANSFORM_NORMAL = 0, HYPRUTILS_TRANSFORM_90 = 1, HYPRUTILS_TRANSFORM_180 = 2, HYPRUTILS_TRANSFORM_270 = 3, HYPRUTILS_TRANSFORM_FLIPPED = 4, HYPRUTILS_TRANSFORM_FLIPPED_90 = 5, HYPRUTILS_TRANSFORM_FLIPPED_180 = 6, HYPRUTILS_TRANSFORM_FLIPPED_270 = 7, }; } }hyprwm-hyprutils-bc9803c/include/hyprutils/math/Region.hpp000066400000000000000000000063261511106400200240660ustar00rootroot00000000000000#pragma once #include #include #include "Vector2D.hpp" #include "Box.hpp" namespace Hyprutils { namespace Math { class CRegion { public: /* Create an empty region */ CRegion(); /* Create from a reference. Copies, does not own. */ CRegion(const pixman_region32_t* const ref); /* Create from a box */ CRegion(double x, double y, double w, double h); /* Create from a CBox */ CRegion(const CBox& box); /* Create from a pixman_box32_t */ CRegion(pixman_box32_t* box); CRegion(const CRegion&); CRegion(CRegion&&) noexcept; ~CRegion(); CRegion& operator=(CRegion&& other) noexcept { if (this != &other) pixman_region32_copy(&m_rRegion, other.pixman()); return *this; } CRegion& operator=(const CRegion& other) { if (this != &other) pixman_region32_copy(&m_rRegion, other.pixman()); return *this; } CRegion& clear(); CRegion& set(const CRegion& other); CRegion& add(const CRegion& other); CRegion& add(double x, double y, double w, double h); CRegion& add(const CBox& other); CRegion& subtract(const CRegion& other); CRegion& intersect(const CRegion& other); CRegion& intersect(double x, double y, double w, double h); CRegion& translate(const Vector2D& vec); CRegion& transform(const eTransform t, double w, double h); CRegion& invert(pixman_box32_t* box); CRegion& invert(const CBox& box); CRegion& scale(float scale); CRegion& scale(const Vector2D& scale); CRegion& expand(double units); CRegion& rationalize(); CBox getExtents(); bool containsPoint(const Vector2D& vec) const; bool empty() const; Vector2D closestPoint(const Vector2D& vec) const; CRegion copy() const; std::vector getRects() const; template 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(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; }; } } hyprwm-hyprutils-bc9803c/include/hyprutils/math/Vector2D.hpp000066400000000000000000000207321511106400200242700ustar00rootroot00000000000000#pragma once #include #include #include #include namespace Hyprutils { namespace Math { class Vector2D { public: constexpr Vector2D(double xx, double yy) : x(xx), y(yy) { ; } constexpr Vector2D(int xx, int yy) : x(Hyprutils::Memory::sc(xx)), y(Hyprutils::Memory::sc(yy)) { ; } constexpr Vector2D() = default; ~Vector2D() = default; double x = 0; double y = 0; // returns the scale double normalize(); constexpr Vector2D operator+(const Vector2D& a) const { return Vector2D(this->x + a.x, this->y + a.y); } constexpr Vector2D operator-(const Vector2D& a) const { return Vector2D(this->x - a.x, this->y - a.y); } constexpr Vector2D operator-() const { return Vector2D(-this->x, -this->y); } constexpr Vector2D operator*(const double& a) const { return Vector2D(this->x * a, this->y * a); } constexpr Vector2D operator/(const double& a) const { return Vector2D(this->x / a, this->y / a); } constexpr bool operator==(const Vector2D& a) const { return a.x == x && a.y == y; } constexpr bool operator!=(const Vector2D& a) const { return a.x != x || a.y != y; } constexpr Vector2D operator*(const Vector2D& a) const { return Vector2D(this->x * a.x, this->y * a.y); } constexpr Vector2D operator/(const Vector2D& a) const { return Vector2D(this->x / a.x, this->y / a.y); } constexpr bool operator>(const Vector2D& a) const { return this->x > a.x && this->y > a.y; } constexpr bool operator<(const Vector2D& a) const { return this->x < a.x && this->y < a.y; } constexpr Vector2D& operator+=(const Vector2D& a) { this->x += a.x; this->y += a.y; return *this; } constexpr Vector2D& operator-=(const Vector2D& a) { this->x -= a.x; this->y -= a.y; return *this; } constexpr Vector2D& operator*=(const Vector2D& a) { this->x *= a.x; this->y *= a.y; return *this; } constexpr Vector2D& operator/=(const Vector2D& a) { this->x /= a.x; this->y /= a.y; return *this; } constexpr Vector2D& operator*=(const double& a) { this->x *= a; this->y *= a; return *this; } constexpr Vector2D& operator/=(const double& a) { this->x /= a; this->y /= a; return *this; } double distance(const Vector2D& other) const; double distanceSq(const Vector2D& other) const; double size() const; Vector2D clamp(const Vector2D& min, const Vector2D& max = Vector2D{-1, -1}) const; Vector2D floor() const; Vector2D round() const; Vector2D getComponentMax(const Vector2D& other) const; Vector2D transform(eTransform transform, const Vector2D& monitorSize) const; }; } } // absolutely ridiculous formatter spec parsing #define AQ_FORMAT_PARSE(specs__, type__) \ template \ constexpr auto parse(FormatContext& ctx) { \ auto it = ctx.begin(); \ for (; it != ctx.end() && *it != '}'; it++) { \ switch (*it) { specs__ default : throw std::format_error("invalid format specification"); } \ } \ return it; \ } #define AQ_FORMAT_FLAG(spec__, flag__) \ case spec__: (flag__) = true; break; #define AQ_FORMAT_NUMBER(buf__) \ case '0': \ case '1': \ case '2': \ case '3': \ case '4': \ case '5': \ case '6': \ case '7': \ case '8': \ case '9': (buf__).push_back(*it); break; /** format specification - 'j', as json array - 'X', same as std::format("{}x{}", vec.x, vec.y) - number, floating point precision, use `0` to format as integer */ template struct std::formatter : std::formatter { bool formatJson = false; bool formatX = false; std::string precision = ""; AQ_FORMAT_PARSE(AQ_FORMAT_FLAG('j', formatJson) // AQ_FORMAT_FLAG('X', formatX) // AQ_FORMAT_NUMBER(precision), Hyprutils::Math::Vector2D) template auto format(const Hyprutils::Math::Vector2D& vec, FormatContext& ctx) const { std::string formatString = precision.empty() ? "{}" : std::format("{{:.{}f}}", precision); if (formatJson) formatString = std::format("[{0}, {0}]", formatString); else if (formatX) formatString = std::format("{0}x{0}", formatString); else formatString = std::format("[Vector2D: x: {0}, y: {0}]", formatString); try { string buf = std::vformat(formatString, std::make_format_args(vec.x, vec.y)); return std::format_to(ctx.out(), "{}", buf); } catch (std::format_error& e) { return std::format_to(ctx.out(), "[{}, {}]", vec.x, vec.y); } } }; hyprwm-hyprutils-bc9803c/include/hyprutils/memory/000077500000000000000000000000001511106400200225025ustar00rootroot00000000000000hyprwm-hyprutils-bc9803c/include/hyprutils/memory/Atomic.hpp000066400000000000000000000301341511106400200244300ustar00rootroot00000000000000#pragma once #include "./ImplBase.hpp" #include "./SharedPtr.hpp" #include "./WeakPtr.hpp" #include /* 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_ { class impl : public Impl_::impl_base { std::recursive_mutex m_mutex; public: impl(void* data, DeleteFn deleter) noexcept : Impl_::impl_base(data, deleter) { ; } std::lock_guard lockGuard() { return std::lock_guard(m_mutex); } // Needed when unlock order or mutex lifetime matters. std::recursive_mutex& getMutex() { return m_mutex; } }; } // Forward declaration for friend template class CAtomicWeakPointer; template class CAtomicSharedPointer { template using isConstructible = std::enable_if_t>; template using validHierarchy = std::enable_if_t&, X>, CAtomicSharedPointer&>; public: explicit CAtomicSharedPointer(T* object) noexcept : m_ptr(new Atomic_::impl(sc(object), _delete)) { ; } CAtomicSharedPointer(Impl_::impl_base* impl) noexcept : m_ptr(impl) { ; } CAtomicSharedPointer(const CAtomicSharedPointer& ref) { if (!ref.m_ptr.impl_) return; auto lg = ref.implLockGuard(); m_ptr = ref.m_ptr; } template > CAtomicSharedPointer(const CAtomicSharedPointer& ref) { if (!ref.m_ptr.impl_) return; auto lg = ref.implLockGuard(); m_ptr = ref.m_ptr; } template > CAtomicSharedPointer(CAtomicSharedPointer&& 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 validHierarchy&> operator=(const CAtomicSharedPointer& 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 validHierarchy&> operator=(CAtomicSharedPointer&& 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(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.impl_->destroy(); delete sc(m_ptr.impl_); m_ptr.impl_ = nullptr; // 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 guard = m_ptr; m_ptr.reset(); // destroys the data // Now we can safely check if guard is the last wref. if (guard.impl_->wref() == 1) { mutex.unlock(); // destroy impl_ (includes the mutex) delete sc(guard.impl_); guard.impl_ = nullptr; // mutex invalid return; } 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: static void _delete(void* p) { std::default_delete{}(sc(p)); } std::lock_guard implLockGuard() const { return sc(m_ptr.impl_)->lockGuard(); } CSharedPointer m_ptr; template friend class CAtomicWeakPointer; template friend class CAtomicSharedPointer; }; template class CAtomicWeakPointer { template using isConstructible = std::enable_if_t>; template using validHierarchy = std::enable_if_t&, X>, CAtomicWeakPointer&>; public: CAtomicWeakPointer(const CAtomicWeakPointer& ref) { if (!ref.m_ptr.impl_) return; auto lg = ref.implLockGuard(); m_ptr = ref.m_ptr; } template > CAtomicWeakPointer(const CAtomicWeakPointer& ref) { if (!ref.m_ptr.impl_) return; auto lg = ref.implLockGuard(); m_ptr = ref.m_ptr; } template > CAtomicWeakPointer(CAtomicWeakPointer&& ref) noexcept { std::swap(m_ptr, ref.m_ptr); } CAtomicWeakPointer(CAtomicWeakPointer&& ref) noexcept { std::swap(m_ptr, ref.m_ptr); } CAtomicWeakPointer(const CAtomicSharedPointer& 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 validHierarchy&> operator=(const CAtomicWeakPointer& 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 validHierarchy&> operator=(CAtomicWeakPointer&& 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(m_ptr.impl_)->getMutex(); mutex.lock(); if (m_ptr.impl_->ref() == 0 && m_ptr.impl_->wref() == 1) { mutex.unlock(); delete sc(m_ptr.impl_); m_ptr.impl_ = nullptr; // 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& 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 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(m_ptr.impl_); } private: std::lock_guard implLockGuard() const { return sc(m_ptr.impl_)->lockGuard(); } CWeakPointer m_ptr; template friend class CAtomicWeakPointer; template friend class CAtomicSharedPointer; }; template [[nodiscard]] inline CAtomicSharedPointer makeAtomicShared(Args&&... args) { return CAtomicSharedPointer(new U(std::forward(args)...)); } } hyprwm-hyprutils-bc9803c/include/hyprutils/memory/Casts.hpp000066400000000000000000000014601511106400200242710ustar00rootroot00000000000000#pragma once #include #include namespace Hyprutils::Memory { template constexpr To sc(From&& from) noexcept { return static_cast(std::forward(from)); } template constexpr To cc(From&& from) noexcept { return const_cast(std::forward(from)); } template constexpr To rc(From&& from) noexcept { return reinterpret_cast(std::forward(from)); } template constexpr To dc(From&& from) { return dynamic_cast(std::forward(from)); } template constexpr To bc(const From& from) noexcept { return std::bit_cast(from); } } hyprwm-hyprutils-bc9803c/include/hyprutils/memory/ImplBase.hpp000066400000000000000000000051401511106400200247070ustar00rootroot00000000000000#pragma once #include #include namespace Hyprutils { namespace Memory { namespace Impl_ { class impl_base { public: using DeleteFn = void (*)(void*); impl_base(void* data, DeleteFn deleter, bool lock = true) noexcept : _lockable(lock), _data(data), _deleter(deleter) { ; } void inc() noexcept { _ref++; } void dec() noexcept { _ref--; } void incWeak() noexcept { _weak++; } void decWeak() noexcept { _weak--; } unsigned int ref() noexcept { return _ref; } unsigned int wref() noexcept { return _weak; } void destroy() noexcept { _destroy(); } bool destroying() noexcept { return _destroying; } bool lockable() noexcept { return _lockable; } bool dataNonNull() noexcept { return _data != nullptr; } void* getData() noexcept { return _data; } ~impl_base() { destroy(); } private: /* strong refcount */ unsigned int _ref = 0; /* weak refcount */ unsigned int _weak = 0; /* if this is lockable (shared) */ bool _lockable = true; void* _data = nullptr; /* 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; } DeleteFn _deleter = nullptr; }; } } } hyprwm-hyprutils-bc9803c/include/hyprutils/memory/SharedPtr.hpp000066400000000000000000000142451511106400200251150ustar00rootroot00000000000000#pragma once #include #include "ImplBase.hpp" #include "Casts.hpp" /* 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 { template class CSharedPointer { public: template using validHierarchy = std::enable_if_t&, X>, CSharedPointer&>; template using isConstructible = std::enable_if_t>; /* creates a new shared pointer managing a resource avoid calling. Could duplicate ownership. Prefer makeShared */ explicit CSharedPointer(T* object) noexcept : impl_(new Impl_::impl_base(sc(object), _delete)) { increment(); } /* creates a shared pointer from a reference */ template > CSharedPointer(const CSharedPointer& ref) noexcept : impl_(ref.impl_) { increment(); } CSharedPointer(const CSharedPointer& ref) noexcept : impl_(ref.impl_) { increment(); } template > CSharedPointer(CSharedPointer&& ref) noexcept { std::swap(impl_, ref.impl_); } CSharedPointer(CSharedPointer&& ref) noexcept { std::swap(impl_, ref.impl_); } /* allows weakPointer to create from an impl */ CSharedPointer(Impl_::impl_base* implementation) noexcept : impl_(implementation) { increment(); } /* creates an empty shared pointer with no implementation */ CSharedPointer() noexcept = default; /* creates an empty shared pointer with no implementation */ CSharedPointer(std::nullptr_t) noexcept { ; // empty } ~CSharedPointer() { decrement(); } template validHierarchy&> operator=(const CSharedPointer& 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 validHierarchy&> operator=(CSharedPointer&& rhs) { std::swap(impl_, rhs.impl_); return *this; } CSharedPointer& operator=(CSharedPointer&& rhs) noexcept { 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 rc(lhs.impl_) < rc(rhs.impl_); } bool operator<(const CSharedPointer& rhs) const { return rc(impl_) < rc(rhs.impl_); } T* operator->() const { return get(); } T& operator*() const { return *get(); } void reset() { decrement(); impl_ = nullptr; } T* get() const { return impl_ ? sc(impl_->getData()) : nullptr; } unsigned int strongRef() const { return impl_ ? impl_->ref() : 0; } Impl_::impl_base* impl_ = nullptr; private: static void _delete(void* p) { std::default_delete{}(sc(p)); } /* 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 [[nodiscard]] inline CSharedPointer makeShared(Args&&... args) { return CSharedPointer(new U(std::forward(args)...)); } template CSharedPointer reinterpretPointerCast(const CSharedPointer& ref) { return CSharedPointer(ref.impl_); } } } template struct std::hash> { std::size_t operator()(const Hyprutils::Memory::CSharedPointer& p) const noexcept { return std::hash{}(p.impl_); } }; hyprwm-hyprutils-bc9803c/include/hyprutils/memory/UniquePtr.hpp000066400000000000000000000112221511106400200251450ustar00rootroot00000000000000#pragma once #include "ImplBase.hpp" #include "Casts.hpp" /* This is a custom impl of std::unique_ptr. In contrast to the STL one, it allows for creation of a weak_ptr, that will then be unable to be locked. */ namespace Hyprutils { namespace Memory { template class CUniquePointer { public: template using validHierarchy = std::enable_if_t&, X>, CUniquePointer&>; template using isConstructible = std::enable_if_t>; /* creates a new unique pointer managing a resource avoid calling. Could duplicate ownership. Prefer makeUnique */ explicit CUniquePointer(T* object) noexcept : impl_(new Impl_::impl_base(sc(object), [](void* p) { std::default_delete{}(sc(p)); }, false)) { increment(); } /* creates a shared pointer from a reference */ template > CUniquePointer(const CUniquePointer& ref) = delete; CUniquePointer(const CUniquePointer& ref) = delete; template > CUniquePointer(CUniquePointer&& ref) noexcept { std::swap(impl_, ref.impl_); } CUniquePointer(CUniquePointer&& ref) noexcept { std::swap(impl_, ref.impl_); } /* creates an empty unique pointer with no implementation */ CUniquePointer() noexcept = default; /* creates an empty unique pointer with no implementation */ CUniquePointer(std::nullptr_t) noexcept { ; // empty } ~CUniquePointer() { decrement(); } template validHierarchy&> operator=(const CUniquePointer& rhs) = delete; CUniquePointer& operator=(const CUniquePointer& rhs) = delete; template validHierarchy&> operator=(CUniquePointer&& rhs) { std::swap(impl_, rhs.impl_); return *this; } CUniquePointer& operator=(CUniquePointer&& rhs) noexcept { std::swap(impl_, rhs.impl_); return *this; } operator bool() const { return impl_; } bool operator()(const CUniquePointer& lhs, const CUniquePointer& rhs) const { return rc(lhs.impl_) < rc(rhs.impl_); } T* operator->() const { return get(); } T& operator*() const { return *get(); } void reset() { decrement(); impl_ = nullptr; } T* get() const { return impl_ ? sc(impl_->getData()) : nullptr; } Impl_::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 [[nodiscard]] inline CUniquePointer makeUnique(Args&&... args) { return CUniquePointer(new U(std::forward(args)...)); } } } template struct std::hash> { std::size_t operator()(const Hyprutils::Memory::CUniquePointer& p) const noexcept { return std::hash{}(p.impl_); } }; hyprwm-hyprutils-bc9803c/include/hyprutils/memory/WeakPtr.hpp000066400000000000000000000153361511106400200246000ustar00rootroot00000000000000#pragma once #include "./SharedPtr.hpp" #include "./UniquePtr.hpp" #include "./Casts.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 class CWeakPointer { public: template using validHierarchy = std::enable_if_t&, X>, CWeakPointer&>; template using isConstructible = std::enable_if_t>; /* create a weak ptr from a reference */ template > CWeakPointer(const CSharedPointer& ref) noexcept { if (!ref.impl_) return; impl_ = ref.impl_; incrementWeak(); } /* create a weak ptr from a reference */ template > CWeakPointer(const CUniquePointer& ref) noexcept { if (!ref.impl_) return; impl_ = ref.impl_; incrementWeak(); } /* create a weak ptr from another weak ptr */ template > CWeakPointer(const CWeakPointer& ref) noexcept { if (!ref.impl_) return; impl_ = ref.impl_; incrementWeak(); } CWeakPointer(const CWeakPointer& ref) noexcept { if (!ref.impl_) return; impl_ = ref.impl_; incrementWeak(); } template > CWeakPointer(CWeakPointer&& 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 validHierarchy&> operator=(const CWeakPointer& rhs) { if (impl_ == rhs.impl_) return *this; decrementWeak(); impl_ = rhs.impl_; incrementWeak(); return *this; } CWeakPointer& 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 validHierarchy&> operator=(const CSharedPointer& rhs) { if (rc(impl_) == rc(rhs.impl_)) return *this; decrementWeak(); impl_ = rhs.impl_; incrementWeak(); return *this; } /* create an empty weak ptr */ CWeakPointer() noexcept = default; ~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 lock() const { if (!impl_ || !impl_->dataNonNull() || impl_->destroying() || !impl_->lockable()) return {}; return CSharedPointer(impl_); } /* this returns valid() */ operator bool() const { return valid(); } bool operator==(const CWeakPointer& rhs) const { return impl_ == rhs.impl_; } bool operator==(const CSharedPointer& rhs) const { return impl_ == rhs.impl_; } bool operator==(const CUniquePointer& rhs) const { return impl_ == rhs.impl_; } bool operator==(std::nullptr_t) const { return !valid(); } bool operator!=(std::nullptr_t) const { return valid(); } bool operator()(const CWeakPointer& lhs, const CWeakPointer& rhs) const { return rc(lhs.impl_) < rc(rhs.impl_); } bool operator<(const CWeakPointer& rhs) const { return rc(impl_) < rc(rhs.impl_); } T* get() const { return impl_ ? sc(impl_->getData()) : nullptr; } T* operator->() const { return get(); } T& operator*() const { return *get(); } Impl_::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 struct std::hash> { std::size_t operator()(const Hyprutils::Memory::CWeakPointer& p) const noexcept { return std::hash{}(p.impl_); } }; hyprwm-hyprutils-bc9803c/include/hyprutils/os/000077500000000000000000000000001511106400200216135ustar00rootroot00000000000000hyprwm-hyprutils-bc9803c/include/hyprutils/os/File.hpp000066400000000000000000000003051511106400200232010ustar00rootroot00000000000000#pragma once #include #include #include namespace Hyprutils::File { std::expected readFileAsString(const std::string_view& path); }hyprwm-hyprutils-bc9803c/include/hyprutils/os/FileDescriptor.hpp000066400000000000000000000023061511106400200252430ustar00rootroot00000000000000#pragma once #include namespace Hyprutils { namespace OS { class CFileDescriptor { public: CFileDescriptor() = default; explicit CFileDescriptor(int const fd); CFileDescriptor(CFileDescriptor&&); CFileDescriptor& operator=(CFileDescriptor&&); ~CFileDescriptor(); CFileDescriptor(const CFileDescriptor&) = delete; CFileDescriptor& operator=(const CFileDescriptor&) = delete; bool operator==(const CFileDescriptor& rhs) const { return m_fd == rhs.m_fd; } bool isValid() const; int get() const; int getFlags() const; bool setFlags(int flags); int take(); void reset(); CFileDescriptor duplicate(int flags = F_DUPFD_CLOEXEC) const; bool isReadable() const; bool isClosed() const; static bool isReadable(int fd); static bool isClosed(int fd); private: int m_fd = -1; }; }; }; hyprwm-hyprutils-bc9803c/include/hyprutils/os/Process.hpp000066400000000000000000000032271511106400200237460ustar00rootroot00000000000000#pragma once #include #include #include #include namespace Hyprutils { namespace OS { class CProcess { public: /* Creates a process object, doesn't run yet */ CProcess(const std::string& binary_, const std::vector& args_); ~CProcess(); 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 void setStderrFD(int fd); /* Run the process, synchronously, get the stdout and stderr. False on fail */ bool runSync(); /* Run the process, asynchronously. This will detach the process from this object (and process) and let it live a happy life. False on fail. */ bool runAsync(); // only populated when ran sync const std::string& stdOut(); const std::string& stdErr(); pid_t pid(); // only for sync int exitCode(); private: struct impl; impl* m_impl; }; } } hyprwm-hyprutils-bc9803c/include/hyprutils/path/000077500000000000000000000000001511106400200221265ustar00rootroot00000000000000hyprwm-hyprutils-bc9803c/include/hyprutils/path/Path.hpp000066400000000000000000000032231511106400200235330ustar00rootroot00000000000000#pragma once #include "../string/VarList.hpp" #include #include #include namespace Hyprutils { namespace Path { /** Check whether a config in the form basePath/hypr/programName.conf exists. @param basePath the path where the config will be searched @param programName name of the program (and config file) to search for */ bool checkConfigExists(const std::string basePath, const std::string programName); /** Constructs a full config path given the basePath and programName. @param basePath the path where the config hypr/programName.conf is located @param programName name of the program (and config file) */ std::string fullConfigPath(const std::string basePath, const std::string programName); /** Retrieves the absolute path of the $HOME env variable. */ std::optional getHome(); /** Retrieves a CVarList of paths from the $XDG_CONFIG_DIRS env variable. */ std::optional getXdgConfigDirs(); /** Retrieves the absolute path of the $XDG_CONFIG_HOME env variable. */ std::optional getXdgConfigHome(); /** Searches for a config according to the XDG Base Directory specification. Returns a pair of the full path to a config and the base path. Returns std::nullopt in case of a non-existent value. @param programName name of the program (and config file) */ using T = std::optional; std::pair findConfig(const std::string programName); } } hyprwm-hyprutils-bc9803c/include/hyprutils/signal/000077500000000000000000000000001511106400200224475ustar00rootroot00000000000000hyprwm-hyprutils-bc9803c/include/hyprutils/signal/Listener.hpp000066400000000000000000000017041511106400200247470ustar00rootroot00000000000000#pragma once #include #include #include namespace Hyprutils { namespace Signal { class CSignalBase; class CSignalListener { public: CSignalListener(CSignalListener&&) = delete; CSignalListener(CSignalListener&) = delete; CSignalListener(const CSignalListener&) = delete; CSignalListener(const CSignalListener&&) = delete; [[deprecated("Relic of the legacy untyped signal API. Using this with CSignalT is undefined behavior.")]] void emit(std::any data); private: CSignalListener(std::function handler); void emitInternal(void* args); std::function m_fHandler; friend class CSignalBase; }; typedef Hyprutils::Memory::CSharedPointer CHyprSignalListener; } } hyprwm-hyprutils-bc9803c/include/hyprutils/signal/Signal.hpp000066400000000000000000000112051511106400200243740ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include #include #include #include "./Listener.hpp" namespace Hyprutils { namespace Signal { class CSignalBase { protected: CHyprSignalListener registerListenerInternal(std::function handler); void registerStaticListenerInternal(std::function handler); void emitInternal(void* args); std::vector> m_vListeners; std::vector> m_vStaticListeners; }; template class CSignalT : public CSignalBase { template using RefArg = std::conditional_t || std::is_arithmetic_v, T, const T&>; public: void emit(RefArg... args) { if constexpr (sizeof...(Args) == 0) emitInternal(nullptr); else { auto argsTuple = std::tuple...>(args...); if constexpr (sizeof...(Args) == 1) // NOLINTNEXTLINE: const is reapplied by handler invocation if required emitInternal(Memory::cc(Memory::sc(&std::get<0>(argsTuple)))); else emitInternal(&argsTuple); } } [[nodiscard("Listener is unregistered when the ptr is lost")]] CHyprSignalListener listen(std::function...)> handler) { return registerListenerInternal(mkHandler(handler)); } [[nodiscard("Listener is unregistered when the ptr is lost")]] CHyprSignalListener listen(std::function handler) requires(sizeof...(Args) != 0) { return listen([handler](RefArg... args) { handler(); }); } template [[nodiscard("Listener is unregistered when the ptr is lost")]] CHyprSignalListener forward(CSignalT& signal) { if constexpr (sizeof...(OtherArgs) == 0) return listen([&signal](RefArg... args) { signal.emit(); }); else return listen([&signal](RefArg... args) { signal.emit(args...); }); } // deprecated, use listen() CHyprSignalListener registerListener(std::function 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. void listenStatic(std::function...)> handler) { registerStaticListenerInternal(mkHandler(handler)); } void listenStatic(std::function handler) requires(sizeof...(Args) != 0) { return listenStatic([handler](RefArg... args) { handler(); }); } // Deprecated: use listenStatic() void registerStaticListener(std::function handler, void* owner) { return listenStatic([handler, owner](const RefArg&... args) { constexpr auto mkAny = [](std::any d = {}) { return d; }; handler(owner, mkAny(args...)); }); } private: std::function mkHandler(std::function...)> handler) { return [handler](void* args) { if constexpr (sizeof...(Args) == 0) handler(); else if constexpr (sizeof...(Args) == 1) handler(*Memory::sc...>>>*>(args)); else std::apply(handler, *Memory::sc...>*>(args)); }; } }; // compat. Deprecated. class CSignal : public CSignalT { public: void emit(std::any data = {}); }; } } hyprwm-hyprutils-bc9803c/include/hyprutils/string/000077500000000000000000000000001511106400200225005ustar00rootroot00000000000000hyprwm-hyprutils-bc9803c/include/hyprutils/string/ConstVarList.hpp000066400000000000000000000040461511106400200256100ustar00rootroot00000000000000#pragma once #include #include #include 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 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::iterator begin() { return m_args.begin(); } std::vector::const_iterator begin() const { return m_args.begin(); } std::vector::iterator end() { return m_args.end(); } std::vector::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 m_args; }; } } hyprwm-hyprutils-bc9803c/include/hyprutils/string/String.hpp000066400000000000000000000010621511106400200244560ustar00rootroot00000000000000#pragma once #include namespace Hyprutils { namespace String { // trims beginning and end of whitespace characters std::string trim(const char* in); std::string trim(const std::string& in); std::string_view trim(const std::string_view& in); bool isNumber(const std::string& str, bool allowfloat = false); void replaceInString(std::string& string, const std::string& what, const std::string& to); bool truthy(const std::string_view& in); }; };hyprwm-hyprutils-bc9803c/include/hyprutils/string/VarList.hpp000066400000000000000000000040401511106400200245730ustar00rootroot00000000000000#pragma once #include #include #include 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 lastArgNo = 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 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::iterator begin() { return m_vArgs.begin(); } std::vector::const_iterator begin() const { return m_vArgs.begin(); } std::vector::iterator end() { return m_vArgs.end(); } std::vector::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 m_vArgs; }; } } hyprwm-hyprutils-bc9803c/include/hyprutils/string/VarList2.hpp000066400000000000000000000034121511106400200246570ustar00rootroot00000000000000#pragma once #include #include #include namespace Hyprutils { namespace String { class CVarList2 { public: /** Split string into arg list Prefer this over CConstVarList / CVarList, this is better. @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 @param allowEscape whether to allow escaping the delimiter */ CVarList2(std::string&& in, const size_t lastArgNo = 0, const char delim = ',', const bool removeEmpty = false, const bool allowEscape = true); ~CVarList2() = 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 append(std::string&& arg); bool contains(const std::string& el); 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::const_iterator begin() const { return m_args.begin(); } std::vector::const_iterator end() const { return m_args.end(); } private: std::string m_inString; std::vector m_copyStrings; std::vector m_args; }; } } hyprwm-hyprutils-bc9803c/include/hyprutils/utils/000077500000000000000000000000001511106400200223325ustar00rootroot00000000000000hyprwm-hyprutils-bc9803c/include/hyprutils/utils/ScopeGuard.hpp000066400000000000000000000005331511106400200251000ustar00rootroot00000000000000#pragma once #include namespace Hyprutils { namespace Utils { // calls a function when it goes out of scope class CScopeGuard { public: CScopeGuard(const std::function& fn_); ~CScopeGuard(); private: std::function fn; }; }; }; hyprwm-hyprutils-bc9803c/nix/000077500000000000000000000000001511106400200163025ustar00rootroot00000000000000hyprwm-hyprutils-bc9803c/nix/default.nix000066400000000000000000000022121511106400200204430ustar00rootroot00000000000000{ lib, stdenv, stdenvAdapters, cmake, pkg-config, gtest, pixman, version ? "git", 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 optional; inherit (lib.strings) optionalString; adapters = flatten [ (lib.optional withMold stdenvAdapters.useMoldLinker) (lib.optional debug stdenvAdapters.keepDebugInfo) ]; customStdenv = foldl' (acc: adapter: adapter acc) stdenv adapters; in customStdenv.mkDerivation { pname = "hyprutils" + optionalString debug "-debug"; inherit version; src = ../.; doCheck = debug; nativeBuildInputs = [ cmake pkg-config ]; buildInputs = flatten [ (optional debug gtest) pixman ]; outputs = [ "out" "dev" ]; cmakeBuildType = if debug then "Debug" else "RelWithDebInfo"; 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; }; } hyprwm-hyprutils-bc9803c/nix/overlays.nix000066400000000000000000000012421511106400200206650ustar00rootroot00000000000000{ 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-debug; }; } hyprwm-hyprutils-bc9803c/src/000077500000000000000000000000001511106400200162735ustar00rootroot00000000000000hyprwm-hyprutils-bc9803c/src/animation/000077500000000000000000000000001511106400200202525ustar00rootroot00000000000000hyprwm-hyprutils-bc9803c/src/animation/AnimatedVariable.cpp000066400000000000000000000111401511106400200241430ustar00rootroot00000000000000#include #include #include using namespace Hyprutils::Animation; using namespace Hyprutils::Memory; static const std::string DEFAULTBEZIERNAME = "default"; static const std::string DEFAULTSTYLE = ""; #define SP CSharedPointer #define WP CWeakPointer void CBaseAnimatedVariable::create(CAnimationManager* pManager, int typeInfo, SP pSelf) { m_Type = typeInfo; m_pSelf = pSelf; m_pAnimationManager = pManager; m_pSignals = pManager->getSignals(); m_bDummy = false; } void CBaseAnimatedVariable::connectToActive() { if (m_bDummy || m_bIsConnectedToActive || isAnimationManagerDead()) return; m_pSignals->connect.emit(m_pSelf); m_bIsConnectedToActive = true; } void CBaseAnimatedVariable::disconnectFromActive() { if (isAnimationManagerDead()) return; m_pSignals->disconnect.emit(m_pSelf); m_bIsConnectedToActive = false; } bool Hyprutils::Animation::CBaseAnimatedVariable::enabled() const { if (const auto PCONFIG = m_pConfig.lock()) { const auto PVALUES = PCONFIG->pValues.lock(); return PVALUES ? PVALUES->internalEnabled : false; } return false; } const std::string& CBaseAnimatedVariable::getBezierName() const { if (const auto PCONFIG = m_pConfig.lock()) { const auto PVALUES = PCONFIG->pValues.lock(); return PVALUES ? PVALUES->internalBezier : DEFAULTBEZIERNAME; } return DEFAULTBEZIERNAME; } const std::string& CBaseAnimatedVariable::getStyle() const { if (const auto PCONFIG = m_pConfig.lock()) { const auto PVALUES = PCONFIG->pValues.lock(); return PVALUES ? PVALUES->internalStyle : DEFAULTSTYLE; } return DEFAULTSTYLE; } float CBaseAnimatedVariable::getPercent() const { const auto DURATIONPASSED = std::chrono::duration_cast(std::chrono::steady_clock::now() - animationBegin).count(); if (const auto PCONFIG = m_pConfig.lock()) { const auto PVALUES = PCONFIG->pValues.lock(); return PVALUES ? std::clamp((DURATIONPASSED / 100.f) / PVALUES->internalSpeed, 0.f, 1.f) : 1.f; } return 1.f; } float CBaseAnimatedVariable::getCurveValue() const { if (!m_bIsBeingAnimated || isAnimationManagerDead()) return 1.f; std::string bezierName = ""; if (const auto PCONFIG = m_pConfig.lock()) { const auto PVALUES = PCONFIG->pValues.lock(); if (PVALUES) bezierName = PVALUES->internalBezier; } const auto BEZIER = m_pAnimationManager->getBezier(bezierName); if (!BEZIER) return 1.f; const auto SPENT = getPercent(); if (SPENT >= 1.f) return 1.f; return BEZIER->getYForPoint(SPENT); } bool CBaseAnimatedVariable::ok() const { return m_pConfig && !m_bDummy && !isAnimationManagerDead(); } void CBaseAnimatedVariable::onUpdate() { if (m_bIsBeingAnimated && m_fUpdateCallback) m_fUpdateCallback(m_pSelf); } void CBaseAnimatedVariable::setCallbackOnEnd(CallbackFun func, bool remove) { m_fEndCallback = std::move(func); m_bRemoveEndAfterRan = remove; if (!isBeingAnimated()) onAnimationEnd(); } void CBaseAnimatedVariable::setCallbackOnBegin(CallbackFun func, bool remove) { m_fBeginCallback = std::move(func); m_bRemoveBeginAfterRan = remove; } void CBaseAnimatedVariable::setUpdateCallback(CallbackFun func) { m_fUpdateCallback = std::move(func); } void CBaseAnimatedVariable::resetAllCallbacks() { m_fBeginCallback = nullptr; m_fEndCallback = nullptr; m_fUpdateCallback = nullptr; m_bRemoveBeginAfterRan = false; m_bRemoveEndAfterRan = false; } void CBaseAnimatedVariable::onAnimationEnd() { m_bIsBeingAnimated = false; /* We do not call disconnectFromActive here. The animation manager will remove it on a call to tickDone. */ if (m_fEndCallback) { CallbackFun cb = nullptr; m_fEndCallback.swap(cb); cb(m_pSelf); if (!m_bRemoveEndAfterRan && /* callback did not set a new one by itself */ !m_fEndCallback) m_fEndCallback = cb; // restore } } void CBaseAnimatedVariable::onAnimationBegin() { m_bIsBeingAnimated = true; animationBegin = std::chrono::steady_clock::now(); connectToActive(); if (m_fBeginCallback) { m_fBeginCallback(m_pSelf); if (m_bRemoveBeginAfterRan) m_fBeginCallback = nullptr; // reset } } bool CBaseAnimatedVariable::isAnimationManagerDead() const { return m_pSignals.expired(); } hyprwm-hyprutils-bc9803c/src/animation/AnimationConfig.cpp000066400000000000000000000044621511106400200240310ustar00rootroot00000000000000#include using namespace Hyprutils::Animation; using namespace Hyprutils::Memory; #define SP CSharedPointer #define WP CWeakPointer void CAnimationConfigTree::createNode(const std::string& nodeName, const std::string& parent) { auto pConfig = m_mAnimationConfig[nodeName]; if (!pConfig) pConfig = makeShared(); WP parentRef; if (!parent.empty() && m_mAnimationConfig.find(parent) != m_mAnimationConfig.end()) parentRef = m_mAnimationConfig[parent]; *pConfig = { .overridden = false, .internalBezier = "", .internalStyle = "", .internalSpeed = 0.f, .internalEnabled = -1, .pValues = (parentRef) ? parentRef->pValues : pConfig, .pParentAnimation = (parentRef) ? parentRef : pConfig, }; m_mAnimationConfig[nodeName] = pConfig; } bool CAnimationConfigTree::nodeExists(const std::string& nodeName) const { return m_mAnimationConfig.find(nodeName) != m_mAnimationConfig.end(); } void CAnimationConfigTree::setConfigForNode(const std::string& nodeName, int enabled, float speed, const std::string& bezier, const std::string& style) { auto pConfig = m_mAnimationConfig[nodeName]; if (!pConfig) return; *pConfig = { .overridden = true, .internalBezier = bezier, .internalStyle = style, .internalSpeed = speed, .internalEnabled = enabled, .pValues = pConfig, .pParentAnimation = pConfig->pParentAnimation, // keep the parent! }; setAnimForChildren(pConfig); } SP CAnimationConfigTree::getConfig(const std::string& name) const { return m_mAnimationConfig.at(name); } const std::unordered_map>& CAnimationConfigTree::getFullConfig() const { return m_mAnimationConfig; } void CAnimationConfigTree::setAnimForChildren(SP PANIM) { for (auto& [name, anim] : m_mAnimationConfig) { if (anim->pParentAnimation == PANIM && !anim->overridden) { // if a child isnt overridden, set the values of the parent anim->pValues = PANIM->pValues; setAnimForChildren(anim); } } } hyprwm-hyprutils-bc9803c/src/animation/AnimationManager.cpp000066400000000000000000000060421511106400200241720ustar00rootroot00000000000000#include #include #include using namespace Hyprutils::Animation; using namespace Hyprutils::Math; using namespace Hyprutils::Memory; using namespace Hyprutils::Signal; #define SP CSharedPointer #define WP CWeakPointer const std::array DEFAULTBEZIERPOINTS = {Vector2D(0.0, 0.75), Vector2D(0.15, 1.0)}; CAnimationManager::CAnimationManager() { const auto BEZIER = makeShared(); BEZIER->setup(DEFAULTBEZIERPOINTS); m_mBezierCurves["default"] = BEZIER; m_events = makeUnique(); m_listeners = makeUnique(); m_listeners->connect = m_events->connect.listen([this](const WP& animVar) { if (!m_bTickScheduled) scheduleTick(); if (animVar) m_vActiveAnimatedVariables.emplace_back(animVar); }); m_listeners->disconnect = m_events->disconnect.listen([this](const WP& animVar) { if (animVar) std::erase_if(m_vActiveAnimatedVariables, [&](const auto& other) { return !other || other == animVar; }); }); } void CAnimationManager::removeAllBeziers() { m_mBezierCurves.clear(); // add the default one const auto BEZIER = makeShared(); BEZIER->setup(DEFAULTBEZIERPOINTS); m_mBezierCurves["default"] = BEZIER; } void CAnimationManager::addBezierWithName(std::string name, const Vector2D& p1, const Vector2D& p2) { const auto BEZIER = makeShared(); BEZIER->setup({ p1, p2, }); m_mBezierCurves[name] = BEZIER; } bool CAnimationManager::shouldTickForNext() { return !m_vActiveAnimatedVariables.empty(); } void CAnimationManager::tickDone() { rotateActive(); } void CAnimationManager::rotateActive() { std::vector> active; active.reserve(m_vActiveAnimatedVariables.size()); // avoid reallocations for (auto const& av : m_vActiveAnimatedVariables) { const auto PAV = av.lock(); if (!PAV) continue; if (PAV->ok() && PAV->isBeingAnimated()) active.emplace_back(av); else PAV->m_bIsConnectedToActive = false; } m_vActiveAnimatedVariables = std::move(active); } bool CAnimationManager::bezierExists(const std::string& bezier) { for (auto const& [bc, bz] : m_mBezierCurves) { if (bc == bezier) return true; } return false; } SP CAnimationManager::getBezier(const std::string& name) { const auto BEZIER = std::ranges::find_if(m_mBezierCurves, [&](const auto& other) { return other.first == name; }); return BEZIER == m_mBezierCurves.end() ? m_mBezierCurves["default"] : BEZIER->second; } const std::unordered_map>& CAnimationManager::getAllBeziers() { return m_mBezierCurves; } CWeakPointer CAnimationManager::getSignals() const { return m_events; } hyprwm-hyprutils-bc9803c/src/animation/BezierCurve.cpp000066400000000000000000000061431511106400200232070ustar00rootroot00000000000000#include #include #include #include using namespace Hyprutils::Animation; using namespace Hyprutils::Math; using namespace Hyprutils::Memory; void CBezierCurve::setup(const std::array& pVec) { setup4(std::array{ Vector2D(0, 0), // Start point pVec[0], pVec[1], // Control points Vector2D(1, 1) // End point }); } void CBezierCurve::setup4(const std::array& pVec) { // Avoid reallocations by reserving enough memory upfront m_vPoints.resize(4); m_vPoints = { pVec[0], pVec[1], pVec[2], pVec[3], }; // Pre-bake curve // // We start baking at t=(i+1)/n not at t=0 // That means the first baked x can be > 0 if curve itself starts at x>0 for (int i = 0; i < BAKEDPOINTS; ++i) { // When i=0 -> t=1/255 const float t = (i + 1) * INVBAKEDPOINTS; m_aPointsBaked[i] = Vector2D(getXForT(t), getYForT(t)); } } float CBezierCurve::getXForT(float const& t) const { float t2 = t * t; float t3 = t2 * t; return ((1 - t) * (1 - t) * (1 - t) * m_vPoints[0].x) + (3 * t * (1 - t) * (1 - t) * m_vPoints[1].x) + (3 * t2 * (1 - t) * m_vPoints[2].x) + (t3 * m_vPoints[3].x); } float CBezierCurve::getYForT(float const& t) const { float t2 = t * t; float t3 = t2 * t; return ((1 - t) * (1 - t) * (1 - t) * m_vPoints[0].y) + (3 * t * (1 - t) * (1 - t) * m_vPoints[1].y) + (3 * t2 * (1 - t) * m_vPoints[2].y) + (t3 * m_vPoints[3].y); } // Todo: this probably can be done better and faster float CBezierCurve::getYForPoint(float const& x) const { if (x >= 1.f) return 1.f; if (x <= 0.f) return 0.f; int index = 0; bool below = true; for (int step = (BAKEDPOINTS + 1) / 2; step > 0; step /= 2) { if (below) index += step; else index -= step; // Clamp to avoid index walking off if (index < 0) index = 0; else if (index > BAKEDPOINTS - 1) index = BAKEDPOINTS - 1; below = m_aPointsBaked[index].x < x; } int lowerIndex = index - (!below || index == BAKEDPOINTS - 1); // Clamp final indices if (lowerIndex < 0) lowerIndex = 0; else if (lowerIndex > BAKEDPOINTS - 2) lowerIndex = BAKEDPOINTS - 2; // In the name of performance I shall make a hack const auto& LOWERPOINT = m_aPointsBaked[lowerIndex]; const auto& UPPERPOINT = m_aPointsBaked[lowerIndex + 1]; const float dx = (UPPERPOINT.x - LOWERPOINT.x); // If two baked points have almost the same x // just return the lower one if (dx <= 1e-6f) return LOWERPOINT.y; const auto PERCINDELTA = (x - LOWERPOINT.x) / dx; // Can sometimes happen for VERY small x if (std::isnan(PERCINDELTA) || std::isinf(PERCINDELTA)) return LOWERPOINT.y; return LOWERPOINT.y + ((UPPERPOINT.y - LOWERPOINT.y) * PERCINDELTA); } const std::vector& CBezierCurve::getControlPoints() const { return m_vPoints; } hyprwm-hyprutils-bc9803c/src/cli/000077500000000000000000000000001511106400200170425ustar00rootroot00000000000000hyprwm-hyprutils-bc9803c/src/cli/ArgumentParser.cpp000066400000000000000000000236771511106400200225240ustar00rootroot00000000000000#include "ArgumentParser.hpp" #include #include #include #include #include using namespace Hyprutils::CLI; using namespace Hyprutils::Memory; using namespace Hyprutils::String; using namespace Hyprutils; CArgumentParser::CArgumentParser(const std::span& args) : m_impl(makeUnique(args)) { ; } std::expected CArgumentParser::registerBoolOption(const std::string_view& name, const std::string_view& abbrev, const std::string_view& description) { return m_impl->registerOption(name, abbrev, description, ARG_TYPE_BOOL); } std::expected CArgumentParser::registerIntOption(const std::string_view& name, const std::string_view& abbrev, const std::string_view& description) { return m_impl->registerOption(name, abbrev, description, ARG_TYPE_INT); } std::expected CArgumentParser::registerFloatOption(const std::string_view& name, const std::string_view& abbrev, const std::string_view& description) { return m_impl->registerOption(name, abbrev, description, ARG_TYPE_FLOAT); } std::expected CArgumentParser::registerStringOption(const std::string_view& name, const std::string_view& abbrev, const std::string_view& description) { return m_impl->registerOption(name, abbrev, description, ARG_TYPE_STR); } std::optional CArgumentParser::getBool(const std::string_view& name) { auto ref = m_impl->getValue(name); if (ref == m_impl->m_values.end()) return std::nullopt; if (const auto pval = std::get_if(&ref->val); pval) return *pval; return std::nullopt; } std::optional CArgumentParser::getInt(const std::string_view& name) { auto ref = m_impl->getValue(name); if (ref == m_impl->m_values.end()) return std::nullopt; if (const auto pval = std::get_if(&ref->val); pval) return *pval; return std::nullopt; } std::optional CArgumentParser::getFloat(const std::string_view& name) { auto ref = m_impl->getValue(name); if (ref == m_impl->m_values.end()) return std::nullopt; if (const auto pval = std::get_if(&ref->val); pval) return *pval; return std::nullopt; } std::optional CArgumentParser::getString(const std::string_view& name) { auto ref = m_impl->getValue(name); if (ref == m_impl->m_values.end()) return std::nullopt; if (const auto pval = std::get_if(&ref->val); pval) return *pval; return std::nullopt; } std::string CArgumentParser::getDescription(const std::string_view& header, std::optional maxWidth) { return m_impl->getDescription(header, maxWidth); } std::expected CArgumentParser::parse() { return m_impl->parse(); } CArgumentParserImpl::CArgumentParserImpl(const std::span& args) { m_argv.reserve(args.size()); for (const auto& a : args) { m_argv.emplace_back(a); } } std::vector::iterator CArgumentParserImpl::getValue(const std::string_view& sv) { auto it = std::ranges::find_if(m_values, [&sv](const auto& e) { return e.full == sv || e.abbrev == sv; }); return it; } std::expected CArgumentParserImpl::registerOption(const std::string_view& name, const std::string_view& abbrev, const std::string_view& description, eArgumentType type) { if (getValue(name) != m_values.end() || getValue(abbrev) != m_values.end()) return std::unexpected("Value already exists"); m_values.emplace_back(SArgumentKey{ .full = std::string{name}, .abbrev = std::string{abbrev}, .desc = std::string{description}, .argType = type, .val = std::monostate{}, }); return {}; } std::expected CArgumentParserImpl::parse() { // walk the args for (size_t i = 1; i < m_argv.size(); ++i) { auto val = m_values.end(); const auto& arg = m_argv.at(i); if (arg.starts_with("--")) val = getValue(std::string_view{arg}.substr(2)); else if (arg.starts_with('-')) val = getValue(std::string_view{arg}.substr(1)); else return std::unexpected(std::format("Invalid element found while parsing: {}", arg)); if (val == m_values.end()) return std::unexpected(std::format("Invalid argument found while parsing: {}", arg)); switch (val->argType) { case ARG_TYPE_BOOL: { val->val = true; break; } case ARG_TYPE_INT: { if (i + 1 >= m_argv.size()) return std::unexpected(std::format("Failed parsing argument {}, no value supplied", arg)); const auto& next = std::string{m_argv.at(++i)}; if (!isNumber(next)) return std::unexpected(std::format("Failed parsing argument {}, value {} is not an int", arg, next)); try { val->val = sc(std::stoi(next)); } catch (...) { return std::unexpected(std::format("Failed parsing argument {}, value {} is not an int", arg, next)); } break; } case ARG_TYPE_FLOAT: { if (i + 1 >= m_argv.size()) return std::unexpected(std::format("Failed parsing argument {}, no value supplied", arg)); const auto& next = std::string{m_argv.at(++i)}; if (!isNumber(next, true)) return std::unexpected(std::format("Failed parsing argument {}, value {} is not a float", arg, next)); try { val->val = sc(std::stof(next)); } catch (...) { return std::unexpected(std::format("Failed parsing argument {}, value {} is not a float", arg, next)); } break; } case ARG_TYPE_STR: { if (i + 1 >= m_argv.size()) return std::unexpected(std::format("Failed parsing argument {}, no value supplied", arg)); val->val = std::string{m_argv.at(++i)}; break; } case ARG_TYPE_END: break; } } return {}; } std::string CArgumentParserImpl::getDescription(const std::string_view& header, std::optional maxWidth) { const size_t MAX_COLS = maxWidth.value_or(80); const std::string PAD_STR = " "; constexpr const std::array TYPE_STRS = { "", // bool "[int]", // int "[float]", // float "[str]", // str }; // auto wrap = [](const std::string_view& str, size_t maxW) -> std::vector { std::vector result; // walk word by word size_t nextSpacePos = 0, lastBreakPos = 0; while (true) { size_t lastSpacePos = nextSpacePos; nextSpacePos = str.find(' ', nextSpacePos + 1); if (nextSpacePos == std::string::npos) break; if (nextSpacePos - lastBreakPos > maxW) { if (lastSpacePos - lastBreakPos <= maxW) { // break result.emplace_back(str.substr(lastBreakPos, lastSpacePos - lastBreakPos)); lastBreakPos = lastSpacePos + 1; } else { while (lastSpacePos - lastBreakPos > maxW) { // break result.emplace_back(str.substr(lastBreakPos, maxW)); lastBreakPos += maxW; } } continue; } } result.emplace_back(str.substr(lastBreakPos)); return result; }; auto pad = [&PAD_STR](size_t len) -> std::string_view { if (len >= PAD_STR.size()) return PAD_STR; return std::string_view{PAD_STR}.substr(0, len); }; std::string rolling; rolling += std::format("┏ {}\n", header); rolling += "┣"; for (size_t i = 0; i < MAX_COLS; ++i) { rolling += "━"; } rolling += "┓\n"; // get max widths size_t maxArgWidth = 0, maxShortWidth = 0; for (const auto& v : m_values) { maxShortWidth = std::max(maxShortWidth, v.abbrev.size() + 4 + std::string_view{TYPE_STRS[v.argType]}.length()); maxArgWidth = std::max(maxArgWidth, v.full.size() + 3); } // write the table for (const auto& v : m_values) { size_t lenUsed = 0; rolling += "┣ --" + v.full; lenUsed += 3 + v.full.size(); rolling += pad(maxArgWidth - lenUsed); lenUsed = maxArgWidth; rolling += " -" + v.abbrev; lenUsed += 2 + v.abbrev.size(); rolling += " "; rolling += TYPE_STRS[v.argType]; lenUsed += std::string_view{TYPE_STRS[v.argType]}.length() + 1; rolling += pad(maxArgWidth + maxShortWidth - lenUsed); lenUsed = maxArgWidth + maxShortWidth; rolling += " | "; lenUsed += 3; const auto ROWS = wrap(v.desc, MAX_COLS - lenUsed); const auto LEN_START_DESC = lenUsed; rolling += ROWS[0]; lenUsed += ROWS[0].size(); rolling += pad(MAX_COLS - lenUsed); rolling += "┃\n"; for (size_t i = 1; i < ROWS.size(); ++i) { lenUsed = LEN_START_DESC; rolling += "┣"; rolling += pad(LEN_START_DESC); rolling += ROWS[i]; lenUsed += ROWS[i].size(); rolling += pad(MAX_COLS - lenUsed); rolling += "┃\n"; } } rolling += "┗"; for (size_t i = 0; i < MAX_COLS; ++i) { rolling += "━"; } rolling += "┛\n"; return rolling; } hyprwm-hyprutils-bc9803c/src/cli/ArgumentParser.hpp000066400000000000000000000023211511106400200225100ustar00rootroot00000000000000#include #include #include #include namespace Hyprutils::CLI { enum eArgumentType : uint8_t { ARG_TYPE_BOOL = 0, ARG_TYPE_INT, ARG_TYPE_FLOAT, ARG_TYPE_STR, ARG_TYPE_END, }; struct SArgumentKey { using Value = std::variant; std::string full, abbrev, desc; eArgumentType argType = ARG_TYPE_BOOL; Value val; }; class CArgumentParserImpl { public: CArgumentParserImpl(const std::span& args); ~CArgumentParserImpl() = default; std::string getDescription(const std::string_view& header, std::optional maxWidth = {}); std::expected parse(); std::vector::iterator getValue(const std::string_view& sv); std::expected registerOption(const std::string_view& name, const std::string_view& abbrev, const std::string_view& description, eArgumentType type); std::vector m_values; std::vector m_argv; }; }hyprwm-hyprutils-bc9803c/src/cli/Logger.cpp000066400000000000000000000121611511106400200207660ustar00rootroot00000000000000#include "Logger.hpp" #include #include using namespace Hyprutils; using namespace Hyprutils::CLI; CLogger::CLogger() { m_impl = Memory::makeUnique(this); } CLogger::~CLogger() = default; void CLogger::setLogLevel(eLogLevel level) { m_logLevel = level; } void CLogger::setTime(bool enabled) { m_impl->m_timeEnabled = enabled; } void CLogger::setEnableStdout(bool enabled) { m_impl->m_stdoutEnabled = enabled; m_impl->updateParentShouldLog(); } void CLogger::setEnableColor(bool enabled) { m_impl->m_colorEnabled = enabled; } void CLogger::setEnableRolling(bool enabled) { m_impl->m_rollingEnabled = enabled; } std::expected CLogger::setOutputFile(const std::string_view& file) { if (file.empty()) { m_impl->m_fileEnabled = false; m_impl->m_logOfs = {}; return {}; } std::filesystem::path filePath{file}; std::error_code ec; if (!filePath.has_parent_path()) return std::unexpected("Path has no parent"); auto dir = filePath.parent_path(); if (!std::filesystem::exists(dir, ec) || ec) return std::unexpected("Parent path is inaccessible, or doesn't exist"); m_impl->m_logOfs = std::ofstream{filePath, std::ios::trunc}; m_impl->m_logFilePath = filePath; if (!m_impl->m_logOfs.good()) return std::unexpected("Failed to open a write stream"); m_impl->m_fileEnabled = true; m_impl->updateParentShouldLog(); return {}; } void CLogger::log(eLogLevel level, const std::string_view& msg) { if (!m_shouldLogAtAll) return; if (level < m_logLevel) return; m_impl->log(level, msg); } const std::string& CLogger::rollingLog() { return m_impl->m_rollingLog; } CLoggerImpl::CLoggerImpl(CLogger* parent) : m_parent(parent) { updateParentShouldLog(); } void CLoggerImpl::log(eLogLevel level, const std::string_view& msg, const std::string_view& from) { std::lock_guard lg(m_logMtx); std::string logPrefix = "", logPrefixColor = ""; std::string logMsg = ""; switch (level) { case LOG_TRACE: logPrefix += "TRACE "; logPrefixColor += "\033[1;34mTRACE \033[0m"; break; case LOG_DEBUG: logPrefix += "DEBUG "; logPrefixColor += "\033[1;32mDEBUG \033[0m"; break; case LOG_WARN: logPrefix += "WARN "; logPrefixColor += "\033[1;33mWARN \033[0m"; break; case LOG_ERR: logPrefix += "ERR "; logPrefixColor += "\033[1;31mERR \033[0m"; break; case LOG_CRIT: logPrefix += "CRIT "; logPrefixColor += "\033[1;35mCRIT \033[0m"; break; } if (m_timeEnabled) { #ifndef _LIBCPP_VERSION static auto current_zone = std::chrono::current_zone(); const auto zt = std::chrono::zoned_time{current_zone, std::chrono::system_clock::now()}; const auto hms = std::chrono::hh_mm_ss{zt.get_local_time() - std::chrono::floor(zt.get_local_time())}; #else // TODO: current clang 17 does not support `zoned_time`, remove this once clang 19 is ready const auto hms = std::chrono::hh_mm_ss{std::chrono::system_clock::now() - std::chrono::floor(std::chrono::system_clock::now())}; #endif logMsg += std::format("@ {} ", hms); } if (!from.empty()) { logMsg += "from "; logMsg += from; logMsg += " "; } logMsg += "]: "; logMsg += msg; if (m_stdoutEnabled) std::println("{}{}", m_colorEnabled ? logPrefixColor : logPrefix, logMsg); if (m_fileEnabled) m_logOfs << logPrefix << logMsg << "\n"; if (m_rollingEnabled) appendToRolling(logPrefix + logMsg); } void CLoggerImpl::updateParentShouldLog() { m_parent->m_shouldLogAtAll = m_fileEnabled || m_stdoutEnabled; } void CLoggerImpl::appendToRolling(const std::string& s) { constexpr const size_t ROLLING_LOG_SIZE = 4096; if (!m_rollingLog.empty()) m_rollingLog += "\n"; m_rollingLog += s; if (m_rollingLog.size() > ROLLING_LOG_SIZE) m_rollingLog = m_rollingLog.substr(m_rollingLog.find('\n', m_rollingLog.size() - ROLLING_LOG_SIZE) + 1); } CLoggerConnection::CLoggerConnection(CLogger& logger) : m_impl(logger.m_impl), m_logger(&logger), m_logLevel(logger.m_logLevel) { ; } CLoggerConnection::~CLoggerConnection() = default; void CLoggerConnection::setName(const std::string_view& name) { m_name = name; } void CLoggerConnection::setLogLevel(eLogLevel level) { m_logLevel = level; } void CLoggerConnection::log(eLogLevel level, const std::string_view& msg) { if (!m_impl || !m_logger) return; if (!m_logger->m_shouldLogAtAll) return; if (level < m_logLevel) return; m_impl->log(level, msg, m_name); } CLogger* CLoggerConnection::getLogger() { if (!m_impl) return nullptr; return m_logger; } void CLoggerConnection::redirect(CLogger& logger) { m_impl = logger.m_impl; m_logger = &logger; } hyprwm-hyprutils-bc9803c/src/cli/Logger.hpp000066400000000000000000000022261511106400200207740ustar00rootroot00000000000000#include #include #include #include namespace Hyprutils::CLI { class CLoggerImpl { public: CLoggerImpl(CLogger*); ~CLoggerImpl() = default; CLoggerImpl(const CLoggerImpl&) = delete; CLoggerImpl(CLoggerImpl&) = delete; CLoggerImpl(CLoggerImpl&&) = delete; void updateParentShouldLog(); void appendToRolling(const std::string& s); void log(eLogLevel level, const std::string_view& msg, const std::string_view& from = ""); std::string m_rollingLog; std::ofstream m_logOfs; std::filesystem::path m_logFilePath; bool m_timeEnabled = false; bool m_stdoutEnabled = true; bool m_fileEnabled = false; bool m_colorEnabled = true; bool m_rollingEnabled = false; std::mutex m_logMtx; // this is fine because CLogger is NOMOVE and NOCOPY CLogger* m_parent = nullptr; }; }hyprwm-hyprutils-bc9803c/src/i18n/000077500000000000000000000000001511106400200170525ustar00rootroot00000000000000hyprwm-hyprutils-bc9803c/src/i18n/I18nEngine.cpp000066400000000000000000000123501511106400200214240ustar00rootroot00000000000000#include "I18nEngine.hpp" #include #include #include #include using namespace Hyprutils::I18n; using namespace Hyprutils; using namespace Hyprutils::Utils; CI18nEngine::CI18nEngine() : m_impl(Memory::makeUnique()) { ; } CI18nEngine::~CI18nEngine() = default; void CI18nEngine::registerEntry(const std::string& locale, uint64_t key, std::string&& translationUTF8) { auto& entryVec = m_impl->entries[locale]; if (entryVec.size() <= key) entryVec.resize(key + 1); entryVec[key].entry = std::move(translationUTF8); entryVec[key].exists = true; } void CI18nEngine::registerEntry(const std::string& locale, uint64_t key, translationFn&& translationFn) { auto& entryVec = m_impl->entries[locale]; if (entryVec.size() <= key) entryVec.resize(key + 1); entryVec[key].fn = std::move(translationFn); entryVec[key].exists = true; } void CI18nEngine::setFallbackLocale(const std::string& locale) { m_impl->fallbackLocale = locale; } std::string CI18nEngine::localizeEntry(const std::string& locale, uint64_t key, const translationVarMap& map) { SI18nTranslationEntry* entry = nullptr; if (m_impl->entries.contains(locale) && m_impl->entries[locale].size() > key) entry = &m_impl->entries[locale][key]; if (locale.contains('_')) { if (!entry || !entry->exists) { // try to fall back to lang_LANG auto stem = locale.substr(0, locale.find('_')); auto stemUpper = stem; std::ranges::transform(stemUpper, stemUpper.begin(), ::toupper); auto newLocale = std::format("{}_{}", stem, stemUpper); if (m_impl->entries.contains(newLocale) && m_impl->entries[newLocale].size() > key) entry = &m_impl->entries[newLocale][key]; } if (!entry || !entry->exists) { // try to fall back to any lang prefixed with our prefix const auto stem = locale.substr(0, locale.find('_') + 1); const auto stemRaw = locale.substr(0, locale.find('_')); for (const auto& [k, v] : m_impl->entries) { if (k.starts_with(stem) || k == stemRaw) { if (m_impl->entries.contains(k) && m_impl->entries[k].size() > key) entry = &m_impl->entries[k][key]; if (entry && entry->exists) break; } } } } else { // locale doesn't have a _, e.g. pl // find any locale that has the same stem for (const auto& [k, v] : m_impl->entries) { if (k.starts_with(locale + "_") || k == locale) { if (m_impl->entries.contains(k) && m_impl->entries[k].size() > key) entry = &m_impl->entries[k][key]; if (entry && entry->exists) break; } } } if (!entry || !entry->exists) { // fall back to general fallback if (m_impl->entries.contains(m_impl->fallbackLocale) && m_impl->entries[m_impl->fallbackLocale].size() > key) entry = &m_impl->entries[m_impl->fallbackLocale][key]; } if (!entry || !entry->exists) return ""; std::string_view rawStr = entry->entry; std::string fnStringContainer; if (entry->fn) { fnStringContainer = entry->fn(map); rawStr = fnStringContainer; } struct SRange { size_t begin = 0; size_t end = 0; const std::string* val = nullptr; }; std::vector rangesFound; // discover all replacable ranges for (const auto& [k, v] : map) { size_t start = rawStr.find(k, 0); while (start != std::string::npos) { if (start == 0 || start + k.size() >= rawStr.size()) break; // always move the pointer CScopeGuard x([&start, &rawStr, &k] { start = rawStr.find(k, start + 1); }); if (rawStr[start - 1] != '{' || rawStr[start + k.size()] != '}') continue; // add range rangesFound.emplace_back(SRange{.begin = start - 1, .end = start + k.size() + 1, .val = &v}); } } if (rangesFound.empty()) return std::string{rawStr}; // build the new string. First, sort our entries std::ranges::sort(rangesFound, [](const auto& a, const auto& b) { return a.begin - b.begin; }); // calc the size size_t stringLen = 0; size_t lastBegin = 0; for (const auto& r : rangesFound) { stringLen += r.begin - lastBegin + r.val->size(); lastBegin = r.end; } stringLen += rawStr.size() - lastBegin; lastBegin = 0; const auto ORIGINAL_STR = std::string_view{rawStr}; std::string newStr; newStr.reserve(stringLen); for (const auto& r : rangesFound) { newStr += ORIGINAL_STR.substr(lastBegin, r.begin - lastBegin); newStr += *r.val; lastBegin = r.end; } newStr += ORIGINAL_STR.substr(lastBegin); return newStr; } CI18nLocale CI18nEngine::getSystemLocale() { try { return CI18nLocale(std::locale("").name()); } catch (...) { return CI18nLocale("en_US.UTF-8"); } } hyprwm-hyprutils-bc9803c/src/i18n/I18nEngine.hpp000066400000000000000000000010051511106400200214240ustar00rootroot00000000000000#pragma once #include namespace Hyprutils::I18n { struct SI18nTranslationEntry { bool exists = false; std::string entry = ""; translationFn fn = nullptr; }; struct SI18nEngineImpl { std::unordered_map> entries; std::string fallbackLocale = "en_US"; }; std::string extractLocale(std::string locale); };hyprwm-hyprutils-bc9803c/src/i18n/I18nLocale.cpp000066400000000000000000000017651511106400200214260ustar00rootroot00000000000000#include "I18nEngine.hpp" using namespace Hyprutils::I18n; std::string Hyprutils::I18n::extractLocale(std::string locale) { // localeStr is very arbitrary... from my testing, it can be: // en_US.UTF-8 // LC_CTYPE=en_US // POSIX // * // // We only return e.g. en_US or pl_PL, or pl if (locale == "POSIX") return "en_US"; if (locale == "*") return "en_US"; if (locale.contains('=')) locale = locale.substr(locale.find('=') + 1); if (locale.contains('.')) locale = locale.substr(0, locale.find('.')); return locale; } CI18nLocale::CI18nLocale(std::string fullLocale) : m_rawFullLocale(std::move(fullLocale)) { m_locale = extractLocale(m_rawFullLocale); } std::string CI18nLocale::locale() { return m_locale; } std::string CI18nLocale::stem() { if (m_locale.contains('_')) return m_locale.substr(0, m_locale.find('_')); return m_locale; } std::string CI18nLocale::full() { return m_rawFullLocale; } hyprwm-hyprutils-bc9803c/src/math/000077500000000000000000000000001511106400200172245ustar00rootroot00000000000000hyprwm-hyprutils-bc9803c/src/math/Box.cpp000066400000000000000000000134211511106400200204610ustar00rootroot00000000000000#include #include #include #include #define VECINRECT(vec, x1, y1, x2, y2) ((vec).x >= (x1) && (vec).x < (x2) && (vec).y >= (y1) && (vec).y < (y2)) using namespace Hyprutils::Math; constexpr double HALF = 0.5; constexpr double DOUBLE = 2.0; constexpr double EPSILON = 1e-9; CBox& Hyprutils::Math::CBox::scale(double scale) { x *= scale; y *= scale; w *= scale; h *= scale; return *this; } CBox& Hyprutils::Math::CBox::scale(const Vector2D& scale) { x *= scale.x; y *= scale.y; w *= scale.x; h *= scale.y; return *this; } CBox& Hyprutils::Math::CBox::translate(const Vector2D& vec) { x += vec.x; y += vec.y; return *this; } Vector2D Hyprutils::Math::CBox::middle() const { return Vector2D{x + (w * HALF), y + (h * HALF)}; } bool Hyprutils::Math::CBox::containsPoint(const Vector2D& vec) const { return VECINRECT(vec, x, y, x + w, y + h); } bool Hyprutils::Math::CBox::empty() const { return std::fabs(w) < EPSILON || std::fabs(h) < EPSILON; } CBox& Hyprutils::Math::CBox::round() { double roundedX = std::round(x); double roundedY = std::round(y); double newW = x + w - roundedX; double newH = y + h - roundedY; x = roundedX; y = roundedY; w = std::round(newW); h = std::round(newH); return *this; } CBox& Hyprutils::Math::CBox::transform(const eTransform t, double w, double h) { CBox temp = *this; if (t % 2 == 0) { width = temp.width; height = temp.height; } else { width = temp.height; height = temp.width; } switch (t) { case HYPRUTILS_TRANSFORM_NORMAL: x = temp.x; y = temp.y; break; case HYPRUTILS_TRANSFORM_90: x = h - temp.y - temp.height; y = temp.x; break; case HYPRUTILS_TRANSFORM_180: x = w - temp.x - temp.width; y = h - temp.y - temp.height; break; case HYPRUTILS_TRANSFORM_270: x = temp.y; y = w - temp.x - temp.width; break; case HYPRUTILS_TRANSFORM_FLIPPED: x = w - temp.x - temp.width; y = temp.y; break; case HYPRUTILS_TRANSFORM_FLIPPED_90: x = temp.y; y = temp.x; break; case HYPRUTILS_TRANSFORM_FLIPPED_180: x = temp.x; y = h - temp.y - temp.height; break; case HYPRUTILS_TRANSFORM_FLIPPED_270: x = h - temp.y - temp.height; y = w - temp.x - temp.width; break; } return *this; } CBox& Hyprutils::Math::CBox::addExtents(const SBoxExtents& e) { x -= e.topLeft.x; y -= e.topLeft.y; w += e.topLeft.x + e.bottomRight.x; h += e.topLeft.y + e.bottomRight.y; return *this; } CBox& Hyprutils::Math::CBox::scaleFromCenter(double scale) { double oldW = w, oldH = h; w *= scale; h *= scale; x -= (w - oldW) * HALF; y -= (h - oldH) * HALF; return *this; } CBox& Hyprutils::Math::CBox::expand(const double& value) { x -= value; y -= value; w += value * DOUBLE; h += value * DOUBLE; if (w <= EPSILON || h <= EPSILON) { w = 0; h = 0; } return *this; } CBox& Hyprutils::Math::CBox::noNegativeSize() { w = std::clamp(w, 0.0, std::numeric_limits::infinity()); h = std::clamp(h, 0.0, std::numeric_limits::infinity()); return *this; } CBox Hyprutils::Math::CBox::intersection(const CBox& other) const { const double newX = std::max(x, other.x); const double newY = std::max(y, other.y); const double newBottom = std::min(y + h, other.y + other.h); const double newRight = std::min(x + w, other.x + other.w); double newW = newRight - newX; double newH = newBottom - newY; if (newW <= EPSILON || newH <= EPSILON) { newW = 0; newH = 0; } return {newX, newY, newW, newH}; } bool Hyprutils::Math::CBox::overlaps(const CBox& other) const { return (other.x + other.w >= x) && (x + w >= other.x) && (other.y + other.h >= y) && (y + h >= other.y); } bool Hyprutils::Math::CBox::inside(const CBox& bound) const { return bound.x < x && bound.y < y && x + w < bound.x + bound.w && y + h < bound.y + bound.h; } CBox Hyprutils::Math::CBox::roundInternal() { double flooredX = std::floor(x); double flooredY = std::floor(y); double newW = x + w - flooredX; double newH = y + h - flooredY; return CBox{flooredX, flooredY, std::floor(newW), std::floor(newH)}; } CBox Hyprutils::Math::CBox::copy() const { return CBox{*this}; } Vector2D Hyprutils::Math::CBox::pos() const { return {x, y}; } Vector2D Hyprutils::Math::CBox::size() const { return {w, h}; } Vector2D Hyprutils::Math::CBox::extent() const { return pos() + size(); } Vector2D Hyprutils::Math::CBox::closestPoint(const Vector2D& vec) const { if (containsPoint(vec)) return vec; Vector2D nv = vec; Vector2D maxPoint = {x + w - EPSILON, y + h - EPSILON}; if (x < maxPoint.x) nv.x = std::clamp(nv.x, x, maxPoint.x); else nv.x = x; if (y < maxPoint.y) nv.y = std::clamp(nv.y, y, maxPoint.y); else nv.y = y; if (std::fabs(nv.x - x) < EPSILON) nv.x = x; else if (std::fabs(nv.x - (maxPoint.x)) < EPSILON) nv.x = maxPoint.x; if (std::fabs(nv.y - y) < EPSILON) nv.y = y; else if (std::fabs(nv.y - (maxPoint.y)) < EPSILON) nv.y = maxPoint.y; return nv; } SBoxExtents Hyprutils::Math::CBox::extentsFrom(const CBox& small) { return {.topLeft = {small.x - x, small.y - y}, .bottomRight = {w - small.w - (small.x - x), h - small.h - (small.y - y)}}; } hyprwm-hyprutils-bc9803c/src/math/Mat3x3.cpp000066400000000000000000000117441511106400200210160ustar00rootroot00000000000000#include #include #include #include #include #include #include using namespace Hyprutils::Math; using namespace Hyprutils::Memory; static std::unordered_map transforms = { {HYPRUTILS_TRANSFORM_NORMAL, std::array{1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f}}, {HYPRUTILS_TRANSFORM_90, std::array{0.0f, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}}, {HYPRUTILS_TRANSFORM_180, std::array{-1.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f}}, {HYPRUTILS_TRANSFORM_270, std::array{0.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}}, {HYPRUTILS_TRANSFORM_FLIPPED, std::array{-1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f}}, {HYPRUTILS_TRANSFORM_FLIPPED_90, std::array{0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}}, {HYPRUTILS_TRANSFORM_FLIPPED_180, std::array{1.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f}}, {HYPRUTILS_TRANSFORM_FLIPPED_270, std::array{0.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}}, }; Mat3x3::Mat3x3() { matrix = {0}; } Mat3x3::Mat3x3(std::array mat) : matrix(mat) { ; } Mat3x3::Mat3x3(std::vector mat) { for (size_t i = 0; i < 9; ++i) { matrix.at(i) = mat.size() < i ? mat.at(i) : 0.F; } } Mat3x3 Mat3x3::identity() { return Mat3x3(std::array{1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f}); } Mat3x3 Mat3x3::outputProjection(const Vector2D& size, eTransform transform) { Mat3x3 mat; const auto& t = transforms.at(transform); float x = 2.0f / size.x; float y = 2.0f / size.y; // Rotation + reflection mat.matrix[0] = x * t.matrix[0]; mat.matrix[1] = x * t.matrix[1]; mat.matrix[3] = y * t.matrix[3]; mat.matrix[4] = y * t.matrix[4]; // Translation mat.matrix[2] = -copysign(1.0f, mat.matrix[0] + mat.matrix[1]); mat.matrix[5] = -copysign(1.0f, mat.matrix[3] + mat.matrix[4]); // Identity mat.matrix[8] = 1.0f; return mat; } std::array Mat3x3::getMatrix() const { return matrix; } Mat3x3 Mat3x3::projectBox(const CBox& box, eTransform transform, float rot /* rad, CCW */) const { Mat3x3 mat = Mat3x3::identity(); const auto boxSize = box.size(); mat.translate(box.pos()); if (rot != 0) { mat.translate(boxSize / 2); mat.rotate(rot); mat.translate(-boxSize / 2); } mat.scale(boxSize); if (transform != HYPRUTILS_TRANSFORM_NORMAL) { mat.translate({0.5, 0.5}); mat.transform(transform); mat.translate({-0.5, -0.5}); } return this->copy().multiply(mat); } Mat3x3& Mat3x3::transform(eTransform transform) { multiply(transforms.at(transform)); return *this; } Mat3x3& Mat3x3::rotate(float rot) { multiply(std::array{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{sc(scale_.x), 0.0f, 0.0f, 0.0f, sc(scale_.y), 0.0f, 0.0f, 0.0f, 1.0f}); return *this; } Mat3x3& Mat3x3::scale(const float scale_) { return scale({scale_, scale_}); } Mat3x3& Mat3x3::translate(const Vector2D& offset) { multiply(std::array{1.0f, 0.0f, sc(offset.x), 0.0f, 1.0f, sc(offset.y), 0.0f, 0.0f, 1.0f}); return *this; } Mat3x3& Mat3x3::transpose() { matrix = std::array{matrix[0], matrix[3], matrix[6], matrix[1], matrix[4], matrix[7], matrix[2], matrix[5], matrix[8]}; return *this; } 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 product; 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] = 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] = 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; } Mat3x3 Mat3x3::copy() const { return *this; } 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)); } hyprwm-hyprutils-bc9803c/src/math/Region.cpp000066400000000000000000000144111511106400200211540ustar00rootroot00000000000000#include "hyprutils/memory/Casts.hpp" #include #include using namespace Hyprutils::Math; using namespace Hyprutils::Memory; constexpr const int64_t MAX_REGION_SIDE = 10000000; Hyprutils::Math::CRegion::CRegion() { pixman_region32_init(&m_rRegion); } Hyprutils::Math::CRegion::CRegion(const pixman_region32_t* const ref) { pixman_region32_init(&m_rRegion); pixman_region32_copy(&m_rRegion, ref); } Hyprutils::Math::CRegion::CRegion(double x, double y, double w, double h) { pixman_region32_init_rect(&m_rRegion, x, y, w, h); } Hyprutils::Math::CRegion::CRegion(const CBox& box) { pixman_region32_init_rect(&m_rRegion, box.x, box.y, box.w, box.h); } Hyprutils::Math::CRegion::CRegion(pixman_box32_t* box) { pixman_region32_init_rect(&m_rRegion, box->x1, box->y1, box->x2 - box->x1, box->y2 - box->y1); } Hyprutils::Math::CRegion::CRegion(const CRegion& other) { pixman_region32_init(&m_rRegion); pixman_region32_copy(&m_rRegion, other.pixman()); } Hyprutils::Math::CRegion::CRegion(CRegion&& other) noexcept { pixman_region32_init(&m_rRegion); pixman_region32_copy(&m_rRegion, other.pixman()); } Hyprutils::Math::CRegion::~CRegion() { pixman_region32_fini(&m_rRegion); } CRegion& Hyprutils::Math::CRegion::clear() { pixman_region32_clear(&m_rRegion); return *this; } CRegion& Hyprutils::Math::CRegion::set(const CRegion& other) { pixman_region32_copy(&m_rRegion, other.pixman()); return *this; } CRegion& Hyprutils::Math::CRegion::add(const CRegion& other) { pixman_region32_union(&m_rRegion, &m_rRegion, other.pixman()); return *this; } CRegion& Hyprutils::Math::CRegion::add(double x, double y, double w, double h) { pixman_region32_union_rect(&m_rRegion, &m_rRegion, x, y, w, h); return *this; } CRegion& Hyprutils::Math::CRegion::add(const CBox& other) { pixman_region32_union_rect(&m_rRegion, &m_rRegion, other.x, other.y, other.w, other.h); return *this; } CRegion& Hyprutils::Math::CRegion::subtract(const CRegion& other) { 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, other.pixman()); return *this; } CRegion& Hyprutils::Math::CRegion::intersect(double x, double y, double w, double h) { pixman_region32_intersect_rect(&m_rRegion, &m_rRegion, x, y, w, h); return *this; } CRegion& Hyprutils::Math::CRegion::invert(pixman_box32_t* box) { pixman_region32_inverse(&m_rRegion, &m_rRegion, box); return *this; } CRegion& Hyprutils::Math::CRegion::invert(const CBox& box) { pixman_box32 pixmanBox = {.x1 = sc(box.x), .y1 = sc(box.y), .x2 = sc(box.w) + sc(box.x), .y2 = sc(box.h) + sc(box.y)}; return this->invert(&pixmanBox); } CRegion& Hyprutils::Math::CRegion::translate(const Vector2D& vec) { pixman_region32_translate(&m_rRegion, vec.x, vec.y); return *this; } CRegion& Hyprutils::Math::CRegion::transform(const eTransform t, double w, double h) { if (t == HYPRUTILS_TRANSFORM_NORMAL) return *this; auto rects = getRects(); clear(); for (auto& r : rects) { CBox xfmd{r.x1, r.y1, r.x2 - r.x1, r.y2 - r.y1}; xfmd.transform(t, w, h); add(xfmd); } return *this; } CRegion& Hyprutils::Math::CRegion::expand(double units) { auto rects = getRects(); clear(); for (auto& r : rects) { CBox b{sc(r.x1) - units, sc(r.y1) - units, sc(r.x2) - r.x1 + (units * 2), sc(r.y2) - r.y1 + (units * 2)}; add(b); } return *this; } CRegion& Hyprutils::Math::CRegion::rationalize() { intersect(CBox{-MAX_REGION_SIDE, -MAX_REGION_SIDE, MAX_REGION_SIDE * 2, MAX_REGION_SIDE * 2}); return *this; } CRegion Hyprutils::Math::CRegion::copy() const { return CRegion(*this); } CRegion& Hyprutils::Math::CRegion::scale(float scale_) { scale({scale_, scale_}); return *this; } CRegion& Hyprutils::Math::CRegion::scale(const Vector2D& scale) { if (scale == Vector2D{1, 1}) return *this; int rectsNum = 0; auto RECTSARR = pixman_region32_rectangles(&m_rRegion, &rectsNum); std::vector boxes; boxes.resize(rectsNum); for (int i = 0; i < rectsNum; ++i) { boxes[i].x1 = std::floor(RECTSARR[i].x1 * scale.x); boxes[i].x2 = std::ceil(RECTSARR[i].x2 * scale.x); boxes[i].y1 = std::floor(RECTSARR[i].y1 * scale.y); boxes[i].y2 = std::ceil(RECTSARR[i].y2 * scale.y); } pixman_region32_fini(&m_rRegion); pixman_region32_init_rects(&m_rRegion, boxes.data(), boxes.size()); return *this; } std::vector Hyprutils::Math::CRegion::getRects() const { std::vector result; int rectsNum = 0; const auto RECTSARR = pixman_region32_rectangles(&m_rRegion, &rectsNum); result.assign(RECTSARR, RECTSARR + rectsNum); return result; } CBox Hyprutils::Math::CRegion::getExtents() { pixman_box32_t* box = pixman_region32_extents(&m_rRegion); return {sc(box->x1), sc(box->y1), sc(box->x2) - box->x1, sc(box->y2) - box->y1}; } bool Hyprutils::Math::CRegion::containsPoint(const Vector2D& vec) const { return pixman_region32_contains_point(&m_rRegion, vec.x, vec.y, nullptr); } bool Hyprutils::Math::CRegion::empty() const { return !pixman_region32_not_empty(&m_rRegion); } Vector2D Hyprutils::Math::CRegion::closestPoint(const Vector2D& vec) const { if (containsPoint(vec)) return vec; double bestDist = __FLT_MAX__; Vector2D leader = vec; for (auto& box : getRects()) { double x = 0, y = 0; if (vec.x >= box.x2) x = box.x2 - 1; else if (vec.x < box.x1) x = box.x1; else x = vec.x; if (vec.y >= box.y2) y = box.y2 - 1; else if (vec.y < box.y1) y = box.y1; else y = vec.y; double distance = pow(x, 2) + pow(y, 2); if (distance < bestDist) { bestDist = distance; leader = {x, y}; } } return leader; } hyprwm-hyprutils-bc9803c/src/math/Vector2D.cpp000066400000000000000000000041651511106400200213660ustar00rootroot00000000000000#include #include #include #include #include using namespace Hyprutils::Math; double Hyprutils::Math::Vector2D::normalize() { // get max abs const auto max = std::abs(x) > std::abs(y) ? std::abs(x) : std::abs(y); x /= max; y /= max; return max; } Vector2D Hyprutils::Math::Vector2D::floor() const { return Vector2D(std::floor(x), std::floor(y)); } Vector2D Hyprutils::Math::Vector2D::round() const { return Vector2D(std::round(x), std::round(y)); } Vector2D Hyprutils::Math::Vector2D::clamp(const Vector2D& min, const Vector2D& max) const { return Vector2D(std::clamp(this->x, min.x, max.x < min.x ? INFINITY : max.x), std::clamp(this->y, min.y, max.y < min.y ? INFINITY : max.y)); } double Hyprutils::Math::Vector2D::distance(const Vector2D& other) const { return std::sqrt(distanceSq(other)); } double Hyprutils::Math::Vector2D::distanceSq(const Vector2D& other) const { return ((x - other.x) * (x - other.x)) + ((y - other.y) * (y - other.y)); } double Hyprutils::Math::Vector2D::size() const { return std::sqrt((x * x) + (y * y)); } 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; } } hyprwm-hyprutils-bc9803c/src/os/000077500000000000000000000000001511106400200167145ustar00rootroot00000000000000hyprwm-hyprutils-bc9803c/src/os/File.cpp000066400000000000000000000011051511106400200202740ustar00rootroot00000000000000#include #include #include using namespace Hyprutils; using namespace Hyprutils::File; std::expected File::readFileAsString(const std::string_view& path) { std::error_code ec; if (!std::filesystem::exists(path, ec) || ec) return std::unexpected("File not found"); std::ifstream file(std::string{path}); if (!file.good()) return std::unexpected("Failed to open file"); return std::string((std::istreambuf_iterator(file)), (std::istreambuf_iterator())); } hyprwm-hyprutils-bc9803c/src/os/FileDescriptor.cpp000066400000000000000000000034171511106400200223430ustar00rootroot00000000000000#include #include #include #include #include #include using namespace Hyprutils::OS; CFileDescriptor::CFileDescriptor(int const fd) : m_fd(fd) {} CFileDescriptor::CFileDescriptor(CFileDescriptor&& other) : m_fd(std::exchange(other.m_fd, -1)) {} CFileDescriptor& CFileDescriptor::operator=(CFileDescriptor&& other) { if (this == &other) // Shit will go haywire if there is duplicate ownership abort(); reset(); m_fd = std::exchange(other.m_fd, -1); return *this; } CFileDescriptor::~CFileDescriptor() { reset(); } bool CFileDescriptor::isValid() const { return m_fd != -1; } int CFileDescriptor::get() const { return m_fd; } int CFileDescriptor::getFlags() const { return fcntl(m_fd, F_GETFD); } bool CFileDescriptor::setFlags(int flags) { return fcntl(m_fd, F_SETFD, flags) != -1; } int CFileDescriptor::take() { return std::exchange(m_fd, -1); } void CFileDescriptor::reset() { if (m_fd != -1) { close(m_fd); m_fd = -1; } } CFileDescriptor CFileDescriptor::duplicate(int flags) const { if (m_fd == -1) return {}; return CFileDescriptor{fcntl(m_fd, flags, 0)}; } bool CFileDescriptor::isClosed() const { return isClosed(m_fd); } bool CFileDescriptor::isReadable() const { return isReadable(m_fd); } bool CFileDescriptor::isClosed(int fd) { pollfd pfd = { .fd = fd, .events = POLLIN, .revents = 0, }; if (poll(&pfd, 1, 0) < 0) return true; return pfd.revents & (POLLHUP | POLLERR); } bool CFileDescriptor::isReadable(int fd) { pollfd pfd = {.fd = fd, .events = POLLIN, .revents = 0}; return poll(&pfd, 1, 0) > 0 && (pfd.revents & POLLIN); } hyprwm-hyprutils-bc9803c/src/os/Process.cpp000066400000000000000000000163211511106400200210410ustar00rootroot00000000000000#include #include using namespace Hyprutils::OS; using namespace Hyprutils::Memory; #include #include #include #include #include #include #include #include #include struct Hyprutils::OS::CProcess::impl { std::string binary, out, err; std::vector args; std::vector> env; pid_t grandchildPid = 0; int stdoutFD = -1, stderrFD = -1, exitCode = 0, stdinFD = -1; }; Hyprutils::OS::CProcess::CProcess(const std::string& binary, const std::vector& args) : m_impl(new impl()) { m_impl->binary = binary; m_impl->args = args; } Hyprutils::OS::CProcess::~CProcess() { delete m_impl; } void Hyprutils::OS::CProcess::addEnv(const std::string& name, const std::string& value) { m_impl->env.emplace_back(std::make_pair<>(name, value)); } bool Hyprutils::OS::CProcess::runSync() { int outPipe[2], errPipe[2]; if (pipe(outPipe)) return false; if (pipe(errPipe)) { close(outPipe[0]); close(outPipe[1]); return false; } int pid = fork(); if (pid == -1) { close(outPipe[0]); close(outPipe[1]); close(outPipe[0]); close(outPipe[1]); return false; } if (!pid) { // child close(outPipe[0]); close(errPipe[0]); dup2(outPipe[1], 1 /* stdout */); dup2(errPipe[1], 2 /* stderr */); // build argv std::vector 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? argsC.emplace_back(strdup(arg.c_str())); } argsC.emplace_back(nullptr); // pass env for (auto& [n, v] : m_impl->env) { setenv(n.c_str(), v.c_str(), 1); } execvp(m_impl->binary.c_str(), argsC.data()); exit(1); } else { // parent close(outPipe[1]); close(errPipe[1]); m_impl->out = ""; m_impl->err = ""; m_impl->grandchildPid = pid; std::array buf; buf.fill(0); // wait for read ssize_t ret = 0; int fdFlags = fcntl(outPipe[0], F_GETFL, 0); if (fcntl(outPipe[0], F_SETFL, fdFlags | O_NONBLOCK) < 0) return false; fdFlags = fcntl(errPipe[0], F_GETFL, 0); if (fcntl(errPipe[0], F_SETFL, fdFlags | O_NONBLOCK) < 0) return false; pollfd pollfds[2] = { {.fd = outPipe[0], .events = POLLIN, .revents = 0}, {.fd = errPipe[0], .events = POLLIN, .revents = 0}, }; while (1337) { int ret = poll(pollfds, 2, 5000); if (ret < 0) { if (errno == EINTR) continue; return false; } bool hupd = false; for (size_t i = 0; i < 2; ++i) { if (pollfds[i].revents & POLLHUP) { hupd = true; break; } } if (hupd) break; if (pollfds[0].revents & POLLIN) { while ((ret = read(outPipe[0], buf.data(), 1023)) > 0) { m_impl->out += std::string_view{buf.data(), sc(ret)}; } buf.fill(0); } if (pollfds[1].revents & POLLIN) { while ((ret = read(errPipe[0], buf.data(), 1023)) > 0) { m_impl->err += std::string_view{buf.data(), sc(ret)}; } buf.fill(0); } } // Final reads. Nonblock, so its ok. while ((ret = read(outPipe[0], buf.data(), 1023)) > 0) { m_impl->out += std::string_view{buf.data(), sc(ret)}; } buf.fill(0); while ((ret = read(errPipe[0], buf.data(), 1023)) > 0) { m_impl->err += std::string_view{buf.data(), sc(ret)}; } buf.fill(0); close(outPipe[0]); close(errPipe[0]); // reap child int status = 0; waitpid(pid, &status, 0); if (WIFEXITED(status)) m_impl->exitCode = WEXITSTATUS(status); return true; } return true; } bool Hyprutils::OS::CProcess::runAsync() { int socket[2]; if (pipe(socket) != 0) return false; pid_t child, grandchild; child = fork(); if (child < 0) { close(socket[0]); close(socket[1]); return false; } if (child == 0) { // run in child sigset_t set; sigemptyset(&set); sigprocmask(SIG_SETMASK, &set, nullptr); grandchild = fork(); if (grandchild == 0) { // run in grandchild close(socket[0]); close(socket[1]); // build argv std::vector argsC; argsC.emplace_back(strdup(m_impl->binary.c_str())); for (auto& arg : m_impl->args) { argsC.emplace_back(strdup(arg.c_str())); } argsC.emplace_back(nullptr); // pass env for (auto& [n, v] : m_impl->env) { setenv(n.c_str(), v.c_str(), 1); } 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]); if (write(socket[1], &grandchild, sizeof(grandchild)) != sizeof(grandchild)) { close(socket[1]); _exit(1); } close(socket[1]); _exit(0); } // run in parent close(socket[1]); ssize_t bytesRead = read(socket[0], &grandchild, sizeof(grandchild)); close(socket[0]); if (bytesRead != sizeof(grandchild)) { waitpid(child, nullptr, 0); return false; } // clear child and leave grandchild to init waitpid(child, nullptr, 0); m_impl->grandchildPid = grandchild; return true; } const std::string& Hyprutils::OS::CProcess::stdOut() { return m_impl->out; } const std::string& Hyprutils::OS::CProcess::stdErr() { return m_impl->err; } pid_t Hyprutils::OS::CProcess::pid() { return m_impl->grandchildPid; } 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; } void Hyprutils::OS::CProcess::setStderrFD(int fd) { m_impl->stderrFD = fd; } hyprwm-hyprutils-bc9803c/src/path/000077500000000000000000000000001511106400200172275ustar00rootroot00000000000000hyprwm-hyprutils-bc9803c/src/path/Path.cpp000066400000000000000000000054531511106400200206360ustar00rootroot00000000000000#include #include #include using namespace Hyprutils; namespace Hyprutils::Path { std::string fullConfigPath(std::string basePath, std::string programName) { return basePath + "/hypr/" + programName + ".conf"; } bool checkConfigExists(std::string basePath, std::string programName) { return std::filesystem::exists(fullConfigPath(basePath, programName)); } std::optional getHome() { static const auto homeDir = getenv("HOME"); if (!homeDir || !std::filesystem::path(homeDir).is_absolute()) return std::nullopt; return std::string(homeDir).append("/.config"); } std::optional getXdgConfigDirs() { static const auto xdgConfigDirs = getenv("XDG_CONFIG_DIRS"); if (!xdgConfigDirs) return std::nullopt; static const auto xdgConfigDirsList = String::CVarList(xdgConfigDirs, 0, ':'); return xdgConfigDirsList; } std::optional getXdgConfigHome() { static const auto xdgConfigHome = getenv("XDG_CONFIG_HOME"); if (!xdgConfigHome || !std::filesystem::path(xdgConfigHome).is_absolute()) return std::nullopt; return xdgConfigHome; } using T = std::optional; std::pair findConfig(std::string programName) { bool xdgConfigHomeExists = false; static const auto xdgConfigHome = getXdgConfigHome(); if (xdgConfigHome.has_value()) { xdgConfigHomeExists = true; if (checkConfigExists(xdgConfigHome.value(), programName)) return std::make_pair(fullConfigPath(xdgConfigHome.value(), programName), xdgConfigHome); } bool homeExists = false; static const auto home = getHome(); if (home.has_value()) { homeExists = true; if (checkConfigExists(home.value(), programName)) return std::make_pair(fullConfigPath(home.value(), programName), home); } static const auto xdgConfigDirs = getXdgConfigDirs(); if (xdgConfigDirs.has_value()) { for (auto& dir : xdgConfigDirs.value()) { if (checkConfigExists(dir, programName)) return std::make_pair(fullConfigPath(dir, programName), std::nullopt); } } if (checkConfigExists("/etc/xdg", programName)) return std::make_pair(fullConfigPath("/etc/xdg", programName), std::nullopt); if (xdgConfigHomeExists) return std::make_pair(std::nullopt, xdgConfigHome); else if (homeExists) return std::make_pair(std::nullopt, home); return std::make_pair(std::nullopt, std::nullopt); } } hyprwm-hyprutils-bc9803c/src/signal/000077500000000000000000000000001511106400200175505ustar00rootroot00000000000000hyprwm-hyprutils-bc9803c/src/signal/Listener.cpp000066400000000000000000000007531511106400200220460ustar00rootroot00000000000000#include #include using namespace Hyprutils::Signal; Hyprutils::Signal::CSignalListener::CSignalListener(std::function handler) : m_fHandler(handler) { ; } void Hyprutils::Signal::CSignalListener::emitInternal(void* data) { if (!m_fHandler) return; m_fHandler(data); } void Hyprutils::Signal::CSignalListener::emit(std::any data) { auto dataTuple = std::tuple(data); emitInternal(&dataTuple); } hyprwm-hyprutils-bc9803c/src/signal/Signal.cpp000066400000000000000000000034011511106400200214670ustar00rootroot00000000000000#include "hyprutils/memory/SharedPtr.hpp" #include #include #include using namespace Hyprutils::Signal; using namespace Hyprutils::Memory; #define SP CSharedPointer #define WP CWeakPointer void Hyprutils::Signal::CSignalBase::emitInternal(void* args) { std::vector> listeners; listeners.reserve(m_vListeners.size()); for (auto& l : m_vListeners) { if (l.expired()) continue; listeners.emplace_back(l.lock()); } auto statics = m_vStaticListeners; 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) continue; l->emitInternal(args); } for (auto& l : statics) { l->emitInternal(args); } // release SPs listeners.clear(); // we cannot release any expired refs here as one of the listeners could've removed this object and // as such we'd be doing a UAF } CHyprSignalListener Hyprutils::Signal::CSignalBase::registerListenerInternal(std::function handler) { CHyprSignalListener listener = SP(new CSignalListener(handler)); m_vListeners.emplace_back(listener); // housekeeping: remove any stale listeners std::erase_if(m_vListeners, [](const auto& other) { return other.expired(); }); return listener; } void Hyprutils::Signal::CSignalBase::registerStaticListenerInternal(std::function handler) { m_vStaticListeners.emplace_back(SP(new CSignalListener(handler))); } void Hyprutils::Signal::CSignal::emit(std::any data) { CSignalT::emit(data); } hyprwm-hyprutils-bc9803c/src/string/000077500000000000000000000000001511106400200176015ustar00rootroot00000000000000hyprwm-hyprutils-bc9803c/src/string/ConstVarList.cpp000066400000000000000000000023151511106400200227010ustar00rootroot00000000000000#include #include #include #include using namespace Hyprutils::String; 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(std::string_view{m_str}.substr(pos))); break; } pos += s.size() + 1; m_args.emplace_back(trim(std::string_view{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; } hyprwm-hyprutils-bc9803c/src/string/String.cpp000066400000000000000000000050201511106400200215500ustar00rootroot00000000000000#include #include using namespace Hyprutils::String; std::string Hyprutils::String::trim(const std::string& in) { if (in.empty()) return in; size_t countBefore = 0; while (countBefore < in.length() && std::isspace(in.at(countBefore))) { countBefore++; } size_t 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; } std::string_view Hyprutils::String::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); } std::string Hyprutils::String::trim(const char* in) { return trim(std::string{in}); } bool Hyprutils::String::isNumber(const std::string& str, bool allowfloat) { if (str.empty()) return false; bool decimalParsed = 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 (decimalParsed) return false; decimalParsed = true; continue; } } return isdigit(str.back()) != 0; } 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(); } } bool Hyprutils::String::truthy(const std::string_view& in) { if (in == "1") return true; if (in == "0") return false; std::string lower = std::string{in}; std::ranges::transform(lower, lower.begin(), ::tolower); return lower.starts_with("true") || lower.starts_with("yes") || lower.starts_with("on"); } hyprwm-hyprutils-bc9803c/src/string/VarList.cpp000066400000000000000000000022451511106400200216740ustar00rootroot00000000000000#include #include #include #include using namespace Hyprutils::String; Hyprutils::String::CVarList::CVarList(const std::string& in, const size_t lastArgNo, const char delim, const bool removeEmpty) { if (!removeEmpty && 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{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; } hyprwm-hyprutils-bc9803c/src/string/VarList2.cpp000066400000000000000000000103311511106400200217510ustar00rootroot00000000000000#include #include #include using namespace Hyprutils::String; CVarList2::CVarList2(std::string&& in, const size_t lastArgNo, const char delim, const bool removeEmpty, const bool allowEscape) : m_inString(std::move(in)) { if (m_inString.empty()) return; auto isDelimiter = [&delim](const char& c) { return delim == 's' ? std::isspace(c) : delim == c; }; size_t argBegin = 0; std::vector escapedIndices; // local to the current arg for (size_t i = 0; i < m_inString.size(); ++i) { const char& c = m_inString[i]; if (!isDelimiter(c)) continue; if (allowEscape) { // we allow escape, so this might be escaped. Check first if (i - argBegin != 0) { const char& previousC = m_inString[i - 1]; if (i - argBegin == 1) { if (previousC == '\\') { escapedIndices.emplace_back(i - argBegin - 1); continue; // escaped } // fall to breaking, not escaped } else { const char& prevPreviousC = m_inString[i - 2]; if (previousC == '\\') { // whether or not escaped, pop char escapedIndices.emplace_back(i - argBegin - 1); if (prevPreviousC != '\\') { // escaped continue; } } // fall to breaking, not escaped, but mark the \\ to be popped } // fall to breaking, couldn't be escaped } } // here we found a delimiter and need to break up the string (not escaped) if (escapedIndices.empty()) { // we didn't escape anything, so we can use inString const auto ARG = trim(std::string_view{m_inString}.substr(argBegin, i - argBegin)); if (!ARG.empty() || !removeEmpty) m_args.emplace_back(ARG); } else { // we escaped something, fixup the string, add to copies, then emplace std::string cpy = m_inString.substr(argBegin, i - argBegin); for (size_t i = 0; i < escapedIndices.size(); ++i) { cpy = cpy.substr(0, escapedIndices[i] - i) + cpy.substr(escapedIndices[i] - i + 1); } m_copyStrings.emplace_back(std::move(cpy)); m_args.emplace_back(trim(std::string_view{m_copyStrings.back()})); } // update next argBegin argBegin = i + 1; escapedIndices.clear(); } // append anything left if (argBegin < m_inString.size()) { if (escapedIndices.empty()) { // we didn't escape anything, so we can use inString const auto ARG = trim(std::string_view{m_inString}.substr(argBegin, m_inString.size() - argBegin)); if (!ARG.empty() || !removeEmpty) m_args.emplace_back(ARG); } else { // we escaped something, fixup the string, add to copies, then emplace std::string cpy = m_inString.substr(argBegin, m_inString.size() - argBegin); for (size_t i = 0; i < escapedIndices.size(); ++i) { cpy = cpy.substr(0, escapedIndices[i] - i) + cpy.substr(escapedIndices[i] - i + 1); } m_copyStrings.emplace_back(std::move(cpy)); m_args.emplace_back(trim(std::string_view{m_copyStrings.back()})); } } } std::string CVarList2::join(const std::string& joiner, size_t from, size_t to) const { if (to == 0 || to <= from) to = m_args.size(); std::string roll; for (size_t i = from; i < to && i < m_args.size(); ++i) { roll += m_args[i]; if (i + 1 < to && i + 1 < m_args.size()) roll += joiner; } return roll; } void CVarList2::append(std::string&& arg) { m_copyStrings.emplace_back(std::move(arg)); m_args.emplace_back(m_copyStrings.back()); } bool CVarList2::contains(const std::string& el) { return std::ranges::any_of(m_args, [&el](const auto& e) { return e == el; }); } hyprwm-hyprutils-bc9803c/src/utils/000077500000000000000000000000001511106400200174335ustar00rootroot00000000000000hyprwm-hyprutils-bc9803c/src/utils/ScopeGuard.cpp000066400000000000000000000003741511106400200221770ustar00rootroot00000000000000#include using namespace Hyprutils::Utils; Hyprutils::Utils::CScopeGuard::CScopeGuard(const std::function& fn_) : fn(fn_) { ; } Hyprutils::Utils::CScopeGuard::~CScopeGuard() { if (fn) fn(); } hyprwm-hyprutils-bc9803c/tests/000077500000000000000000000000001511106400200166465ustar00rootroot00000000000000hyprwm-hyprutils-bc9803c/tests/animation/000077500000000000000000000000001511106400200206255ustar00rootroot00000000000000hyprwm-hyprutils-bc9803c/tests/animation/Animation.cpp000066400000000000000000000272761511106400200232660ustar00rootroot00000000000000 #include #include #include #include #include #include #define SP CSharedPointer #define WP CWeakPointer #define UP CUniquePointer using namespace Hyprutils::Animation; using namespace Hyprutils::Math; using namespace Hyprutils::Memory; class EmtpyContext {}; template using CAnimatedVariable = CGenericAnimatedVariable; template using PANIMVAR = SP>; template using PANIMVARREF = WP>; enum eAVTypes { INT = 1, TEST, }; struct SomeTestType { bool done = false; bool operator==(const SomeTestType& other) const { return done == other.done; } SomeTestType& operator=(const SomeTestType& other) { done = other.done; return *this; } }; CAnimationConfigTree animationTree; class CMyAnimationManager : public CAnimationManager { public: void tick() { for (size_t i = 0; i < m_vActiveAnimatedVariables.size(); i++) { const auto PAV = m_vActiveAnimatedVariables[i].lock(); if (!PAV || !PAV->ok() || !PAV->isBeingAnimated()) continue; const auto SPENT = PAV->getPercent(); const auto PBEZIER = getBezier(PAV->getBezierName()); if (SPENT >= 1.f || !PAV->enabled()) { PAV->warp(true, false); continue; } const auto POINTY = PBEZIER->getYForPoint(SPENT); switch (PAV->m_Type) { case eAVTypes::INT: { auto avInt = dc*>(PAV.get()); if (!avInt) std::cout << "Dynamic cast upcast failed\n"; const auto DELTA = avInt->goal() - avInt->value(); avInt->value() = avInt->begun() + (DELTA * POINTY); } break; case eAVTypes::TEST: { auto avCustom = dc*>(PAV.get()); if (!avCustom) std::cout << "Dynamic cast upcast failed\n"; if (SPENT >= 1.f) avCustom->value().done = true; } break; default: { std::cout << "What are we even doing?\n"; } break; } PAV->onUpdate(); } tickDone(); } template void createAnimation(const VarType& v, PANIMVAR& av, const std::string& animationConfigName) { constexpr const eAVTypes EAVTYPE = std::is_same_v ? eAVTypes::INT : eAVTypes::TEST; const auto PAV = makeShared>(); PAV->create(EAVTYPE, sc(this), PAV, v); PAV->setConfig(animationTree.getConfig(animationConfigName)); av = std::move(PAV); } virtual void scheduleTick() { ; } virtual void onTicked() { ; } }; UP pAnimationManager; class Subject { public: Subject(const int& a, const int& b) { pAnimationManager->createAnimation(a, m_iA, "default"); pAnimationManager->createAnimation(b, m_iB, "internal"); pAnimationManager->createAnimation({}, m_iC, "default"); } PANIMVAR m_iA; PANIMVAR m_iB; PANIMVAR m_iC; }; static int config() { pAnimationManager = makeUnique(); int ret = 0; animationTree.createNode("global"); animationTree.createNode("internal"); animationTree.createNode("foo", "internal"); animationTree.createNode("default", "global"); animationTree.createNode("bar", "default"); /* internal ↳ foo global ↳ default ↳ bar */ auto barCfg = animationTree.getConfig("bar"); auto internalCfg = animationTree.getConfig("internal"); // internal is a root node and should point to itself EXPECT_EQ(internalCfg->pParentAnimation.get(), internalCfg.get()); EXPECT_EQ(internalCfg->pValues.get(), internalCfg.get()); animationTree.setConfigForNode("global", 1, 4.0, "default", "asdf"); EXPECT_EQ(barCfg->internalEnabled, -1); { const auto PVALUES = barCfg->pValues.lock(); EXPECT_EQ(PVALUES->internalEnabled, 1); EXPECT_EQ(PVALUES->internalBezier, "default"); EXPECT_EQ(PVALUES->internalStyle, "asdf"); EXPECT_EQ(PVALUES->internalSpeed, 4.0); } EXPECT_EQ(barCfg->pParentAnimation.get(), animationTree.getConfig("default").get()); // Overwrite our own values animationTree.setConfigForNode("bar", 1, 4.2, "test", "qwer"); { const auto PVALUES = barCfg->pValues.lock(); EXPECT_EQ(PVALUES->internalEnabled, 1); EXPECT_EQ(PVALUES->internalBezier, "test"); EXPECT_EQ(PVALUES->internalStyle, "qwer"); EXPECT_EQ(PVALUES->internalSpeed, 4.2f); } // Now overwrite the parent animationTree.setConfigForNode("default", 0, 0.0, "zxcv", "foo"); { // Expecting no change const auto PVALUES = barCfg->pValues.lock(); EXPECT_EQ(PVALUES->internalEnabled, 1); EXPECT_EQ(PVALUES->internalBezier, "test"); EXPECT_EQ(PVALUES->internalStyle, "qwer"); EXPECT_EQ(PVALUES->internalSpeed, 4.2f); } return ret; } TEST(Animation, animation) { config(); animationTree.createNode("global"); animationTree.createNode("internal"); animationTree.createNode("default", "global"); animationTree.setConfigForNode("global", 1, 4.0, "default", "asdf"); Subject s(0, 0); EXPECT_EQ(s.m_iA->value(), 0); EXPECT_EQ(s.m_iB->value(), 0); // Test destruction of a CAnimatedVariable { Subject s2(10, 10); // Adds them to active *s2.m_iA = 1; *s2.m_iB = 2; // We deliberately do not tick here, to make sure the destructor removes active animated variables } EXPECT_EQ(pAnimationManager->shouldTickForNext(), false); EXPECT_EQ(s.m_iC->value().done, false); *s.m_iA = 10; *s.m_iB = 100; *s.m_iC = SomeTestType(true); EXPECT_EQ(s.m_iC->value().done, false); while (pAnimationManager->shouldTickForNext()) { pAnimationManager->tick(); } EXPECT_EQ(s.m_iA->value(), 10); EXPECT_EQ(s.m_iB->value(), 100); EXPECT_EQ(s.m_iC->value().done, true); s.m_iA->setValue(0); s.m_iB->setValue(0); while (pAnimationManager->shouldTickForNext()) { pAnimationManager->tick(); } EXPECT_EQ(s.m_iA->value(), 10); EXPECT_EQ(s.m_iB->value(), 100); // Test config stuff EXPECT_EQ(s.m_iA->getBezierName(), "default"); EXPECT_EQ(s.m_iA->getStyle(), "asdf"); EXPECT_EQ(s.m_iA->enabled(), true); animationTree.getConfig("global")->internalEnabled = 0; EXPECT_EQ(s.m_iA->enabled(), false); *s.m_iA = 50; pAnimationManager->tick(); // Expecting a warp EXPECT_EQ(s.m_iA->value(), 50); // Test missing pValues animationTree.getConfig("global")->internalEnabled = 0; animationTree.getConfig("default")->pValues.reset(); EXPECT_EQ(s.m_iA->enabled(), false); EXPECT_EQ(s.m_iA->getBezierName(), "default"); EXPECT_EQ(s.m_iA->getStyle(), ""); EXPECT_EQ(s.m_iA->getPercent(), 1.f); // Reset animationTree.setConfigForNode("default", 1, 1, "default"); // // Test callbacks // int beginCallbackRan = 0; int updateCallbackRan = 0; int endCallbackRan = 0; s.m_iA->setCallbackOnBegin([&beginCallbackRan](WP pav) { beginCallbackRan++; }); s.m_iA->setUpdateCallback([&updateCallbackRan](WP pav) { updateCallbackRan++; }); s.m_iA->setCallbackOnEnd([&endCallbackRan](WP pav) { endCallbackRan++; }, false); s.m_iA->setValueAndWarp(42); EXPECT_EQ(beginCallbackRan, 0); EXPECT_EQ(updateCallbackRan, 1); EXPECT_EQ(endCallbackRan, 2); // first called when setting the callback, then when warping. *s.m_iA = 1337; while (pAnimationManager->shouldTickForNext()) { pAnimationManager->tick(); } EXPECT_EQ(beginCallbackRan, 1); EXPECT_EQ(updateCallbackRan > 2, true); EXPECT_EQ(endCallbackRan, 3); std::vector> vars; for (int i = 0; i < 10; i++) { vars.resize(vars.size() + 1); pAnimationManager->createAnimation(1, vars.back(), "default"); *vars.back() = 1337; } // test adding / removing vars during a tick s.m_iA->resetAllCallbacks(); s.m_iA->setUpdateCallback([&vars](WP v) { if (v.lock() != vars.back()) vars.back()->warp(); }); s.m_iA->setCallbackOnEnd([&s, &vars](auto) { vars.resize(vars.size() + 1); pAnimationManager->createAnimation(1, vars.back(), "default"); *vars.back() = 1337; }); *s.m_iA = 1000000; while (pAnimationManager->shouldTickForNext()) { pAnimationManager->tick(); } EXPECT_EQ(s.m_iA->value(), 1000000); // all vars should be set to 1337 EXPECT_EQ(std::find_if(vars.begin(), vars.end(), [](const auto& v) { return v->value() != 1337; }) == vars.end(), true); // test one-time callbacks s.m_iA->resetAllCallbacks(); s.m_iA->setCallbackOnEnd([&endCallbackRan](auto) { endCallbackRan++; }, true); EXPECT_EQ(endCallbackRan, 4); s.m_iA->setValueAndWarp(10); EXPECT_EQ(endCallbackRan, 4); EXPECT_EQ(s.m_iA->value(), 10); // test warp *s.m_iA = 3; s.m_iA->setCallbackOnEnd([&endCallbackRan](auto) { endCallbackRan++; }, false); s.m_iA->warp(false); EXPECT_EQ(endCallbackRan, 4); *s.m_iA = 4; s.m_iA->warp(true); EXPECT_EQ(endCallbackRan, 5); // test getCurveValue *s.m_iA = 0; EXPECT_EQ(s.m_iA->getCurveValue(), 0.f); s.m_iA->warp(); EXPECT_EQ(s.m_iA->getCurveValue(), 1.f); EXPECT_EQ(endCallbackRan, 6); // test end callback readding the var *s.m_iA = 5; s.m_iA->setCallbackOnEnd([&endCallbackRan](WP v) { endCallbackRan++; const auto PAV = dc*>(v.lock().get()); *PAV = 10; PAV->setCallbackOnEnd([&endCallbackRan](WP v) { endCallbackRan++; }); }); while (pAnimationManager->shouldTickForNext()) { pAnimationManager->tick(); } EXPECT_EQ(endCallbackRan, 8); EXPECT_EQ(s.m_iA->value(), 10); // Test duplicate active anim vars are not allowed { EXPECT_EQ(pAnimationManager->m_vActiveAnimatedVariables.size(), 0); PANIMVAR a; pAnimationManager->createAnimation(1, a, "default"); EXPECT_EQ(pAnimationManager->m_vActiveAnimatedVariables.size(), 0); *a = 10; EXPECT_EQ(pAnimationManager->m_vActiveAnimatedVariables.size(), 1); *a = 20; EXPECT_EQ(pAnimationManager->m_vActiveAnimatedVariables.size(), 1); a->warp(); EXPECT_EQ(pAnimationManager->m_vActiveAnimatedVariables.size(), 0); EXPECT_EQ(a->value(), 20); } // Test no crash when animation manager gets destroyed { PANIMVAR a; pAnimationManager->createAnimation(1, a, "default"); *a = 10; pAnimationManager.reset(); EXPECT_EQ(a->isAnimationManagerDead(), true); a->setValueAndWarp(11); EXPECT_EQ(a->value(), 11); *a = 12; a->warp(); EXPECT_EQ(a->value(), 12); *a = 13; } // a gets destroyed EXPECT_EQ(pAnimationManager.get(), nullptr); }hyprwm-hyprutils-bc9803c/tests/animation/Bezier.cpp000066400000000000000000000046541511106400200225620ustar00rootroot00000000000000#include #include #include using Hyprutils::Animation::CBezierCurve; using Hyprutils::Math::Vector2D; static void test_nonmonotonic4_clamps_out_of_range() { // Non-monotonic curve in X // This used to drive the step-halving search to OOB. It should now clamp CBezierCurve curve; std::array pts = { Vector2D{0.5f, 1.0f}, // P0 Vector2D{1.0f, 1.0f}, // P1 Vector2D{0.0f, 0.0f}, // P2 Vector2D{0.5f, 0.0f} // P3 }; curve.setup4(pts); // x > last baked x EXPECT_EQ(std::isfinite(curve.getYForPoint(0.6f)), true); // Far beyond range EXPECT_EQ(std::isfinite(curve.getYForPoint(std::numeric_limits::max())), true); EXPECT_EQ(std::isfinite(curve.getYForPoint(-std::numeric_limits::max())), true); } static void test_adjacent_baked_x_equal() { // Curve with flat tail (X=1, Y=1) CBezierCurve curve; std::array pts = { Vector2D{0.0f, 0.0f}, // P0 Vector2D{0.2f, 0.2f}, // P1 Vector2D{1.0f, 1.0f}, // P2 Vector2D{1.0f, 1.0f} // P3 }; curve.setup4(pts); // Exactly at last baked X const float y_at_end = curve.getYForPoint(1.0f); // Slightly beyond last baked X const float y_past_end = curve.getYForPoint(1.0001f); EXPECT_EQ(y_at_end, 1.0f); EXPECT_EQ(y_past_end, y_at_end); } static void test_all_baked_x_equal() { // Extreme case: X is constant along the whole curve CBezierCurve curve; std::array pts = { Vector2D{0.0f, 0.0f}, // P0 Vector2D{0.0f, 0.3f}, // P1 Vector2D{0.0f, 0.7f}, // P2 Vector2D{0.0f, 1.0f} // P3 }; curve.setup4(pts); // Below any baked X const float y_lo = curve.getYForPoint(-100.0f); const float y_0 = curve.getYForPoint(0.0f); // Above any baked X const float y_hi = curve.getYForPoint(100.0f); EXPECT_EQ(std::isfinite(y_lo), true); EXPECT_EQ(std::isfinite(y_0), true); EXPECT_EQ(std::isfinite(y_hi), true); // For this curve Y should stay within [0,1] EXPECT_EQ((y_lo >= 0.0f && y_lo <= 1.0f), true); EXPECT_EQ((y_0 >= 0.0f && y_0 <= 1.0f), true); EXPECT_EQ((y_hi >= 0.0f && y_hi <= 1.0f), true); } TEST(Animation, beziercurve) { test_nonmonotonic4_clamps_out_of_range(); test_adjacent_baked_x_equal(); test_all_baked_x_equal(); }hyprwm-hyprutils-bc9803c/tests/cli/000077500000000000000000000000001511106400200174155ustar00rootroot00000000000000hyprwm-hyprutils-bc9803c/tests/cli/ArgumentParser.cpp000066400000000000000000000106601511106400200230630ustar00rootroot00000000000000#include #include #include using namespace Hyprutils::CLI; using namespace Hyprutils; constexpr const char* DESC_TEST = R"#(┏ My description ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┣ --hello -h | Says hello ┃ ┣ --hello2 -e | Says hello 2 ┃ ┣ --value -v [float] | Sets a valueeeeeee ┃ ┣ --longlonglonglongintopt -l [int] | Long long ┃ ┣ maaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa┃ ┣ aaaaaaaaaaan maaan man maaan man maaan ┃ ┣ man maaan man ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ )#"; TEST(CLI, ArgumentParser) { std::vector argv = {"app", "--hello", "--value", "0.2"}; CArgumentParser parser(argv); EXPECT_TRUE(parser.registerBoolOption("hello", "h", "Says hello")); EXPECT_TRUE(parser.registerBoolOption("hello2", "e", "Says hello 2")); EXPECT_TRUE(parser.registerFloatOption("value", "v", "Sets a valueeeeeee")); EXPECT_TRUE(parser.registerIntOption("longlonglonglongintopt", "l", "Long long maaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaan maaan man maaan man maaan man maaan man")); auto result = parser.parse(); EXPECT_TRUE(result.has_value()); std::println("{}", parser.getDescription("My description")); if (!result.has_value()) std::println("Error: {}", result.error()); EXPECT_EQ(parser.getBool("hello").value_or(false), true); EXPECT_EQ(parser.getBool("hello2").value_or(false), false); EXPECT_EQ(parser.getFloat("value").value_or(0.F), 0.2F); EXPECT_EQ(parser.getDescription("My description"), DESC_TEST); CArgumentParser parser2(argv); EXPECT_TRUE(parser2.registerBoolOption("hello2", "e", "Says hello 2")); EXPECT_TRUE(parser2.registerFloatOption("value", "v", "Sets a valueeeeeee")); EXPECT_TRUE(parser2.registerIntOption("longlonglonglongintopt", "l", "Long long maaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaan maaan man maaan man maaan man maaan man")); auto result2 = parser2.parse(); EXPECT_TRUE(!result2.has_value()); std::vector argv3 = {"app", "--hello", "--value"}; CArgumentParser parser3(argv3); EXPECT_TRUE(parser3.registerBoolOption("hello2", "e", "Says hello 2")); EXPECT_TRUE(parser3.registerFloatOption("value", "v", "Sets a valueeeeeee")); EXPECT_TRUE(parser3.registerIntOption("longlonglonglongintopt", "l", "Long long maaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaan maaan man maaan man maaan man maaan man")); auto result3 = parser3.parse(); EXPECT_TRUE(!result3.has_value()); std::vector argv4 = {"app", "--value", "hi", "-w", "2"}; CArgumentParser parser4(argv4); EXPECT_TRUE(parser4.registerStringOption("value", "v", "Sets a valueeeeeee")); EXPECT_TRUE(parser4.registerIntOption("value2", "w", "Sets a valueeeeeee 2")); auto result4 = parser4.parse(); EXPECT_TRUE(result4.has_value()); EXPECT_EQ(parser4.getString("value").value_or(""), "hi"); EXPECT_EQ(parser4.getInt("value2").value_or(0), 2); std::vector argv5 = { "app", "e", }; CArgumentParser parser5(argv5); EXPECT_TRUE(parser5.registerStringOption("value", "v", "Sets a valueeeeeee")); EXPECT_TRUE(parser5.registerStringOption("value2", "w", "Sets a valueeeeeee 2")); auto result5 = parser5.parse(); EXPECT_TRUE(!result5.has_value()); CArgumentParser parser6(argv5); EXPECT_TRUE(parser6.registerStringOption("aa", "v", "Sets a valueeeeeee")); EXPECT_TRUE(!parser6.registerStringOption("aa", "w", "Sets a valueeeeeee 2")); EXPECT_TRUE(parser6.registerStringOption("bb", "b", "Sets a valueeeeeee")); EXPECT_TRUE(!parser6.registerStringOption("cc", "b", "Sets a valueeeeeee 2")); }hyprwm-hyprutils-bc9803c/tests/cli/Logger.cpp000066400000000000000000000056541511106400200213520ustar00rootroot00000000000000#include #include #include #include using namespace Hyprutils::CLI; using namespace Hyprutils; TEST(CLI, Logger) { CLogger logger; logger.setEnableRolling(true); logger.log(Hyprutils::CLI::LOG_DEBUG, "Hello!"); EXPECT_EQ(logger.rollingLog(), "DEBUG ]: Hello!"); logger.log(Hyprutils::CLI::LOG_TRACE, "Hello!"); EXPECT_EQ(logger.rollingLog(), "DEBUG ]: Hello!"); logger.setLogLevel(LOG_TRACE); logger.log(Hyprutils::CLI::LOG_TRACE, "Hello, {}!", "Trace"); EXPECT_EQ(logger.rollingLog(), "DEBUG ]: Hello!\nTRACE ]: Hello, Trace!"); CLoggerConnection connection(logger); connection.setName("conn"); connection.log(Hyprutils::CLI::LOG_TRACE, "Hello from connection!"); EXPECT_EQ(logger.rollingLog(), "DEBUG ]: Hello!\nTRACE ]: Hello, Trace!\nTRACE from conn ]: Hello from connection!"); connection.setLogLevel(Hyprutils::CLI::LOG_WARN); connection.log(Hyprutils::CLI::LOG_DEBUG, "Hello from connection!"); EXPECT_EQ(logger.rollingLog(), "DEBUG ]: Hello!\nTRACE ]: Hello, Trace!\nTRACE from conn ]: Hello from connection!"); logger.setEnableRolling(false); connection.log(Hyprutils::CLI::LOG_ERR, "Err!"); EXPECT_EQ(logger.rollingLog(), "DEBUG ]: Hello!\nTRACE ]: Hello, Trace!\nTRACE from conn ]: Hello from connection!"); logger.setEnableStdout(false); logger.log(Hyprutils::CLI::LOG_ERR, "Error"); EXPECT_EQ(logger.rollingLog(), "DEBUG ]: Hello!\nTRACE ]: Hello, Trace!\nTRACE from conn ]: Hello from connection!"); auto res = logger.setOutputFile("./loggerFile.log"); EXPECT_TRUE(res); logger.log(LOG_DEBUG, "Hi file!"); res = logger.setOutputFile(""); // clear EXPECT_TRUE(res); auto fileRead = File::readFileAsString("./loggerFile.log"); EXPECT_TRUE(fileRead); EXPECT_EQ(fileRead.value_or(""), "DEBUG ]: Hi file!\n"); std::error_code ec; std::filesystem::remove("./loggerFile.log", ec); // TODO: maybe find a way to test the times and color? logger.setEnableStdout(true); logger.setTime(true); logger.log(Hyprutils::CLI::LOG_WARN, "Timed warning!"); logger.setEnableColor(false); logger.log(Hyprutils::CLI::LOG_CRIT, "rip"); logger.setEnableRolling(true); // spam some logs to check rolling for (size_t i = 0; i < 200; ++i) { logger.log(LOG_ERR, "Oh noes!!!"); } EXPECT_TRUE(logger.rollingLog().size() < 4096); EXPECT_TRUE(logger.rollingLog().starts_with("ERR")); // test the breaking is done correctly // test scoping CLogger* pLogger = new CLogger(); CLoggerConnection* pConnection = new CLoggerConnection(*pLogger); pLogger->setEnableStdout(false); pConnection->log(LOG_DEBUG, "This shouldn't log anything."); EXPECT_TRUE(pLogger->rollingLog().empty()); delete pLogger; pConnection->log(LOG_DEBUG, "This shouldn't do anything, or crash."); }hyprwm-hyprutils-bc9803c/tests/i18n/000077500000000000000000000000001511106400200174255ustar00rootroot00000000000000hyprwm-hyprutils-bc9803c/tests/i18n/Engine.cpp000066400000000000000000000064661511106400200213520ustar00rootroot00000000000000 #include #include using namespace Hyprutils::I18n; enum eTxtKeys : uint64_t { TXT_KEY_HELLO, TXT_KEY_I_HAVE_APPLES, TXT_KEY_FALLBACK, }; TEST(I18n, Engine) { CI18nEngine engine; engine.setFallbackLocale("en_US"); engine.registerEntry("en_US", TXT_KEY_HELLO, "Hello World!"); engine.registerEntry("en_US", TXT_KEY_I_HAVE_APPLES, [](const translationVarMap& m) { if (std::stoi(m.at("count")) == 1) return "I have {count} apple."; else return "I have {count} apples."; }); engine.registerEntry("en_US", TXT_KEY_FALLBACK, "Fallback string!"); engine.registerEntry("pl_PL", TXT_KEY_HELLO, "Witaj świecie!"); engine.registerEntry("pl_PL", TXT_KEY_I_HAVE_APPLES, [](const translationVarMap& m) { const auto COUNT = std::stoi(m.at("count")); if (COUNT == 1) return "Mam {count} jabłko."; else if (COUNT < 5) return "Mam {count} jabłka."; else return "Mam {count} jabłek."; }); engine.registerEntry("es_XX", TXT_KEY_FALLBACK, "I don't speak spanish"); engine.registerEntry("es_ES", TXT_KEY_FALLBACK, "I don't speak spanish here either"); engine.registerEntry("ts_TST", TXT_KEY_FALLBACK, "Hello {var1} world {var2}"); engine.registerEntry("am", TXT_KEY_FALLBACK, "Amongus!"); EXPECT_EQ(engine.localizeEntry("en_US", TXT_KEY_HELLO, {}), "Hello World!"); EXPECT_EQ(engine.localizeEntry("pl_PL", TXT_KEY_HELLO, {}), "Witaj świecie!"); EXPECT_EQ(engine.localizeEntry("de_DE", TXT_KEY_HELLO, {}), "Hello World!"); EXPECT_EQ(engine.localizeEntry("en_US", TXT_KEY_I_HAVE_APPLES, {{"count", "1"}}), "I have 1 apple."); EXPECT_EQ(engine.localizeEntry("en_US", TXT_KEY_I_HAVE_APPLES, {{"count", "2"}}), "I have 2 apples."); EXPECT_EQ(engine.localizeEntry("pl_PL", TXT_KEY_I_HAVE_APPLES, {{"count", "1"}}), "Mam 1 jabłko."); EXPECT_EQ(engine.localizeEntry("pl_PL", TXT_KEY_I_HAVE_APPLES, {{"count", "2"}}), "Mam 2 jabłka."); EXPECT_EQ(engine.localizeEntry("pl_PL", TXT_KEY_I_HAVE_APPLES, {{"count", "5"}}), "Mam 5 jabłek."); EXPECT_EQ(engine.localizeEntry("pl", TXT_KEY_I_HAVE_APPLES, {{"count", "5"}}), "Mam 5 jabłek."); EXPECT_EQ(engine.localizeEntry("pl_XX", TXT_KEY_I_HAVE_APPLES, {{"count", "5"}}), "Mam 5 jabłek."); EXPECT_EQ(engine.localizeEntry("en_XX", TXT_KEY_I_HAVE_APPLES, {{"count", "2"}}), "I have 2 apples."); EXPECT_EQ(engine.localizeEntry("es_YY", TXT_KEY_FALLBACK, {}), "I don't speak spanish here either"); EXPECT_EQ(engine.localizeEntry("es_XX", TXT_KEY_FALLBACK, {}), "I don't speak spanish"); EXPECT_EQ(engine.localizeEntry("pl_PL", TXT_KEY_FALLBACK, {}), "Fallback string!"); EXPECT_EQ(engine.localizeEntry("am_AM", TXT_KEY_FALLBACK, {}), "Amongus!"); // test weird translations engine.registerEntry("ts", TXT_KEY_HELLO, "count}"); EXPECT_EQ(engine.localizeEntry("ts", TXT_KEY_HELLO, {{"count", "1"}}), "count}"); engine.registerEntry("ts", TXT_KEY_HELLO, "{count"); EXPECT_EQ(engine.localizeEntry("ts", TXT_KEY_HELLO, {{"count", "1"}}), "{count"); EXPECT_EQ(engine.localizeEntry("ts", 42069 /* invalid key */, {{"count", "1"}}), ""); EXPECT_EQ(engine.localizeEntry("ts_TST", TXT_KEY_FALLBACK, {{"var1", "hi"}, {"var2", "!"}}), "Hello hi world !"); }hyprwm-hyprutils-bc9803c/tests/i18n/Locale.cpp000066400000000000000000000005021511106400200213250ustar00rootroot00000000000000#include #include using namespace Hyprutils::I18n; TEST(I18n, Locale) { EXPECT_EQ(extractLocale("pl_PL.UTF-8"), "pl_PL"); EXPECT_EQ(extractLocale("POSIX"), "en_US"); EXPECT_EQ(extractLocale("*"), "en_US"); EXPECT_EQ(extractLocale("LC_CTYPE=pl_PL.UTF-8"), "pl_PL"); }hyprwm-hyprutils-bc9803c/tests/math/000077500000000000000000000000001511106400200175775ustar00rootroot00000000000000hyprwm-hyprutils-bc9803c/tests/math/Box.cpp000066400000000000000000000040701511106400200210340ustar00rootroot00000000000000#include #include using namespace Hyprutils::Math; TEST(Math, box) { // Test default constructor and accessors { CBox box1; EXPECT_EQ(box1.x, 0); EXPECT_EQ(box1.y, 0); EXPECT_EQ(box1.width, 0); EXPECT_EQ(box1.height, 0); // Test parameterized constructor and accessors CBox box2(10, 20, 30, 40); EXPECT_EQ(box2.x, 10); EXPECT_EQ(box2.y, 20); EXPECT_EQ(box2.width, 30); EXPECT_EQ(box2.height, 40); // Test setters and getters box2.translate(Vector2D(5, -5)); EXPECT_EQ(box2.pos(), Vector2D(15, 15)); } //Test Scaling and Transformation { CBox box(10, 10, 20, 30); // Test scaling box.scale(2.0); EXPECT_EQ(box.size(), Vector2D(40, 60)); EXPECT_EQ(box.pos(), Vector2D(20, 20)); // Test scaling from center box.scaleFromCenter(0.5); EXPECT_EQ(box.size(), Vector2D(20, 30)); EXPECT_EQ(box.pos(), Vector2D(30, 35)); // Test transformation box.transform(HYPRUTILS_TRANSFORM_90, 100, 200); EXPECT_EQ(box.pos(), Vector2D(135, 30)); EXPECT_EQ(box.size(), Vector2D(30, 20)); // Test Intersection and Extents } { CBox box1(0, 0, 100, 100); CBox box2(50, 50, 100, 100); CBox intersection = box1.intersection(box2); EXPECT_EQ(intersection.pos(), Vector2D(50, 50)); EXPECT_EQ(intersection.size(), Vector2D(50, 50)); SBoxExtents extents = box1.extentsFrom(box2); EXPECT_EQ(extents.topLeft, Vector2D(50, 50)); EXPECT_EQ(extents.bottomRight, Vector2D(-50, -50)); } // Test Boundary Conditions and Special Cases { CBox box(0, 0, 50, 50); EXPECT_EQ(box.empty(), false); EXPECT_EQ(box.containsPoint(Vector2D(25, 25)), true); EXPECT_EQ(box.containsPoint(Vector2D(60, 60)), false); EXPECT_EQ(box.overlaps(CBox(25, 25, 50, 50)), true); EXPECT_EQ(box.inside(CBox(0, 0, 100, 100)), false); } }hyprwm-hyprutils-bc9803c/tests/math/Mat3x3.cpp000066400000000000000000000026161511106400200213670ustar00rootroot00000000000000#include #include #include using namespace Hyprutils::Math; TEST(Math, mat3x3) { Mat3x3 jeremy = Mat3x3::outputProjection({1920, 1080}, HYPRUTILS_TRANSFORM_FLIPPED_90); 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{0, 0.46296296, 0, 0.3125, 0, 0, 19.84375, 36.055557, 1}; // we need to do this to avoid precision errors on 32-bit archs EXPECT_EQ(std::abs(expected.getMatrix().at(0) - matrixBox.getMatrix().at(0)) < 0.1, true); EXPECT_EQ(std::abs(expected.getMatrix().at(1) - matrixBox.getMatrix().at(1)) < 0.1, true); EXPECT_EQ(std::abs(expected.getMatrix().at(2) - matrixBox.getMatrix().at(2)) < 0.1, true); EXPECT_EQ(std::abs(expected.getMatrix().at(3) - matrixBox.getMatrix().at(3)) < 0.1, true); EXPECT_EQ(std::abs(expected.getMatrix().at(4) - matrixBox.getMatrix().at(4)) < 0.1, true); EXPECT_EQ(std::abs(expected.getMatrix().at(5) - matrixBox.getMatrix().at(5)) < 0.1, true); EXPECT_EQ(std::abs(expected.getMatrix().at(6) - matrixBox.getMatrix().at(6)) < 0.1, true); EXPECT_EQ(std::abs(expected.getMatrix().at(7) - matrixBox.getMatrix().at(7)) < 0.1, true); EXPECT_EQ(std::abs(expected.getMatrix().at(8) - matrixBox.getMatrix().at(8)) < 0.1, true); }hyprwm-hyprutils-bc9803c/tests/math/Region.cpp000066400000000000000000000006751511106400200215360ustar00rootroot00000000000000#include #include using namespace Hyprutils::Math; TEST(Math, region) { CRegion rg(CBox{{20, 20}, {40, 40}}); auto extents = rg.getExtents(); EXPECT_EQ(extents.pos(), Vector2D(20, 20)); EXPECT_EQ(extents.size(), Vector2D(40, 40)); rg.scale(2); extents = rg.getExtents(); EXPECT_EQ(extents.pos(), Vector2D(40, 40)); EXPECT_EQ(extents.size(), Vector2D(80, 80)); }hyprwm-hyprutils-bc9803c/tests/math/Vector2D.cpp000066400000000000000000000017451511106400200217420ustar00rootroot00000000000000#include #include using namespace Hyprutils::Math; TEST(Math, vector2d) { Vector2D original(30, 40); Vector2D monitorSize(100, 200); EXPECT_EQ(original.transform(HYPRUTILS_TRANSFORM_NORMAL, monitorSize), Vector2D(30, 40)); EXPECT_EQ(original.transform(HYPRUTILS_TRANSFORM_90, monitorSize), Vector2D(40, 200 - 30)); EXPECT_EQ(original.transform(HYPRUTILS_TRANSFORM_180, monitorSize), Vector2D(100 - 30, 200 - 40)); EXPECT_EQ(original.transform(HYPRUTILS_TRANSFORM_270, monitorSize), Vector2D(100 - 40, 30)); EXPECT_EQ(original.transform(HYPRUTILS_TRANSFORM_FLIPPED, monitorSize), Vector2D(100 - 30, 40)); EXPECT_EQ(original.transform(HYPRUTILS_TRANSFORM_FLIPPED_90, monitorSize), Vector2D(40, 30)); EXPECT_EQ(original.transform(HYPRUTILS_TRANSFORM_FLIPPED_180, monitorSize), Vector2D(30, 200 - 40)); EXPECT_EQ(original.transform(HYPRUTILS_TRANSFORM_FLIPPED_270, monitorSize), Vector2D(100 - 40, 200 - 30)); }hyprwm-hyprutils-bc9803c/tests/memory/000077500000000000000000000000001511106400200201565ustar00rootroot00000000000000hyprwm-hyprutils-bc9803c/tests/memory/Memory.cpp000066400000000000000000000117171511106400200221410ustar00rootroot00000000000000 #include #include #include #include #include #include #include using namespace Hyprutils::Memory; #define SP CSharedPointer #define WP CWeakPointer #define UP CUniquePointer #define ASP CAtomicSharedPointer #define AWP CAtomicWeakPointer #define NTHREADS 8 #define ITERATIONS 10000 static void testAtomicImpl() { { // Using makeShared here could lead to invalid refcounts. ASP shared = makeAtomicShared(0); std::vector 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 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_EQ(shared, false); } { ASP shared = makeAtomicShared(0); AWP weak = shared; std::vector 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_EQ(shared.strongRef(), 0); EXPECT_EQ(weak.valid(), false); auto shared2 = weak.lock(); EXPECT_EQ(shared, false); EXPECT_EQ(shared2.get(), nullptr); EXPECT_EQ(shared.strongRef(), 0); EXPECT_EQ(weak.valid(), false); EXPECT_EQ(weak.expired(), true); } { // This tests recursive deletion. When foo will be deleted, bar will be deleted within the foo dtor. class CFoo { public: AWP bar; }; ASP foo = makeAtomicShared(); foo->bar = foo; } { // This tests destroying the data when storing the base class of a type class ITest { public: size_t num = 0; ITest() : num(1234) {}; }; class CA : public ITest { public: size_t num2 = 0; CA() : ITest(), num2(4321) {}; }; class CB : public ITest { public: int num2 = 0; CB() : ITest(), num2(-1) {}; }; ASP genericAtomic = nullptr; SP genericNormal = nullptr; { auto derivedAtomic = makeAtomicShared(); auto derivedNormal = makeShared(); genericAtomic = derivedAtomic; genericNormal = derivedNormal; } EXPECT_EQ(!!genericAtomic, true); EXPECT_EQ(!!genericNormal, true); } } TEST(Memory, memory) { SP intPtr = makeShared(10); SP intPtr2 = makeShared(-1337); UP intUnique = makeUnique(420); EXPECT_EQ(*intPtr, 10); EXPECT_EQ(intPtr.strongRef(), 1); EXPECT_EQ(*intUnique, 420); WP weak = intPtr; WP weakUnique = intUnique; EXPECT_EQ(*intPtr, 10); EXPECT_EQ(intPtr.strongRef(), 1); EXPECT_EQ(*weak, 10); EXPECT_EQ(weak.expired(), false); EXPECT_EQ(!!weak.lock(), true); EXPECT_EQ(*weakUnique, 420); EXPECT_EQ(weakUnique.expired(), false); EXPECT_EQ(intUnique.impl_->wref(), 1); SP sharedFromUnique = weakUnique.lock(); EXPECT_EQ(sharedFromUnique, nullptr); std::vector> sps; sps.push_back(intPtr); sps.emplace_back(intPtr); sps.push_back(intPtr2); sps.emplace_back(intPtr2); std::erase_if(sps, [intPtr](const auto& e) { return e == intPtr; }); intPtr.reset(); intUnique.reset(); EXPECT_EQ(weak.impl_->ref(), 0); EXPECT_EQ(weakUnique.impl_->ref(), 0); EXPECT_EQ(weakUnique.impl_->wref(), 1); EXPECT_EQ(intPtr2.strongRef(), 3); EXPECT_EQ(weak.expired(), true); EXPECT_EQ(weakUnique.expired(), true); auto intPtr2AsUint = reinterpretPointerCast(intPtr2); EXPECT_EQ(intPtr2.strongRef(), 4); EXPECT_EQ(intPtr2AsUint.strongRef(), 4); EXPECT_EQ(*intPtr2AsUint > 0, true); EXPECT_EQ(*intPtr2AsUint, (unsigned int)(int)-1337); *intPtr2AsUint = 10; EXPECT_EQ(*intPtr2AsUint, 10); EXPECT_EQ(*intPtr2, 10); testAtomicImpl(); } hyprwm-hyprutils-bc9803c/tests/os/000077500000000000000000000000001511106400200172675ustar00rootroot00000000000000hyprwm-hyprutils-bc9803c/tests/os/Fd.cpp000066400000000000000000000022411511106400200203230ustar00rootroot00000000000000#include #include #include using namespace Hyprutils::OS; TEST(OS, fd) { std::string name = "/test_filedescriptors"; CFileDescriptor fd(shm_open(name.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600)); EXPECT_EQ(fd.isValid(), true); EXPECT_EQ(fd.isReadable(), true); int flags = fd.getFlags(); EXPECT_EQ(fd.getFlags(), FD_CLOEXEC); flags &= ~FD_CLOEXEC; fd.setFlags(flags); EXPECT_EQ(fd.getFlags(), !FD_CLOEXEC); CFileDescriptor fd2 = fd.duplicate(); EXPECT_EQ(fd.isValid(), true); EXPECT_EQ(fd.isReadable(), true); EXPECT_EQ(fd2.isValid(), true); EXPECT_EQ(fd2.isReadable(), true); CFileDescriptor fd3(fd2.take()); EXPECT_EQ(fd.isValid(), true); EXPECT_EQ(fd.isReadable(), true); EXPECT_EQ(fd2.isValid(), false); EXPECT_EQ(fd2.isReadable(), false); // .duplicate default flags is FD_CLOEXEC EXPECT_EQ(fd3.getFlags(), FD_CLOEXEC); fd.reset(); fd2.reset(); fd3.reset(); EXPECT_EQ(fd.isReadable(), false); EXPECT_EQ(fd2.isReadable(), false); EXPECT_EQ(fd3.isReadable(), false); shm_unlink(name.c_str()); }hyprwm-hyprutils-bc9803c/tests/os/Process.cpp000066400000000000000000000015551511106400200214170ustar00rootroot00000000000000#include #include using namespace Hyprutils::OS; TEST(OS, process) { CProcess process("sh", {"-c", "echo \"Hello $WORLD!\""}); process.addEnv("WORLD", "World"); EXPECT_EQ(process.runAsync(), true); EXPECT_EQ(process.runSync(), true); EXPECT_EQ(process.stdOut(), std::string{"Hello World!\n"}); EXPECT_EQ(process.stdErr(), std::string{""}); EXPECT_EQ(process.exitCode(), 0); CProcess process2("sh", {"-c", "while true; do sleep 1; done;"}); EXPECT_EQ(process2.runAsync(), true); EXPECT_EQ(getpgid(process2.pid()) >= 0, true); kill(process2.pid(), SIGKILL); CProcess process3("sh", {"-c", "cat /geryueruggbuergheruger/reugiheruygyuerghuryeghyer/eruihgyuerguyerghyuerghuyergerguyer/NON_EXISTENT"}); EXPECT_EQ(process3.runSync(), true); EXPECT_EQ(process3.exitCode(), 1); }hyprwm-hyprutils-bc9803c/tests/signal/000077500000000000000000000000001511106400200201235ustar00rootroot00000000000000hyprwm-hyprutils-bc9803c/tests/signal/Signal.cpp000066400000000000000000000220731511106400200220500ustar00rootroot00000000000000 #include #include #include #include #include #include using namespace Hyprutils::Signal; using namespace Hyprutils::Memory; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" // static void legacy() { CSignal signal; int data = 0; auto listener = signal.registerListener([&]([[maybe_unused]] std::any d) { data = 1; }); signal.emit(); EXPECT_EQ(data, 1); data = 0; listener.reset(); signal.emit(); EXPECT_EQ(data, 0); } static void legacyListenerEmit() { int data = 0; CSignal signal; auto listener = signal.registerListener([&](std::any d) { data = std::any_cast(d); }); listener->emit(1); // not a typo EXPECT_EQ(data, 1); } static void legacyListeners() { int data = 0; CSignalT<> signal0; CSignalT signal1; auto listener0 = signal0.registerListener([&](std::any d) { data += 1; }); auto listener1 = signal1.registerListener([&](std::any d) { data += std::any_cast(d); }); signal0.registerStaticListener([&](void* o, std::any d) { data += 10; }, nullptr); signal1.registerStaticListener([&](void* o, std::any d) { data += std::any_cast(d) * 10; }, nullptr); signal0.emit(); signal1.emit(2); EXPECT_EQ(data, 33); } #pragma GCC diagnostic pop // static void empty() { int data = 0; CSignalT<> signal; auto listener = signal.listen([&] { data = 1; }); signal.emit(); EXPECT_EQ(data, 1); data = 0; listener.reset(); signal.emit(); EXPECT_EQ(data, 0); } static void typed() { int data = 0; CSignalT signal; auto listener = signal.listen([&](int newData) { data = newData; }); signal.emit(1); EXPECT_EQ(data, 1); } static void ignoreParams() { int data = 0; CSignalT signal; auto listener = signal.listen([&] { data += 1; }); signal.listenStatic([&] { data += 1; }); signal.emit(2); EXPECT_EQ(data, 2); } static void typedMany() { int data1 = 0; int data2 = 0; int data3 = 0; CSignalT signal; auto listener = signal.listen([&](int d1, int d2, int d3) { data1 = d1; data2 = d2; data3 = d3; }); signal.emit(1, 2, 3); EXPECT_EQ(data1, 1); EXPECT_EQ(data2, 2); EXPECT_EQ(data3, 3); } static void ref() { int count = 0; int data = 0; CSignalT signal; auto l1 = signal.listen([&](int& v) { v += 1; }); auto l2 = signal.listen([&](int v) { count += v; }); signal.emit(data); CSignalT constSignal; auto l3 = constSignal.listen([&](const int& v) { count += v; }); auto l4 = constSignal.listen([&](int v) { count += v; }); constSignal.emit(data); EXPECT_EQ(data, 1); EXPECT_EQ(count, 3); } static void refMany() { int count = 0; int data1 = 0; int data2 = 10; CSignalT 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_EQ(data1, 1); EXPECT_EQ(count, 11); } static void autoRefTypes() { 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 signal; auto listener = signal.listen([](const CCopyCounter& counter) {}); signal.emit(CCopyCounter(createCount, destroyCount)); EXPECT_EQ(createCount, 1); EXPECT_EQ(destroyCount, 1); } static void forward() { int count = 0; CSignalT sig; CSignalT 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_EQ(count, 3); } static void listenerAdded() { int count = 0; CSignalT<> signal; CHyprSignalListener secondListener; auto listener = signal.listen([&] { count += 1; if (!secondListener) secondListener = signal.listen([&] { count += 1; }); }); signal.emit(); EXPECT_EQ(count, 1); // second should NOT be invoked as it was registed during emit signal.emit(); EXPECT_EQ(count, 3); // second should be invoked } static void lastListenerSwapped() { 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_EQ(count, 0); // neither the removed nor added listeners should fire signal.emit(); EXPECT_EQ(count, 2); // only the new listener should fire } static void signalDestroyed() { int count = 0; auto signal = std::make_unique>(); // 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_EQ(count, 2); // all listeners should fire regardless of signal deletion } // purely an asan test static void signalDestroyedBeforeListener() { CHyprSignalListener listener1; CHyprSignalListener listener2; CSignalT<> signal; listener1 = signal.listen([] {}); listener2 = signal.listen([] {}); } static void signalDestroyedWithAddedListener() { int count = 0; auto signal = std::make_unique>(); CHyprSignalListener shouldNotRun; auto listener = signal->listen([&] { shouldNotRun = signal->listen([&] { count += 2; }); signal.reset(); }); signal->emit(); EXPECT_EQ(count, 0); } static void signalDestroyedWithRemovedAndAddedListener() { int count = 0; auto signal = std::make_unique>(); 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_EQ(count, 0); } static void staticListener() { int data = 0; CSignalT signal; signal.listenStatic([&](int newData) { data = newData; }); signal.emit(1); EXPECT_EQ(data, 1); } static void staticListenerDestroy() { int count = 0; auto signal = makeShared>(); 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_EQ(count, 2); } // purely an asan test static void listenerDestroysSelf() { CSignalT<> signal; CHyprSignalListener listener; listener = signal.listen([&] { listener.reset(); }); // the static signal case is taken care of above signal.emit(); } TEST(Signal, signal) { legacy(); legacyListenerEmit(); legacyListeners(); empty(); typed(); ignoreParams(); typedMany(); ref(); refMany(); autoRefTypes(); forward(); listenerAdded(); lastListenerSwapped(); signalDestroyed(); signalDestroyedBeforeListener(); signalDestroyedWithAddedListener(); signalDestroyedWithRemovedAndAddedListener(); staticListener(); staticListenerDestroy(); signalDestroyed(); listenerDestroysSelf(); }hyprwm-hyprutils-bc9803c/tests/string/000077500000000000000000000000001511106400200201545ustar00rootroot00000000000000hyprwm-hyprutils-bc9803c/tests/string/ConstVarList.cpp000066400000000000000000000006151511106400200232550ustar00rootroot00000000000000#include #include using namespace Hyprutils::String; TEST(String, constvarlist) { CConstVarList listConst("hello world!", 0, 's', true); EXPECT_EQ(listConst[0], "hello"); EXPECT_EQ(listConst[1], "world!"); CConstVarList listConst2("0 set", 2, ' '); EXPECT_EQ(listConst2[0], "0"); EXPECT_EQ(listConst2[1], "set"); }hyprwm-hyprutils-bc9803c/tests/string/String.cpp000066400000000000000000000032731511106400200221330ustar00rootroot00000000000000#include #include using namespace Hyprutils::String; TEST(String, string) { EXPECT_EQ(trim(" a "), "a"); EXPECT_EQ(trim(" a a "), "a a"); EXPECT_EQ(trim("a"), "a"); EXPECT_EQ(trim(" "), ""); EXPECT_EQ(isNumber("99214123434"), true); EXPECT_EQ(isNumber("-35252345234"), true); EXPECT_EQ(isNumber("---3423--432"), false); EXPECT_EQ(isNumber("s---3423--432"), false); EXPECT_EQ(isNumber("---3423--432s"), false); EXPECT_EQ(isNumber("1s"), false); EXPECT_EQ(isNumber(""), false); EXPECT_EQ(isNumber("-"), false); EXPECT_EQ(isNumber("--0"), false); EXPECT_EQ(isNumber("abc"), false); EXPECT_EQ(isNumber("0.0", true), true); EXPECT_EQ(isNumber("0.2", true), true); EXPECT_EQ(isNumber("0.", true), false); EXPECT_EQ(isNumber(".0", true), false); EXPECT_EQ(isNumber("", true), false); EXPECT_EQ(isNumber("vvss", true), false); EXPECT_EQ(isNumber("0.9999s", true), false); EXPECT_EQ(isNumber("s0.9999", true), false); EXPECT_EQ(isNumber("-1.0", true), true); EXPECT_EQ(isNumber("-1..0", true), false); EXPECT_EQ(isNumber("-10.0000000001", true), true); EXPECT_EQ(truthy("frgeujgeruibger"), false); EXPECT_EQ(truthy("false"), false); EXPECT_EQ(truthy("0"), false); EXPECT_EQ(truthy("yees"), false); EXPECT_EQ(truthy("naa"), false); EXPECT_EQ(truthy("-1"), false); EXPECT_EQ(truthy("true"), true); EXPECT_EQ(truthy("true eeee ee"), true); EXPECT_EQ(truthy("yesss"), true); EXPECT_EQ(truthy("1"), true); EXPECT_EQ(truthy("on"), true); EXPECT_EQ(truthy("onn"), true); }hyprwm-hyprutils-bc9803c/tests/string/VarList.cpp000066400000000000000000000003671511106400200222520ustar00rootroot00000000000000#include #include using namespace Hyprutils::String; TEST(String, varlist) { CVarList list("hello world!", 0, 's', true); EXPECT_EQ(list[0], "hello"); EXPECT_EQ(list[1], "world!"); }hyprwm-hyprutils-bc9803c/tests/string/VarList2.cpp000066400000000000000000000041611511106400200223300ustar00rootroot00000000000000#include #include using namespace Hyprutils::String; TEST(String, varlist2) { CVarList2 varList2("0 set", 2, ' '); EXPECT_EQ(varList2[0], "0"); EXPECT_EQ(varList2[1], "set"); varList2.append("Hello"); EXPECT_EQ(varList2[1], "set"); EXPECT_EQ(varList2[2], "Hello"); EXPECT_EQ(varList2[3], ""); EXPECT_EQ(varList2.contains("set"), true); EXPECT_EQ(varList2.contains("sett"), false); EXPECT_EQ(varList2.contains(""), false); EXPECT_EQ(varList2.size(), 3); CVarList2 varList2B("hello, world\\, ok?", 0, ',', true, true); EXPECT_EQ(varList2B[0], "hello"); EXPECT_EQ(varList2B[1], "world, ok?"); CVarList2 varList2C("hello, , ok?", 0, ',', true, true); EXPECT_EQ(varList2C[0], "hello"); EXPECT_EQ(varList2C[1], "ok?"); CVarList2 varList2D("\\\\, , ok?", 0, ',', true, true); EXPECT_EQ(varList2D[0], "\\"); EXPECT_EQ(varList2D[1], "ok?"); CVarList2 varList2E("\\, , ok?", 0, ',', true, true); EXPECT_EQ(varList2E[0], ","); EXPECT_EQ(varList2E[1], "ok?"); CVarList2 varList2F("Hello, world\\\\, ok?", 0, ',', true, true); EXPECT_EQ(varList2F[0], "Hello"); EXPECT_EQ(varList2F[1], "world\\"); EXPECT_EQ(varList2F[2], "ok?"); CVarList2 varList2G("Hello,\\, ok?", 0, ',', true, true); EXPECT_EQ(varList2G[0], "Hello"); EXPECT_EQ(varList2G[1], ", ok?"); CVarList2 varList2H("Hello,\\\\, ok?", 0, ',', true, true); EXPECT_EQ(varList2H[0], "Hello"); EXPECT_EQ(varList2H[1], "\\"); EXPECT_EQ(varList2H[2], "ok?"); CVarList2 varList2I("Hello,\\, ok?", 0, ',', true, false); EXPECT_EQ(varList2I[0], "Hello"); EXPECT_EQ(varList2I[1], "\\"); EXPECT_EQ(varList2I[2], "ok?"); CVarList2 varList2J("", 0, ',', true, false); EXPECT_EQ(varList2J[0], ""); CVarList2 varList2K(",\\, ok?", 0, ',', true); EXPECT_EQ(varList2K[0], ", ok?"); CVarList2 varList2L("Hello, world", 0, ',', true); EXPECT_EQ(varList2L.join(" "), "Hello world"); EXPECT_EQ(varList2L.join(" ", 0, 1000), "Hello world"); EXPECT_EQ(varList2L.join(" ", 0, 1), "Hello"); }