2024-06-28 14:08:13 +02:00
# include "../globals.hpp"
2024-07-03 00:38:50 +02:00
# include "../config/config.hpp"
2024-06-28 14:08:13 +02:00
# include "Shake.hpp"
2025-01-18 22:40:48 +01:00
2024-08-25 20:00:16 +02:00
# include <algorithm>
# include <chrono>
2024-06-28 14:08:13 +02:00
# include <hyprland/src/Compositor.hpp>
2024-07-03 17:51:19 +02:00
# include <hyprland/src/debug/Log.hpp>
2025-01-18 22:40:48 +01:00
# include <hyprland/src/managers/AnimationManager.hpp>
# include <hyprland/src/managers/EventManager.hpp>
2025-01-08 22:04:30 +01:00
# include <hyprutils/animation/AnimationConfig.hpp>
2025-01-18 22:40:48 +01:00
# include <hyprland/src/render/Renderer.hpp>
2024-06-28 14:08:13 +02:00
2024-08-25 20:00:16 +02:00
CShake : : CShake ( ) {
// the timing and the bezier are quite crucial, as things will break down if they are just changed slighly
// this is not ideal and should be fixed some time in the future, then it may be made configurable (if it has a substatntial enough effect on behaviour)
int time = 400 ;
// add custom bezier (and readd it after config reload)
static auto bezier = " dynamic-cursors-magnification " ;
g_pAnimationManager - > addBezierWithName ( bezier , { 0.22 , 1.0 } , { 0.36 , 1.0 } ) ;
static const auto PCALLBACK = HyprlandAPI : : registerCallbackDynamic ( PHANDLE , " configReloaded " , [ & ] ( void * self , SCallbackInfo & , std : : any data ) {
g_pAnimationManager - > addBezierWithName ( bezier , { 0.22 , 1.0 } , { 0.36 , 1.0 } ) ;
} ) ;
2025-01-08 22:04:30 +01:00
// wtf is this struct, what is pValues?
static SP < SAnimationPropertyConfig > properties = makeShared < SAnimationPropertyConfig > ( ) ;
properties - > internalBezier = bezier ;
properties - > internalSpeed = time / 100.f ;
properties - > internalEnabled = 1 ;
properties - > pValues = properties ;
2024-08-25 20:00:16 +02:00
2025-01-08 22:04:30 +01:00
g_pAnimationManager - > createAnimation ( 1.f , zoom , properties , AVARDAMAGE_NONE ) ;
2024-08-25 20:00:16 +02:00
}
2024-06-28 14:08:13 +02:00
double CShake : : update ( Vector2D pos ) {
2024-07-03 00:38:50 +02:00
static auto * const * PTHRESHOLD = ( Hyprlang : : FLOAT * const * ) getConfig ( CONFIG_SHAKE_THRESHOLD ) ;
2024-08-25 20:00:16 +02:00
static auto * const * PBASE = ( Hyprlang : : FLOAT * const * ) getConfig ( CONFIG_SHAKE_BASE ) ;
static auto * const * PSPEED = ( Hyprlang : : FLOAT * const * ) getConfig ( CONFIG_SHAKE_SPEED ) ;
static auto * const * PINFLUENCE = ( Hyprlang : : FLOAT * const * ) getConfig ( CONFIG_SHAKE_INFLUENCE ) ;
static auto * const * PLIMIT = ( Hyprlang : : FLOAT * const * ) getConfig ( CONFIG_SHAKE_LIMIT ) ;
static auto * const * PTIMEOUT = ( Hyprlang : : INT * const * ) getConfig ( CONFIG_SHAKE_TIMEOUT ) ;
2024-07-03 00:38:50 +02:00
static auto * const * PIPC = ( Hyprlang : : INT * const * ) getConfig ( CONFIG_SHAKE_IPC ) ;
2024-06-28 14:08:13 +02:00
2025-05-01 12:45:53 +02:00
int max = std : : max ( 1 , ( int ) ( g_pHyprRenderer - > m_pMostHzMonitor - > m_refreshRate ) ) ; // 1s worth of history, avoiding divide by 0
2024-06-28 14:08:13 +02:00
samples . resize ( max ) ;
samples_distance . resize ( max ) ;
int previous = samples_index = = 0 ? max - 1 : samples_index - 1 ;
samples [ samples_index ] = Vector2D { pos } ;
samples_distance [ samples_index ] = samples [ samples_index ] . distance ( samples [ previous ] ) ;
samples_index = ( samples_index + 1 ) % max ; // increase for next sample
// The idea for this algorith was largely inspired by KDE Plasma
// https://invent.kde.org/plasma/kwin/-/blob/master/src/plugins/shakecursor/shakedetector.cpp
// calculate total distance travelled
double trail = 0 ;
for ( double distance : samples_distance ) trail + = distance ;
// calculate diagonal of bounding box travelled within
double left = 1e100 , right = 0 , bottom = 0 , top = 1e100 ;
for ( Vector2D position : samples ) {
left = std : : min ( left , position . x ) ;
right = std : : max ( right , position . x ) ;
top = std : : min ( top , position . y ) ;
bottom = std : : max ( bottom , position . y ) ;
}
double diagonal = Vector2D { left , top } . distance ( Vector2D ( right , bottom ) ) ;
2024-08-25 20:00:16 +02:00
// if diagonal sufficiently large and over threshold
double amount = ( trail / diagonal ) - * * PTHRESHOLD ;
if ( diagonal > 100 & & amount > 0 ) {
2025-05-01 12:45:53 +02:00
float delta = 1.F / g_pHyprRenderer - > m_pMostHzMonitor - > m_refreshRate ;
2024-08-25 20:00:16 +02:00
2025-01-08 22:04:30 +01:00
float next = this - > zoom - > goal ( ) ;
2024-06-28 14:08:13 +02:00
2024-08-25 20:00:16 +02:00
if ( ! started ) next = * * PBASE ; // start on base zoom
next + = delta * ( * * PSPEED + ( amount * amount ) * * * PINFLUENCE ) ; // increase when moving
if ( * * PLIMIT > 1 ) next = std : : min ( * * PLIMIT , next ) ; // limit overall zoom
2025-01-08 22:04:30 +01:00
* this - > zoom = next ;
2024-08-25 20:00:16 +02:00
this - > end = steady_clock : : now ( ) + milliseconds ( * * PTIMEOUT ) ;
started = true ;
} else {
if ( started & & end < std : : chrono : : steady_clock : : now ( ) ) {
2025-01-08 22:04:30 +01:00
* this - > zoom = 1 ;
2024-08-25 20:00:16 +02:00
started = false ;
}
}
2024-06-28 18:03:59 +02:00
if ( * * PIPC ) {
2025-01-08 22:04:30 +01:00
if ( started | | this - > zoom - > value ( ) > 1 ) {
2024-06-28 18:03:59 +02:00
if ( ! ipc ) {
g_pEventManager - > postEvent ( SHyprIPCEvent { IPC_SHAKE_START } ) ;
ipc = true ;
}
2025-01-08 22:04:30 +01:00
g_pEventManager - > postEvent ( SHyprIPCEvent { IPC_SHAKE_UPDATE , std : : format ( " {},{},{},{},{} " , ( int ) pos . x , ( int ) pos . y , trail , diagonal , this - > zoom - > value ( ) ) } ) ;
2024-06-28 18:03:59 +02:00
} else {
if ( ipc ) {
g_pEventManager - > postEvent ( SHyprIPCEvent { IPC_SHAKE_END } ) ;
ipc = false ;
}
}
}
2025-01-08 22:04:30 +01:00
return this - > zoom - > value ( ) ;
2024-06-28 14:08:13 +02:00
}
2024-08-25 22:58:55 +02:00
2025-01-10 22:06:23 +01:00
void CShake : : force ( std : : optional < int > duration , std : : optional < float > size ) {
static auto * const * PTIMEOUT = ( Hyprlang : : INT * const * ) getConfig ( CONFIG_SHAKE_TIMEOUT ) ;
static auto * const * PBASE = ( Hyprlang : : FLOAT * const * ) getConfig ( CONFIG_SHAKE_BASE ) ;
started = true ;
* this - > zoom = size . value_or ( * * PBASE ) ;
this - > end = steady_clock : : now ( ) + milliseconds ( duration . value_or ( * * PTIMEOUT ) ) ;
}
2024-08-25 22:58:55 +02:00
void CShake : : warp ( Vector2D old , Vector2D pos ) {
auto delta = pos - old ;
for ( auto & sample : samples )
sample + = delta ;
}