Update upstream source from tag 'upstream/0.5.1'
Update to upstream version '0.5.1'
with Debian dir 89b2508bdd
This commit is contained in:
commit
a2ebf2d72c
|
|
@ -0,0 +1,101 @@
|
|||
WarningsAsErrors: '*'
|
||||
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
|
||||
|
|
@ -14,7 +14,6 @@ jobs:
|
|||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: cachix/install-nix-action@v26
|
||||
- uses: DeterminateSystems/magic-nix-cache-action@main
|
||||
|
||||
# not needed (yet)
|
||||
# - uses: cachix/cachix-action@v12
|
||||
|
|
|
|||
|
|
@ -19,6 +19,16 @@ 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")
|
||||
|
|
@ -40,7 +50,7 @@ target_include_directories(
|
|||
PUBLIC "./include"
|
||||
PRIVATE "./src")
|
||||
set_target_properties(hyprutils PROPERTIES VERSION ${hyprutils_VERSION}
|
||||
SOVERSION 1)
|
||||
SOVERSION 4)
|
||||
target_link_libraries(hyprutils PkgConfig::deps)
|
||||
|
||||
# tests
|
||||
|
|
@ -78,6 +88,30 @@ add_test(
|
|||
COMMAND hyprutils_math "math")
|
||||
add_dependencies(tests hyprutils_math)
|
||||
|
||||
add_executable(hyprutils_os "tests/os.cpp")
|
||||
target_link_libraries(hyprutils_os PRIVATE hyprutils PkgConfig::deps)
|
||||
add_test(
|
||||
NAME "OS"
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
|
||||
COMMAND hyprutils_os "os")
|
||||
add_dependencies(tests hyprutils_os)
|
||||
|
||||
add_executable(hyprutils_filedescriptor "tests/filedescriptor.cpp")
|
||||
target_link_libraries(hyprutils_filedescriptor PRIVATE hyprutils PkgConfig::deps)
|
||||
add_test(
|
||||
NAME "Filedescriptor"
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
|
||||
COMMAND hyprutils_filedescriptor "filedescriptor")
|
||||
add_dependencies(tests hyprutils_filedescriptor)
|
||||
|
||||
add_executable(hyprutils_animation "tests/animation.cpp")
|
||||
target_link_libraries(hyprutils_animation PRIVATE hyprutils PkgConfig::deps)
|
||||
add_test(
|
||||
NAME "Animation"
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
|
||||
COMMAND hyprutils_animation "utils")
|
||||
add_dependencies(tests hyprutils_animation)
|
||||
|
||||
# Installation
|
||||
install(TARGETS hyprutils)
|
||||
install(DIRECTORY "include/hyprutils" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
|
|
|
|||
|
|
@ -12,6 +12,6 @@ Hyprutils depends on the ABI stability of the stdlib implementation of your comp
|
|||
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`
|
||||
cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
|
||||
sudo cmake --install build
|
||||
```
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@
|
|||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1721138476,
|
||||
"narHash": "sha256-+W5eZOhhemLQxelojLxETfbFbc19NWawsXBlapYpqIA=",
|
||||
"lastModified": 1734119587,
|
||||
"narHash": "sha256-AKU6qqskl0yf2+JdRdD0cfxX4b9x3KKV5RqA6wijmPM=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "ad0b5eed1b6031efaed382844806550c3dcb4206",
|
||||
"rev": "3566ab7246670a43abd2ffa913cc62dad9cdf7d5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
default = self.overlays.hyprutils;
|
||||
hyprutils = final: prev: {
|
||||
hyprutils = final.callPackage ./nix/default.nix {
|
||||
stdenv = final.gcc13Stdenv;
|
||||
stdenv = final.gcc14Stdenv;
|
||||
version = version + "+date=" + (mkDate (self.lastModifiedDate or "19700101")) + "_" + (self.shortRev or "dirty");
|
||||
};
|
||||
hyprutils-with-tests = final.hyprutils.override {doCheck = true;};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,226 @@
|
|||
#pragma once
|
||||
|
||||
#include "AnimationConfig.hpp"
|
||||
#include "../memory/WeakPtr.hpp"
|
||||
#include "../memory/SharedPtr.hpp"
|
||||
#include "../signal/Signal.hpp"
|
||||
#include "AnimationManager.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <chrono>
|
||||
|
||||
namespace Hyprutils {
|
||||
namespace Animation {
|
||||
|
||||
/* A base class for animated variables. */
|
||||
class CBaseAnimatedVariable {
|
||||
public:
|
||||
using CallbackFun = std::function<void(Memory::CWeakPointer<CBaseAnimatedVariable> thisptr)>;
|
||||
|
||||
CBaseAnimatedVariable() {
|
||||
; // m_bDummy = true;
|
||||
};
|
||||
|
||||
void create(CAnimationManager*, int, Memory::CSharedPointer<CBaseAnimatedVariable>);
|
||||
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<SAnimationPropertyConfig> pConfig) {
|
||||
m_pConfig = pConfig;
|
||||
}
|
||||
|
||||
Memory::CWeakPointer<SAnimationPropertyConfig> 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<CBaseAnimatedVariable> m_pSelf;
|
||||
|
||||
Memory::CWeakPointer<CAnimationManager::SAnimationManagerSignals> m_pSignals;
|
||||
|
||||
private:
|
||||
Memory::CWeakPointer<SAnimationPropertyConfig> 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 <class ValueImpl>
|
||||
concept AnimatedType = requires(ValueImpl val) {
|
||||
requires std::is_copy_constructible_v<ValueImpl>;
|
||||
{ val == val } -> std::same_as<bool>; // 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 <AnimatedType VarType, class AnimationContext>
|
||||
class CGenericAnimatedVariable : public CBaseAnimatedVariable {
|
||||
public:
|
||||
CGenericAnimatedVariable() = default;
|
||||
|
||||
void create(const int typeInfo, CAnimationManager* pAnimationManager, Memory::CSharedPointer<CGenericAnimatedVariable<VarType, AnimationContext>> 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{};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
#pragma once
|
||||
|
||||
#include "../memory/WeakPtr.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
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<SAnimationPropertyConfig> pValues;
|
||||
Memory::CWeakPointer<SAnimationPropertyConfig> 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<SAnimationPropertyConfig> getConfig(const std::string& name) const;
|
||||
const std::unordered_map<std::string, Memory::CSharedPointer<SAnimationPropertyConfig>>& getFullConfig() const;
|
||||
|
||||
CAnimationConfigTree(const CAnimationConfigTree&) = delete;
|
||||
CAnimationConfigTree(CAnimationConfigTree&&) = delete;
|
||||
CAnimationConfigTree& operator=(const CAnimationConfigTree&) = delete;
|
||||
CAnimationConfigTree& operator=(CAnimationConfigTree&&) = delete;
|
||||
|
||||
private:
|
||||
void setAnimForChildren(Memory::CSharedPointer<SAnimationPropertyConfig> PANIM);
|
||||
std::unordered_map<std::string, Memory::CSharedPointer<SAnimationPropertyConfig>> m_mAnimationConfig;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
#pragma once
|
||||
|
||||
#include "./BezierCurve.hpp"
|
||||
#include "../math/Vector2D.hpp"
|
||||
#include "../memory/WeakPtr.hpp"
|
||||
#include "../signal/Signal.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
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<CBezierCurve> getBezier(const std::string&);
|
||||
|
||||
const std::unordered_map<std::string, Memory::CSharedPointer<CBezierCurve>>& getAllBeziers();
|
||||
|
||||
struct SAnimationManagerSignals {
|
||||
Signal::CSignal connect; // WP<CBaseAnimatedVariable>
|
||||
Signal::CSignal disconnect; // WP<CBaseAnimatedVariable>
|
||||
};
|
||||
|
||||
Memory::CWeakPointer<SAnimationManagerSignals> getSignals() const;
|
||||
|
||||
std::vector<Memory::CWeakPointer<CBaseAnimatedVariable>> m_vActiveAnimatedVariables;
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, Memory::CSharedPointer<CBezierCurve>> m_mBezierCurves;
|
||||
|
||||
bool m_bTickScheduled = false;
|
||||
|
||||
void onConnect(std::any data);
|
||||
void onDisconnect(std::any data);
|
||||
|
||||
struct SAnimVarListeners {
|
||||
Signal::CHyprSignalListener connect;
|
||||
Signal::CHyprSignalListener disconnect;
|
||||
};
|
||||
|
||||
Memory::CUniquePointer<SAnimVarListeners> m_listeners;
|
||||
Memory::CUniquePointer<SAnimationManagerSignals> m_events;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
#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<Hyprutils::Math::Vector2D, 2>& points);
|
||||
|
||||
float getYForT(float const& t) const;
|
||||
float getXForT(float const& t) const;
|
||||
float getYForPoint(float const& x) const;
|
||||
|
||||
private:
|
||||
/* this INCLUDES the 0,0 and 1,1 points. */
|
||||
std::vector<Hyprutils::Math::Vector2D> m_vPoints;
|
||||
|
||||
std::array<Hyprutils::Math::Vector2D, BAKEDPOINTS> m_aPointsBaked;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -24,7 +24,7 @@ namespace Hyprutils {
|
|||
/* create an output projection matrix */
|
||||
static Mat3x3 outputProjection(const Vector2D& size, eTransform transform);
|
||||
|
||||
/* get the matrix as an array, in a RTL TTB order. */
|
||||
/* get the matrix as an array, in a row-major order. */
|
||||
std::array<float, 9> getMatrix() const;
|
||||
|
||||
/* create a box projection matrix */
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ namespace Hyprutils {
|
|||
}
|
||||
|
||||
// absolutely ridiculous formatter spec parsing
|
||||
#define AQ_FORMAT_PARSE(specs__, type__) \
|
||||
#define AQ_FORMAT_PARSE(specs__, type__) \
|
||||
template <typename FormatContext> \
|
||||
constexpr auto parse(FormatContext& ctx) { \
|
||||
auto it = ctx.begin(); \
|
||||
|
|
@ -112,10 +112,10 @@ namespace Hyprutils {
|
|||
return it; \
|
||||
}
|
||||
|
||||
#define AQ_FORMAT_FLAG(spec__, flag__) \
|
||||
#define AQ_FORMAT_FLAG(spec__, flag__) \
|
||||
case spec__: (flag__) = true; break;
|
||||
|
||||
#define AQ_FORMAT_NUMBER(buf__) \
|
||||
#define AQ_FORMAT_NUMBER(buf__) \
|
||||
case '0': \
|
||||
case '1': \
|
||||
case '2': \
|
||||
|
|
@ -139,9 +139,9 @@ struct std::formatter<Hyprutils::Math::Vector2D, CharT> : std::formatter<CharT>
|
|||
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)
|
||||
AQ_FORMAT_FLAG('X', formatX) //
|
||||
AQ_FORMAT_NUMBER(precision),
|
||||
Hyprutils::Math::Vector2D)
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(const Hyprutils::Math::Vector2D& vec, FormatContext& ctx) const {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,119 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace Hyprutils {
|
||||
namespace Memory {
|
||||
namespace Impl_ {
|
||||
class impl_base {
|
||||
public:
|
||||
virtual ~impl_base() {};
|
||||
|
||||
virtual void inc() noexcept = 0;
|
||||
virtual void dec() noexcept = 0;
|
||||
virtual void incWeak() noexcept = 0;
|
||||
virtual void decWeak() noexcept = 0;
|
||||
virtual unsigned int ref() noexcept = 0;
|
||||
virtual unsigned int wref() noexcept = 0;
|
||||
virtual void destroy() noexcept = 0;
|
||||
virtual bool destroying() noexcept = 0;
|
||||
virtual bool dataNonNull() noexcept = 0;
|
||||
virtual bool lockable() noexcept = 0;
|
||||
virtual void* getData() noexcept = 0;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class impl : public impl_base {
|
||||
public:
|
||||
impl(T* data, bool lock = true) noexcept : _lockable(lock), _data(data) {
|
||||
;
|
||||
}
|
||||
|
||||
/* strong refcount */
|
||||
unsigned int _ref = 0;
|
||||
/* weak refcount */
|
||||
unsigned int _weak = 0;
|
||||
/* if this is lockable (shared) */
|
||||
bool _lockable = true;
|
||||
|
||||
T* _data = nullptr;
|
||||
|
||||
friend void swap(impl*& a, impl*& b) {
|
||||
impl* tmp = a;
|
||||
a = b;
|
||||
b = tmp;
|
||||
}
|
||||
|
||||
/* if the destructor was called,
|
||||
creating shared_ptrs is no longer valid */
|
||||
bool _destroying = false;
|
||||
|
||||
void _destroy() {
|
||||
if (!_data || _destroying)
|
||||
return;
|
||||
|
||||
// first, we destroy the data, but keep the pointer.
|
||||
// this way, weak pointers will still be able to
|
||||
// reference and use, but no longer create shared ones.
|
||||
_destroying = true;
|
||||
__deleter(_data);
|
||||
// now, we can reset the data and call it a day.
|
||||
_data = nullptr;
|
||||
_destroying = false;
|
||||
}
|
||||
|
||||
std::default_delete<T> __deleter{};
|
||||
|
||||
//
|
||||
virtual void inc() noexcept {
|
||||
_ref++;
|
||||
}
|
||||
|
||||
virtual void dec() noexcept {
|
||||
_ref--;
|
||||
}
|
||||
|
||||
virtual void incWeak() noexcept {
|
||||
_weak++;
|
||||
}
|
||||
|
||||
virtual void decWeak() noexcept {
|
||||
_weak--;
|
||||
}
|
||||
|
||||
virtual unsigned int ref() noexcept {
|
||||
return _ref;
|
||||
}
|
||||
|
||||
virtual unsigned int wref() noexcept {
|
||||
return _weak;
|
||||
}
|
||||
|
||||
virtual void destroy() noexcept {
|
||||
_destroy();
|
||||
}
|
||||
|
||||
virtual bool destroying() noexcept {
|
||||
return _destroying;
|
||||
}
|
||||
|
||||
virtual bool lockable() noexcept {
|
||||
return _lockable;
|
||||
}
|
||||
|
||||
virtual bool dataNonNull() noexcept {
|
||||
return _data != nullptr;
|
||||
}
|
||||
|
||||
virtual void* getData() noexcept {
|
||||
return _data;
|
||||
}
|
||||
|
||||
virtual ~impl() {
|
||||
destroy();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,8 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include "ImplBase.hpp"
|
||||
|
||||
/*
|
||||
This is a custom impl of std::shared_ptr.
|
||||
|
|
@ -17,109 +15,6 @@
|
|||
|
||||
namespace Hyprutils {
|
||||
namespace Memory {
|
||||
namespace CSharedPointer_ {
|
||||
class impl_base {
|
||||
public:
|
||||
virtual ~impl_base() {};
|
||||
|
||||
virtual void inc() noexcept = 0;
|
||||
virtual void dec() noexcept = 0;
|
||||
virtual void incWeak() noexcept = 0;
|
||||
virtual void decWeak() noexcept = 0;
|
||||
virtual unsigned int ref() noexcept = 0;
|
||||
virtual unsigned int wref() noexcept = 0;
|
||||
virtual void destroy() noexcept = 0;
|
||||
virtual bool destroying() noexcept = 0;
|
||||
virtual bool dataNonNull() noexcept = 0;
|
||||
virtual void* getData() noexcept = 0;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class impl : public impl_base {
|
||||
public:
|
||||
impl(T* data) noexcept : _data(data) {
|
||||
;
|
||||
}
|
||||
|
||||
/* strong refcount */
|
||||
unsigned int _ref = 0;
|
||||
/* weak refcount */
|
||||
unsigned int _weak = 0;
|
||||
|
||||
T* _data = nullptr;
|
||||
|
||||
friend void swap(impl*& a, impl*& b) {
|
||||
impl* tmp = a;
|
||||
a = b;
|
||||
b = tmp;
|
||||
}
|
||||
|
||||
/* if the destructor was called,
|
||||
creating shared_ptrs is no longer valid */
|
||||
bool _destroying = false;
|
||||
|
||||
void _destroy() {
|
||||
if (!_data || _destroying)
|
||||
return;
|
||||
|
||||
// first, we destroy the data, but keep the pointer.
|
||||
// this way, weak pointers will still be able to
|
||||
// reference and use, but no longer create shared ones.
|
||||
_destroying = true;
|
||||
__deleter(_data);
|
||||
// now, we can reset the data and call it a day.
|
||||
_data = nullptr;
|
||||
_destroying = false;
|
||||
}
|
||||
|
||||
std::default_delete<T> __deleter{};
|
||||
|
||||
//
|
||||
virtual void inc() noexcept {
|
||||
_ref++;
|
||||
}
|
||||
|
||||
virtual void dec() noexcept {
|
||||
_ref--;
|
||||
}
|
||||
|
||||
virtual void incWeak() noexcept {
|
||||
_weak++;
|
||||
}
|
||||
|
||||
virtual void decWeak() noexcept {
|
||||
_weak--;
|
||||
}
|
||||
|
||||
virtual unsigned int ref() noexcept {
|
||||
return _ref;
|
||||
}
|
||||
|
||||
virtual unsigned int wref() noexcept {
|
||||
return _weak;
|
||||
}
|
||||
|
||||
virtual void destroy() noexcept {
|
||||
_destroy();
|
||||
}
|
||||
|
||||
virtual bool destroying() noexcept {
|
||||
return _destroying;
|
||||
}
|
||||
|
||||
virtual bool dataNonNull() noexcept {
|
||||
return _data != nullptr;
|
||||
}
|
||||
|
||||
virtual void* getData() noexcept {
|
||||
return _data;
|
||||
}
|
||||
|
||||
virtual ~impl() {
|
||||
destroy();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class CSharedPointer {
|
||||
|
|
@ -132,7 +27,7 @@ namespace Hyprutils {
|
|||
/* creates a new shared pointer managing a resource
|
||||
avoid calling. Could duplicate ownership. Prefer makeShared */
|
||||
explicit CSharedPointer(T* object) noexcept {
|
||||
impl_ = new CSharedPointer_::impl<T>(object);
|
||||
impl_ = new Impl_::impl<T>(object);
|
||||
increment();
|
||||
}
|
||||
|
||||
|
|
@ -158,7 +53,7 @@ namespace Hyprutils {
|
|||
}
|
||||
|
||||
/* allows weakPointer to create from an impl */
|
||||
CSharedPointer(CSharedPointer_::impl_base* implementation) noexcept {
|
||||
CSharedPointer(Impl_::impl_base* implementation) noexcept {
|
||||
impl_ = implementation;
|
||||
increment();
|
||||
}
|
||||
|
|
@ -246,7 +141,7 @@ namespace Hyprutils {
|
|||
return impl_ ? impl_->ref() : 0;
|
||||
}
|
||||
|
||||
CSharedPointer_::impl_base* impl_ = nullptr;
|
||||
Impl_::impl_base* impl_ = nullptr;
|
||||
|
||||
private:
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -0,0 +1,149 @@
|
|||
#pragma once
|
||||
|
||||
#include "ImplBase.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 <typename T>
|
||||
class CUniquePointer {
|
||||
public:
|
||||
template <typename X>
|
||||
using validHierarchy = typename std::enable_if<std::is_assignable<CUniquePointer<T>&, X>::value, CUniquePointer&>::type;
|
||||
template <typename X>
|
||||
using isConstructible = typename std::enable_if<std::is_constructible<T&, X&>::value>::type;
|
||||
|
||||
/* creates a new unique pointer managing a resource
|
||||
avoid calling. Could duplicate ownership. Prefer makeUnique */
|
||||
explicit CUniquePointer(T* object) noexcept {
|
||||
impl_ = new Impl_::impl<T>(object, false);
|
||||
increment();
|
||||
}
|
||||
|
||||
/* creates a shared pointer from a reference */
|
||||
template <typename U, typename = isConstructible<U>>
|
||||
CUniquePointer(const CUniquePointer<U>& ref) = delete;
|
||||
CUniquePointer(const CUniquePointer& ref) = delete;
|
||||
|
||||
template <typename U, typename = isConstructible<U>>
|
||||
CUniquePointer(CUniquePointer<U>&& 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 {
|
||||
; // empty
|
||||
}
|
||||
|
||||
/* creates an empty unique pointer with no implementation */
|
||||
CUniquePointer(std::nullptr_t) noexcept {
|
||||
; // empty
|
||||
}
|
||||
|
||||
~CUniquePointer() {
|
||||
decrement();
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
validHierarchy<const CUniquePointer<U>&> operator=(const CUniquePointer<U>& rhs) = delete;
|
||||
CUniquePointer& operator=(const CUniquePointer& rhs) = delete;
|
||||
|
||||
template <typename U>
|
||||
validHierarchy<const CUniquePointer<U>&> operator=(CUniquePointer<U>&& rhs) {
|
||||
std::swap(impl_, rhs.impl_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
CUniquePointer& operator=(CUniquePointer&& rhs) {
|
||||
std::swap(impl_, rhs.impl_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator bool() const {
|
||||
return impl_;
|
||||
}
|
||||
|
||||
bool operator()(const CUniquePointer& lhs, const CUniquePointer& rhs) const {
|
||||
return reinterpret_cast<uintptr_t>(lhs.impl_) < reinterpret_cast<uintptr_t>(rhs.impl_);
|
||||
}
|
||||
|
||||
T* operator->() const {
|
||||
return get();
|
||||
}
|
||||
|
||||
T& operator*() const {
|
||||
return *get();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
decrement();
|
||||
impl_ = nullptr;
|
||||
}
|
||||
|
||||
T* get() const {
|
||||
return impl_ ? static_cast<T*>(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 <typename U, typename... Args>
|
||||
static CUniquePointer<U> makeUnique(Args&&... args) {
|
||||
return CUniquePointer<U>(new U(std::forward<Args>(args)...));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct std::hash<Hyprutils::Memory::CUniquePointer<T>> {
|
||||
std::size_t operator()(const Hyprutils::Memory::CUniquePointer<T>& p) const noexcept {
|
||||
return std::hash<void*>{}(p.impl_);
|
||||
}
|
||||
};
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "./SharedPtr.hpp"
|
||||
#include "./UniquePtr.hpp"
|
||||
|
||||
/*
|
||||
This is a Hyprland implementation of std::weak_ptr.
|
||||
|
|
@ -28,6 +29,16 @@ namespace Hyprutils {
|
|||
incrementWeak();
|
||||
}
|
||||
|
||||
/* create a weak ptr from a reference */
|
||||
template <typename U, typename = isConstructible<U>>
|
||||
CWeakPointer(const CUniquePointer<U>& ref) noexcept {
|
||||
if (!ref.impl_)
|
||||
return;
|
||||
|
||||
impl_ = ref.impl_;
|
||||
incrementWeak();
|
||||
}
|
||||
|
||||
/* create a weak ptr from another weak ptr */
|
||||
template <typename U, typename = isConstructible<U>>
|
||||
CWeakPointer(const CWeakPointer<U>& ref) noexcept {
|
||||
|
|
@ -119,7 +130,7 @@ namespace Hyprutils {
|
|||
}
|
||||
|
||||
CSharedPointer<T> lock() const {
|
||||
if (!impl_ || !impl_->dataNonNull() || impl_->destroying())
|
||||
if (!impl_ || !impl_->dataNonNull() || impl_->destroying() || !impl_->lockable())
|
||||
return {};
|
||||
|
||||
return CSharedPointer<T>(impl_);
|
||||
|
|
@ -138,6 +149,18 @@ namespace Hyprutils {
|
|||
return impl_ == rhs.impl_;
|
||||
}
|
||||
|
||||
bool operator==(const CUniquePointer<T>& 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 reinterpret_cast<uintptr_t>(lhs.impl_) < reinterpret_cast<uintptr_t>(rhs.impl_);
|
||||
}
|
||||
|
|
@ -154,7 +177,11 @@ namespace Hyprutils {
|
|||
return get();
|
||||
}
|
||||
|
||||
CSharedPointer_::impl_base* impl_ = nullptr;
|
||||
T& operator*() const {
|
||||
return *get();
|
||||
}
|
||||
|
||||
Impl_::impl_base* impl_ = nullptr;
|
||||
|
||||
private:
|
||||
/* no-op if there is no impl_ */
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
#include <fcntl.h>
|
||||
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;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <sys/types.h>
|
||||
|
||||
namespace Hyprutils {
|
||||
namespace OS {
|
||||
class CProcess {
|
||||
public:
|
||||
/* Creates a process object, doesn't run yet */
|
||||
CProcess(const std::string& binary_, const std::vector<std::string>& args_);
|
||||
|
||||
void addEnv(const std::string& name, const std::string& value);
|
||||
|
||||
/* 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();
|
||||
|
||||
const pid_t pid();
|
||||
|
||||
private:
|
||||
std::string binary, out, err;
|
||||
std::vector<std::string> args;
|
||||
std::vector<std::pair<std::string, std::string>> env;
|
||||
pid_t grandchildPid = 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ namespace Hyprutils {
|
|||
namespace String {
|
||||
// trims beginning and end of whitespace characters
|
||||
std::string trim(const std::string& in);
|
||||
bool isNumber(const std::string& str, bool allowfloat = false);
|
||||
void replaceInString(std::string& string, const std::string& what, const std::string& to);
|
||||
bool isNumber(const std::string& str, bool allowfloat = false);
|
||||
void replaceInString(std::string& string, const std::string& what, const std::string& to);
|
||||
};
|
||||
};
|
||||
|
|
@ -1,36 +1,49 @@
|
|||
{
|
||||
lib,
|
||||
stdenv,
|
||||
stdenvAdapters,
|
||||
cmake,
|
||||
pkg-config,
|
||||
pixman,
|
||||
version ? "git",
|
||||
doCheck ? false,
|
||||
}:
|
||||
stdenv.mkDerivation {
|
||||
pname = "hyprutils";
|
||||
inherit version doCheck;
|
||||
src = ../.;
|
||||
debug ? false,
|
||||
}: let
|
||||
inherit (builtins) foldl';
|
||||
inherit (lib.lists) flatten;
|
||||
|
||||
nativeBuildInputs = [
|
||||
cmake
|
||||
pkg-config
|
||||
adapters = flatten [
|
||||
stdenvAdapters.useMoldLinker
|
||||
(lib.optional debug stdenvAdapters.keepDebugInfo)
|
||||
];
|
||||
|
||||
buildInputs = [
|
||||
pixman
|
||||
];
|
||||
customStdenv = foldl' (acc: adapter: adapter acc) stdenv adapters;
|
||||
in
|
||||
customStdenv.mkDerivation {
|
||||
pname = "hyprutils";
|
||||
inherit version doCheck;
|
||||
src = ../.;
|
||||
|
||||
outputs = ["out" "dev"];
|
||||
nativeBuildInputs = [
|
||||
cmake
|
||||
pkg-config
|
||||
];
|
||||
|
||||
cmakeBuildType = "RelWithDebInfo";
|
||||
buildInputs = [
|
||||
pixman
|
||||
];
|
||||
|
||||
dontStrip = true;
|
||||
outputs = ["out" "dev"];
|
||||
|
||||
meta = with lib; {
|
||||
homepage = "https://github.com/hyprwm/hyprutils";
|
||||
description = "Small C++ library for utilities used across the Hypr* ecosystem";
|
||||
license = licenses.bsd3;
|
||||
platforms = platforms.linux;
|
||||
};
|
||||
}
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,162 @@
|
|||
#include <hyprutils/animation/AnimatedVariable.hpp>
|
||||
#include <hyprutils/animation/AnimationManager.hpp>
|
||||
#include <hyprutils/memory/WeakPtr.hpp>
|
||||
|
||||
using namespace Hyprutils::Animation;
|
||||
using namespace Hyprutils::Memory;
|
||||
|
||||
#define SP CSharedPointer
|
||||
#define WP CWeakPointer
|
||||
|
||||
void CBaseAnimatedVariable::create(CAnimationManager* pManager, int typeInfo, SP<CBaseAnimatedVariable> 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 {
|
||||
static constexpr const std::string DEFAULTBEZIERNAME = "default";
|
||||
|
||||
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 {
|
||||
static constexpr const std::string DEFAULTSTYLE = "";
|
||||
|
||||
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::milliseconds>(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();
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
#include <hyprutils/animation/AnimationConfig.hpp>
|
||||
|
||||
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<SAnimationPropertyConfig>();
|
||||
|
||||
WP<SAnimationPropertyConfig> 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<SAnimationPropertyConfig> CAnimationConfigTree::getConfig(const std::string& name) const {
|
||||
return m_mAnimationConfig.at(name);
|
||||
}
|
||||
|
||||
const std::unordered_map<std::string, SP<SAnimationPropertyConfig>>& CAnimationConfigTree::getFullConfig() const {
|
||||
return m_mAnimationConfig;
|
||||
}
|
||||
|
||||
void CAnimationConfigTree::setAnimForChildren(SP<SAnimationPropertyConfig> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
#include <algorithm>
|
||||
#include <hyprutils/animation/AnimationManager.hpp>
|
||||
#include <hyprutils/animation/AnimatedVariable.hpp>
|
||||
|
||||
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<Vector2D, 2> DEFAULTBEZIERPOINTS = {Vector2D(0.0, 0.75), Vector2D(0.15, 1.0)};
|
||||
|
||||
CAnimationManager::CAnimationManager() {
|
||||
const auto BEZIER = makeShared<CBezierCurve>();
|
||||
BEZIER->setup(DEFAULTBEZIERPOINTS);
|
||||
m_mBezierCurves["default"] = BEZIER;
|
||||
|
||||
m_events = makeUnique<SAnimationManagerSignals>();
|
||||
m_listeners = makeUnique<SAnimVarListeners>();
|
||||
|
||||
m_listeners->connect = m_events->connect.registerListener([this](std::any data) { onConnect(data); });
|
||||
m_listeners->disconnect = m_events->disconnect.registerListener([this](std::any data) { onDisconnect(data); });
|
||||
}
|
||||
|
||||
void CAnimationManager::onConnect(std::any data) {
|
||||
if (!m_bTickScheduled)
|
||||
scheduleTick();
|
||||
|
||||
try {
|
||||
const auto PAV = std::any_cast<WP<CBaseAnimatedVariable>>(data);
|
||||
if (!PAV)
|
||||
return;
|
||||
|
||||
m_vActiveAnimatedVariables.emplace_back(PAV);
|
||||
} catch (const std::bad_any_cast&) { return; }
|
||||
}
|
||||
|
||||
void CAnimationManager::onDisconnect(std::any data) {
|
||||
try {
|
||||
const auto PAV = std::any_cast<WP<CBaseAnimatedVariable>>(data);
|
||||
if (!PAV)
|
||||
return;
|
||||
|
||||
std::erase_if(m_vActiveAnimatedVariables, [&](const auto& other) { return !other || other == PAV; });
|
||||
} catch (const std::bad_any_cast&) { return; }
|
||||
}
|
||||
|
||||
void CAnimationManager::removeAllBeziers() {
|
||||
m_mBezierCurves.clear();
|
||||
|
||||
// add the default one
|
||||
const auto BEZIER = makeShared<CBezierCurve>();
|
||||
BEZIER->setup(DEFAULTBEZIERPOINTS);
|
||||
m_mBezierCurves["default"] = BEZIER;
|
||||
}
|
||||
|
||||
void CAnimationManager::addBezierWithName(std::string name, const Vector2D& p1, const Vector2D& p2) {
|
||||
const auto BEZIER = makeShared<CBezierCurve>();
|
||||
BEZIER->setup({
|
||||
p1,
|
||||
p2,
|
||||
});
|
||||
m_mBezierCurves[name] = BEZIER;
|
||||
}
|
||||
|
||||
bool CAnimationManager::shouldTickForNext() {
|
||||
return !m_vActiveAnimatedVariables.empty();
|
||||
}
|
||||
|
||||
void CAnimationManager::tickDone() {
|
||||
rotateActive();
|
||||
}
|
||||
|
||||
void CAnimationManager::rotateActive() {
|
||||
std::vector<CWeakPointer<CBaseAnimatedVariable>> 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<CBezierCurve> 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<std::string, SP<CBezierCurve>>& CAnimationManager::getAllBeziers() {
|
||||
return m_mBezierCurves;
|
||||
}
|
||||
|
||||
CWeakPointer<CAnimationManager::SAnimationManagerSignals> CAnimationManager::getSignals() const {
|
||||
return m_events;
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
#include <hyprutils/animation/BezierCurve.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
|
||||
using namespace Hyprutils::Animation;
|
||||
using namespace Hyprutils::Math;
|
||||
|
||||
void CBezierCurve::setup(const std::array<Vector2D, 2>& pVec) {
|
||||
// Avoid reallocations by reserving enough memory upfront
|
||||
m_vPoints.resize(pVec.size() + 2);
|
||||
m_vPoints = {
|
||||
Vector2D(0, 0), // Start point
|
||||
pVec[0], pVec[1], // Control points
|
||||
Vector2D(1, 1) // End point
|
||||
};
|
||||
|
||||
if (m_vPoints.size() != 4)
|
||||
std::abort();
|
||||
|
||||
// bake BAKEDPOINTS points for faster lookups
|
||||
// T -> X ( / BAKEDPOINTS )
|
||||
for (int i = 0; i < BAKEDPOINTS; ++i) {
|
||||
float const t = (i + 1) / (float)BAKEDPOINTS;
|
||||
m_aPointsBaked[i] = Vector2D(getXForT(t), getYForT(t));
|
||||
}
|
||||
|
||||
for (int j = 1; j < 10; ++j) {
|
||||
float i = j / 10.0f;
|
||||
getYForPoint(i);
|
||||
}
|
||||
}
|
||||
|
||||
float CBezierCurve::getXForT(float const& t) const {
|
||||
float t2 = t * t;
|
||||
float t3 = t2 * t;
|
||||
|
||||
return (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 (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;
|
||||
|
||||
below = m_aPointsBaked[index].x < x;
|
||||
}
|
||||
|
||||
int lowerIndex = index - (!below || index == BAKEDPOINTS - 1);
|
||||
|
||||
// in the name of performance i shall make a hack
|
||||
const auto LOWERPOINT = &m_aPointsBaked[lowerIndex];
|
||||
const auto UPPERPOINT = &m_aPointsBaked[lowerIndex + 1];
|
||||
|
||||
const auto PERCINDELTA = (x - LOWERPOINT->x) / (UPPERPOINT->x - LOWERPOINT->x);
|
||||
|
||||
if (std::isnan(PERCINDELTA) || std::isinf(PERCINDELTA)) // can sometimes happen for VERY small x
|
||||
return 0.f;
|
||||
|
||||
return LOWERPOINT->y + ((UPPERPOINT->y - LOWERPOINT->y) * PERCINDELTA);
|
||||
}
|
||||
|
|
@ -37,7 +37,7 @@ CBox& Hyprutils::Math::CBox::translate(const Vector2D& vec) {
|
|||
}
|
||||
|
||||
Vector2D Hyprutils::Math::CBox::middle() const {
|
||||
return Vector2D{x + w * HALF, y + h * HALF};
|
||||
return Vector2D{x + (w * HALF), y + (h * HALF)};
|
||||
}
|
||||
|
||||
bool Hyprutils::Math::CBox::containsPoint(const Vector2D& vec) const {
|
||||
|
|
@ -200,7 +200,7 @@ Vector2D Hyprutils::Math::CBox::size() const {
|
|||
}
|
||||
|
||||
Vector2D Hyprutils::Math::CBox::extent() const {
|
||||
return pos() + size();
|
||||
return pos() + size();
|
||||
}
|
||||
|
||||
Vector2D Hyprutils::Math::CBox::closestPoint(const Vector2D& vec) const {
|
||||
|
|
@ -233,5 +233,5 @@ Vector2D Hyprutils::Math::CBox::closestPoint(const Vector2D& vec) const {
|
|||
}
|
||||
|
||||
SBoxExtents Hyprutils::Math::CBox::extentsFrom(const CBox& small) {
|
||||
return {{small.x - x, small.y - y}, {w - small.w - (small.x - x), h - small.h - (small.y - y)}};
|
||||
return {.topLeft = {small.x - x, small.y - y}, .bottomRight = {w - small.w - (small.x - x), h - small.h - (small.y - y)}};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ CRegion& Hyprutils::Math::CRegion::invert(pixman_box32_t* box) {
|
|||
}
|
||||
|
||||
CRegion& Hyprutils::Math::CRegion::invert(const CBox& box) {
|
||||
pixman_box32 pixmanBox = {(int32_t)box.x, (int32_t)box.y, (int32_t)box.w + (int32_t)box.x, (int32_t)box.h + (int32_t)box.y};
|
||||
pixman_box32 pixmanBox = {.x1 = (int32_t)box.x, .y1 = (int32_t)box.y, .x2 = (int32_t)box.w + (int32_t)box.x, .y2 = (int32_t)box.h + (int32_t)box.y};
|
||||
return this->invert(&pixmanBox);
|
||||
}
|
||||
|
||||
|
|
@ -118,7 +118,7 @@ CRegion& Hyprutils::Math::CRegion::expand(double units) {
|
|||
clear();
|
||||
|
||||
for (auto& r : rects) {
|
||||
CBox b{(double)r.x1 - units, (double)r.y1 - units, (double)r.x2 - r.x1 + units * 2, (double)r.y2 - r.y1 + units * 2};
|
||||
CBox b{(double)r.x1 - units, (double)r.y1 - units, (double)r.x2 - r.x1 + (units * 2), (double)r.y2 - r.y1 + (units * 2)};
|
||||
add(b);
|
||||
}
|
||||
|
||||
|
|
@ -149,9 +149,9 @@ CRegion& Hyprutils::Math::CRegion::scale(const Vector2D& scale) {
|
|||
|
||||
for (auto& r : rects) {
|
||||
r.x1 = std::floor(r.x1 * scale.x);
|
||||
r.y1 = std::floor(r.y1 * scale.x);
|
||||
r.y1 = std::floor(r.y1 * scale.y);
|
||||
r.x2 = std::ceil(r.x2 * scale.x);
|
||||
r.y2 = std::ceil(r.y2 * scale.x);
|
||||
r.y2 = std::ceil(r.y2 * scale.y);
|
||||
add(&r);
|
||||
}
|
||||
|
||||
|
|
@ -214,4 +214,4 @@ Vector2D Hyprutils::Math::CRegion::closestPoint(const Vector2D& vec) const {
|
|||
}
|
||||
|
||||
return leader;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,19 +4,16 @@
|
|||
|
||||
using namespace Hyprutils::Math;
|
||||
|
||||
Hyprutils::Math::Vector2D::Vector2D(double xx, double yy) {
|
||||
x = xx;
|
||||
y = yy;
|
||||
Hyprutils::Math::Vector2D::Vector2D(double xx, double yy) : x(xx), y(yy) {
|
||||
;
|
||||
}
|
||||
|
||||
Hyprutils::Math::Vector2D::Vector2D(int xx, int yy) {
|
||||
x = (double)xx;
|
||||
y = (double)yy;
|
||||
Hyprutils::Math::Vector2D::Vector2D(int xx, int yy) : x((double)xx), y((double)yy) {
|
||||
;
|
||||
}
|
||||
|
||||
Hyprutils::Math::Vector2D::Vector2D() {
|
||||
x = 0;
|
||||
y = 0;
|
||||
Hyprutils::Math::Vector2D::Vector2D() : x(0), y(0) {
|
||||
;
|
||||
}
|
||||
|
||||
Hyprutils::Math::Vector2D::~Vector2D() {}
|
||||
|
|
@ -48,11 +45,11 @@ double Hyprutils::Math::Vector2D::distance(const Vector2D& other) const {
|
|||
}
|
||||
|
||||
double Hyprutils::Math::Vector2D::distanceSq(const Vector2D& other) const {
|
||||
return (x - other.x) * (x - other.x) + (y - other.y) * (y - other.y);
|
||||
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);
|
||||
return std::sqrt((x * x) + (y * y));
|
||||
}
|
||||
|
||||
Vector2D Hyprutils::Math::Vector2D::getComponentMax(const Vector2D& other) const {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,86 @@
|
|||
#include <cstdlib>
|
||||
#include <hyprutils/os/FileDescriptor.hpp>
|
||||
#include <fcntl.h>
|
||||
#include <sys/poll.h>
|
||||
#include <unistd.h>
|
||||
#include <utility>
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
@ -0,0 +1,232 @@
|
|||
#include <hyprutils/os/Process.hpp>
|
||||
using namespace Hyprutils::OS;
|
||||
|
||||
#include <csignal>
|
||||
#include <cstdio>
|
||||
#include <unistd.h>
|
||||
#include <cstring>
|
||||
#include <array>
|
||||
#include <thread>
|
||||
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/poll.h>
|
||||
|
||||
Hyprutils::OS::CProcess::CProcess(const std::string& binary_, const std::vector<std::string>& args_) : binary(binary_), args(args_) {
|
||||
;
|
||||
}
|
||||
|
||||
void Hyprutils::OS::CProcess::addEnv(const std::string& name, const std::string& value) {
|
||||
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<const char*> argsC;
|
||||
argsC.emplace_back(strdup(binary.c_str()));
|
||||
for (auto& arg : 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] : env) {
|
||||
setenv(n.c_str(), v.c_str(), 1);
|
||||
}
|
||||
|
||||
execvp(binary.c_str(), (char* const*)argsC.data());
|
||||
exit(1);
|
||||
} else {
|
||||
// parent
|
||||
close(outPipe[1]);
|
||||
close(errPipe[1]);
|
||||
|
||||
out = "";
|
||||
err = "";
|
||||
|
||||
grandchildPid = pid;
|
||||
|
||||
std::array<char, 1024> 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) {
|
||||
out += std::string_view{(char*)buf.data(), (size_t)ret};
|
||||
}
|
||||
|
||||
buf.fill(0);
|
||||
}
|
||||
|
||||
if (pollfds[1].revents & POLLIN) {
|
||||
while ((ret = read(errPipe[0], buf.data(), 1023)) > 0) {
|
||||
err += std::string_view{(char*)buf.data(), (size_t)ret};
|
||||
}
|
||||
|
||||
buf.fill(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Final reads. Nonblock, so its ok.
|
||||
while ((ret = read(outPipe[0], buf.data(), 1023)) > 0) {
|
||||
out += std::string_view{(char*)buf.data(), (size_t)ret};
|
||||
}
|
||||
|
||||
buf.fill(0);
|
||||
|
||||
while ((ret = read(errPipe[0], buf.data(), 1023)) > 0) {
|
||||
err += std::string_view{(char*)buf.data(), (size_t)ret};
|
||||
}
|
||||
|
||||
buf.fill(0);
|
||||
|
||||
close(outPipe[0]);
|
||||
close(errPipe[0]);
|
||||
|
||||
// reap child
|
||||
waitpid(pid, nullptr, 0);
|
||||
|
||||
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<const char*> argsC;
|
||||
argsC.emplace_back(strdup(binary.c_str()));
|
||||
for (auto& arg : args) {
|
||||
argsC.emplace_back(strdup(arg.c_str()));
|
||||
}
|
||||
|
||||
argsC.emplace_back(nullptr);
|
||||
|
||||
execvp(binary.c_str(), (char* const*)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);
|
||||
|
||||
grandchildPid = grandchild;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::string& Hyprutils::OS::CProcess::stdOut() {
|
||||
return out;
|
||||
}
|
||||
|
||||
const std::string& Hyprutils::OS::CProcess::stdErr() {
|
||||
return err;
|
||||
}
|
||||
|
||||
const pid_t Hyprutils::OS::CProcess::pid() {
|
||||
return grandchildPid;
|
||||
}
|
||||
|
|
@ -62,7 +62,7 @@ namespace Hyprutils::Path {
|
|||
|
||||
static const auto xdgConfigDirs = getXdgConfigDirs();
|
||||
if (xdgConfigDirs.has_value()) {
|
||||
for (auto dir : xdgConfigDirs.value()) {
|
||||
for (auto& dir : xdgConfigDirs.value()) {
|
||||
if (checkConfigExists(dir, programName))
|
||||
return std::make_pair(fullConfigPath(dir, programName), std::nullopt);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ void Hyprutils::Signal::CSignal::emit(std::any data) {
|
|||
}
|
||||
|
||||
std::vector<CStaticSignalListener*> statics;
|
||||
statics.reserve(m_vStaticListeners.size());
|
||||
for (auto& l : m_vStaticListeners) {
|
||||
statics.emplace_back(l.get());
|
||||
}
|
||||
|
|
@ -27,7 +28,7 @@ void Hyprutils::Signal::CSignal::emit(std::any data) {
|
|||
// vector and was removed during our iteration
|
||||
if (l.strongRef() == 1)
|
||||
continue;
|
||||
|
||||
|
||||
l->emit(data);
|
||||
}
|
||||
|
||||
|
|
@ -38,7 +39,7 @@ void Hyprutils::Signal::CSignal::emit(std::any data) {
|
|||
// release SPs
|
||||
listeners.clear();
|
||||
|
||||
// we cannot release any expired refs here as one of the listeners could've removed this object and
|
||||
// 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
|
||||
}
|
||||
|
||||
|
|
@ -48,10 +49,10 @@ CHyprSignalListener Hyprutils::Signal::CSignal::registerListener(std::function<v
|
|||
|
||||
// housekeeping: remove any stale listeners
|
||||
std::erase_if(m_vListeners, [](const auto& other) { return other.expired(); });
|
||||
|
||||
|
||||
return listener;
|
||||
}
|
||||
|
||||
void Hyprutils::Signal::CSignal::registerStaticListener(std::function<void(void*, std::any)> handler, void* owner) {
|
||||
m_vStaticListeners.emplace_back(std::make_unique<CStaticSignalListener>(handler, owner));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,12 +7,12 @@ std::string Hyprutils::String::trim(const std::string& in) {
|
|||
if (in.empty())
|
||||
return in;
|
||||
|
||||
int countBefore = 0;
|
||||
size_t countBefore = 0;
|
||||
while (countBefore < in.length() && std::isspace(in.at(countBefore))) {
|
||||
countBefore++;
|
||||
}
|
||||
|
||||
int countAfter = 0;
|
||||
size_t countAfter = 0;
|
||||
while (countAfter < in.length() - countBefore && std::isspace(in.at(in.length() - countAfter - 1))) {
|
||||
countAfter++;
|
||||
}
|
||||
|
|
@ -55,10 +55,7 @@ bool Hyprutils::String::isNumber(const std::string& str, bool allowfloat) {
|
|||
}
|
||||
}
|
||||
|
||||
if (!isdigit(str.back()))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
return isdigit(str.back()) != 0;
|
||||
}
|
||||
|
||||
void Hyprutils::String::replaceInString(std::string& string, const std::string& what, const std::string& to) {
|
||||
|
|
|
|||
|
|
@ -12,8 +12,7 @@ Hyprutils::String::CVarList::CVarList(const std::string& in, const size_t lastAr
|
|||
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);
|
||||
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())
|
||||
|
|
@ -23,7 +22,7 @@ Hyprutils::String::CVarList::CVarList(const std::string& in, const size_t lastAr
|
|||
break;
|
||||
}
|
||||
pos += s.size() + 1;
|
||||
m_vArgs.emplace_back(trim(std::string_view{s}.data()));
|
||||
m_vArgs.emplace_back(trim(s.data()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -36,4 +35,4 @@ std::string Hyprutils::String::CVarList::join(const std::string& joiner, size_t
|
|||
}
|
||||
|
||||
return rolling;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,397 @@
|
|||
#include <hyprutils/animation/AnimationConfig.hpp>
|
||||
#include <hyprutils/animation/AnimationManager.hpp>
|
||||
#include <hyprutils/animation/AnimatedVariable.hpp>
|
||||
#include <hyprutils/memory/WeakPtr.hpp>
|
||||
#include <hyprutils/memory/UniquePtr.hpp>
|
||||
#include "shared.hpp"
|
||||
|
||||
#define SP CSharedPointer
|
||||
#define WP CWeakPointer
|
||||
#define UP CUniquePointer
|
||||
|
||||
using namespace Hyprutils::Animation;
|
||||
using namespace Hyprutils::Math;
|
||||
using namespace Hyprutils::Memory;
|
||||
|
||||
class EmtpyContext {};
|
||||
|
||||
template <typename VarType>
|
||||
using CAnimatedVariable = CGenericAnimatedVariable<VarType, EmtpyContext>;
|
||||
|
||||
template <typename VarType>
|
||||
using PANIMVAR = SP<CAnimatedVariable<VarType>>;
|
||||
|
||||
template <typename VarType>
|
||||
using PANIMVARREF = WP<CAnimatedVariable<VarType>>;
|
||||
|
||||
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 = dynamic_cast<CAnimatedVariable<int>*>(PAV.get());
|
||||
if (!avInt)
|
||||
std::cout << Colors::RED << "Dynamic cast upcast failed" << Colors::RESET;
|
||||
|
||||
const auto DELTA = avInt->goal() - avInt->value();
|
||||
avInt->value() = avInt->begun() + (DELTA * POINTY);
|
||||
} break;
|
||||
case eAVTypes::TEST: {
|
||||
auto avCustom = dynamic_cast<CAnimatedVariable<SomeTestType>*>(PAV.get());
|
||||
if (!avCustom)
|
||||
std::cout << Colors::RED << "Dynamic cast upcast failed" << Colors::RESET;
|
||||
|
||||
if (SPENT >= 1.f)
|
||||
avCustom->value().done = true;
|
||||
} break;
|
||||
default: {
|
||||
std::cout << Colors::RED << "What are we even doing?" << Colors::RESET;
|
||||
} break;
|
||||
}
|
||||
|
||||
PAV->onUpdate();
|
||||
}
|
||||
|
||||
tickDone();
|
||||
}
|
||||
|
||||
template <typename VarType>
|
||||
void createAnimation(const VarType& v, PANIMVAR<VarType>& av, const std::string& animationConfigName) {
|
||||
constexpr const eAVTypes EAVTYPE = std::is_same_v<VarType, int> ? eAVTypes::INT : eAVTypes::TEST;
|
||||
const auto PAV = makeShared<CGenericAnimatedVariable<VarType, EmtpyContext>>();
|
||||
|
||||
PAV->create(EAVTYPE, static_cast<CAnimationManager*>(this), PAV, v);
|
||||
PAV->setConfig(animationTree.getConfig(animationConfigName));
|
||||
av = std::move(PAV);
|
||||
}
|
||||
|
||||
virtual void scheduleTick() {
|
||||
;
|
||||
}
|
||||
|
||||
virtual void onTicked() {
|
||||
;
|
||||
}
|
||||
};
|
||||
|
||||
UP<CMyAnimationManager> 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<int> m_iA;
|
||||
PANIMVAR<int> m_iB;
|
||||
PANIMVAR<SomeTestType> m_iC;
|
||||
};
|
||||
|
||||
int config() {
|
||||
pAnimationManager = makeUnique<CMyAnimationManager>();
|
||||
|
||||
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(internalCfg->pParentAnimation.get(), internalCfg.get());
|
||||
EXPECT(internalCfg->pValues.get(), internalCfg.get());
|
||||
|
||||
animationTree.setConfigForNode("global", 1, 4.0, "default", "asdf");
|
||||
|
||||
EXPECT(barCfg->internalEnabled, -1);
|
||||
{
|
||||
const auto PVALUES = barCfg->pValues.lock();
|
||||
EXPECT(PVALUES->internalEnabled, 1);
|
||||
EXPECT(PVALUES->internalBezier, "default");
|
||||
EXPECT(PVALUES->internalStyle, "asdf");
|
||||
EXPECT(PVALUES->internalSpeed, 4.0);
|
||||
}
|
||||
EXPECT(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(PVALUES->internalEnabled, 1);
|
||||
EXPECT(PVALUES->internalBezier, "test");
|
||||
EXPECT(PVALUES->internalStyle, "qwer");
|
||||
EXPECT(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(PVALUES->internalEnabled, 1);
|
||||
EXPECT(PVALUES->internalBezier, "test");
|
||||
EXPECT(PVALUES->internalStyle, "qwer");
|
||||
EXPECT(PVALUES->internalSpeed, 4.2f);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv, char** envp) {
|
||||
int ret = config();
|
||||
|
||||
animationTree.createNode("global");
|
||||
animationTree.createNode("internal");
|
||||
|
||||
animationTree.createNode("default", "global");
|
||||
animationTree.setConfigForNode("global", 1, 4.0, "default", "asdf");
|
||||
|
||||
Subject s(0, 0);
|
||||
|
||||
EXPECT(s.m_iA->value(), 0);
|
||||
EXPECT(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(pAnimationManager->shouldTickForNext(), false);
|
||||
EXPECT(s.m_iC->value().done, false);
|
||||
|
||||
*s.m_iA = 10;
|
||||
*s.m_iB = 100;
|
||||
*s.m_iC = SomeTestType(true);
|
||||
|
||||
EXPECT(s.m_iC->value().done, false);
|
||||
|
||||
while (pAnimationManager->shouldTickForNext()) {
|
||||
pAnimationManager->tick();
|
||||
}
|
||||
|
||||
EXPECT(s.m_iA->value(), 10);
|
||||
EXPECT(s.m_iB->value(), 100);
|
||||
EXPECT(s.m_iC->value().done, true);
|
||||
|
||||
s.m_iA->setValue(0);
|
||||
s.m_iB->setValue(0);
|
||||
|
||||
while (pAnimationManager->shouldTickForNext()) {
|
||||
pAnimationManager->tick();
|
||||
}
|
||||
|
||||
EXPECT(s.m_iA->value(), 10);
|
||||
EXPECT(s.m_iB->value(), 100);
|
||||
|
||||
// Test config stuff
|
||||
EXPECT(s.m_iA->getBezierName(), "default");
|
||||
EXPECT(s.m_iA->getStyle(), "asdf");
|
||||
EXPECT(s.m_iA->enabled(), true);
|
||||
|
||||
animationTree.getConfig("global")->internalEnabled = 0;
|
||||
|
||||
EXPECT(s.m_iA->enabled(), false);
|
||||
|
||||
*s.m_iA = 50;
|
||||
pAnimationManager->tick(); // Expecting a warp
|
||||
EXPECT(s.m_iA->value(), 50);
|
||||
|
||||
// Test missing pValues
|
||||
animationTree.getConfig("global")->internalEnabled = 0;
|
||||
animationTree.getConfig("default")->pValues.reset();
|
||||
|
||||
EXPECT(s.m_iA->enabled(), false);
|
||||
EXPECT(s.m_iA->getBezierName(), "default");
|
||||
EXPECT(s.m_iA->getStyle(), "");
|
||||
EXPECT(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<CBaseAnimatedVariable> pav) { beginCallbackRan++; });
|
||||
s.m_iA->setUpdateCallback([&updateCallbackRan](WP<CBaseAnimatedVariable> pav) { updateCallbackRan++; });
|
||||
s.m_iA->setCallbackOnEnd([&endCallbackRan](WP<CBaseAnimatedVariable> pav) { endCallbackRan++; }, false);
|
||||
|
||||
s.m_iA->setValueAndWarp(42);
|
||||
|
||||
EXPECT(beginCallbackRan, 0);
|
||||
EXPECT(updateCallbackRan, 1);
|
||||
EXPECT(endCallbackRan, 2); // first called when setting the callback, then when warping.
|
||||
|
||||
*s.m_iA = 1337;
|
||||
while (pAnimationManager->shouldTickForNext()) {
|
||||
pAnimationManager->tick();
|
||||
}
|
||||
|
||||
EXPECT(beginCallbackRan, 1);
|
||||
EXPECT(updateCallbackRan > 2, true);
|
||||
EXPECT(endCallbackRan, 3);
|
||||
|
||||
std::vector<PANIMVAR<int>> 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<CBaseAnimatedVariable> 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(s.m_iA->value(), 1000000);
|
||||
// all vars should be set to 1337
|
||||
EXPECT(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(endCallbackRan, 4);
|
||||
|
||||
s.m_iA->setValueAndWarp(10);
|
||||
|
||||
EXPECT(endCallbackRan, 4);
|
||||
EXPECT(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(endCallbackRan, 4);
|
||||
|
||||
*s.m_iA = 4;
|
||||
s.m_iA->warp(true);
|
||||
EXPECT(endCallbackRan, 5);
|
||||
|
||||
// test getCurveValue
|
||||
*s.m_iA = 0;
|
||||
EXPECT(s.m_iA->getCurveValue(), 0.f);
|
||||
s.m_iA->warp();
|
||||
EXPECT(s.m_iA->getCurveValue(), 1.f);
|
||||
EXPECT(endCallbackRan, 6);
|
||||
|
||||
// test end callback readding the var
|
||||
*s.m_iA = 5;
|
||||
s.m_iA->setCallbackOnEnd([&endCallbackRan](WP<CBaseAnimatedVariable> v) {
|
||||
endCallbackRan++;
|
||||
const auto PAV = dynamic_cast<CAnimatedVariable<int>*>(v.lock().get());
|
||||
|
||||
*PAV = 10;
|
||||
PAV->setCallbackOnEnd([&endCallbackRan](WP<CBaseAnimatedVariable> v) { endCallbackRan++; });
|
||||
});
|
||||
|
||||
while (pAnimationManager->shouldTickForNext()) {
|
||||
pAnimationManager->tick();
|
||||
}
|
||||
|
||||
EXPECT(endCallbackRan, 8);
|
||||
EXPECT(s.m_iA->value(), 10);
|
||||
|
||||
// Test duplicate active anim vars are not allowed
|
||||
{
|
||||
EXPECT(pAnimationManager->m_vActiveAnimatedVariables.size(), 0);
|
||||
PANIMVAR<int> a;
|
||||
pAnimationManager->createAnimation(1, a, "default");
|
||||
EXPECT(pAnimationManager->m_vActiveAnimatedVariables.size(), 0);
|
||||
*a = 10;
|
||||
EXPECT(pAnimationManager->m_vActiveAnimatedVariables.size(), 1);
|
||||
*a = 20;
|
||||
EXPECT(pAnimationManager->m_vActiveAnimatedVariables.size(), 1);
|
||||
a->warp();
|
||||
EXPECT(pAnimationManager->m_vActiveAnimatedVariables.size(), 0);
|
||||
EXPECT(a->value(), 20);
|
||||
}
|
||||
|
||||
// Test no crash when animation manager gets destroyed
|
||||
{
|
||||
PANIMVAR<int> a;
|
||||
pAnimationManager->createAnimation(1, a, "default");
|
||||
*a = 10;
|
||||
pAnimationManager.reset();
|
||||
EXPECT(a->isAnimationManagerDead(), true);
|
||||
a->setValueAndWarp(11);
|
||||
EXPECT(a->value(), 11);
|
||||
*a = 12;
|
||||
a->warp();
|
||||
EXPECT(a->value(), 12);
|
||||
*a = 13;
|
||||
} // a gets destroyed
|
||||
|
||||
EXPECT(pAnimationManager.get(), nullptr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
#include <hyprutils/os/FileDescriptor.hpp>
|
||||
#include "shared.hpp"
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
using namespace Hyprutils::OS;
|
||||
|
||||
int main(int argc, char** argv, char** envp) {
|
||||
std::string name = "/test_filedescriptors";
|
||||
CFileDescriptor fd(shm_open(name.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600));
|
||||
|
||||
int ret = 0;
|
||||
EXPECT(fd.isValid(), true);
|
||||
EXPECT(fd.isReadable(), true);
|
||||
|
||||
int flags = fd.getFlags();
|
||||
EXPECT(fd.getFlags(), FD_CLOEXEC);
|
||||
flags &= ~FD_CLOEXEC;
|
||||
fd.setFlags(flags);
|
||||
EXPECT(fd.getFlags(), !FD_CLOEXEC);
|
||||
|
||||
CFileDescriptor fd2 = fd.duplicate();
|
||||
EXPECT(fd.isValid(), true);
|
||||
EXPECT(fd.isReadable(), true);
|
||||
EXPECT(fd2.isValid(), true);
|
||||
EXPECT(fd2.isReadable(), true);
|
||||
|
||||
CFileDescriptor fd3(fd2.take());
|
||||
EXPECT(fd.isValid(), true);
|
||||
EXPECT(fd.isReadable(), true);
|
||||
EXPECT(fd2.isValid(), false);
|
||||
EXPECT(fd2.isReadable(), false);
|
||||
|
||||
// .duplicate default flags is FD_CLOEXEC
|
||||
EXPECT(fd3.getFlags(), FD_CLOEXEC);
|
||||
|
||||
fd.reset();
|
||||
fd2.reset();
|
||||
fd3.reset();
|
||||
|
||||
EXPECT(fd.isReadable(), false);
|
||||
EXPECT(fd2.isReadable(), false);
|
||||
EXPECT(fd3.isReadable(), false);
|
||||
|
||||
shm_unlink(name.c_str());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -88,7 +88,7 @@ int main(int argc, char** argv, char** envp) {
|
|||
|
||||
// Test matrices
|
||||
{
|
||||
Mat3x3 jeremy = Mat3x3::outputProjection({1920, 1080}, HYPRUTILS_TRANSFORM_FLIPPED_90);
|
||||
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<float, 9>{0, 0.46296296, 0, 0.3125, 0, 0, 19.84375, 36.055557, 1};
|
||||
|
|
|
|||
|
|
@ -1,29 +1,56 @@
|
|||
#include <hyprutils/memory/WeakPtr.hpp>
|
||||
#include <hyprutils/memory/UniquePtr.hpp>
|
||||
#include "shared.hpp"
|
||||
#include <vector>
|
||||
|
||||
using namespace Hyprutils::Memory;
|
||||
|
||||
#define SP CSharedPointer
|
||||
#define WP CWeakPointer
|
||||
#define UP CUniquePointer
|
||||
|
||||
int main(int argc, char** argv, char** envp) {
|
||||
SP<int> intPtr = makeShared<int>(10);
|
||||
SP<int> intPtr = makeShared<int>(10);
|
||||
SP<int> intPtr2 = makeShared<int>(1337);
|
||||
UP<int> intUnique = makeUnique<int>(420);
|
||||
|
||||
int ret = 0;
|
||||
int ret = 0;
|
||||
|
||||
EXPECT(*intPtr, 10);
|
||||
EXPECT(intPtr.strongRef(), 1);
|
||||
EXPECT(*intUnique, 420);
|
||||
|
||||
WP<int> weak = intPtr;
|
||||
WP<int> weak = intPtr;
|
||||
WP<int> weakUnique = intUnique;
|
||||
|
||||
EXPECT(*intPtr, 10);
|
||||
EXPECT(intPtr.strongRef(), 1);
|
||||
EXPECT(*weak.get(), 10);
|
||||
EXPECT(*weak, 10);
|
||||
EXPECT(weak.expired(), false);
|
||||
EXPECT(*weakUnique, 420);
|
||||
EXPECT(weakUnique.expired(), false);
|
||||
EXPECT(intUnique.impl_->wref(), 1);
|
||||
|
||||
intPtr = {};
|
||||
SP<int> sharedFromUnique = weakUnique.lock();
|
||||
EXPECT(sharedFromUnique, nullptr);
|
||||
|
||||
std::vector<SP<int>> 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(weak.impl_->ref(), 0);
|
||||
EXPECT(weakUnique.impl_->ref(), 0);
|
||||
EXPECT(weakUnique.impl_->wref(), 1);
|
||||
EXPECT(intPtr2.strongRef(), 3);
|
||||
|
||||
EXPECT(weak.expired(), true);
|
||||
EXPECT(weakUnique.expired(), true);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
#include <csignal>
|
||||
#include <cerrno>
|
||||
#include <cstdlib>
|
||||
#include <unistd.h>
|
||||
#include <hyprutils/os/Process.hpp>
|
||||
#include "shared.hpp"
|
||||
|
||||
using namespace Hyprutils::OS;
|
||||
|
||||
int main(int argc, char** argv, char** envp) {
|
||||
int ret = 0;
|
||||
|
||||
CProcess process("sh", {"-c", "echo \"Hello $WORLD!\""});
|
||||
process.addEnv("WORLD", "World");
|
||||
|
||||
EXPECT(process.runAsync(), true);
|
||||
EXPECT(process.runSync(), true);
|
||||
|
||||
EXPECT(process.stdOut(), std::string{"Hello World!\n"});
|
||||
EXPECT(process.stdErr(), std::string{""});
|
||||
|
||||
CProcess process2("sh", {"-c", "while true; do sleep 1; done;"});
|
||||
|
||||
EXPECT(process2.runAsync(), true);
|
||||
EXPECT(getpgid(process2.pid()) >= 0, true);
|
||||
|
||||
kill(process2.pid(), SIGKILL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -13,7 +13,7 @@ namespace Colors {
|
|||
|
||||
#define EXPECT(expr, val) \
|
||||
if (const auto RESULT = expr; RESULT != (val)) { \
|
||||
std::cout << Colors::RED << "Failed: " << Colors::RESET << #expr << ", expected " << val << " but got " << RESULT << "\n"; \
|
||||
std::cout << Colors::RED << "Failed: " << Colors::RESET << #expr << ", expected " << val << " but got " << RESULT << "\n"; \
|
||||
ret = 1; \
|
||||
} else { \
|
||||
std::cout << Colors::GREEN << "Passed " << Colors::RESET << #expr << ". Got " << val << "\n"; \
|
||||
|
|
|
|||
|
|
@ -6,13 +6,11 @@ using namespace Hyprutils::Signal;
|
|||
using namespace Hyprutils::Memory;
|
||||
|
||||
int main(int argc, char** argv, char** envp) {
|
||||
int ret = 0;
|
||||
int ret = 0;
|
||||
|
||||
CSignal signal;
|
||||
int data = 0;
|
||||
auto listener = signal.registerListener([&] (std::any d) {
|
||||
data = 1;
|
||||
});
|
||||
int data = 0;
|
||||
auto listener = signal.registerListener([&]([[maybe_unused]] std::any d) { data = 1; });
|
||||
|
||||
signal.emit();
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue