2025-06-19 17:26:02 +02:00
# define WLR_USE_UNSTABLE
# include <unistd.h>
# include <vector>
# include <hyprland/src/includes.hpp>
# include <any>
# include <sstream>
# define private public
# include <hyprland/src/Compositor.hpp>
# include <hyprland/src/config/ConfigValue.hpp>
2026-04-26 16:01:45 +01:00
# include <hyprland/src/config/shared/animation/AnimationTree.hpp>
2025-06-19 17:26:02 +02:00
# include <hyprland/src/helpers/AnimatedVariable.hpp>
2025-08-29 05:58:09 -04:00
# include <hyprland/src/managers/animation/AnimationManager.hpp>
2025-06-19 17:26:02 +02:00
# include <hyprland/src/managers/eventLoop/EventLoopManager.hpp>
2026-02-23 16:57:22 +00:00
# include <hyprland/src/layout/LayoutManager.hpp>
2025-06-19 17:26:02 +02:00
# include <hyprland/src/config/ConfigManager.hpp>
2026-04-26 16:01:45 +01:00
# include <hyprland/src/config/values/types/BoolValue.hpp>
# include <hyprland/src/config/values/types/FloatValue.hpp>
# include <hyprland/src/config/values/types/StringValue.hpp>
2026-02-23 16:57:22 +00:00
# include <hyprland/src/event/EventBus.hpp>
2025-06-19 17:26:02 +02:00
# undef private
# include "globals.hpp"
# include <hyprutils/string/VarList.hpp>
# include <hyprutils/animation/BezierCurve.hpp>
using namespace Hyprutils : : String ;
using namespace Hyprutils : : Animation ;
2026-04-26 16:01:45 +01:00
static struct {
SP < Config : : Values : : CBoolValue > onlyOnMonitorChange ;
SP < Config : : Values : : CFloatValue > fadeOpacity , slideHeight , bounceStrength ;
SP < Config : : Values : : CStringValue > mode ;
} configValues ;
2025-06-19 17:26:02 +02:00
// Do NOT change this function.
APICALL EXPORT std : : string PLUGIN_API_VERSION ( ) {
return HYPRLAND_API_VERSION ;
}
static void onFocusChange ( PHLWINDOW window ) {
if ( ! window )
return ;
2025-06-19 17:39:44 +02:00
static PHLWINDOWREF lastWindow ;
if ( lastWindow = = window )
return ;
2026-04-26 16:01:45 +01:00
if ( configValues . onlyOnMonitorChange - > value ( ) & & lastWindow & & lastWindow - > m_monitor = = window - > m_monitor )
2025-11-15 20:13:23 +01:00
return ;
2025-06-19 17:39:44 +02:00
lastWindow = window ;
2026-04-26 16:01:45 +01:00
const auto PIN = Config : : animationTree ( ) - > getAnimationPropertyConfig ( " hyprfocusIn " ) ;
const auto POUT = Config : : animationTree ( ) - > getAnimationPropertyConfig ( " hyprfocusOut " ) ;
2025-06-19 17:39:44 +02:00
2026-04-26 16:01:45 +01:00
if ( configValues . mode - > value ( ) = = " flash " ) {
2025-11-19 20:07:46 +01:00
const auto ORIGINAL = window - > m_activeInactiveAlpha - > goal ( ) ;
2025-06-19 17:26:02 +02:00
window - > m_activeInactiveAlpha - > setConfig ( PIN ) ;
2026-04-26 16:01:45 +01:00
* window - > m_activeInactiveAlpha = configValues . fadeOpacity - > value ( ) ;
2025-06-19 17:26:02 +02:00
2025-11-19 20:07:46 +01:00
window - > m_activeInactiveAlpha - > setCallbackOnEnd ( [ w = PHLWINDOWREF { window } , POUT , ORIGINAL ] ( WP < CBaseAnimatedVariable > pav ) {
2025-06-19 17:26:02 +02:00
if ( ! w )
return ;
w - > m_activeInactiveAlpha - > setConfig ( POUT ) ;
2025-11-19 20:07:46 +01:00
* w - > m_activeInactiveAlpha = ORIGINAL ;
2025-06-19 17:26:02 +02:00
w - > m_activeInactiveAlpha - > setCallbackOnEnd ( nullptr ) ;
} ) ;
2026-04-26 16:01:45 +01:00
} else if ( configValues . mode - > value ( ) = = " bounce " ) {
2025-06-19 17:26:02 +02:00
const auto ORIGINAL = CBox { window - > m_realPosition - > goal ( ) , window - > m_realSize - > goal ( ) } ;
window - > m_realPosition - > setConfig ( PIN ) ;
window - > m_realSize - > setConfig ( PIN ) ;
2026-04-26 16:01:45 +01:00
auto box = ORIGINAL . copy ( ) . scaleFromCenter ( configValues . bounceStrength - > value ( ) ) ;
2025-06-19 17:26:02 +02:00
* window - > m_realPosition = box . pos ( ) ;
* window - > m_realSize = box . size ( ) ;
window - > m_realSize - > setCallbackOnEnd ( [ w = PHLWINDOWREF { window } , POUT , ORIGINAL ] ( WP < CBaseAnimatedVariable > pav ) {
if ( ! w )
return ;
w - > m_realSize - > setConfig ( POUT ) ;
w - > m_realPosition - > setConfig ( POUT ) ;
2025-11-13 00:13:28 +01:00
if ( w - > m_isFloating | | w - > isFullscreen ( ) ) {
2025-06-19 17:26:02 +02:00
* w - > m_realPosition = ORIGINAL . pos ( ) ;
* w - > m_realSize = ORIGINAL . size ( ) ;
} else
2026-02-23 16:57:22 +00:00
w - > layoutTarget ( ) - > recalc ( ) ;
2025-06-19 17:26:02 +02:00
w - > m_realSize - > setCallbackOnEnd ( nullptr ) ;
} ) ;
2026-04-26 16:01:45 +01:00
} else if ( configValues . mode - > value ( ) = = " slide " ) {
2025-06-19 17:26:02 +02:00
const auto ORIGINAL = window - > m_realPosition - > goal ( ) ;
window - > m_realPosition - > setConfig ( PIN ) ;
2026-04-26 16:01:45 +01:00
* window - > m_realPosition = ORIGINAL - Vector2D { 0.F , configValues . slideHeight - > value ( ) } ;
2025-06-19 17:26:02 +02:00
window - > m_realPosition - > setCallbackOnEnd ( [ w = PHLWINDOWREF { window } , POUT , ORIGINAL ] ( WP < CBaseAnimatedVariable > pav ) {
if ( ! w )
return ;
w - > m_realPosition - > setConfig ( POUT ) ;
2025-11-13 00:13:28 +01:00
if ( w - > m_isFloating | | w - > isFullscreen ( ) )
2025-06-19 17:26:02 +02:00
* w - > m_realPosition = ORIGINAL ;
else
2026-02-23 16:57:22 +00:00
w - > layoutTarget ( ) - > recalc ( ) ;
2025-06-19 17:26:02 +02:00
w - > m_realPosition - > setCallbackOnEnd ( nullptr ) ;
} ) ;
}
}
APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT ( HANDLE handle ) {
PHANDLE = handle ;
2025-10-23 20:54:45 +01:00
const std : : string HASH = __hyprland_api_get_hash ( ) ;
const std : : string CLIENT_HASH = __hyprland_api_get_client_hash ( ) ;
2025-06-19 17:26:02 +02:00
2025-10-23 20:54:45 +01:00
if ( HASH ! = CLIENT_HASH ) {
2025-06-19 17:26:02 +02:00
HyprlandAPI : : addNotification ( PHANDLE , " [hyprwinwrap] Failure in initialization: Version mismatch (headers ver is not equal to running hyprland ver) " ,
CHyprColor { 1.0 , 0.2 , 0.2 , 1.0 } , 5000 ) ;
throw std : : runtime_error ( " [hww] Version mismatch " ) ;
}
2026-02-23 16:57:22 +00:00
static auto P = Event : : bus ( ) - > m_events . window . active . listen ( [ & ] ( PHLWINDOW w , Desktop : : eFocusReason r ) { onFocusChange ( w ) ; } ) ;
2025-06-19 17:26:02 +02:00
2026-04-26 16:01:45 +01:00
configValues . mode = makeShared < Config : : Values : : CStringValue > ( " plugin:hyprfocus:mode " , " mode to use " , " flash " ) ;
configValues . onlyOnMonitorChange = makeShared < Config : : Values : : CBoolValue > ( " plugin:hyprfocus:only_on_monitor_change " , " whether to fire the animation only on monitor change " , false ) ;
configValues . fadeOpacity = makeShared < Config : : Values : : CFloatValue > ( " plugin:hyprfocus:fade_opacity " , " fade opacity " , 0.8F , Config : : Values : : SFloatValueOptions { . min = 0.F , . max = 1.F } ) ;
configValues . slideHeight = makeShared < Config : : Values : : CFloatValue > ( " plugin:hyprfocus:slide_height " , " slide height " , 20.F , Config : : Values : : SFloatValueOptions { . min = 0.F , . max = 150.F } ) ;
configValues . bounceStrength = makeShared < Config : : Values : : CFloatValue > ( " plugin:hyprfocus:bounce_strength " , " bounce strength " , 0.95F , Config : : Values : : SFloatValueOptions { . min = 0.F , . max = 1.F } ) ;
HyprlandAPI : : addConfigValueV2 ( PHANDLE , configValues . mode ) ;
HyprlandAPI : : addConfigValueV2 ( PHANDLE , configValues . onlyOnMonitorChange ) ;
HyprlandAPI : : addConfigValueV2 ( PHANDLE , configValues . fadeOpacity ) ;
HyprlandAPI : : addConfigValueV2 ( PHANDLE , configValues . slideHeight ) ;
HyprlandAPI : : addConfigValueV2 ( PHANDLE , configValues . bounceStrength ) ;
2025-06-19 17:26:02 +02:00
// this will not be cleaned up after we are unloaded but it doesn't really matter,
// as if we create this again it will just overwrite the old one.
2026-04-26 16:01:45 +01:00
Config : : animationTree ( ) - > m_animationTree . createNode ( " hyprfocusIn " , " windowsIn " ) ;
Config : : animationTree ( ) - > m_animationTree . createNode ( " hyprfocusOut " , " windowsOut " ) ;
2025-06-19 17:26:02 +02:00
return { " hyprfocus " , " Flashfocus for Hyprland " , " Vaxry " , " 1.0 " } ;
}
APICALL EXPORT void PLUGIN_EXIT ( ) {
// reset the callbacks to avoid crashes
for ( const auto & w : g_pCompositor - > m_windows ) {
if ( ! validMapped ( w ) )
continue ;
w - > m_realSize - > setCallbackOnEnd ( nullptr ) ;
w - > m_realPosition - > setCallbackOnEnd ( nullptr ) ;
w - > m_activeInactiveAlpha - > setCallbackOnEnd ( nullptr ) ;
}
}