hyprland-plugins/hyprbars/barDeco.cpp

659 lines
26 KiB
C++
Raw Normal View History

2023-02-28 21:30:51 +01:00
#include "barDeco.hpp"
2023-05-01 03:57:48 +02:00
#include <hyprland/src/Compositor.hpp>
2024-03-20 04:02:10 +01:00
#include <hyprland/src/desktop/Window.hpp>
#include <hyprland/src/helpers/MiscFunctions.hpp>
2024-12-13 19:03:42 +01:00
#include <hyprland/src/managers/input/InputManager.hpp>
#include <pango/pangocairo.h>
2023-02-28 21:30:51 +01:00
#include "globals.hpp"
2024-04-27 14:03:46 +02:00
CHyprBar::CHyprBar(PHLWINDOW pWindow) : IHyprWindowDecoration(pWindow) {
2023-11-11 15:39:46 +01:00
m_pWindow = pWindow;
2023-02-28 22:17:46 +01:00
2024-10-28 15:25:34 +01:00
const auto PMONITOR = pWindow->m_pMonitor.lock();
2023-02-28 22:17:46 +01:00
PMONITOR->scheduledRecalc = true;
2023-02-28 22:59:58 +01:00
2024-12-13 19:03:42 +01:00
m_pTouchDownCallback = HyprlandAPI::registerCallbackDynamic(
PHANDLE, "touchDown", [&](void* self, SCallbackInfo& info, std::any param) { onTouchDown(info, std::any_cast<ITouch::SDownEvent>(param)); });
m_pTouchUpCallback =
HyprlandAPI::registerCallbackDynamic(PHANDLE, "touchUp", [&](void* self, SCallbackInfo& info, std::any param) { onTouchUp(info, std::any_cast<ITouch::SUpEvent>(param)); });
m_pTouchMoveCallback = HyprlandAPI::registerCallbackDynamic(
PHANDLE, "touchMove", [&](void* self, SCallbackInfo& info, std::any param) { onTouchMove(info, std::any_cast<ITouch::SMotionEvent>(param)); });
2023-10-21 15:55:42 +02:00
m_pMouseButtonCallback = HyprlandAPI::registerCallbackDynamic(
2024-05-14 23:40:06 +02:00
PHANDLE, "mouseButton", [&](void* self, SCallbackInfo& info, std::any param) { onMouseDown(info, std::any_cast<IPointer::SButtonEvent>(param)); });
2023-02-28 23:53:49 +01:00
2023-10-21 15:55:42 +02:00
m_pMouseMoveCallback =
HyprlandAPI::registerCallbackDynamic(PHANDLE, "mouseMove", [&](void* self, SCallbackInfo& info, std::any param) { onMouseMove(std::any_cast<Vector2D>(param)); });
2024-06-08 11:12:34 +02:00
m_pTextTex = makeShared<CTexture>();
2024-06-08 11:12:34 +02:00
m_pButtonsTex = makeShared<CTexture>();
2023-02-28 21:30:51 +01:00
}
CHyprBar::~CHyprBar() {
damageEntire();
2023-02-28 22:59:58 +01:00
HyprlandAPI::unregisterCallback(PHANDLE, m_pMouseButtonCallback);
HyprlandAPI::unregisterCallback(PHANDLE, m_pTouchDownCallback);
2024-12-13 19:03:42 +01:00
HyprlandAPI::unregisterCallback(PHANDLE, m_pTouchUpCallback);
HyprlandAPI::unregisterCallback(PHANDLE, m_pTouchMoveCallback);
HyprlandAPI::unregisterCallback(PHANDLE, m_pMouseMoveCallback);
std::erase(g_pGlobalState->bars, this);
2023-02-28 21:30:51 +01:00
}
2023-11-11 15:39:46 +01:00
SDecorationPositioningInfo CHyprBar::getPositioningInfo() {
2024-02-18 16:30:21 +01:00
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();
2023-11-11 15:39:46 +01:00
SDecorationPositioningInfo info;
info.policy = m_bHidden ? DECORATION_POSITION_ABSOLUTE : DECORATION_POSITION_STICKY;
2023-11-11 15:39:46 +01:00
info.edges = DECORATION_EDGE_TOP;
2024-02-18 16:30:21 +01:00
info.priority = **PPRECEDENCE ? 10005 : 5000;
2023-11-11 15:39:46 +01:00
info.reserved = true;
info.desiredExtents = {{0, m_bHidden ? 0 : **PHEIGHT}, {0, 0}};
2023-11-11 15:39:46 +01:00
return info;
}
void CHyprBar::onPositioningReply(const SDecorationPositioningReply& reply) {
if (reply.assignedGeometry.size() != m_bAssignedBox.size())
m_bWindowSizeChanged = true;
m_bAssignedBox = reply.assignedGeometry;
2023-02-28 21:30:51 +01:00
}
std::string CHyprBar::getDisplayName() {
return "Hyprbar";
}
2024-05-14 23:40:06 +02:00
void CHyprBar::onMouseDown(SCallbackInfo& info, IPointer::SButtonEvent e) {
2024-12-13 01:33:03 +01:00
if (m_pWindow.lock() != g_pCompositor->m_pLastWindow.lock())
2024-12-13 19:03:42 +01:00
return;
2024-12-13 01:33:03 +01:00
2024-04-27 14:03:46 +02:00
const auto PWINDOW = m_pWindow.lock();
const auto COORDS = cursorRelativeToBar();
2024-04-27 14:03:46 +02:00
2024-12-13 19:03:42 +01:00
static auto* const PHEIGHT = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_height")->getDataStaticPtr();
2024-02-18 16:30:21 +01:00
if (!VECINRECT(COORDS, 0, 0, assignedBoxGlobal().w, **PHEIGHT - 1)) {
2023-04-18 20:50:09 +02:00
if (m_bDraggingThis) {
g_pKeybindManager->m_mDispatchers["mouse"]("0movewindow");
2024-04-27 14:03:46 +02:00
Debug::log(LOG, "[hyprbars] Dragging ended on {:x}", (uintptr_t)PWINDOW.get());
2023-04-18 20:50:09 +02:00
}
2023-03-16 17:00:59 +01:00
m_bDraggingThis = false;
m_bDragPending = false;
return;
}
2023-02-28 22:59:58 +01:00
if (e.state != WL_POINTER_BUTTON_STATE_PRESSED) {
2023-10-29 18:33:32 +01:00
if (m_bCancelledDown)
info.cancelled = true;
m_bCancelledDown = false;
2023-02-28 23:53:49 +01:00
if (m_bDraggingThis) {
g_pKeybindManager->m_mDispatchers["mouse"]("0movewindow");
m_bDraggingThis = false;
2024-04-27 14:03:46 +02:00
Debug::log(LOG, "[hyprbars] Dragging ended on {:x}", (uintptr_t)PWINDOW.get());
2023-02-28 23:53:49 +01:00
}
2023-10-29 18:33:32 +01:00
m_bDragPending = false;
2023-02-28 22:59:58 +01:00
return;
2023-02-28 23:53:49 +01:00
}
2023-02-28 22:59:58 +01:00
2024-04-27 14:03:46 +02:00
if (PWINDOW->m_bIsFloating)
g_pCompositor->changeWindowZOrder(PWINDOW, true);
2023-10-29 18:33:32 +01:00
info.cancelled = true;
m_bCancelledDown = true;
// do the button press
2023-02-28 22:59:58 +01:00
CHyprBar::doButtonPress(COORDS);
2023-02-28 23:53:49 +01:00
2023-03-16 17:00:59 +01:00
m_bDragPending = true;
2023-02-28 23:53:49 +01:00
}
2024-12-13 19:03:42 +01:00
void CHyprBar::onTouchDown(SCallbackInfo& info, ITouch::SDownEvent e) {
if (m_pWindow.lock() != g_pCompositor->m_pLastWindow.lock())
return;
const auto PWINDOW = m_pWindow.lock();
const auto COORDS = cursorRelativeToBar();
2024-12-13 19:03:42 +01:00
static auto* const PHEIGHT = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_height")->getDataStaticPtr();
2024-12-13 19:03:42 +01:00
if (!VECINRECT(COORDS, 0, 0, assignedBoxGlobal().w, **PHEIGHT - 1)) {
if (m_bDraggingThis) {
g_pCompositor->warpCursorTo(Vector2D(e.pos.x, e.pos.y));
g_pInputManager->mouseMoveUnified(e.timeMs);
g_pKeybindManager->m_mDispatchers["mouse"]("0movewindow");
Debug::log(LOG, "[hyprbars] Dragging ended on touchdown {:x}", (uintptr_t)PWINDOW.get());
}
m_bDraggingThis = false;
m_bDragPending = false;
m_bTouchEv = false;
return;
2024-12-13 19:03:42 +01:00
}
g_pCompositor->warpCursorTo(Vector2D(e.pos.x, e.pos.y));
if (PWINDOW->m_bIsFloating)
g_pCompositor->changeWindowZOrder(PWINDOW, true);
// do the button press
CHyprBar::doButtonPress(COORDS);
2024-12-13 19:03:42 +01:00
m_bTouchEv = true;
m_bDragPending = true;
}
void CHyprBar::onTouchUp(SCallbackInfo& info, ITouch::SUpEvent e) {
if (m_pWindow.lock() != g_pCompositor->m_pLastWindow.lock())
return;
const auto PWINDOW = m_pWindow.lock();
if (m_bCancelledDown)
info.cancelled = true;
m_bCancelledDown = false;
if (m_bDraggingThis) {
g_pKeybindManager->m_mDispatchers["mouse"]("0movewindow");
m_bDraggingThis = false;
Debug::log(LOG, "[hyprbars] Dragging ended on touchup {:x}", (uintptr_t)PWINDOW.get());
}
m_bDragPending = false;
m_bTouchEv = false;
}
void CHyprBar::onTouchMove(SCallbackInfo& info, ITouch::SMotionEvent e) {
if (m_bDragPending && m_bTouchEv) {
g_pInputManager->mouseMoveUnified(e.timeMs);
g_pKeybindManager->m_mDispatchers["mouse"]("1movewindow");
m_bDraggingThis = true;
return;
}
}
void CHyprBar::doButtonPress(Vector2D coords) {
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();
const bool BUTTONSRIGHT = std::string{*PALIGNBUTTONS} != "left";
float offset = **PBARPADDING;
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();
if (VECINRECT(coords, currentPos.x, currentPos.y, currentPos.x + b.size + **PBARBUTTONPADDING, currentPos.y + b.size)) {
// hit on close
g_pKeybindManager->m_mDispatchers["exec"](b.cmd);
return;
}
offset += **PBARBUTTONPADDING + b.size;
}
}
2023-02-28 23:53:49 +01:00
void CHyprBar::onMouseMove(Vector2D coords) {
2024-12-13 19:03:42 +01:00
if (m_bDragPending && !m_bTouchEv) {
2023-02-28 23:53:49 +01:00
m_bDragPending = false;
g_pKeybindManager->m_mDispatchers["mouse"]("1movewindow");
m_bDraggingThis = true;
2024-04-27 14:03:46 +02:00
Debug::log(LOG, "[hyprbars] Dragging initiated on {:x}", (uintptr_t)m_pWindow.lock().get());
2023-02-28 23:53:49 +01:00
return;
}
2023-02-28 22:59:58 +01:00
}
2024-12-04 15:58:09 +01:00
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);
int layoutWidth, layoutHeight;
pango_layout_get_size(layout, &layoutWidth, &layoutHeight);
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);
2024-06-08 11:12:34 +02:00
out->allocate();
glBindTexture(GL_TEXTURE_2D, out->m_iTexID);
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) {
2024-12-10 23:51:23 +01:00
static auto* const PCOLOR = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:col.text")->getDataStaticPtr();
2024-02-18 16:30:21 +01:00
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();
2024-02-18 16:30:21 +01:00
const bool BUTTONSRIGHT = std::string{*PALIGNBUTTONS} != "left";
2024-04-27 14:03:46 +02:00
const auto PWINDOW = m_pWindow.lock();
const auto BORDERSIZE = PWINDOW->getRealBorderSize();
2023-02-28 21:30:51 +01:00
2024-02-18 16:30:21 +01:00
float buttonSizes = **PBARBUTTONPADDING;
2023-10-29 18:33:32 +01:00
for (auto& b : g_pGlobalState->buttons) {
2024-02-18 16:30:21 +01:00
buttonSizes += b.size + **PBARBUTTONPADDING;
2023-10-29 18:33:32 +01:00
}
2024-12-04 15:58:09 +01:00
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;
2024-12-04 15:58:09 +01:00
const CHyprColor COLOR = m_bForcedTitleColor.value_or(**PCOLOR);
2023-02-28 21:30:51 +01:00
2024-12-04 15:58:09 +01:00
const auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, bufferSize.x, bufferSize.y);
const auto CAIRO = cairo_create(CAIROSURFACE);
2023-02-28 21:30:51 +01:00
// 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);
2024-02-18 16:30:21 +01:00
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);
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);
2023-02-28 21:30:51 +01:00
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)) :
2024-04-27 14:03:46 +02:00
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);
2023-02-28 21:30:51 +01:00
g_object_unref(layout);
2023-02-28 21:30:51 +01:00
cairo_surface_flush(CAIROSURFACE);
// copy the data to an OpenGL texture we have
const auto DATA = cairo_image_surface_get_data(CAIROSURFACE);
2024-06-08 11:12:34 +02:00
m_pTextTex->allocate();
glBindTexture(GL_TEXTURE_2D, m_pTextTex->m_iTexID);
2023-02-28 21:30:51 +01:00
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::renderBarButtons(const Vector2D& bufferSize, const float scale) {
2024-02-18 16:30:21 +01:00
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();
2023-12-13 02:29:13 +01:00
2024-02-18 16:30:21 +01:00
const auto scaledButtonsPad = **PBARBUTTONPADDING * scale;
const auto scaledBarPadding = **PBARPADDING * scale;
const auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, bufferSize.x, bufferSize.y);
const auto CAIRO = cairo_create(CAIROSURFACE);
2024-02-18 16:30:21 +01:00
static auto* const PALIGNBUTTONS = (Hyprlang::STRING const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_buttons_alignment")->getDataStaticPtr();
2024-02-18 16:30:21 +01:00
const bool BUTTONSRIGHT = std::string{*PALIGNBUTTONS} != "left";
2023-02-28 22:59:58 +01:00
// clear the pixmap
cairo_save(CAIRO);
cairo_set_operator(CAIRO, CAIRO_OPERATOR_CLEAR);
cairo_paint(CAIRO);
cairo_restore(CAIRO);
2023-10-29 18:33:32 +01:00
// draw buttons
2023-12-13 02:29:13 +01:00
int offset = scaledBarPadding;
2023-02-28 22:59:58 +01:00
auto drawButton = [&](SHyprButton& button) -> void {
const auto scaledButtonSize = button.size * scale;
2023-02-28 22:59:58 +01:00
2023-12-13 02:29:13 +01:00
Vector2D currentPos =
Vector2D{BUTTONSRIGHT ? bufferSize.x - offset - scaledButtonSize / 2.0 : offset + scaledButtonSize / 2.0, (bufferSize.y - scaledButtonSize) / 2.0}.floor();
2023-02-28 22:59:58 +01:00
2023-12-13 02:29:13 +01:00
const int X = currentPos.x;
const int Y = currentPos.y;
const int RADIUS = static_cast<int>(std::ceil(scaledButtonSize / 2.0));
2023-02-28 22:59:58 +01:00
cairo_set_source_rgba(CAIRO, button.bgcol.r, button.bgcol.g, button.bgcol.b, button.bgcol.a);
2023-10-29 18:33:32 +01:00
cairo_arc(CAIRO, X, Y + RADIUS, RADIUS, 0, 2 * M_PI);
cairo_fill(CAIRO);
2023-02-28 22:59:58 +01:00
2023-10-29 18:33:32 +01:00
offset += scaledButtonsPad + scaledButtonSize;
};
2023-02-28 22:59:58 +01:00
2023-10-29 18:33:32 +01:00
for (auto& b : g_pGlobalState->buttons) {
drawButton(b);
}
2023-02-28 22:59:58 +01:00
// copy the data to an OpenGL texture we have
const auto DATA = cairo_image_surface_get_data(CAIROSURFACE);
2024-06-08 11:12:34 +02:00
m_pButtonsTex->allocate();
glBindTexture(GL_TEXTURE_2D, m_pButtonsTex->m_iTexID);
2023-02-28 22:59:58 +01:00
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);
}
2023-11-04 18:22:55 +01:00
void CHyprBar::renderBarButtonsText(CBox* barBox, const float scale, const float a) {
2024-02-18 16:30:21 +01:00
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();
2023-12-13 02:29:13 +01:00
2024-02-18 16:30:21 +01:00
const bool BUTTONSRIGHT = std::string{*PALIGNBUTTONS} != "left";
2024-02-18 16:30:21 +01:00
const auto scaledButtonsPad = **PBARBUTTONPADDING * scale;
const auto scaledBarPad = **PBARPADDING * scale;
int offset = scaledBarPad;
//
2024-02-18 16:30:21 +01:00
auto drawButton = [&](SHyprButton& button) -> void {
const auto scaledButtonSize = button.size * scale;
2024-06-08 11:12:34 +02:00
if (button.iconTex->m_iTexID == 0 /* icon is not rendered */ && !button.icon.empty()) {
// render icon
const Vector2D BUFSIZE = {scaledButtonSize, scaledButtonSize};
2024-12-10 21:35:46 +01:00
auto fgcol = button.fgcol;
2024-12-10 21:35:46 +01:00
if (!button.userfg) {
const bool LIGHT = button.bgcol.r + button.bgcol.g + button.bgcol.b < 1;
2024-12-10 21:35:46 +01:00
fgcol = LIGHT ? CHyprColor(0xFFFFFFFF) : CHyprColor(0xFF000000);
}
2024-12-10 21:35:46 +01:00
renderText(button.iconTex, button.icon, fgcol, BUFSIZE, scale, button.size * 0.62);
}
2024-06-08 11:12:34 +02:00
if (button.iconTex->m_iTexID == 0)
return;
2023-12-13 02:29:13 +01:00
CBox pos = {barBox->x + (BUTTONSRIGHT ? barBox->width - offset - scaledButtonSize : offset), barBox->y + (barBox->height - scaledButtonSize) / 2.0, scaledButtonSize,
scaledButtonSize};
2023-11-04 14:15:30 +01:00
g_pHyprOpenGL->renderTexture(button.iconTex, &pos, a);
offset += scaledButtonsPad + scaledButtonSize;
};
for (auto& b : g_pGlobalState->buttons) {
drawButton(b);
}
}
2024-12-04 15:58:09 +01:00
void CHyprBar::draw(PHLMONITOR pMonitor, const float& a) {
if (m_bHidden || !validMapped(m_pWindow))
2023-02-28 21:30:51 +01:00
return;
2024-04-27 14:03:46 +02:00
const auto PWINDOW = m_pWindow.lock();
2024-07-13 15:10:57 +02:00
if (!PWINDOW->m_sWindowData.decorate.valueOrDefault())
2023-02-28 21:30:51 +01:00
return;
2024-02-18 16:30:21 +01:00
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();
2024-02-18 16:30:21 +01:00
const bool BUTTONSRIGHT = std::string{*PALIGNBUTTONS} != "left";
2024-02-18 16:30:21 +01:00
if (**PHEIGHT < 1) {
m_iLastHeight = **PHEIGHT;
return;
}
2023-02-28 21:30:51 +01:00
2024-04-27 14:03:46 +02:00
const auto PWORKSPACE = PWINDOW->m_pWorkspace;
const auto WORKSPACEOFFSET = PWORKSPACE && !PWINDOW->m_bPinned ? PWORKSPACE->m_vRenderOffset.value() : Vector2D();
2024-04-27 14:03:46 +02:00
const auto ROUNDING = PWINDOW->rounding() + (*PPRECEDENCE ? 0 : PWINDOW->getRealBorderSize());
const auto scaledRounding = ROUNDING > 0 ? ROUNDING * pMonitor->scale - 2 /* idk why but otherwise it looks bad due to the gaps */ : 0;
2024-12-04 15:58:09 +01:00
CHyprColor color = m_bForcedBarColor.value_or(**PCOLOR);
2023-02-28 21:30:51 +01:00
color.a *= a;
2024-02-18 16:30:21 +01:00
m_seExtents = {{0, **PHEIGHT}, {}};
2023-02-28 21:30:51 +01:00
2023-11-11 15:39:46 +01:00
const auto DECOBOX = assignedBoxGlobal();
2023-02-28 21:30:51 +01:00
2023-11-11 15:39:46 +01:00
const auto BARBUF = DECOBOX.size() * pMonitor->scale;
CBox titleBarBox = {DECOBOX.x - pMonitor->vecPosition.x, DECOBOX.y - pMonitor->vecPosition.y, DECOBOX.w,
DECOBOX.h + ROUNDING * 3 /* to fill the bottom cuz we can't disable rounding there */};
2023-02-28 21:30:51 +01:00
2024-04-27 14:03:46 +02:00
titleBarBox.translate(PWINDOW->m_vFloatingOffset).scale(pMonitor->scale).round();
2023-02-28 21:30:51 +01:00
if (titleBarBox.w < 1 || titleBarBox.h < 1)
2023-11-11 15:39:46 +01:00
return;
2023-02-28 21:30:51 +01:00
g_pHyprOpenGL->scissor(&titleBarBox);
2023-11-04 14:15:30 +01:00
if (ROUNDING) {
2024-03-23 22:24:42 +01:00
// the +1 is a shit garbage temp fix until renderRect supports an alpha matte
2024-04-27 14:03:46 +02:00
CBox windowBox = {PWINDOW->m_vRealPosition.value().x + PWINDOW->m_vFloatingOffset.x - pMonitor->vecPosition.x + 1,
PWINDOW->m_vRealPosition.value().y + PWINDOW->m_vFloatingOffset.y - pMonitor->vecPosition.y + 1, PWINDOW->m_vRealSize.value().x - 2,
PWINDOW->m_vRealSize.value().y - 2};
2024-03-23 22:24:42 +01:00
if (windowBox.w < 1 || windowBox.h < 1)
return;
2023-02-28 21:30:51 +01:00
glClearStencil(0);
glClear(GL_STENCIL_BUFFER_BIT);
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_ALWAYS, 1, -1);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
2024-03-23 22:24:42 +01:00
windowBox.translate(WORKSPACEOFFSET).scale(pMonitor->scale).round();
2024-12-04 15:58:09 +01:00
g_pHyprOpenGL->renderRect(&windowBox, CHyprColor(0, 0, 0, 0), scaledRounding);
2023-02-28 21:30:51 +01:00
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
2023-02-28 22:17:46 +01:00
glStencilFunc(GL_NOTEQUAL, 1, -1);
2023-02-28 21:30:51 +01:00
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
}
g_pHyprOpenGL->renderRect(&titleBarBox, color, scaledRounding);
2023-02-28 21:30:51 +01:00
// render title
if (**PENABLETITLE && (m_szLastTitle != PWINDOW->m_szTitle || m_bWindowSizeChanged || m_pTextTex->m_iTexID == 0 || m_bTitleColorChanged)) {
2024-04-27 14:03:46 +02:00
m_szLastTitle = PWINDOW->m_szTitle;
renderBarTitle(BARBUF, pMonitor->scale);
2023-02-28 21:30:51 +01:00
}
2023-11-04 14:15:30 +01:00
if (ROUNDING) {
2023-02-28 21:30:51 +01:00
// cleanup stencil
glClearStencil(0);
glClear(GL_STENCIL_BUFFER_BIT);
glDisable(GL_STENCIL_TEST);
glStencilMask(-1);
glStencilFunc(GL_ALWAYS, 1, 0xFF);
}
2023-11-04 18:22:55 +01:00
CBox textBox = {titleBarBox.x, titleBarBox.y, (int)BARBUF.x, (int)BARBUF.y};
2024-02-18 16:30:21 +01:00
if (**PENABLETITLE)
2024-06-08 11:12:34 +02:00
g_pHyprOpenGL->renderTexture(m_pTextTex, &textBox, a);
2023-02-28 22:17:46 +01:00
if (m_bButtonsDirty || m_bWindowSizeChanged) {
renderBarButtons(BARBUF, pMonitor->scale);
m_bButtonsDirty = false;
}
2023-02-28 22:59:58 +01:00
2024-06-08 11:12:34 +02:00
g_pHyprOpenGL->renderTexture(m_pButtonsTex, &textBox, a);
2023-02-28 22:17:46 +01:00
2023-11-04 18:22:55 +01:00
g_pHyprOpenGL->scissor((CBox*)nullptr);
2023-02-28 22:17:46 +01:00
2023-11-04 14:15:30 +01:00
renderBarButtonsText(&textBox, pMonitor->scale, a);
2023-02-28 22:17:46 +01:00
m_bWindowSizeChanged = false;
m_bTitleColorChanged = false;
// dynamic updates change the extents
2024-02-18 16:30:21 +01:00
if (m_iLastHeight != **PHEIGHT) {
2024-04-27 14:03:46 +02:00
g_pLayoutManager->getCurrentLayout()->recalculateWindow(PWINDOW);
2024-02-18 16:30:21 +01:00
m_iLastHeight = **PHEIGHT;
}
2023-02-28 21:30:51 +01:00
}
eDecorationType CHyprBar::getDecorationType() {
return DECORATION_CUSTOM;
}
2024-04-27 14:03:46 +02:00
void CHyprBar::updateWindow(PHLWINDOW pWindow) {
2023-02-28 21:30:51 +01:00
damageEntire();
}
void CHyprBar::damageEntire() {
2023-11-11 15:39:46 +01:00
; // ignored
2023-02-28 22:59:58 +01:00
}
Vector2D CHyprBar::cursorRelativeToBar() {
2023-11-11 15:39:46 +01:00
return g_pInputManager->getMouseCoordsInternal() - assignedBoxGlobal().pos();
}
2023-11-04 14:15:30 +01:00
eDecorationLayer CHyprBar::getDecorationLayer() {
return DECORATION_LAYER_UNDER;
}
uint64_t CHyprBar::getDecorationFlags() {
2024-02-18 16:30:21 +01:00
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);
2023-11-11 15:39:46 +01:00
}
CBox CHyprBar::assignedBoxGlobal() {
2024-04-27 14:03:46 +02:00
const auto PWINDOW = m_pWindow.lock();
CBox box = m_bAssignedBox;
box.translate(g_pDecorationPositioner->getEdgeDefinedPoint(DECORATION_EDGE_TOP, PWINDOW));
2024-04-27 14:03:46 +02:00
const auto PWORKSPACE = PWINDOW->m_pWorkspace;
const auto WORKSPACEOFFSET = PWORKSPACE && !PWINDOW->m_bPinned ? PWORKSPACE->m_vRenderOffset.value() : Vector2D();
return box.translate(WORKSPACEOFFSET);
}
2024-04-27 14:03:46 +02:00
PHLWINDOW CHyprBar::getOwner() {
return m_pWindow.lock();
}
void CHyprBar::updateRules() {
const auto PWINDOW = m_pWindow.lock();
auto rules = PWINDOW->m_vMatchedRules;
auto prev_m_bHidden = m_bHidden;
auto prev_m_bForcedTitleColor = m_bForcedTitleColor;
m_bForcedBarColor = std::nullopt;
m_bForcedTitleColor = std::nullopt;
m_bHidden = false;
for (auto& r : rules) {
applyRule(r);
}
if (prev_m_bHidden != m_bHidden)
g_pDecorationPositioner->repositionDeco(this);
if (prev_m_bForcedTitleColor != m_bForcedTitleColor)
m_bTitleColorChanged = true;
}
2024-12-17 01:27:36 +01:00
void CHyprBar::applyRule(const SP<CWindowRule>& r) {
auto arg = r->szRule.substr(r->szRule.find_first_of(' ') + 1);
2024-12-17 01:27:36 +01:00
if (r->szRule == "plugin:hyprbars:nobar")
m_bHidden = true;
2024-12-17 01:27:36 +01:00
else if (r->szRule.starts_with("plugin:hyprbars:bar_color"))
2024-12-04 15:58:09 +01:00
m_bForcedBarColor = CHyprColor(configStringToInt(arg).value_or(0));
2024-12-17 01:27:36 +01:00
else if (r->szRule.starts_with("plugin:hyprbars:title_color"))
2024-12-04 15:58:09 +01:00
m_bForcedTitleColor = CHyprColor(configStringToInt(arg).value_or(0));
}