hyprbars: Improve (touch + button) functionality (#265)

Various miscellaneous improvements

---------

Co-authored-by: vaxerski <vaxry@vaxry.net>
This commit is contained in:
caffeine 2025-01-08 17:13:42 +00:00 committed by GitHub
parent 25d0292857
commit 948d70b26e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 186 additions and 88 deletions

View file

@ -53,7 +53,7 @@ plugin {
Use the `hyprbars-button` keyword.
```ini
hyprbars-button = color, size, icon, on-click
hyprbars-button = bgcolor, size, icon, on-click, fgcolor
```
## Window rules

View file

@ -4,6 +4,7 @@
#include <hyprland/src/desktop/Window.hpp>
#include <hyprland/src/helpers/MiscFunctions.hpp>
#include <hyprland/src/managers/SeatManager.hpp>
#include <hyprland/src/managers/input/InputManager.hpp>
#include <hyprland/src/render/Renderer.hpp>
#include <pango/pangocairo.h>
@ -16,11 +17,19 @@ CHyprBar::CHyprBar(PHLWINDOW pWindow) : IHyprWindowDecoration(pWindow) {
const auto PMONITOR = pWindow->m_pMonitor.lock();
PMONITOR->scheduledRecalc = true;
//button events
m_pMouseButtonCallback = HyprlandAPI::registerCallbackDynamic(
PHANDLE, "mouseButton", [&](void* self, SCallbackInfo& info, std::any param) { onMouseDown(info, std::any_cast<IPointer::SButtonEvent>(param)); });
PHANDLE, "mouseButton", [&](void* self, SCallbackInfo& info, std::any param) { onMouseButton(info, std::any_cast<IPointer::SButtonEvent>(param)); });
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) { handleUpEvent(info); });
m_pMouseMoveCallback =
HyprlandAPI::registerCallbackDynamic(PHANDLE, "mouseMove", [&](void* self, SCallbackInfo& info, std::any param) { onMouseMove(std::any_cast<Vector2D>(param)); });
//move events
m_pTouchMoveCallback = HyprlandAPI::registerCallbackDynamic(
PHANDLE, "touchMove", [&](void* self, SCallbackInfo& info, std::any param) { onTouchMove(info, std::any_cast<ITouch::SMotionEvent>(param)); });
m_pMouseMoveCallback = HyprlandAPI::registerCallbackDynamic( //
PHANDLE, "mouseMove", [&](void* self, SCallbackInfo& info, std::any param) { onMouseMove(std::any_cast<Vector2D>(param)); });
m_pTextTex = makeShared<CTexture>();
m_pButtonsTex = makeShared<CTexture>();
@ -29,6 +38,9 @@ CHyprBar::CHyprBar(PHLWINDOW pWindow) : IHyprWindowDecoration(pWindow) {
CHyprBar::~CHyprBar() {
damageEntire();
HyprlandAPI::unregisterCallback(PHANDLE, m_pMouseButtonCallback);
HyprlandAPI::unregisterCallback(PHANDLE, m_pTouchDownCallback);
HyprlandAPI::unregisterCallback(PHANDLE, m_pTouchUpCallback);
HyprlandAPI::unregisterCallback(PHANDLE, m_pTouchMoveCallback);
HyprlandAPI::unregisterCallback(PHANDLE, m_pMouseMoveCallback);
std::erase(g_pGlobalState->bars, this);
}
@ -57,16 +69,61 @@ std::string CHyprBar::getDisplayName() {
return "Hyprbar";
}
void CHyprBar::onMouseDown(SCallbackInfo& info, IPointer::SButtonEvent e) {
bool CHyprBar::inputIsValid() {
if (!m_pWindow->m_pWorkspace->isVisible() || !g_pInputManager->m_dExclusiveLSes.empty() ||
(g_pSeatManager->seatGrab && !g_pSeatManager->seatGrab->accepts(m_pWindow->m_pWLSurface->resource())))
return;
return false;
const auto WINDOWATCURSOR = g_pCompositor->vectorToWindowUnified(g_pInputManager->getMouseCoordsInternal(), RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING);
if (WINDOWATCURSOR != m_pWindow && m_pWindow != g_pCompositor->m_pLastWindow)
return false;
return true;
}
void CHyprBar::onMouseButton(SCallbackInfo& info, IPointer::SButtonEvent e) {
if (!inputIsValid())
return;
if (e.state != WL_POINTER_BUTTON_STATE_PRESSED) {
handleUpEvent(info);
return;
}
handleDownEvent(info, std::nullopt);
}
void CHyprBar::onTouchDown(SCallbackInfo& info, ITouch::SDownEvent e) {
if (!inputIsValid())
return;
auto PMONITOR = g_pCompositor->getMonitorFromName(!e.device->boundOutput.empty() ? e.device->boundOutput : "");
PMONITOR = PMONITOR ? PMONITOR : g_pCompositor->m_pLastMonitor.lock();
g_pCompositor->warpCursorTo({PMONITOR->vecPosition.x + e.pos.x * PMONITOR->vecSize.x, PMONITOR->vecPosition.y + e.pos.y * PMONITOR->vecSize.y}, true);
handleDownEvent(info, e);
}
void CHyprBar::onMouseMove(Vector2D coords) {
if (!m_bDragPending || m_bTouchEv)
return;
m_bDragPending = false;
handleMovement();
}
void CHyprBar::onTouchMove(SCallbackInfo& info, ITouch::SMotionEvent e) {
if (!m_bDragPending || !m_bTouchEv)
return;
g_pInputManager->mouseMoveUnified(e.timeMs);
handleMovement();
}
void CHyprBar::handleDownEvent(SCallbackInfo& info, std::optional<ITouch::SDownEvent> touchEvent) {
m_bTouchEv = touchEvent.has_value();
const auto PWINDOW = m_pWindow.lock();
const auto COORDS = cursorRelativeToBar();
@ -81,31 +138,18 @@ void CHyprBar::onMouseDown(SCallbackInfo& info, IPointer::SButtonEvent e) {
if (!VECINRECT(COORDS, 0, 0, assignedBoxGlobal().w, **PHEIGHT - 1)) {
if (m_bDraggingThis) {
if (m_bTouchEv) {
ITouch::SDownEvent e = touchEvent.value();
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 {:x}", (uintptr_t)PWINDOW.get());
}
m_bDraggingThis = false;
m_bDragPending = false;
return;
}
if (e.state != WL_POINTER_BUTTON_STATE_PRESSED) {
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 {:x}", (uintptr_t)PWINDOW.get());
}
m_bDragPending = false;
m_bTouchEv = false;
return;
}
@ -117,8 +161,40 @@ void CHyprBar::onMouseDown(SCallbackInfo& info, IPointer::SButtonEvent e) {
info.cancelled = true;
m_bCancelledDown = true;
// check if on a button
doButtonPress(PBARPADDING, PBARBUTTONPADDING, PHEIGHT, COORDS, BUTTONSRIGHT);
m_bDragPending = true;
}
void CHyprBar::handleUpEvent(SCallbackInfo& info) {
if (m_pWindow.lock() != g_pCompositor->m_pLastWindow.lock())
return;
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 {:x}", (uintptr_t)m_pWindow.lock().get());
}
m_bDragPending = false;
m_bTouchEv = false;
}
void CHyprBar::handleMovement() {
g_pKeybindManager->m_mDispatchers["mouse"]("1movewindow");
m_bDraggingThis = true;
Debug::log(LOG, "[hyprbars] Dragging initiated on {:x}", (uintptr_t)m_pWindow.lock().get());
return;
}
void CHyprBar::doButtonPress(long int* const* PBARPADDING, long int* const* PBARBUTTONPADDING, long int* const* PHEIGHT, Vector2D COORDS, const bool BUTTONSRIGHT) {
//check if on a button
float offset = **PBARPADDING;
for (auto& b : g_pGlobalState->buttons) {
@ -133,20 +209,6 @@ void CHyprBar::onMouseDown(SCallbackInfo& info, IPointer::SButtonEvent e) {
offset += **PBARBUTTONPADDING + b.size;
}
m_bDragPending = true;
}
void CHyprBar::onMouseMove(Vector2D coords) {
if (m_bDragPending) {
m_bDragPending = false;
g_pKeybindManager->m_mDispatchers["mouse"]("1movewindow");
m_bDraggingThis = true;
Debug::log(LOG, "[hyprbars] Dragging initiated on {:x}", (uintptr_t)m_pWindow.lock().get());
return;
}
}
void CHyprBar::renderText(SP<CTexture> out, const std::string& text, const CHyprColor& color, const Vector2D& bufferSize, const float scale, const int fontSize) {
@ -292,20 +354,33 @@ void CHyprBar::renderBarTitle(const Vector2D& bufferSize, const float scale) {
cairo_surface_destroy(CAIROSURFACE);
}
size_t CHyprBar::getVisibleButtonCount(long int* const* PBARBUTTONPADDING, long int* const* PBARPADDING, const Vector2D& bufferSize, const float scale) {
float availableSpace = bufferSize.x - **PBARPADDING * scale * 2;
size_t count = 0;
for (const auto& button : g_pGlobalState->buttons) {
const float buttonSpace = (button.size + **PBARBUTTONPADDING) * scale;
if (availableSpace >= buttonSpace) {
count++;
availableSpace -= buttonSpace;
} else
break;
}
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();
const auto scaledButtonsPad = **PBARBUTTONPADDING * scale;
const auto scaledBarPadding = **PBARPADDING * scale;
const bool BUTTONSRIGHT = std::string{*PALIGNBUTTONS} != "left";
const auto visibleCount = getVisibleButtonCount(PBARBUTTONPADDING, PBARPADDING, bufferSize, scale);
const auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, bufferSize.x, bufferSize.y);
const auto CAIRO = cairo_create(CAIROSURFACE);
static auto* const PALIGNBUTTONS = (Hyprlang::STRING const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_buttons_alignment")->getDataStaticPtr();
const bool BUTTONSRIGHT = std::string{*PALIGNBUTTONS} != "left";
// clear the pixmap
cairo_save(CAIRO);
cairo_set_operator(CAIRO, CAIRO_OPERATOR_CLEAR);
@ -313,27 +388,19 @@ void CHyprBar::renderBarButtons(const Vector2D& bufferSize, const float scale) {
cairo_restore(CAIRO);
// draw buttons
int offset = scaledBarPadding;
int offset = **PBARPADDING * 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 drawButton = [&](SHyprButton& button) -> void {
const auto scaledButtonSize = button.size * scale;
const auto pos = Vector2D{BUTTONSRIGHT ? bufferSize.x - offset - scaledButtonSize / 2.0 : offset + scaledButtonSize / 2.0, bufferSize.y / 2.0}.floor();
Vector2D currentPos =
Vector2D{BUTTONSRIGHT ? bufferSize.x - offset - scaledButtonSize / 2.0 : offset + scaledButtonSize / 2.0, (bufferSize.y - scaledButtonSize) / 2.0}.floor();
const int X = currentPos.x;
const int Y = currentPos.y;
const int RADIUS = static_cast<int>(std::ceil(scaledButtonSize / 2.0));
cairo_set_source_rgba(CAIRO, button.col.r, button.col.g, button.col.b, button.col.a);
cairo_arc(CAIRO, X, Y + RADIUS, RADIUS, 0, 2 * M_PI);
cairo_set_source_rgba(CAIRO, button.bgcol.r, button.bgcol.g, button.bgcol.b, button.bgcol.a);
cairo_arc(CAIRO, pos.x, pos.y, scaledButtonSize / 2, 0, 2 * M_PI);
cairo_fill(CAIRO);
offset += scaledButtonsPad + scaledButtonSize;
};
for (auto& b : g_pGlobalState->buttons) {
drawButton(b);
}
// copy the data to an OpenGL texture we have
@ -361,37 +428,30 @@ void CHyprBar::renderBarButtonsText(CBox* barBox, const float scale, const float
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 visibleCount = getVisibleButtonCount(PBARBUTTONPADDING, PBARPADDING, Vector2D{barBox->w, barBox->h}, scale);
const auto scaledButtonsPad = **PBARBUTTONPADDING * scale;
const auto scaledBarPad = **PBARPADDING * scale;
int offset = scaledBarPad;
//
auto drawButton = [&](SHyprButton& button) -> void {
int offset = **PBARPADDING * scale;
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;
if (button.iconTex->m_iTexID == 0 /* icon is not rendered */ && !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);
const bool LIGHT = button.col.r + button.col.g + button.col.b < 1;
renderText(button.iconTex, button.icon, LIGHT ? CHyprColor(0xFFFFFFFF) : CHyprColor(0xFF000000), BUFSIZE, scale, button.size * 0.62);
renderText(button.iconTex, button.icon, fgcol, BUFSIZE, scale, button.size * 0.62);
}
if (button.iconTex->m_iTexID == 0)
return;
continue;
CBox pos = {barBox->x + (BUTTONSRIGHT ? barBox->width - offset - scaledButtonSize : offset), barBox->y + (barBox->height - scaledButtonSize) / 2.0, scaledButtonSize,
scaledButtonSize};
g_pHyprOpenGL->renderTexture(button.iconTex, &pos, a);
offset += scaledButtonsPad + scaledButtonSize;
};
for (auto& b : g_pGlobalState->buttons) {
drawButton(b);
}
}

View file

@ -5,8 +5,13 @@
#include <hyprland/src/render/decorations/IHyprWindowDecoration.hpp>
#include <hyprland/src/render/OpenGL.hpp>
#include <hyprland/src/devices/IPointer.hpp>
#include <hyprland/src/devices/ITouch.hpp>
#include "globals.hpp"
#define private public
#include <hyprland/src/managers/input/InputManager.hpp>
#undef private
class CHyprBar : public IHyprWindowDecoration {
public:
CHyprBar(PHLWINDOW);
@ -60,21 +65,38 @@ class CHyprBar : public IHyprWindowDecoration {
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 onMouseDown(SCallbackInfo& info, IPointer::SButtonEvent e);
bool inputIsValid();
void onMouseButton(SCallbackInfo& info, IPointer::SButtonEvent e);
void onTouchDown(SCallbackInfo& info, ITouch::SDownEvent e);
void onMouseMove(Vector2D coords);
void onTouchMove(SCallbackInfo& info, ITouch::SMotionEvent e);
void handleDownEvent(SCallbackInfo& info, std::optional<ITouch::SDownEvent> touchEvent);
void handleUpEvent(SCallbackInfo& info);
void handleMovement();
void doButtonPress(long int* const* PBARPADDING, long int* const* PBARBUTTONPADDING, long int* const* PHEIGHT, Vector2D COORDS, bool BUTTONSRIGHT);
CBox assignedBoxGlobal();
SP<HOOK_CALLBACK_FN> m_pMouseButtonCallback;
SP<HOOK_CALLBACK_FN> m_pTouchDownCallback;
SP<HOOK_CALLBACK_FN> m_pTouchUpCallback;
SP<HOOK_CALLBACK_FN> m_pTouchMoveCallback;
SP<HOOK_CALLBACK_FN> m_pMouseMoveCallback;
std::string m_szLastTitle;
bool m_bDraggingThis = false;
bool m_bTouchEv = false;
bool m_bDragPending = false;
bool m_bCancelledDown = false;
// for dynamic updates
int m_iLastHeight = 0;
int m_iLastHeight = 0;
size_t getVisibleButtonCount(long int* const* PBARBUTTONPADDING, long int* const* PBARPADDING, const Vector2D& bufferSize, const float scale);
friend class CBarPassElement;
};

View file

@ -7,7 +7,9 @@ inline HANDLE PHANDLE = nullptr;
struct SHyprButton {
std::string cmd = "";
CHyprColor col = CHyprColor(0, 0, 0, 0);
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>();

View file

@ -60,10 +60,10 @@ Hyprlang::CParseResult onNewButton(const char* K, const char* V) {
Hyprlang::CParseResult result;
// hyprbars-button = color, size, icon, action
// hyprbars-button = bgcolor, size, icon, action, fgcolor
if (vars[0].empty() || vars[1].empty()) {
result.setError("var 1 and 2 cannot be empty");
result.setError("bgcolor and size cannot be empty");
return result;
}
@ -71,16 +71,30 @@ Hyprlang::CParseResult onNewButton(const char* K, const char* V) {
try {
size = std::stof(vars[1]);
} catch (std::exception& e) {
result.setError("failed parsing var 2 as int");
result.setError("failed to parse size");
return result;
}
auto X = configStringToInt(vars[0]);
if (!X) {
result.setError("var0 is not a valid number");
bool userfg = false;
auto fgcolor = configStringToInt("rgb(ffffff)");
auto bgcolor = configStringToInt(vars[1]);
if (!bgcolor) {
result.setError("invalid bgcolor");
return result;
}
g_pGlobalState->buttons.push_back(SHyprButton{vars[3], *X, size, vars[2]});
if (vars.size() == 5) {
userfg = true;
fgcolor = configStringToInt(vars[4]);
}
if (!fgcolor) {
result.setError("invalid fgcolor");
return result;
}
g_pGlobalState->buttons.push_back(SHyprButton{vars[3], userfg, *fgcolor, *bgcolor, size, vars[2]});
for (auto& b : g_pGlobalState->bars) {
b->m_bButtonsDirty = true;