2023-02-28 20:30:51 +00:00
# define WLR_USE_UNSTABLE
# include <unistd.h>
# include <any>
2023-05-01 02:57:48 +01:00
# include <hyprland/src/Compositor.hpp>
2025-12-08 15:22:46 +00:00
# include <hyprland/src/desktop/view/Window.hpp>
2023-05-01 02:57:48 +01:00
# include <hyprland/src/config/ConfigManager.hpp>
2025-01-02 18:17:21 +01:00
# include <hyprland/src/render/Renderer.hpp>
2026-02-23 16:57:22 +00:00
# include <hyprland/src/event/EventBus.hpp>
2025-11-18 16:49:28 +00:00
# include <hyprland/src/desktop/rule/windowRule/WindowRuleEffectContainer.hpp>
2026-04-26 16:01:45 +01:00
# include <hyprland/src/config/lua/bindings/LuaBindingsInternal.hpp>
# include <hyprland/src/config/lua/types/LuaConfigColor.hpp>
# include <hyprutils/string/VarList.hpp>
2023-02-28 20:30:51 +00:00
2025-03-24 11:26:31 +00:00
# include <algorithm>
2023-02-28 20:30:51 +00:00
# include "barDeco.hpp"
# include "globals.hpp"
2026-04-26 16:01:45 +01:00
extern " C " {
# include <lua.h>
# include <lauxlib.h>
}
2023-02-28 20:30:51 +00:00
// Do NOT change this function.
APICALL EXPORT std : : string PLUGIN_API_VERSION ( ) {
return HYPRLAND_API_VERSION ;
}
2026-02-23 16:57:22 +00:00
static void onNewWindow ( PHLWINDOW window ) {
if ( ! window - > m_X11DoesntWantBorders ) {
if ( std : : ranges : : any_of ( window - > m_windowDecorations , [ ] ( const auto & d ) { return d - > getDisplayName ( ) = = " Hyprbar " ; } ) )
2025-03-24 11:26:31 +00:00
return ;
2026-02-23 16:57:22 +00:00
auto bar = makeUnique < CHyprBar > ( window ) ;
2025-01-24 01:30:43 +00:00
g_pGlobalState - > bars . emplace_back ( bar ) ;
bar - > m_self = bar ;
2026-02-23 16:57:22 +00:00
HyprlandAPI : : addWindowDecoration ( PHANDLE , window , std : : move ( bar ) ) ;
2023-10-29 22:31:11 +00:00
}
2023-02-28 20:30:51 +00:00
}
2024-05-02 02:31:33 +01:00
static void onPreConfigReload ( ) {
2023-10-29 17:33:32 +00:00
g_pGlobalState - > buttons . clear ( ) ;
}
2026-04-26 16:01:45 +01:00
static void onConfigReloaded ( ) {
for ( auto & b : g_pGlobalState - > bars ) {
if ( ! b )
continue ;
b - > onConfigReloaded ( ) ;
}
}
2024-05-02 02:31:33 +01:00
static void onUpdateWindowRules ( PHLWINDOW window ) {
const auto BARIT = std : : find_if ( g_pGlobalState - > bars . begin ( ) , g_pGlobalState - > bars . end ( ) , [ window ] ( const auto & bar ) { return bar - > getOwner ( ) = = window ; } ) ;
if ( BARIT = = g_pGlobalState - > bars . end ( ) )
return ;
2024-08-08 15:59:51 -03:00
( * BARIT ) - > updateRules ( ) ;
window - > updateWindowDecos ( ) ;
2024-05-02 02:31:33 +01:00
}
2024-02-18 15:30:21 +00:00
Hyprlang : : CParseResult onNewButton ( const char * K , const char * V ) {
2026-04-26 16:01:45 +01:00
std : : string v = V ;
Hyprutils : : String : : CVarList vars ( v ) ;
2024-02-18 15:30:21 +00:00
2026-04-26 16:01:45 +01:00
Hyprlang : : CParseResult result ;
2023-10-29 17:33:32 +00:00
2025-01-08 17:13:42 +00:00
// hyprbars-button = bgcolor, size, icon, action, fgcolor
2023-10-29 17:33:32 +00:00
2024-02-18 15:30:21 +00:00
if ( vars [ 0 ] . empty ( ) | | vars [ 1 ] . empty ( ) ) {
2025-01-08 17:13:42 +00:00
result . setError ( " bgcolor and size cannot be empty " ) ;
2024-02-18 15:30:21 +00:00
return result ;
}
2023-10-29 17:33:32 +00:00
float size = 10 ;
try {
size = std : : stof ( vars [ 1 ] ) ;
2024-02-18 15:30:21 +00:00
} catch ( std : : exception & e ) {
2025-01-08 17:13:42 +00:00
result . setError ( " failed to parse size " ) ;
2024-02-18 15:30:21 +00:00
return result ;
}
2023-10-29 17:33:32 +00:00
2025-01-08 17:13:42 +00:00
bool userfg = false ;
auto fgcolor = configStringToInt ( " rgb(ffffff) " ) ;
2025-02-02 09:47:46 -03:00
auto bgcolor = configStringToInt ( vars [ 0 ] ) ;
2025-01-08 17:13:42 +00:00
if ( ! bgcolor ) {
result . setError ( " invalid bgcolor " ) ;
return result ;
}
if ( vars . size ( ) = = 5 ) {
userfg = true ;
fgcolor = configStringToInt ( vars [ 4 ] ) ;
}
if ( ! fgcolor ) {
result . setError ( " invalid fgcolor " ) ;
2024-11-19 22:45:50 +01:00
return result ;
}
2025-01-08 17:13:42 +00:00
g_pGlobalState - > buttons . push_back ( SHyprButton { vars [ 3 ] , userfg , * fgcolor , * bgcolor , size , vars [ 2 ] } ) ;
2023-10-29 22:31:11 +00:00
for ( auto & b : g_pGlobalState - > bars ) {
b - > m_bButtonsDirty = true ;
}
2024-02-18 15:30:21 +00:00
return result ;
2023-10-29 17:33:32 +00:00
}
2026-04-26 16:01:45 +01:00
int newLuaButton ( lua_State * L ) {
if ( ! lua_istable ( L , 1 ) )
return Config : : Lua : : Bindings : : Internal : : configError ( L , " add_button: expected a table { bg_color, fg_color, size, icon, action } " ) ;
SHyprButton button ;
{
Hyprutils : : Utils : : CScopeGuard x ( [ L ] { lua_pop ( L , 1 ) ; } ) ;
lua_getfield ( L , 1 , " bg_color " ) ;
Config : : Lua : : CLuaConfigColor parser ( 0 ) ;
auto err = parser . parse ( L ) ;
if ( err . errorCode ! = Config : : Lua : : PARSE_ERROR_OK )
return Config : : Lua : : Bindings : : Internal : : configError ( L , " add_button: failed to parse bg_color " ) ;
button . bgcol = parser . parsed ( ) ;
}
{
Hyprutils : : Utils : : CScopeGuard x ( [ L ] { lua_pop ( L , 1 ) ; } ) ;
lua_getfield ( L , 1 , " fg_color " ) ;
Config : : Lua : : CLuaConfigColor parser ( 0 ) ;
auto err = parser . parse ( L ) ;
if ( err . errorCode ! = Config : : Lua : : PARSE_ERROR_OK )
return Config : : Lua : : Bindings : : Internal : : configError ( L , " add_button: failed to parse fg_color " ) ;
button . fgcol = parser . parsed ( ) ;
}
{
Hyprutils : : Utils : : CScopeGuard x ( [ L ] { lua_pop ( L , 1 ) ; } ) ;
lua_getfield ( L , 1 , " size " ) ;
if ( ! lua_isnumber ( L , - 1 ) )
return Config : : Lua : : Bindings : : Internal : : configError ( L , " add_button: size must be an integer " ) ;
button . size = lua_tointeger ( L , - 1 ) ;
}
{
Hyprutils : : Utils : : CScopeGuard x ( [ L ] { lua_pop ( L , 1 ) ; } ) ;
lua_getfield ( L , 1 , " icon " ) ;
if ( ! lua_isstring ( L , - 1 ) )
return Config : : Lua : : Bindings : : Internal : : configError ( L , " add_button: icon must be a string " ) ;
button . icon = lua_tostring ( L , - 1 ) ;
}
{
Hyprutils : : Utils : : CScopeGuard x ( [ L ] { lua_pop ( L , 1 ) ; } ) ;
lua_getfield ( L , 1 , " action " ) ;
if ( ! lua_isstring ( L , - 1 ) )
return Config : : Lua : : Bindings : : Internal : : configError ( L , " add_button: action must be a string " ) ;
button . cmd = lua_tostring ( L , - 1 ) ;
}
g_pGlobalState - > buttons . push_back ( std : : move ( button ) ) ;
for ( auto & b : g_pGlobalState - > bars ) {
b - > m_bButtonsDirty = true ;
}
return 0 ;
}
2023-02-28 20:30:51 +00:00
APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT ( HANDLE handle ) {
2023-10-29 17:36:52 +00:00
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 ( ) ;
2023-10-29 17:36:52 +00:00
2025-10-23 20:54:45 +01:00
if ( HASH ! = CLIENT_HASH ) {
2023-10-29 17:36:52 +00:00
HyprlandAPI : : addNotification ( PHANDLE , " [hyprbars] Failure in initialization: Version mismatch (headers ver is not equal to running hyprland ver) " ,
2024-12-04 09:58:09 -05:00
CHyprColor { 1.0 , 0.2 , 0.2 , 1.0 } , 5000 ) ;
2023-10-29 17:36:52 +00:00
throw std : : runtime_error ( " [hb] Version mismatch " ) ;
}
2025-11-18 16:49:28 +00:00
g_pGlobalState = makeUnique < SGlobalState > ( ) ;
g_pGlobalState - > nobarRuleIdx = Desktop : : Rule : : windowEffects ( ) - > registerEffect ( " hyprbars:no_bar " ) ;
g_pGlobalState - > barColorRuleIdx = Desktop : : Rule : : windowEffects ( ) - > registerEffect ( " hyprbars:bar_color " ) ;
g_pGlobalState - > titleColorRuleIdx = Desktop : : Rule : : windowEffects ( ) - > registerEffect ( " hyprbars:title_color " ) ;
2023-02-28 20:30:51 +00:00
2026-04-26 16:01:45 +01:00
static auto P = Event : : bus ( ) - > m_events . window . open . listen ( [ & ] ( PHLWINDOW w ) { onNewWindow ( w ) ; } ) ;
2026-02-23 16:57:22 +00:00
static auto P3 = Event : : bus ( ) - > m_events . window . updateRules . listen ( [ & ] ( PHLWINDOW w ) { onUpdateWindowRules ( w ) ; } ) ;
2023-02-28 20:30:51 +00:00
2026-04-26 16:01:45 +01:00
g_pGlobalState - > config . barColor = makeShared < Config : : Values : : CColorValue > ( " plugin:hyprbars:bar_color " , " Change the bar color " , * configStringToInt ( " rgba(33333388) " ) ) ;
g_pGlobalState - > config . textColor = makeShared < Config : : Values : : CColorValue > ( " plugin:hyprbars:col.text " , " Change the text color " , * configStringToInt ( " rgba(ffffffff) " ) ) ;
g_pGlobalState - > config . inactiveButtonColor = makeShared < Config : : Values : : CColorValue > (
" plugin:hyprbars:inactive_button_color " , " Change the inactive button's color. 0x00000000 means unset " , * configStringToInt ( " rgba(00000000) " ) ) ;
g_pGlobalState - > config . barHeight = makeShared < Config : : Values : : CIntValue > ( " plugin:hyprbars:bar_height " , " Change the bar's height " , 15 ) ;
g_pGlobalState - > config . barTextSize = makeShared < Config : : Values : : CIntValue > ( " plugin:hyprbars:bar_text_size " , " Change the bar's text size " , 10 ) ;
g_pGlobalState - > config . barTitleEnabled = makeShared < Config : : Values : : CBoolValue > ( " plugin:hyprbars:bar_title_enabled " , " Whether to enable titles in the bar " , true ) ;
g_pGlobalState - > config . barBlur = makeShared < Config : : Values : : CBoolValue > ( " plugin:hyprbars:bar_blur " , " Whether to enable blur of the bar " , false ) ;
g_pGlobalState - > config . barTextFont = makeShared < Config : : Values : : CStringValue > ( " plugin:hyprbars:bar_text_font " , " Bar's text font " , " Sans " ) ;
g_pGlobalState - > config . barTextAlign = makeShared < Config : : Values : : CStringValue > ( " plugin:hyprbars:bar_text_align " , " Bar's text alignment " , " center " ) ;
g_pGlobalState - > config . barPartOfWindow =
makeShared < Config : : Values : : CBoolValue > ( " plugin:hyprbars:bar_part_of_window " , " Whether the bar is a part of the window (reserves space) " , true ) ;
g_pGlobalState - > config . barPrecedenceOverBorder =
makeShared < Config : : Values : : CBoolValue > ( " plugin:hyprbars:bar_precedence_over_border " , " Whether the bar is before, or after the border " , false ) ;
g_pGlobalState - > config . barButtonsAlignment = makeShared < Config : : Values : : CStringValue > ( " plugin:hyprbars:bar_buttons_alignment " , " Alignment of the bar buttons " , " right " ) ;
g_pGlobalState - > config . barPadding = makeShared < Config : : Values : : CIntValue > ( " plugin:hyprbars:bar_padding " , " Padding of the bar " , 7 ) ;
g_pGlobalState - > config . barButtonPadding = makeShared < Config : : Values : : CIntValue > ( " plugin:hyprbars:bar_button_padding " , " Padding of the bar buttons " , 5 ) ;
g_pGlobalState - > config . enabled = makeShared < Config : : Values : : CBoolValue > ( " plugin:hyprbars:enabled " , " Whether bars are enabled " , true ) ;
g_pGlobalState - > config . iconOnHover = makeShared < Config : : Values : : CBoolValue > ( " plugin:hyprbars:icon_on_hover " , " Whether to use an icon on hover of the buttons " , false ) ;
g_pGlobalState - > config . onDoubleClick = makeShared < Config : : Values : : CStringValue > ( " plugin:hyprbars:on_double_click " , " Action to execute on double click of the bar " , " " ) ;
HyprlandAPI : : addConfigValueV2 ( PHANDLE , g_pGlobalState - > config . barColor ) ;
HyprlandAPI : : addConfigValueV2 ( PHANDLE , g_pGlobalState - > config . textColor ) ;
HyprlandAPI : : addConfigValueV2 ( PHANDLE , g_pGlobalState - > config . inactiveButtonColor ) ;
HyprlandAPI : : addConfigValueV2 ( PHANDLE , g_pGlobalState - > config . barHeight ) ;
HyprlandAPI : : addConfigValueV2 ( PHANDLE , g_pGlobalState - > config . barTextSize ) ;
HyprlandAPI : : addConfigValueV2 ( PHANDLE , g_pGlobalState - > config . barTitleEnabled ) ;
HyprlandAPI : : addConfigValueV2 ( PHANDLE , g_pGlobalState - > config . barBlur ) ;
HyprlandAPI : : addConfigValueV2 ( PHANDLE , g_pGlobalState - > config . barTextFont ) ;
HyprlandAPI : : addConfigValueV2 ( PHANDLE , g_pGlobalState - > config . barTextAlign ) ;
HyprlandAPI : : addConfigValueV2 ( PHANDLE , g_pGlobalState - > config . barPartOfWindow ) ;
HyprlandAPI : : addConfigValueV2 ( PHANDLE , g_pGlobalState - > config . barPrecedenceOverBorder ) ;
HyprlandAPI : : addConfigValueV2 ( PHANDLE , g_pGlobalState - > config . barButtonsAlignment ) ;
HyprlandAPI : : addConfigValueV2 ( PHANDLE , g_pGlobalState - > config . barPadding ) ;
HyprlandAPI : : addConfigValueV2 ( PHANDLE , g_pGlobalState - > config . barButtonPadding ) ;
HyprlandAPI : : addConfigValueV2 ( PHANDLE , g_pGlobalState - > config . enabled ) ;
HyprlandAPI : : addConfigValueV2 ( PHANDLE , g_pGlobalState - > config . iconOnHover ) ;
HyprlandAPI : : addConfigValueV2 ( PHANDLE , g_pGlobalState - > config . onDoubleClick ) ;
if ( Config : : mgr ( ) - > type ( ) = = Config : : CONFIG_LEGACY )
HyprlandAPI : : addConfigKeyword ( PHANDLE , " plugin:hyprbars:hyprbars-button " , onNewButton , Hyprlang : : SHandlerOptions { } ) ;
else
HyprlandAPI : : addLuaFunction ( PHANDLE , " hyprbars " , " add_button " , : : newLuaButton ) ;
2026-02-23 16:57:22 +00:00
static auto P4 = Event : : bus ( ) - > m_events . config . preReload . listen ( [ & ] { onPreConfigReload ( ) ; } ) ;
2026-04-26 16:01:45 +01:00
static auto P5 = Event : : bus ( ) - > m_events . config . reloaded . listen ( [ & ] { onConfigReloaded ( ) ; } ) ;
2023-02-28 20:30:51 +00:00
// add deco to existing windows
2025-04-22 21:31:50 +02:00
for ( auto & w : g_pCompositor - > m_windows ) {
2025-04-29 14:21:14 +01:00
if ( w - > isHidden ( ) | | ! w - > m_isMapped )
2023-02-28 20:30:51 +00:00
continue ;
2026-02-23 16:57:22 +00:00
onNewWindow ( w ) ;
2023-02-28 20:30:51 +00:00
}
HyprlandAPI : : reloadConfig ( ) ;
return { " hyprbars " , " A plugin to add title bars to windows. " , " Vaxry " , " 1.0 " } ;
}
APICALL EXPORT void PLUGIN_EXIT ( ) {
2025-04-22 21:31:50 +02:00
for ( auto & m : g_pCompositor - > m_monitors )
2025-05-01 18:04:34 +01:00
m - > m_scheduledRecalc = true ;
2025-01-08 17:13:42 +00:00
2025-05-06 08:57:20 +07:00
g_pHyprRenderer - > m_renderPass . removeAllOfType ( " CBarPassElement " ) ;
2025-11-18 16:49:28 +00:00
Desktop : : Rule : : windowEffects ( ) - > unregisterEffect ( g_pGlobalState - > barColorRuleIdx ) ;
Desktop : : Rule : : windowEffects ( ) - > unregisterEffect ( g_pGlobalState - > titleColorRuleIdx ) ;
Desktop : : Rule : : windowEffects ( ) - > unregisterEffect ( g_pGlobalState - > nobarRuleIdx ) ;
2023-05-01 15:02:05 -04:00
}