bars/vkfix/focus: chase hyprland

Chase hyprland changes, fix for lua. Too lazy to do expo and b++ rn.
This commit is contained in:
Vaxry
2026-04-26 16:01:45 +01:00
parent 6acc0738f2
commit dbe221941a
11 changed files with 507 additions and 434 deletions

View File

@ -10,9 +10,16 @@
#include <hyprland/src/managers/SeatManager.hpp>
#include <hyprland/src/render/Renderer.hpp>
#include <hyprland/src/event/EventBus.hpp>
#include <hyprland/src/config/values/types/BoolValue.hpp>
#include <hyprland/src/config/lua/bindings/LuaBindingsInternal.hpp>
#include "globals.hpp"
extern "C" {
#include <lua.h>
#include <lauxlib.h>
}
#include <hyprutils/string/ConstVarList.hpp>
using namespace Hyprutils::String;
@ -24,6 +31,10 @@ typedef void (*origMotion)(CSeatManager*, uint32_t, const Vector2D&);
typedef void (*origSurfaceSize)(CXWaylandSurface*, const CBox&);
typedef CRegion (*origWLSurfaceDamage)(Desktop::View::CWLSurface*);
static struct {
SP<Config::Values::CBoolValue> fixMouse;
} configValues;
// Do NOT change this function.
APICALL EXPORT std::string PLUGIN_API_VERSION() {
return HYPRLAND_API_VERSION;
@ -46,8 +57,6 @@ static const SAppConfig* getAppConfig(const std::string& appClass) {
}
void hkNotifyMotion(CSeatManager* thisptr, uint32_t time_msec, const Vector2D& local) {
static auto* const PFIX = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:csgo-vulkan-fix:fix_mouse")->getDataStaticPtr();
Vector2D newCoords = local;
auto focusState = Desktop::focusState();
auto window = focusState->window();
@ -55,7 +64,7 @@ void hkNotifyMotion(CSeatManager* thisptr, uint32_t time_msec, const Vector2D& l
const auto CONFIG = window && monitor ? getAppConfig(window->m_initialClass) : nullptr;
if (**PFIX && CONFIG) {
if (configValues.fixMouse->value() && CONFIG) {
// fix the coords
newCoords.x *= (CONFIG->res.x / monitor->m_size.x) / window->m_X11SurfaceScaledBy;
newCoords.y *= (CONFIG->res.y / monitor->m_size.y) / window->m_X11SurfaceScaledBy;
@ -109,6 +118,50 @@ CRegion hkWLSurfaceDamage(Desktop::View::CWLSurface* thisptr) {
return RG;
}
int vkfixAppLua(lua_State* L) {
if (!lua_istable(L, 1))
return Config::Lua::Bindings::Internal::configError(L, "vkfix_app: expected a table { app, w, h }");
SAppConfig config;
{
Hyprutils::Utils::CScopeGuard x([L] { lua_pop(L, 1); });
lua_getfield(L, 1, "app");
if (!lua_isstring(L, -1))
return Config::Lua::Bindings::Internal::configError(L, "vkfix_app: app must be a class string");
config.szClass = lua_tostring(L, -1);
}
{
Hyprutils::Utils::CScopeGuard x([L] { lua_pop(L, 1); });
lua_getfield(L, 1, "w");
if (!lua_isinteger(L, -1))
return Config::Lua::Bindings::Internal::configError(L, "vkfix_app: w must be an integer");
config.res.x = lua_tointeger(L, -1);
}
{
Hyprutils::Utils::CScopeGuard x([L] { lua_pop(L, 1); });
lua_getfield(L, 1, "h");
if (!lua_isinteger(L, -1))
return Config::Lua::Bindings::Internal::configError(L, "vkfix_app: h must be an integer");
config.res.y = lua_tointeger(L, -1);
}
g_appConfigs.emplace_back(std::move(config));
return 0;
}
APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
PHANDLE = handle;
@ -121,47 +174,45 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
throw std::runtime_error("[vkfix] Version mismatch");
}
HyprlandAPI::addConfigValue(PHANDLE, "plugin:csgo-vulkan-fix:res_w", Hyprlang::INT{1680});
HyprlandAPI::addConfigValue(PHANDLE, "plugin:csgo-vulkan-fix:res_h", Hyprlang::INT{1050});
HyprlandAPI::addConfigValue(PHANDLE, "plugin:csgo-vulkan-fix:fix_mouse", Hyprlang::INT{1});
HyprlandAPI::addConfigValue(PHANDLE, "plugin:csgo-vulkan-fix:class", Hyprlang::STRING{"cs2"});
static auto P = Event::bus()->m_events.config.preReload.listen([&] { g_appConfigs.clear(); });
static auto P = Event::bus()->m_events.config.preReload.listen([&] {
g_appConfigs.clear();
if (Config::mgr()->type() == Config::CONFIG_LEGACY) {
HyprlandAPI::addConfigKeyword(
PHANDLE, "vkfix-app",
[](const char* l, const char* r) -> Hyprlang::CParseResult {
const std::string str = r;
CConstVarList data(str, 0, ',', true);
static auto* const RESX = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:csgo-vulkan-fix:res_w")->getDataStaticPtr();
static auto* const RESY = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:csgo-vulkan-fix:res_h")->getDataStaticPtr();
static auto* const PCLASS = (Hyprlang::STRING const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:csgo-vulkan-fix:class")->getDataStaticPtr();
Hyprlang::CParseResult result;
g_appConfigs.emplace_back(SAppConfig{.szClass = *PCLASS, .res = Vector2D{(int)**RESX, (int)**RESY}});
});
if (data.size() != 3) {
result.setError("vkfix-app requires 3 params");
return result;
}
HyprlandAPI::addConfigKeyword(
PHANDLE, "vkfix-app",
[](const char* l, const char* r) -> Hyprlang::CParseResult {
const std::string str = r;
CConstVarList data(str, 0, ',', true);
try {
SAppConfig config;
config.szClass = data[0];
config.res = Vector2D{std::stoi(std::string{data[1]}), std::stoi(std::string{data[2]})};
g_appConfigs.emplace_back(std::move(config));
} catch (std::exception& e) {
result.setError("failed to parse line");
return result;
}
Hyprlang::CParseResult result;
if (data.size() != 3) {
result.setError("vkfix-app requires 3 params");
return result;
}
},
Hyprlang::SHandlerOptions{});
} else if (Config::mgr()->type() == Config::CONFIG_LUA) {
HyprlandAPI::addLuaFunction(PHANDLE, "csgo_vulkan_fix", "vkfix_app", ::vkfixAppLua);
} else {
HyprlandAPI::addNotification(PHANDLE, "[csgo-vulkan-fix] Failure in initialization: Failed to get a valid config manager", CHyprColor{1.0, 0.2, 0.2, 1.0}, 5000);
throw std::runtime_error("[vkfix] Config manager bad");
}
try {
SAppConfig config;
config.szClass = data[0];
config.res = Vector2D{std::stoi(std::string{data[1]}), std::stoi(std::string{data[2]})};
g_appConfigs.emplace_back(std::move(config));
} catch (std::exception& e) {
result.setError("failed to parse line");
return result;
}
return result;
},
Hyprlang::SHandlerOptions{});
configValues.fixMouse =
makeShared<Config::Values::CBoolValue>("plugin:csgo_vulkan_fix:fix_mouse", "Whether to fix the mouse position. A select few apps might be wonky with this.", true);
HyprlandAPI::addConfigValueV2(PHANDLE, configValues.fixMouse);
auto FNS = HyprlandAPI::findFunctionsByName(PHANDLE, "sendPointerMotion");
for (auto& fn : FNS) {
@ -211,5 +262,5 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
}
APICALL EXPORT void PLUGIN_EXIT() {
;
configValues = {};
}

View File

@ -1,32 +1,34 @@
#include "BarPassElement.hpp"
#include <hyprland/src/render/OpenGL.hpp>
#include <hyprland/src/render/Renderer.hpp>
#include "barDeco.hpp"
using namespace Render::GL;
CBarPassElement::CBarPassElement(const CBarPassElement::SBarData& data_) : data(data_) {
;
}
void CBarPassElement::draw(const CRegion& damage) {
data.deco->renderPass(g_pHyprOpenGL->m_renderData.pMonitor.lock(), data.a);
std::vector<UP<IPassElement>> CBarPassElement::draw() {
data.deco->renderPass(g_pHyprRenderer->m_renderData.pMonitor.lock(), data.a);
return {};
}
bool CBarPassElement::needsLiveBlur() {
static auto* const PCOLOR = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_color")->getDataStaticPtr();
static auto* const PENABLEBLUR = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_blur")->getDataStaticPtr();
static auto* const PENABLEBLURGLOBAL = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "decoration:blur:enabled")->getDataStaticPtr();
CHyprColor color = data.deco->m_bForcedBarColor.value_or(**PCOLOR);
CHyprColor color = data.deco->m_bForcedBarColor.value_or(CHyprColor{static_cast<uint64_t>(g_pGlobalState->config.barColor->value())});
color.a *= data.a;
const bool SHOULDBLUR = **PENABLEBLUR && **PENABLEBLURGLOBAL && color.a < 1.F;
const bool SHOULDBLUR = g_pGlobalState->config.barBlur->value() && **PENABLEBLURGLOBAL && color.a < 1.F;
return SHOULDBLUR;
}
std::optional<CBox> CBarPassElement::boundingBox() {
// Temporary fix: expand the bar bb a bit, otherwise occlusion gets too aggressive.
return data.deco->assignedBoxGlobal().translate(-g_pHyprOpenGL->m_renderData.pMonitor->m_position).expand(10);
return data.deco->assignedBoxGlobal().translate(-g_pHyprRenderer->m_renderData.pMonitor->m_position).expand(10);
}
bool CBarPassElement::needsPrecomputeBlur() {
return false;
}
}

View File

@ -13,15 +13,19 @@ class CBarPassElement : public IPassElement {
CBarPassElement(const SBarData& data_);
virtual ~CBarPassElement() = default;
virtual void draw(const CRegion& damage);
virtual bool needsLiveBlur();
virtual bool needsPrecomputeBlur();
virtual std::optional<CBox> boundingBox();
virtual std::vector<UP<IPassElement>> draw() override;
virtual bool needsLiveBlur() override;
virtual bool needsPrecomputeBlur() override;
virtual std::optional<CBox> boundingBox() override;
virtual const char* passName() {
virtual const char* passName() override {
return "CBarPassElement";
}
virtual ePassElementType type() override {
return EK_CUSTOM;
}
private:
SBarData data;
};

View File

@ -17,7 +17,6 @@ pkg_check_modules(deps REQUIRED IMPORTED_TARGET
libdrm
libinput
libudev
pangocairo
pixman-1
wayland-server
xkbcommon

View File

@ -7,8 +7,8 @@ endif
CXXFLAGS ?= -O2
CXXFLAGS += -shared -fPIC -std=c++2b -Wno-c++11-narrowing
INCLUDES = `pkg-config --cflags pixman-1 libdrm hyprland pangocairo libinput libudev wayland-server xkbcommon`
LIBS = `pkg-config --libs pangocairo`
INCLUDES = `pkg-config --cflags pixman-1 libdrm hyprland libinput libudev wayland-server xkbcommon`
LIBS =
SRC = main.cpp barDeco.cpp BarPassElement.cpp
TARGET = hyprbars.so

View File

@ -8,21 +8,28 @@
#include <hyprland/src/managers/input/InputManager.hpp>
#include <hyprland/src/render/Renderer.hpp>
#include <hyprland/src/config/ConfigManager.hpp>
#include <hyprland/src/config/shared/animation/AnimationTree.hpp>
#include <hyprland/src/config/supplementary/executor/Executor.hpp>
#include <hyprland/src/config/shared/actions/ConfigActions.hpp>
#include <hyprland/src/managers/animation/AnimationManager.hpp>
#include <hyprland/src/protocols/LayerShell.hpp>
#include <hyprland/src/event/EventBus.hpp>
#include <hyprland/src/layout/LayoutManager.hpp>
#include <pango/pangocairo.h>
#include <hyprland/src/render/OpenGL.hpp>
#include "globals.hpp"
#include "BarPassElement.hpp"
using namespace Render::GL;
static CHyprColor configColor(Config::INTEGER color) {
return CHyprColor{static_cast<uint64_t>(color)};
}
CHyprBar::CHyprBar(PHLWINDOW pWindow) : IHyprWindowDecoration(pWindow) {
m_pWindow = pWindow;
static auto* const PCOLOR = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_color")->getDataStaticPtr();
const auto PMONITOR = pWindow->m_monitor.lock();
const auto PMONITOR = pWindow->m_monitor.lock();
PMONITOR->m_scheduledRecalc = true;
// button events
@ -34,10 +41,8 @@ CHyprBar::CHyprBar(PHLWINDOW pWindow) : IHyprWindowDecoration(pWindow) {
m_pTouchMoveCallback = Event::bus()->m_events.input.touch.motion.listen([&](ITouch::SMotionEvent e, Event::SCallbackInfo& info) { onTouchMove(info, e); });
m_pMouseMoveCallback = Event::bus()->m_events.input.mouse.move.listen([&](Vector2D c, Event::SCallbackInfo& info) { onMouseMove(c); });
m_pTextTex = makeShared<CTexture>();
m_pButtonsTex = makeShared<CTexture>();
g_pAnimationManager->createAnimation(CHyprColor{**PCOLOR}, m_cRealBarColor, g_pConfigManager->getAnimationPropertyConfig("border"), pWindow, AVARDAMAGE_NONE);
g_pAnimationManager->createAnimation(configColor(g_pGlobalState->config.barColor->value()), m_cRealBarColor, Config::animationTree()->getAnimationPropertyConfig("border"),
pWindow, AVARDAMAGE_NONE);
m_cRealBarColor->setUpdateCallback([&](auto) { damageEntire(); });
}
@ -46,16 +51,16 @@ CHyprBar::~CHyprBar() {
}
SDecorationPositioningInfo CHyprBar::getPositioningInfo() {
static auto* const PHEIGHT = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_height")->getDataStaticPtr();
static auto* const PENABLED = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:enabled")->getDataStaticPtr();
static auto* const PPRECEDENCE = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_precedence_over_border")->getDataStaticPtr();
const auto HEIGHT = g_pGlobalState->config.barHeight->value();
const auto ENABLED = g_pGlobalState->config.enabled->value();
const auto PRECEDENCE = g_pGlobalState->config.barPrecedenceOverBorder->value();
SDecorationPositioningInfo info;
info.policy = m_hidden ? DECORATION_POSITION_ABSOLUTE : DECORATION_POSITION_STICKY;
info.edges = DECORATION_EDGE_TOP;
info.priority = **PPRECEDENCE ? 10005 : 5000;
info.priority = PRECEDENCE ? 10005 : 5000;
info.reserved = true;
info.desiredExtents = {{0, m_hidden || !**PENABLED ? 0 : **PHEIGHT}, {0, 0}};
info.desiredExtents = {{0, m_hidden || !ENABLED ? 0 : HEIGHT}, {0, 0}};
return info;
}
@ -71,9 +76,7 @@ std::string CHyprBar::getDisplayName() {
}
bool CHyprBar::inputIsValid() {
static auto* const PENABLED = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:enabled")->getDataStaticPtr();
if (!**PENABLED)
if (!g_pGlobalState->config.enabled->value())
return false;
if (!m_pWindow->m_workspace || !m_pWindow->m_workspace->isVisible() || !g_pInputManager->m_exclusiveLSes.empty() ||
@ -139,8 +142,7 @@ void CHyprBar::onTouchUp(Event::SCallbackInfo& info, ITouch::SUpEvent e) {
void CHyprBar::onMouseMove(Vector2D coords) {
// ensure proper redraws of button icons on hover when using hardware cursors
static auto* const PICONONHOVER = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:icon_on_hover")->getDataStaticPtr();
if (**PICONONHOVER)
if (g_pGlobalState->config.iconOnHover->value())
damageOnButtonHover();
if (!m_bDragPending || m_bTouchEv || !validMapped(m_pWindow) || m_touchId != 0)
@ -184,16 +186,15 @@ void CHyprBar::handleDownEvent(Event::SCallbackInfo& info, std::optional<ITouch:
COORDS = Vector2D(PMONITOR->m_position.x + e.pos.x * PMONITOR->m_size.x, PMONITOR->m_position.y + e.pos.y * PMONITOR->m_size.y) - assignedBoxGlobal().pos();
}
static auto* const PHEIGHT = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_height")->getDataStaticPtr();
static auto* const PBARBUTTONPADDING = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_button_padding")->getDataStaticPtr();
static auto* const PBARPADDING = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_padding")->getDataStaticPtr();
static auto* const PALIGNBUTTONS = (Hyprlang::STRING const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_buttons_alignment")->getDataStaticPtr();
static auto* const PONDOUBLECLICK = (Hyprlang::STRING const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:on_double_click")->getDataStaticPtr();
const auto HEIGHT = g_pGlobalState->config.barHeight->value();
const auto BARBUTTONPADDING = g_pGlobalState->config.barButtonPadding->value();
const auto BARPADDING = g_pGlobalState->config.barPadding->value();
const auto ALIGNBUTTONS = g_pGlobalState->config.barButtonsAlignment->value();
const auto ON_DOUBLE_CLICK = g_pGlobalState->config.onDoubleClick->value();
const bool BUTTONSRIGHT = std::string{*PALIGNBUTTONS} != "left";
const std::string ON_DOUBLE_CLICK = *PONDOUBLECLICK;
const bool BUTTONSRIGHT = ALIGNBUTTONS != "left";
if (!VECINRECT(COORDS, 0, 0, assignedBoxGlobal().w, **PHEIGHT - 1)) {
if (!VECINRECT(COORDS, 0, 0, assignedBoxGlobal().w, HEIGHT - 1)) {
if (m_bDraggingThis) {
if (m_bTouchEv)
@ -217,12 +218,12 @@ void CHyprBar::handleDownEvent(Event::SCallbackInfo& info, std::optional<ITouch:
info.cancelled = true;
m_bCancelledDown = true;
if (doButtonPress(PBARPADDING, PBARBUTTONPADDING, PHEIGHT, COORDS, BUTTONSRIGHT))
if (doButtonPress(BARPADDING, BARBUTTONPADDING, HEIGHT, COORDS, BUTTONSRIGHT))
return;
if (!ON_DOUBLE_CLICK.empty() &&
std::chrono::duration_cast<std::chrono::milliseconds>(Time::steadyNow() - m_lastMouseDown).count() < 400 /* Arbitrary delay I found suitable */) {
g_pKeybindManager->m_dispatchers["exec"](ON_DOUBLE_CLICK);
Config::Supplementary::executor()->spawn(ON_DOUBLE_CLICK);
m_bDragPending = false;
} else {
m_lastMouseDown = Time::steadyNow();
@ -240,10 +241,10 @@ void CHyprBar::handleUpEvent(Event::SCallbackInfo& info) {
m_bCancelledDown = false;
if (m_bDraggingThis) {
g_pKeybindManager->m_dispatchers["mouse"]("0movewindow");
g_pKeybindManager->changeMouseBindMode(MBIND_INVALID);
m_bDraggingThis = false;
if (m_bTouchEv)
g_pKeybindManager->m_dispatchers["settiled"]("activewindow");
Config::Actions::floatWindow(Config::Actions::eTogglableAction::TOGGLE_ACTION_DISABLE);
Log::logger->log(Log::DEBUG, "[hyprbars] Dragging ended on {:x}", (uintptr_t)m_pWindow.lock().get());
}
@ -254,187 +255,65 @@ void CHyprBar::handleUpEvent(Event::SCallbackInfo& info) {
}
void CHyprBar::handleMovement() {
g_pKeybindManager->m_dispatchers["mouse"]("1movewindow");
g_pKeybindManager->changeMouseBindMode(MBIND_MOVE);
m_bDraggingThis = true;
Log::logger->log(Log::DEBUG, "[hyprbars] Dragging initiated on {:x}", (uintptr_t)m_pWindow.lock().get());
return;
}
bool CHyprBar::doButtonPress(Hyprlang::INT* const* PBARPADDING, Hyprlang::INT* const* PBARBUTTONPADDING, Hyprlang::INT* const* PHEIGHT, Vector2D COORDS, const bool BUTTONSRIGHT) {
bool CHyprBar::doButtonPress(Config::INTEGER barPadding, Config::INTEGER barButtonPadding, Config::INTEGER barHeight, Vector2D COORDS, const bool BUTTONSRIGHT) {
//check if on a button
float offset = **PBARPADDING;
float offset = barPadding;
for (auto& b : g_pGlobalState->buttons) {
const auto BARBUF = Vector2D{(int)assignedBoxGlobal().w, **PHEIGHT};
Vector2D currentPos = Vector2D{(BUTTONSRIGHT ? BARBUF.x - **PBARBUTTONPADDING - b.size - offset : offset), (BARBUF.y - b.size) / 2.0}.floor();
const auto BARBUF = Vector2D{(int)assignedBoxGlobal().w, barHeight};
Vector2D currentPos = Vector2D{(BUTTONSRIGHT ? BARBUF.x - barButtonPadding - b.size - offset : offset), (BARBUF.y - b.size) / 2.0}.floor();
if (VECINRECT(COORDS, currentPos.x, currentPos.y, currentPos.x + b.size + **PBARBUTTONPADDING, currentPos.y + b.size)) {
if (VECINRECT(COORDS, currentPos.x, currentPos.y, currentPos.x + b.size + barButtonPadding, currentPos.y + b.size)) {
// hit on close
g_pKeybindManager->m_dispatchers["exec"](b.cmd);
return true;
}
offset += **PBARBUTTONPADDING + b.size;
offset += barButtonPadding + b.size;
}
return false;
}
void CHyprBar::renderText(SP<CTexture> out, const std::string& text, const CHyprColor& color, const Vector2D& bufferSize, const float scale, const int fontSize) {
const auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, bufferSize.x, bufferSize.y);
const auto CAIRO = cairo_create(CAIROSURFACE);
// clear the pixmap
cairo_save(CAIRO);
cairo_set_operator(CAIRO, CAIRO_OPERATOR_CLEAR);
cairo_paint(CAIRO);
cairo_restore(CAIRO);
// draw title using Pango
PangoLayout* layout = pango_cairo_create_layout(CAIRO);
pango_layout_set_text(layout, text.c_str(), -1);
PangoFontDescription* fontDesc = pango_font_description_from_string("sans");
pango_font_description_set_size(fontDesc, fontSize * scale * PANGO_SCALE);
pango_layout_set_font_description(layout, fontDesc);
pango_font_description_free(fontDesc);
const int maxWidth = bufferSize.x;
pango_layout_set_width(layout, maxWidth * PANGO_SCALE);
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_NONE);
cairo_set_source_rgba(CAIRO, color.r, color.g, color.b, color.a);
PangoRectangle ink_rect, logical_rect;
pango_layout_get_extents(layout, &ink_rect, &logical_rect);
const int layoutWidth = ink_rect.width;
const int layoutHeight = logical_rect.height;
const double xOffset = (bufferSize.x / 2.0 - layoutWidth / PANGO_SCALE / 2.0);
const double yOffset = (bufferSize.y / 2.0 - layoutHeight / PANGO_SCALE / 2.0);
cairo_move_to(CAIRO, xOffset, yOffset);
pango_cairo_show_layout(CAIRO, layout);
g_object_unref(layout);
cairo_surface_flush(CAIROSURFACE);
// copy the data to an OpenGL texture we have
const auto DATA = cairo_image_surface_get_data(CAIROSURFACE);
out->allocate();
glBindTexture(GL_TEXTURE_2D, out->m_texID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
#ifndef GLES2
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED);
#endif
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bufferSize.x, bufferSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA);
// delete cairo
cairo_destroy(CAIRO);
cairo_surface_destroy(CAIROSURFACE);
}
void CHyprBar::renderBarTitle(const Vector2D& bufferSize, const float scale) {
static auto* const PCOLOR = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:col.text")->getDataStaticPtr();
static auto* const PSIZE = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_text_size")->getDataStaticPtr();
static auto* const PFONT = (Hyprlang::STRING const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_text_font")->getDataStaticPtr();
static auto* const PALIGN = (Hyprlang::STRING const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_text_align")->getDataStaticPtr();
static auto* const PALIGNBUTTONS = (Hyprlang::STRING const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_buttons_alignment")->getDataStaticPtr();
static auto* const PBARPADDING = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_padding")->getDataStaticPtr();
static auto* const PBARBUTTONPADDING = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_button_padding")->getDataStaticPtr();
const auto COLORVAL = g_pGlobalState->config.textColor->value();
const auto SIZE = g_pGlobalState->config.barTextSize->value();
const auto FONT = g_pGlobalState->config.barTextFont->value();
const auto ALIGN = g_pGlobalState->config.barTextAlign->value();
const auto BARPADDING = g_pGlobalState->config.barPadding->value();
const auto BARBUTTONPADDING = g_pGlobalState->config.barButtonPadding->value();
const bool BUTTONSRIGHT = std::string{*PALIGNBUTTONS} != "left";
const auto PWINDOW = m_pWindow.lock();
const auto BORDERSIZE = PWINDOW->getRealBorderSize();
float buttonSizes = **PBARBUTTONPADDING;
float buttonSizes = BARBUTTONPADDING;
for (auto& b : g_pGlobalState->buttons) {
buttonSizes += b.size + **PBARBUTTONPADDING;
buttonSizes += b.size + BARBUTTONPADDING;
}
const auto scaledSize = **PSIZE * scale;
const auto scaledBorderSize = BORDERSIZE * scale;
const auto scaledButtonsSize = buttonSizes * scale;
const auto scaledButtonsPad = **PBARBUTTONPADDING * scale;
const auto scaledBarPadding = **PBARPADDING * scale;
const int scaledSize = std::round(SIZE * scale);
const auto scaledButtonsSize = buttonSizes * scale;
const auto scaledBarPadding = BARPADDING * scale;
const int paddingTotal = scaledBarPadding * 2 + scaledButtonsSize + (ALIGN != "left" ? scaledButtonsSize : 0);
const int maxWidth = std::clamp(static_cast<int>(bufferSize.x - paddingTotal), 0, INT_MAX);
const CHyprColor COLOR = m_bForcedTitleColor.value_or(**PCOLOR);
if (m_szLastTitle.empty() || maxWidth < 1) {
m_pTextTex = nullptr;
return;
}
const auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, bufferSize.x, bufferSize.y);
const auto CAIRO = cairo_create(CAIROSURFACE);
// clear the pixmap
cairo_save(CAIRO);
cairo_set_operator(CAIRO, CAIRO_OPERATOR_CLEAR);
cairo_paint(CAIRO);
cairo_restore(CAIRO);
// draw title using Pango
PangoLayout* layout = pango_cairo_create_layout(CAIRO);
pango_layout_set_text(layout, m_szLastTitle.c_str(), -1);
PangoFontDescription* fontDesc = pango_font_description_from_string(*PFONT);
pango_font_description_set_size(fontDesc, scaledSize * PANGO_SCALE);
pango_layout_set_font_description(layout, fontDesc);
pango_font_description_free(fontDesc);
PangoContext* context = pango_layout_get_context(layout);
pango_context_set_base_dir(context, PANGO_DIRECTION_NEUTRAL);
const int paddingTotal = scaledBarPadding * 2 + scaledButtonsSize + (std::string{*PALIGN} != "left" ? scaledButtonsSize : 0);
const int maxWidth = std::clamp(static_cast<int>(bufferSize.x - paddingTotal), 0, INT_MAX);
pango_layout_set_width(layout, maxWidth * PANGO_SCALE);
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END);
cairo_set_source_rgba(CAIRO, COLOR.r, COLOR.g, COLOR.b, COLOR.a);
int layoutWidth, layoutHeight;
pango_layout_get_size(layout, &layoutWidth, &layoutHeight);
const int xOffset = std::string{*PALIGN} == "left" ? std::round(scaledBarPadding + (BUTTONSRIGHT ? 0 : scaledButtonsSize)) :
std::round(((bufferSize.x - scaledBorderSize) / 2.0 - layoutWidth / PANGO_SCALE / 2.0));
const int yOffset = std::round((bufferSize.y / 2.0 - layoutHeight / PANGO_SCALE / 2.0));
cairo_move_to(CAIRO, xOffset, yOffset);
pango_cairo_show_layout(CAIRO, layout);
g_object_unref(layout);
cairo_surface_flush(CAIROSURFACE);
// copy the data to an OpenGL texture we have
const auto DATA = cairo_image_surface_get_data(CAIROSURFACE);
m_pTextTex->allocate();
glBindTexture(GL_TEXTURE_2D, m_pTextTex->m_texID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
#ifndef GLES2
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED);
#endif
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bufferSize.x, bufferSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA);
// delete cairo
cairo_destroy(CAIRO);
cairo_surface_destroy(CAIROSURFACE);
const CHyprColor COLOR = m_bForcedTitleColor.value_or(configColor(COLORVAL));
m_pTextTex = g_pHyprRenderer->renderText(m_szLastTitle, COLOR, scaledSize, false, FONT, maxWidth);
}
size_t CHyprBar::getVisibleButtonCount(Hyprlang::INT* const* PBARBUTTONPADDING, Hyprlang::INT* const* PBARPADDING, const Vector2D& bufferSize, const float scale) {
float availableSpace = bufferSize.x - **PBARPADDING * scale * 2;
size_t CHyprBar::getVisibleButtonCount(Config::INTEGER barButtonPadding, Config::INTEGER barPadding, const Vector2D& bufferSize, const float scale) {
float availableSpace = bufferSize.x - barPadding * scale * 2;
size_t count = 0;
for (const auto& button : g_pGlobalState->buttons) {
const float buttonSpace = (button.size + **PBARBUTTONPADDING) * scale;
const float buttonSpace = (button.size + barButtonPadding) * scale;
if (availableSpace >= buttonSpace) {
count++;
availableSpace -= buttonSpace;
@ -445,106 +324,82 @@ size_t CHyprBar::getVisibleButtonCount(Hyprlang::INT* const* PBARBUTTONPADDING,
return count;
}
void CHyprBar::renderBarButtons(const Vector2D& bufferSize, const float scale) {
static auto* const PBARBUTTONPADDING = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_button_padding")->getDataStaticPtr();
static auto* const PBARPADDING = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_padding")->getDataStaticPtr();
static auto* const PALIGNBUTTONS = (Hyprlang::STRING const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_buttons_alignment")->getDataStaticPtr();
static auto* const PINACTIVECOLOR = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:inactive_button_color")->getDataStaticPtr();
void CHyprBar::renderBarButtons(CBox* barBox, const float scale, const float a) {
const auto BARBUTTONPADDING = g_pGlobalState->config.barButtonPadding->value();
const auto BARPADDING = g_pGlobalState->config.barPadding->value();
const auto ALIGNBUTTONS = g_pGlobalState->config.barButtonsAlignment->value();
const auto INACTIVECOLOR = g_pGlobalState->config.inactiveButtonColor->value();
const bool BUTTONSRIGHT = std::string{*PALIGNBUTTONS} != "left";
const auto visibleCount = getVisibleButtonCount(PBARBUTTONPADDING, PBARPADDING, bufferSize, scale);
const bool BUTTONSRIGHT = ALIGNBUTTONS != "left";
const auto visibleCount = getVisibleButtonCount(BARBUTTONPADDING, BARPADDING, Vector2D{barBox->w, barBox->h}, scale);
const bool INVALIDATEICONS = m_bButtonsDirty || m_bWindowSizeChanged;
const auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, bufferSize.x, bufferSize.y);
const auto CAIRO = cairo_create(CAIROSURFACE);
// clear the pixmap
cairo_save(CAIRO);
cairo_set_operator(CAIRO, CAIRO_OPERATOR_CLEAR);
cairo_paint(CAIRO);
cairo_restore(CAIRO);
// draw buttons
int offset = **PBARPADDING * scale;
int offset = BARPADDING * scale;
for (size_t i = 0; i < visibleCount; ++i) {
const auto& button = g_pGlobalState->buttons[i];
const auto scaledButtonSize = button.size * scale;
const auto scaledButtonsPad = **PBARBUTTONPADDING * scale;
auto& button = g_pGlobalState->buttons[i];
const auto scaledButtonSize = button.size * scale;
const auto scaledButtonsPad = BARBUTTONPADDING * scale;
const auto pos = Vector2D{BUTTONSRIGHT ? bufferSize.x - offset - scaledButtonSize / 2.0 : offset + scaledButtonSize / 2.0, bufferSize.y / 2.0}.floor();
auto color = button.bgcol;
auto color = button.bgcol;
if (**PINACTIVECOLOR > 0) {
color = m_bWindowHasFocus ? color : CHyprColor(**PINACTIVECOLOR);
if (button.userfg && button.iconTex->m_texID != 0)
button.iconTex->destroyTexture();
if (INACTIVECOLOR > 0) {
color = m_bWindowHasFocus ? color : configColor(INACTIVECOLOR);
if (INVALIDATEICONS && button.userfg && button.iconTex)
button.iconTex = nullptr;
}
cairo_set_source_rgba(CAIRO, color.r, color.g, color.b, color.a);
cairo_arc(CAIRO, pos.x, pos.y, scaledButtonSize / 2, 0, 2 * M_PI);
cairo_fill(CAIRO);
color.a *= a;
CBox buttonBox = {barBox->x + (BUTTONSRIGHT ? barBox->w - offset - scaledButtonSize : offset), barBox->y + (barBox->h - scaledButtonSize) / 2.0, scaledButtonSize,
scaledButtonSize};
buttonBox.round();
g_pHyprOpenGL->renderRect(buttonBox, color, {.round = static_cast<int>(std::round(scaledButtonSize / 2.0)), .roundingPower = 2.F});
offset += scaledButtonsPad + scaledButtonSize;
}
// copy the data to an OpenGL texture we have
const auto DATA = cairo_image_surface_get_data(CAIROSURFACE);
m_pButtonsTex->allocate();
glBindTexture(GL_TEXTURE_2D, m_pButtonsTex->m_texID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
#ifndef GLES2
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED);
#endif
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bufferSize.x, bufferSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA);
// delete cairo
cairo_destroy(CAIRO);
cairo_surface_destroy(CAIROSURFACE);
}
void CHyprBar::renderBarButtonsText(CBox* barBox, const float scale, const float a) {
static auto* const PHEIGHT = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_height")->getDataStaticPtr();
static auto* const PBARBUTTONPADDING = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_button_padding")->getDataStaticPtr();
static auto* const PBARPADDING = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_padding")->getDataStaticPtr();
static auto* const PALIGNBUTTONS = (Hyprlang::STRING const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_buttons_alignment")->getDataStaticPtr();
static auto* const PICONONHOVER = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:icon_on_hover")->getDataStaticPtr();
const auto HEIGHT = g_pGlobalState->config.barHeight->value();
const auto BARBUTTONPADDING = g_pGlobalState->config.barButtonPadding->value();
const auto BARPADDING = g_pGlobalState->config.barPadding->value();
const auto ALIGNBUTTONS = g_pGlobalState->config.barButtonsAlignment->value();
const auto ICONONHOVER = g_pGlobalState->config.iconOnHover->value();
const bool BUTTONSRIGHT = std::string{*PALIGNBUTTONS} != "left";
const auto visibleCount = getVisibleButtonCount(PBARBUTTONPADDING, PBARPADDING, Vector2D{barBox->w, barBox->h}, scale);
const auto COORDS = cursorRelativeToBar();
const bool BUTTONSRIGHT = ALIGNBUTTONS != "left";
const auto visibleCount = getVisibleButtonCount(BARBUTTONPADDING, BARPADDING, Vector2D{barBox->w, barBox->h}, scale);
const auto COORDS = cursorRelativeToBar();
int offset = **PBARPADDING * scale;
float noScaleOffset = **PBARPADDING;
int offset = BARPADDING * scale;
float noScaleOffset = BARPADDING;
for (size_t i = 0; i < visibleCount; ++i) {
auto& button = g_pGlobalState->buttons[i];
const auto scaledButtonSize = button.size * scale;
const auto scaledButtonsPad = **PBARBUTTONPADDING * scale;
const auto scaledButtonsPad = BARBUTTONPADDING * scale;
// check if hovering here
const auto BARBUF = Vector2D{(int)assignedBoxGlobal().w, **PHEIGHT};
Vector2D currentPos = Vector2D{(BUTTONSRIGHT ? BARBUF.x - **PBARBUTTONPADDING - button.size - noScaleOffset : noScaleOffset), (BARBUF.y - button.size) / 2.0}.floor();
bool hovering = VECINRECT(COORDS, currentPos.x, currentPos.y, currentPos.x + button.size + **PBARBUTTONPADDING, currentPos.y + button.size);
noScaleOffset += **PBARBUTTONPADDING + button.size;
const auto BARBUF = Vector2D{(int)assignedBoxGlobal().w, HEIGHT};
Vector2D currentPos = Vector2D{(BUTTONSRIGHT ? BARBUF.x - BARBUTTONPADDING - button.size - noScaleOffset : noScaleOffset), (BARBUF.y - button.size) / 2.0}.floor();
bool hovering = VECINRECT(COORDS, currentPos.x, currentPos.y, currentPos.x + button.size + BARBUTTONPADDING, currentPos.y + button.size);
noScaleOffset += BARBUTTONPADDING + button.size;
if (button.iconTex->m_texID == 0 /* icon is not rendered */ && !button.icon.empty()) {
if ((!button.iconTex || button.iconTex->m_texID == 0) && !button.icon.empty()) {
// render icon
const Vector2D BUFSIZE = {scaledButtonSize, scaledButtonSize};
auto fgcol = button.userfg ? button.fgcol : (button.bgcol.r + button.bgcol.g + button.bgcol.b < 1) ? CHyprColor(0xFFFFFFFF) : CHyprColor(0xFF000000);
auto fgcol = button.userfg ? button.fgcol : (button.bgcol.r + button.bgcol.g + button.bgcol.b < 1) ? CHyprColor(0xFFFFFFFF) : CHyprColor(0xFF000000);
renderText(button.iconTex, button.icon, fgcol, BUFSIZE, scale, button.size * 0.62);
button.iconTex = g_pHyprRenderer->renderText(button.icon, fgcol, std::round(button.size * 0.62 * scale), false, "sans", scaledButtonSize);
}
if (button.iconTex->m_texID == 0)
if (!button.iconTex || button.iconTex->m_texID == 0)
continue;
CBox pos = {barBox->x + (BUTTONSRIGHT ? barBox->width - offset - scaledButtonSize : offset), barBox->y + (barBox->height - scaledButtonSize) / 2.0, scaledButtonSize,
scaledButtonSize};
const auto iconX = barBox->x + (BUTTONSRIGHT ? barBox->width - offset - scaledButtonSize / 2.0 : offset + scaledButtonSize / 2.0) - button.iconTex->m_size.x / 2.0;
const auto iconY = barBox->y + barBox->height / 2.0 - button.iconTex->m_size.y / 2.0;
CBox pos = {iconX, iconY, button.iconTex->m_size.x, button.iconTex->m_size.y};
if (!**PICONONHOVER || (**PICONONHOVER && m_iButtonHoverState > 0))
if (!ICONONHOVER || (ICONONHOVER && m_iButtonHoverState > 0))
g_pHyprOpenGL->renderTexture(button.iconTex, pos, {.a = a});
offset += scaledButtonsPad + scaledButtonSize;
@ -558,14 +413,14 @@ void CHyprBar::renderBarButtonsText(CBox* barBox, const float scale, const float
}
void CHyprBar::draw(PHLMONITOR pMonitor, const float& a) {
static auto* const PENABLED = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:enabled")->getDataStaticPtr();
const auto ENABLED = g_pGlobalState->config.enabled->value();
if (m_bLastEnabledState != **PENABLED) {
m_bLastEnabledState = **PENABLED;
if (m_bLastEnabledState != ENABLED) {
m_bLastEnabledState = ENABLED;
g_pDecorationPositioner->repositionDeco(this);
}
if (m_hidden || !validMapped(m_pWindow) || !**PENABLED)
if (m_hidden || !validMapped(m_pWindow) || !ENABLED)
return;
const auto PWINDOW = m_pWindow.lock();
@ -580,16 +435,16 @@ void CHyprBar::draw(PHLMONITOR pMonitor, const float& a) {
void CHyprBar::renderPass(PHLMONITOR pMonitor, const float& a) {
const auto PWINDOW = m_pWindow.lock();
static auto* const PCOLOR = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_color")->getDataStaticPtr();
static auto* const PHEIGHT = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_height")->getDataStaticPtr();
static auto* const PPRECEDENCE = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_precedence_over_border")->getDataStaticPtr();
static auto* const PALIGNBUTTONS = (Hyprlang::STRING const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_buttons_alignment")->getDataStaticPtr();
static auto* const PENABLETITLE = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_title_enabled")->getDataStaticPtr();
static auto* const PENABLEBLUR = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_blur")->getDataStaticPtr();
static auto* const PENABLEBLURGLOBAL = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "decoration:blur:enabled")->getDataStaticPtr();
static auto* const PINACTIVECOLOR = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:inactive_button_color")->getDataStaticPtr();
const auto BARCOLOR = g_pGlobalState->config.barColor->value();
const auto HEIGHT = g_pGlobalState->config.barHeight->value();
const auto PRECEDENCE = g_pGlobalState->config.barPrecedenceOverBorder->value();
const auto ALIGNBUTTONS = g_pGlobalState->config.barButtonsAlignment->value();
const auto ENABLETITLE = g_pGlobalState->config.barTitleEnabled->value();
const auto ENABLEBLUR = g_pGlobalState->config.barBlur->value();
const auto INACTIVECOLOR = g_pGlobalState->config.inactiveButtonColor->value();
if (**PINACTIVECOLOR > 0) {
if (INACTIVECOLOR > 0) {
bool currentWindowFocus = PWINDOW == Desktop::focusState()->window();
if (currentWindowFocus != m_bWindowHasFocus) {
m_bWindowHasFocus = currentWindowFocus;
@ -597,29 +452,29 @@ void CHyprBar::renderPass(PHLMONITOR pMonitor, const float& a) {
}
}
const CHyprColor DEST_COLOR = m_bForcedBarColor.value_or(**PCOLOR);
const CHyprColor DEST_COLOR = m_bForcedBarColor.value_or(configColor(BARCOLOR));
if (DEST_COLOR != m_cRealBarColor->goal())
*m_cRealBarColor = DEST_COLOR;
CHyprColor color = m_cRealBarColor->value();
color.a *= a;
const bool BUTTONSRIGHT = std::string{*PALIGNBUTTONS} != "left";
const bool SHOULDBLUR = **PENABLEBLUR && **PENABLEBLURGLOBAL && color.a < 1.F;
const bool BUTTONSRIGHT = ALIGNBUTTONS != "left";
const bool SHOULDBLUR = ENABLEBLUR && **PENABLEBLURGLOBAL && color.a < 1.F;
if (**PHEIGHT < 1) {
m_iLastHeight = **PHEIGHT;
if (HEIGHT < 1) {
m_iLastHeight = HEIGHT;
return;
}
const auto PWORKSPACE = PWINDOW->m_workspace;
const auto WORKSPACEOFFSET = PWORKSPACE && !PWINDOW->m_pinned ? PWORKSPACE->m_renderOffset->value() : Vector2D();
const auto ROUNDING = PWINDOW->rounding() + (*PPRECEDENCE ? 0 : PWINDOW->getRealBorderSize());
const auto ROUNDING = PWINDOW->rounding() + (PRECEDENCE ? 0 : PWINDOW->getRealBorderSize());
const auto scaledRounding = ROUNDING > 0 ? ROUNDING * pMonitor->m_scale - 2 /* idk why but otherwise it looks bad due to the gaps */ : 0;
m_seExtents = {{0, **PHEIGHT}, {}};
m_seExtents = {{0, HEIGHT}, {}};
const auto DECOBOX = assignedBoxGlobal();
@ -668,7 +523,7 @@ void CHyprBar::renderPass(PHLMONITOR pMonitor, const float& a) {
g_pHyprOpenGL->renderRect(titleBarBox, color, {.round = scaledRounding, .roundingPower = m_pWindow->roundingPower()});
// render title
if (**PENABLETITLE && (m_szLastTitle != PWINDOW->m_title || m_bWindowSizeChanged || m_pTextTex->m_texID == 0 || m_bTitleColorChanged)) {
if (ENABLETITLE && (m_szLastTitle != PWINDOW->m_title || m_bWindowSizeChanged || !m_pTextTex || m_pTextTex->m_texID == 0 || m_bTitleColorChanged)) {
m_szLastTitle = PWINDOW->m_title;
renderBarTitle(BARBUF, pMonitor->m_scale);
}
@ -683,15 +538,29 @@ void CHyprBar::renderPass(PHLMONITOR pMonitor, const float& a) {
}
CBox textBox = {titleBarBox.x, titleBarBox.y, (int)BARBUF.x, (int)BARBUF.y};
if (**PENABLETITLE)
g_pHyprOpenGL->renderTexture(m_pTextTex, textBox, {.a = a});
if (ENABLETITLE && m_pTextTex) {
const auto BARPADDING = g_pGlobalState->config.barPadding->value();
const auto BARBUTTONPADDING = g_pGlobalState->config.barButtonPadding->value();
const auto ALIGN = g_pGlobalState->config.barTextAlign->value();
if (m_bButtonsDirty || m_bWindowSizeChanged) {
renderBarButtons(BARBUF, pMonitor->m_scale);
m_bButtonsDirty = false;
float buttonSizes = BARBUTTONPADDING;
for (auto& b : g_pGlobalState->buttons) {
buttonSizes += b.size + BARBUTTONPADDING;
}
const auto scaledBorderSize = PWINDOW->getRealBorderSize() * pMonitor->m_scale;
const auto scaledButtonsSize = buttonSizes * pMonitor->m_scale;
const auto scaledBarPadding = BARPADDING * pMonitor->m_scale;
const auto xOffset = ALIGN == "left" ? std::round(scaledBarPadding + (BUTTONSRIGHT ? 0 : scaledButtonsSize)) :
std::round(((BARBUF.x - scaledBorderSize) / 2.0 - m_pTextTex->m_size.x / 2.0));
const auto yOffset = std::round((BARBUF.y - m_pTextTex->m_size.y) / 2.0);
CBox titleBox = {textBox.x + xOffset, textBox.y + yOffset, m_pTextTex->m_size.x, m_pTextTex->m_size.y};
g_pHyprOpenGL->renderTexture(m_pTextTex, titleBox, {.a = a});
}
g_pHyprOpenGL->renderTexture(m_pButtonsTex, textBox, {.a = a});
renderBarButtons(&textBox, pMonitor->m_scale, a);
m_bButtonsDirty = false;
g_pHyprOpenGL->scissor(nullptr);
@ -701,9 +570,9 @@ void CHyprBar::renderPass(PHLMONITOR pMonitor, const float& a) {
m_bTitleColorChanged = false;
// dynamic updates change the extents
if (m_iLastHeight != **PHEIGHT) {
if (m_iLastHeight != HEIGHT) {
PWINDOW->layoutTarget()->recalc();
m_iLastHeight = **PHEIGHT;
m_iLastHeight = HEIGHT;
}
}
@ -715,6 +584,15 @@ void CHyprBar::updateWindow(PHLWINDOW pWindow) {
damageEntire();
}
void CHyprBar::onConfigReloaded() {
m_bButtonsDirty = true;
m_bTitleColorChanged = true;
m_pTextTex = nullptr;
g_pDecorationPositioner->repositionDeco(this);
damageEntire();
}
void CHyprBar::damageEntire() {
g_pHyprRenderer->damageBox(assignedBoxGlobal());
}
@ -728,8 +606,7 @@ eDecorationLayer CHyprBar::getDecorationLayer() {
}
uint64_t CHyprBar::getDecorationFlags() {
static auto* const PPART = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_part_of_window")->getDataStaticPtr();
return DECORATION_ALLOWS_MOUSE_INPUT | (**PPART ? DECORATION_PART_OF_MAIN_WINDOW : 0);
return DECORATION_ALLOWS_MOUSE_INPUT | (g_pGlobalState->config.barPartOfWindow->value() ? DECORATION_PART_OF_MAIN_WINDOW : 0);
}
CBox CHyprBar::assignedBoxGlobal() {
@ -772,27 +649,27 @@ void CHyprBar::updateRules() {
}
void CHyprBar::damageOnButtonHover() {
static auto* const PBARPADDING = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_padding")->getDataStaticPtr();
static auto* const PBARBUTTONPADDING = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_button_padding")->getDataStaticPtr();
static auto* const PHEIGHT = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_height")->getDataStaticPtr();
static auto* const PALIGNBUTTONS = (Hyprlang::STRING const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_buttons_alignment")->getDataStaticPtr();
const bool BUTTONSRIGHT = std::string{*PALIGNBUTTONS} != "left";
const auto BARPADDING = g_pGlobalState->config.barPadding->value();
const auto BARBUTTONPADDING = g_pGlobalState->config.barButtonPadding->value();
const auto HEIGHT = g_pGlobalState->config.barHeight->value();
const auto ALIGNBUTTONS = g_pGlobalState->config.barButtonsAlignment->value();
const bool BUTTONSRIGHT = ALIGNBUTTONS != "left";
float offset = **PBARPADDING;
float offset = BARPADDING;
const auto COORDS = cursorRelativeToBar();
const auto COORDS = cursorRelativeToBar();
for (auto& b : g_pGlobalState->buttons) {
const auto BARBUF = Vector2D{(int)assignedBoxGlobal().w, **PHEIGHT};
Vector2D currentPos = Vector2D{(BUTTONSRIGHT ? BARBUF.x - **PBARBUTTONPADDING - b.size - offset : offset), (BARBUF.y - b.size) / 2.0}.floor();
const auto BARBUF = Vector2D{(int)assignedBoxGlobal().w, HEIGHT};
Vector2D currentPos = Vector2D{(BUTTONSRIGHT ? BARBUF.x - BARBUTTONPADDING - b.size - offset : offset), (BARBUF.y - b.size) / 2.0}.floor();
bool hover = VECINRECT(COORDS, currentPos.x, currentPos.y, currentPos.x + b.size + **PBARBUTTONPADDING, currentPos.y + b.size);
bool hover = VECINRECT(COORDS, currentPos.x, currentPos.y, currentPos.x + b.size + BARBUTTONPADDING, currentPos.y + b.size);
if (hover != m_bButtonHovered) {
m_bButtonHovered = hover;
damageEntire();
}
offset += **PBARBUTTONPADDING + b.size;
offset += BARBUTTONPADDING + b.size;
}
}

View File

@ -4,6 +4,7 @@
#include <hyprland/src/render/decorations/IHyprWindowDecoration.hpp>
#include <hyprland/src/render/OpenGL.hpp>
#include <hyprland/src/render/gl/GLTexture.hpp>
#include <hyprland/src/devices/IPointer.hpp>
#include <hyprland/src/devices/ITouch.hpp>
#include <hyprland/src/desktop/rule/windowRule/WindowRule.hpp>
@ -48,52 +49,51 @@ class CHyprBar : public IHyprWindowDecoration {
PHLWINDOW getOwner();
void updateRules();
void onConfigReloaded();
WP<CHyprBar> m_self;
private:
SBoxExtents m_seExtents;
SBoxExtents m_seExtents;
PHLWINDOWREF m_pWindow;
PHLWINDOWREF m_pWindow;
CBox m_bAssignedBox;
CBox m_bAssignedBox;
SP<CTexture> m_pTextTex;
SP<CTexture> m_pButtonsTex;
SP<Render::ITexture> m_pTextTex;
bool m_bWindowSizeChanged = false;
bool m_hidden = false;
bool m_bTitleColorChanged = false;
bool m_bButtonHovered = false;
bool m_bLastEnabledState = false;
bool m_bWindowHasFocus = false;
std::optional<CHyprColor> m_bForcedBarColor;
std::optional<CHyprColor> m_bForcedTitleColor;
bool m_bWindowSizeChanged = false;
bool m_hidden = false;
bool m_bTitleColorChanged = false;
bool m_bButtonHovered = false;
bool m_bLastEnabledState = false;
bool m_bWindowHasFocus = false;
std::optional<CHyprColor> m_bForcedBarColor;
std::optional<CHyprColor> m_bForcedTitleColor;
Time::steady_tp m_lastMouseDown = Time::steadyNow();
Time::steady_tp m_lastMouseDown = Time::steadyNow();
PHLANIMVAR<CHyprColor> m_cRealBarColor;
PHLANIMVAR<CHyprColor> m_cRealBarColor;
Vector2D cursorRelativeToBar();
Vector2D cursorRelativeToBar();
void renderPass(PHLMONITOR, float const& a);
void renderBarTitle(const Vector2D& bufferSize, const float scale);
void renderText(SP<CTexture> out, const std::string& text, const CHyprColor& color, const Vector2D& bufferSize, const float scale, const int fontSize);
void renderBarButtons(const Vector2D& bufferSize, const float scale);
void renderBarButtonsText(CBox* barBox, const float scale, const float a);
void damageOnButtonHover();
void renderPass(PHLMONITOR, float const& a);
void renderBarTitle(const Vector2D& bufferSize, const float scale);
void renderBarButtons(CBox* barBox, const float scale, const float a);
void renderBarButtonsText(CBox* barBox, const float scale, const float a);
void damageOnButtonHover();
bool inputIsValid();
void onMouseButton(Event::SCallbackInfo& info, IPointer::SButtonEvent e);
void onTouchDown(Event::SCallbackInfo& info, ITouch::SDownEvent e);
void onTouchUp(Event::SCallbackInfo& info, ITouch::SUpEvent e);
void onMouseMove(Vector2D coords);
void onTouchMove(Event::SCallbackInfo& info, ITouch::SMotionEvent e);
bool inputIsValid();
void onMouseButton(Event::SCallbackInfo& info, IPointer::SButtonEvent e);
void onTouchDown(Event::SCallbackInfo& info, ITouch::SDownEvent e);
void onTouchUp(Event::SCallbackInfo& info, ITouch::SUpEvent e);
void onMouseMove(Vector2D coords);
void onTouchMove(Event::SCallbackInfo& info, ITouch::SMotionEvent e);
void handleDownEvent(Event::SCallbackInfo& info, std::optional<ITouch::SDownEvent> touchEvent);
void handleUpEvent(Event::SCallbackInfo& info);
void handleMovement();
bool doButtonPress(Hyprlang::INT* const* PBARPADDING, Hyprlang::INT* const* PBARBUTTONPADDING, Hyprlang::INT* const* PHEIGHT, Vector2D COORDS, bool BUTTONSRIGHT);
void handleDownEvent(Event::SCallbackInfo& info, std::optional<ITouch::SDownEvent> touchEvent);
void handleUpEvent(Event::SCallbackInfo& info);
void handleMovement();
bool doButtonPress(Config::INTEGER barPadding, Config::INTEGER barButtonPadding, Config::INTEGER barHeight, Vector2D COORDS, bool BUTTONSRIGHT);
CBox assignedBoxGlobal();
@ -118,7 +118,7 @@ class CHyprBar : public IHyprWindowDecoration {
// for dynamic updates
int m_iLastHeight = 0;
size_t getVisibleButtonCount(Hyprlang::INT* const* PBARBUTTONPADDING, Hyprlang::INT* const* PBARPADDING, const Vector2D& bufferSize, const float scale);
size_t getVisibleButtonCount(Config::INTEGER barButtonPadding, Config::INTEGER barPadding, const Vector2D& bufferSize, const float scale);
friend class CBarPassElement;
};

View File

@ -2,17 +2,21 @@
#include <hyprland/src/plugins/PluginAPI.hpp>
#include <hyprland/src/render/Texture.hpp>
#include <hyprland/src/config/values/types/BoolValue.hpp>
#include <hyprland/src/config/values/types/IntValue.hpp>
#include <hyprland/src/config/values/types/StringValue.hpp>
#include <hyprland/src/config/values/types/ColorValue.hpp>
inline HANDLE PHANDLE = nullptr;
struct SHyprButton {
std::string cmd = "";
bool userfg = false;
CHyprColor fgcol = CHyprColor(0, 0, 0, 0);
CHyprColor bgcol = CHyprColor(0, 0, 0, 0);
float size = 10;
std::string icon = "";
SP<CTexture> iconTex = makeShared<CTexture>();
std::string cmd = "";
bool userfg = false;
CHyprColor fgcol = CHyprColor(0, 0, 0, 0);
CHyprColor bgcol = CHyprColor(0, 0, 0, 0);
float size = 10;
std::string icon = "";
SP<Render::ITexture> iconTex;
};
class CHyprBar;
@ -20,9 +24,19 @@ class CHyprBar;
struct SGlobalState {
std::vector<SHyprButton> buttons;
std::vector<WP<CHyprBar>> bars;
uint32_t nobarRuleIdx = 0;
uint32_t barColorRuleIdx = 0;
uint32_t nobarRuleIdx = 0;
uint32_t barColorRuleIdx = 0;
uint32_t titleColorRuleIdx = 0;
struct {
SP<Config::Values::CColorValue> barColor, textColor, inactiveButtonColor;
SP<Config::Values::CIntValue> barHeight;
SP<Config::Values::CIntValue> barTextSize;
SP<Config::Values::CIntValue> barPadding;
SP<Config::Values::CIntValue> barButtonPadding;
SP<Config::Values::CBoolValue> barBlur, barTitleEnabled, barPartOfWindow, barPrecedenceOverBorder, enabled, iconOnHover;
SP<Config::Values::CStringValue> barTextFont, barTextAlign, barButtonsAlignment, onDoubleClick;
} config;
};
inline UP<SGlobalState> g_pGlobalState;

View File

@ -9,12 +9,21 @@
#include <hyprland/src/render/Renderer.hpp>
#include <hyprland/src/event/EventBus.hpp>
#include <hyprland/src/desktop/rule/windowRule/WindowRuleEffectContainer.hpp>
#include <hyprland/src/config/lua/bindings/LuaBindingsInternal.hpp>
#include <hyprland/src/config/lua/types/LuaConfigColor.hpp>
#include <hyprutils/string/VarList.hpp>
#include <algorithm>
#include "barDeco.hpp"
#include "globals.hpp"
extern "C" {
#include <lua.h>
#include <lauxlib.h>
}
// Do NOT change this function.
APICALL EXPORT std::string PLUGIN_API_VERSION() {
return HYPRLAND_API_VERSION;
@ -36,6 +45,15 @@ static void onPreConfigReload() {
g_pGlobalState->buttons.clear();
}
static void onConfigReloaded() {
for (auto& b : g_pGlobalState->bars) {
if (!b)
continue;
b->onConfigReloaded();
}
}
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; });
@ -47,10 +65,10 @@ static void onUpdateWindowRules(PHLWINDOW window) {
}
Hyprlang::CParseResult onNewButton(const char* K, const char* V) {
std::string v = V;
CVarList vars(v);
std::string v = V;
Hyprutils::String::CVarList vars(v);
Hyprlang::CParseResult result;
Hyprlang::CParseResult result;
// hyprbars-button = bgcolor, size, icon, action, fgcolor
@ -95,6 +113,80 @@ Hyprlang::CParseResult onNewButton(const char* K, const char* V) {
return result;
}
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;
}
APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
PHANDLE = handle;
@ -112,29 +204,54 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
g_pGlobalState->barColorRuleIdx = Desktop::Rule::windowEffects()->registerEffect("hyprbars:bar_color");
g_pGlobalState->titleColorRuleIdx = Desktop::Rule::windowEffects()->registerEffect("hyprbars:title_color");
static auto P = Event::bus()->m_events.window.open.listen([&](PHLWINDOW w) { onNewWindow(w); });
static auto P = Event::bus()->m_events.window.open.listen([&](PHLWINDOW w) { onNewWindow(w); });
static auto P3 = Event::bus()->m_events.window.updateRules.listen([&](PHLWINDOW w) { onUpdateWindowRules(w); });
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprbars:bar_color", Hyprlang::INT{*configStringToInt("rgba(33333388)")});
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprbars:bar_height", Hyprlang::INT{15});
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprbars:col.text", Hyprlang::INT{*configStringToInt("rgba(ffffffff)")});
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprbars:bar_text_size", Hyprlang::INT{10});
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprbars:bar_title_enabled", Hyprlang::INT{1});
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprbars:bar_blur", Hyprlang::INT{0});
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprbars:bar_text_font", Hyprlang::STRING{"Sans"});
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprbars:bar_text_align", Hyprlang::STRING{"center"});
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprbars:bar_part_of_window", Hyprlang::INT{1});
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprbars:bar_precedence_over_border", Hyprlang::INT{0});
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprbars:bar_buttons_alignment", Hyprlang::STRING{"right"});
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprbars:bar_padding", Hyprlang::INT{7});
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprbars:bar_button_padding", Hyprlang::INT{5});
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprbars:enabled", Hyprlang::INT{1});
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprbars:icon_on_hover", Hyprlang::INT{0});
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprbars:inactive_button_color", Hyprlang::INT{0}); // unset
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprbars:on_double_click", Hyprlang::STRING{""});
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::addConfigKeyword(PHANDLE, "plugin:hyprbars:hyprbars-button", onNewButton, Hyprlang::SHandlerOptions{});
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);
static auto P4 = Event::bus()->m_events.config.preReload.listen([&] { onPreConfigReload(); });
static auto P5 = Event::bus()->m_events.config.reloaded.listen([&] { onConfigReloaded(); });
// add deco to existing windows
for (auto& w : g_pCompositor->m_windows) {

View File

@ -28,7 +28,6 @@ shared_module(meson.project_name(), src,
dependency('hyprland'),
dependency('pixman-1'),
dependency('libdrm'),
dependency('pangocairo'),
dependency('libinput'),
dependency('libudev'),
dependency('wayland-server'),

View File

@ -10,11 +10,15 @@
#define private public
#include <hyprland/src/Compositor.hpp>
#include <hyprland/src/config/ConfigValue.hpp>
#include <hyprland/src/config/shared/animation/AnimationTree.hpp>
#include <hyprland/src/helpers/AnimatedVariable.hpp>
#include <hyprland/src/managers/animation/AnimationManager.hpp>
#include <hyprland/src/managers/eventLoop/EventLoopManager.hpp>
#include <hyprland/src/layout/LayoutManager.hpp>
#include <hyprland/src/config/ConfigManager.hpp>
#include <hyprland/src/config/values/types/BoolValue.hpp>
#include <hyprland/src/config/values/types/FloatValue.hpp>
#include <hyprland/src/config/values/types/StringValue.hpp>
#include <hyprland/src/event/EventBus.hpp>
#undef private
@ -25,6 +29,12 @@
using namespace Hyprutils::String;
using namespace Hyprutils::Animation;
static struct {
SP<Config::Values::CBoolValue> onlyOnMonitorChange;
SP<Config::Values::CFloatValue> fadeOpacity, slideHeight, bounceStrength;
SP<Config::Values::CStringValue> mode;
} configValues;
// Do NOT change this function.
APICALL EXPORT std::string PLUGIN_API_VERSION() {
return HYPRLAND_API_VERSION;
@ -39,23 +49,17 @@ static void onFocusChange(PHLWINDOW window) {
if (lastWindow == window)
return;
static const auto PONLY_ON_MONITOR_CHANGE = CConfigValue<Hyprlang::INT>("plugin:hyprfocus:only_on_monitor_change");
if (*PONLY_ON_MONITOR_CHANGE && lastWindow && lastWindow->m_monitor == window->m_monitor)
if (configValues.onlyOnMonitorChange->value() && lastWindow && lastWindow->m_monitor == window->m_monitor)
return;
lastWindow = window;
const auto PIN = Config::animationTree()->getAnimationPropertyConfig("hyprfocusIn");
const auto POUT = Config::animationTree()->getAnimationPropertyConfig("hyprfocusOut");
static const auto POPACITY = CConfigValue<Hyprlang::FLOAT>("plugin:hyprfocus:fade_opacity");
static const auto PBOUNCE = CConfigValue<Hyprlang::FLOAT>("plugin:hyprfocus:bounce_strength");
static const auto PSLIDE = CConfigValue<Hyprlang::FLOAT>("plugin:hyprfocus:slide_height");
static const auto PMODE = CConfigValue<std::string>("plugin:hyprfocus:mode");
const auto PIN = g_pConfigManager->getAnimationPropertyConfig("hyprfocusIn");
const auto POUT = g_pConfigManager->getAnimationPropertyConfig("hyprfocusOut");
if (*PMODE == "flash") {
if (configValues.mode->value() == "flash") {
const auto ORIGINAL = window->m_activeInactiveAlpha->goal();
window->m_activeInactiveAlpha->setConfig(PIN);
*window->m_activeInactiveAlpha = std::clamp(*POPACITY, 0.F, 1.F);
*window->m_activeInactiveAlpha = configValues.fadeOpacity->value();
window->m_activeInactiveAlpha->setCallbackOnEnd([w = PHLWINDOWREF{window}, POUT, ORIGINAL](WP<CBaseAnimatedVariable> pav) {
if (!w)
@ -65,13 +69,13 @@ static void onFocusChange(PHLWINDOW window) {
w->m_activeInactiveAlpha->setCallbackOnEnd(nullptr);
});
} else if (*PMODE == "bounce") {
} else if (configValues.mode->value() == "bounce") {
const auto ORIGINAL = CBox{window->m_realPosition->goal(), window->m_realSize->goal()};
window->m_realPosition->setConfig(PIN);
window->m_realSize->setConfig(PIN);
auto box = ORIGINAL.copy().scaleFromCenter(std::clamp(*PBOUNCE, 0.1F, 1.F));
auto box = ORIGINAL.copy().scaleFromCenter(configValues.bounceStrength->value());
*window->m_realPosition = box.pos();
*window->m_realSize = box.size();
@ -90,12 +94,12 @@ static void onFocusChange(PHLWINDOW window) {
w->m_realSize->setCallbackOnEnd(nullptr);
});
} else if (*PMODE == "slide") {
} else if (configValues.mode->value() == "slide") {
const auto ORIGINAL = window->m_realPosition->goal();
window->m_realPosition->setConfig(PIN);
*window->m_realPosition = ORIGINAL - Vector2D{0.F, std::clamp(*PSLIDE, 0.F, 150.F)};
*window->m_realPosition = ORIGINAL - Vector2D{0.F, configValues.slideHeight->value()};
window->m_realPosition->setCallbackOnEnd([w = PHLWINDOWREF{window}, POUT, ORIGINAL](WP<CBaseAnimatedVariable> pav) {
if (!w)
@ -126,16 +130,22 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
static auto P = Event::bus()->m_events.window.active.listen([&](PHLWINDOW w, Desktop::eFocusReason r) { onFocusChange(w); });
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprfocus:mode", Hyprlang::STRING{"flash"});
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprfocus:only_on_monitor_change", Hyprlang::INT{0});
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprfocus:fade_opacity", Hyprlang::FLOAT{0.8F});
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprfocus:slide_height", Hyprlang::FLOAT{20.F});
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprfocus:bounce_strength", Hyprlang::FLOAT{0.95F});
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);
// 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.
g_pConfigManager->m_animationTree.createNode("hyprfocusIn", "windowsIn");
g_pConfigManager->m_animationTree.createNode("hyprfocusOut", "windowsOut");
Config::animationTree()->m_animationTree.createNode("hyprfocusIn", "windowsIn");
Config::animationTree()->m_animationTree.createNode("hyprfocusOut", "windowsOut");
return {"hyprfocus", "Flashfocus for Hyprland", "Vaxry", "1.0"};
}