diff --git a/flake.nix b/flake.nix index 1ff5a3e..0f598d7 100644 --- a/flake.nix +++ b/flake.nix @@ -34,7 +34,6 @@ hyprbars hyprexpo hyprfocus - hyprscrolling hyprtrails hyprwinwrap xtra-dispatchers diff --git a/hyprexpo/overview.cpp b/hyprexpo/overview.cpp index a909ca5..10a7ef5 100644 --- a/hyprexpo/overview.cpp +++ b/hyprexpo/overview.cpp @@ -2,7 +2,6 @@ #include #include #include -#include #define private public #define protected public #include @@ -27,45 +26,14 @@ using namespace Hyprutils::String; -static const CConfigValue& PCOLUMNS() { - static const CConfigValue VALUE("plugin:hyprexpo:columns"); - return VALUE; -} - -static const CConfigValue& PGAPS() { - static const CConfigValue VALUE("plugin:hyprexpo:gap_size"); - return VALUE; -} - -static const CConfigValue& PCOL() { - static const CConfigValue VALUE("plugin:hyprexpo:bg_col"); - return VALUE; -} - -static const CConfigValue& PSKIP() { - static const CConfigValue VALUE("plugin:hyprexpo:skip_empty"); - return VALUE; -} - -static const CConfigValue& PSHOWNUM() { - static const CConfigValue VALUE("plugin:hyprexpo:show_workspace_numbers"); - return VALUE; -} - -static const CConfigValue& PNUMCOL() { - static const CConfigValue VALUE("plugin:hyprexpo:workspace_number_color"); - return VALUE; -} - -static const CConfigValue& PMETHOD() { - static const CConfigValue VALUE("plugin:hyprexpo:workspace_method"); - return VALUE; -} - -static const CConfigValue& PDISTANCE() { - static const CConfigValue VALUE("plugin:hyprexpo:gesture_distance"); - return VALUE; -} +static const CConfigValue PCOLUMNS("plugin:hyprexpo:columns"); +static const CConfigValue PGAPS("plugin:hyprexpo:gap_size"); +static const CConfigValue PCOL("plugin:hyprexpo:bg_col"); +static const CConfigValue PSKIP("plugin:hyprexpo:skip_empty"); +static const CConfigValue PSHOWNUM("plugin:hyprexpo:show_workspace_numbers"); +static const CConfigValue PNUMCOL("plugin:hyprexpo:workspace_number_color"); +static const CConfigValue PMETHOD("plugin:hyprexpo:workspace_method"); +static const CConfigValue PDISTANCE("plugin:hyprexpo:gesture_distance"); static uint32_t framebufferFormatWithAlpha(uint32_t drmFormat) { const auto alphaFormat = NFormatUtils::alphaFormat(drmFormat); @@ -87,84 +55,6 @@ static void ensureFramebuffer(COverview::SWorkspaceImage& image, const CBox& mon } } -static Vector2D renderLabelTexture(SP out, const std::string& text, const CHyprColor& color, int fontSizePx) { - if (!out || text.empty() || fontSizePx <= 0) - return {}; - - auto measureSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1); - auto measureCairo = cairo_create(measureSurface); - - PangoLayout* measureLayout = pango_cairo_create_layout(measureCairo); - pango_layout_set_text(measureLayout, text.c_str(), -1); - auto* fontDesc = pango_font_description_from_string("Sans Bold"); - pango_font_description_set_size(fontDesc, fontSizePx * PANGO_SCALE); - pango_layout_set_font_description(measureLayout, fontDesc); - pango_font_description_free(fontDesc); - - PangoRectangle inkRect, logicalRect; - pango_layout_get_extents(measureLayout, &inkRect, &logicalRect); - - const int textW = std::max(1, (int)std::ceil(logicalRect.width / (double)PANGO_SCALE)); - const int textH = std::max(1, (int)std::ceil(logicalRect.height / (double)PANGO_SCALE)); - - g_object_unref(measureLayout); - cairo_destroy(measureCairo); - cairo_surface_destroy(measureSurface); - - const int pad = std::max(4, (int)std::round(fontSizePx * 0.35)); - const int width = textW + pad * 2; - const int height = textH + pad * 2; - - auto surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); - auto cairo = cairo_create(surface); - - cairo_save(cairo); - cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); - cairo_paint(cairo); - cairo_restore(cairo); - - cairo_set_source_rgba(cairo, 0.0, 0.0, 0.0, 0.55); - cairo_rectangle(cairo, 0, 0, width, height); - cairo_fill(cairo); - - PangoLayout* layout = pango_cairo_create_layout(cairo); - pango_layout_set_text(layout, text.c_str(), -1); - fontDesc = pango_font_description_from_string("Sans Bold"); - pango_font_description_set_size(fontDesc, fontSizePx * PANGO_SCALE); - pango_layout_set_font_description(layout, fontDesc); - pango_font_description_free(fontDesc); - - pango_layout_get_extents(layout, &inkRect, &logicalRect); - const double xOffset = (width - logicalRect.width / (double)PANGO_SCALE) / 2.0; - const double yOffset = (height - logicalRect.height / (double)PANGO_SCALE) / 2.0; - - cairo_set_source_rgba(cairo, color.r, color.g, color.b, color.a); - cairo_move_to(cairo, xOffset, yOffset); - pango_cairo_show_layout(cairo, layout); - - g_object_unref(layout); - cairo_surface_flush(surface); - - const auto DATA = cairo_image_surface_get_data(surface); - out->allocate({width, height}); - out->bind(); - out->setTexParameter(GL_TEXTURE_MAG_FILTER, GL_NEAREST); - out->setTexParameter(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, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA); - out->unbind(); - - cairo_destroy(cairo); - cairo_surface_destroy(surface); - - return {width, height}; -} - static void damageMonitor(WP thisptr) { g_pOverview->damage(); } @@ -181,15 +71,15 @@ COverview::COverview(PHLWORKSPACE startedOn_, bool swipe_) : startedOn(startedOn const auto PMONITOR = Desktop::focusState()->monitor(); pMonitor = PMONITOR; - SIDE_LENGTH = *PCOLUMNS(); - GAP_WIDTH = *PGAPS(); - BG_COLOR = CHyprColor(*PCOL()); - showWorkspaceNumbers = *PSHOWNUM(); + SIDE_LENGTH = *PCOLUMNS; + GAP_WIDTH = *PGAPS; + BG_COLOR = CHyprColor(*PCOL); + showWorkspaceNumbers = *PSHOWNUM; // process the method bool methodCenter = true; int methodStartID = pMonitor->activeWorkspaceID(); - CVarList method{*PMETHOD(), 0, 's', true}; + CVarList method{*PMETHOD, 0, 's', true}; if (method.size() < 2) Log::logger->log(Log::ERR, "[he] invalid workspace_method"); else { @@ -202,7 +92,7 @@ COverview::COverview(PHLWORKSPACE startedOn_, bool swipe_) : startedOn(startedOn images.resize(SIDE_LENGTH * SIDE_LENGTH); // r includes empty workspaces; m skips over them - std::string selector = *PSKIP() ? "m" : "r"; + std::string selector = *PSKIP ? "m" : "r"; if (methodCenter) { int currentID = methodStartID; @@ -273,13 +163,12 @@ COverview::COverview(PHLWORKSPACE startedOn_, bool swipe_) : startedOn(startedOn CBox monbox{0, 0, tileSize.x * 2, tileSize.y * 2}; if (showWorkspaceNumbers) { - const CHyprColor numberColor = CHyprColor(*PNUMCOL()); + const CHyprColor numberColor = CHyprColor(*PNUMCOL); const int fontSizePx = std::max(12, (int)std::round(tileRenderSize.y * pMonitor->m_scale * 0.22)); for (auto& image : images) { if (image.workspaceID == WORKSPACE_INVALID) continue; - image.labelTex = g_pHyprRenderer->createTexture(false); - image.labelSizePx = renderLabelTexture(image.labelTex, std::to_string(image.workspaceID), numberColor, fontSizePx); + image.labelTex = g_pHyprRenderer->renderText(std::to_string(image.workspaceID), numberColor, fontSizePx, false, "Sans Bold", tileRenderSize.x * pMonitor->m_scale); } } @@ -617,8 +506,8 @@ void COverview::fullRender() { auto& image = images[x + y * SIDE_LENGTH]; Render::GL::g_pHyprOpenGL->renderTextureInternal(image.fb->getTexture(), texbox, {.damage = &damage, .a = 1.0}); - if (showWorkspaceNumbers && image.workspaceID != WORKSPACE_INVALID && image.labelTex && image.labelTex->ok() && image.labelSizePx.x > 0 && image.labelSizePx.y > 0) { - const Vector2D labelSize = image.labelSizePx / pMonitor->m_scale; + if (showWorkspaceNumbers && image.workspaceID != WORKSPACE_INVALID && image.labelTex && image.labelTex->ok()) { + const Vector2D labelSize = image.labelTex->m_size / pMonitor->m_scale; const float margin = std::max(4.0, tileRenderSize.y * 0.05); CBox labelBox = {x * tileRenderSize.x + x * GAPSIZE + margin, y * tileRenderSize.y + y * GAPSIZE + margin, labelSize.x, labelSize.y}; labelBox.scale(pMonitor->m_scale).translate(pos->value()); @@ -648,7 +537,7 @@ void COverview::resetSwipe() { void COverview::onSwipeUpdate(double delta) { m_isSwiping = true; - const float PERC = closing ? std::clamp(delta / (double)*PDISTANCE(), 0.0, 1.0) : 1.0 - std::clamp(delta / (double)*PDISTANCE(), 0.0, 1.0); + const float PERC = closing ? std::clamp(delta / (double)*PDISTANCE, 0.0, 1.0) : 1.0 - std::clamp(delta / (double)*PDISTANCE, 0.0, 1.0); const auto WORKSPACE_FOCUS_ID = closing && closeOnID != -1 ? closeOnID : openedID; Vector2D tileSize = (pMonitor->m_size / SIDE_LENGTH); diff --git a/hyprexpo/overview.hpp b/hyprexpo/overview.hpp index ecad6c5..2aa9bae 100644 --- a/hyprexpo/overview.hpp +++ b/hyprexpo/overview.hpp @@ -49,7 +49,6 @@ class COverview { PHLWORKSPACE pWorkspace; CBox box; SP labelTex; - Vector2D labelSizePx; }; private: diff --git a/hyprscrolling/CMakeLists.txt b/hyprscrolling/CMakeLists.txt index 4e03497..2a05e9f 100644 --- a/hyprscrolling/CMakeLists.txt +++ b/hyprscrolling/CMakeLists.txt @@ -7,7 +7,9 @@ project(hyprscrolling set(CMAKE_CXX_STANDARD 23) -add_library(hyprscrolling SHARED main.cpp) +file(GLOB_RECURSE SRC "*.cpp") + +add_library(hyprscrolling SHARED ${SRC}) find_package(PkgConfig REQUIRED) pkg_check_modules(deps REQUIRED IMPORTED_TARGET diff --git a/hyprscrolling/Makefile b/hyprscrolling/Makefile index 193b454..1471912 100644 --- a/hyprscrolling/Makefile +++ b/hyprscrolling/Makefile @@ -9,6 +9,6 @@ CXXFLAGS ?= -O2 CXXFLAGS += -shared -fPIC -std=c++2b all: - $(CXX) $(CXXFLAGS) $(LDFLAGS) $(EXTRA_FLAGS) main.cpp -o hyprscrolling.so `pkg-config --cflags pixman-1 libdrm hyprland pangocairo libinput libudev wayland-server xkbcommon` + $(CXX) $(CXXFLAGS) $(LDFLAGS) $(EXTRA_FLAGS) main.cpp Scrolling.cpp -o hyprscrolling.so `pkg-config --cflags pixman-1 libdrm hyprland pangocairo libinput libudev wayland-server xkbcommon` clean: rm ./hyprscrolling.so diff --git a/hyprscrolling/main.cpp b/hyprscrolling/main.cpp index 80e31bd..4f09173 100644 --- a/hyprscrolling/main.cpp +++ b/hyprscrolling/main.cpp @@ -1,15 +1,33 @@ #define WLR_USE_UNSTABLE +#include + #include -#include +#include + +#define private public +#include +#include +#include +#include +#include +#undef private + +#include +using namespace Hyprutils::String; #include "globals.hpp" +#include "Scrolling.hpp" // Do NOT change this function. APICALL EXPORT std::string PLUGIN_API_VERSION() { return HYPRLAND_API_VERSION; } +UP g_pScrollingLayout; + +// + APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) { PHANDLE = handle; @@ -22,9 +40,9 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) { throw std::runtime_error("[hs] Version mismatch"); } - const bool success = HyprlandAPI::addTiledAlgo(PHANDLE, "scrolling", &typeid(Layout::Tiled::CScrollingAlgorithm), []() -> UP { - return makeUnique(); - }); + bool success = true; + + g_pScrollingLayout = makeUnique(); HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprscrolling:fullscreen_on_one_column", Hyprlang::INT{0}); HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprscrolling:column_width", Hyprlang::FLOAT{0.5F}); @@ -32,15 +50,17 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) { HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprscrolling:follow_focus", Hyprlang::INT{1}); HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprscrolling:follow_debounce_ms", Hyprlang::INT{0}); HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprscrolling:explicit_column_widths", Hyprlang::STRING{"0.333, 0.5, 0.667, 1.0"}); + HyprlandAPI::addLayout(PHANDLE, "scrolling", g_pScrollingLayout.get()); if (!success) { - HyprlandAPI::addNotification(PHANDLE, "[hyprscrolling] Failure in initialization: failed to register layout algorithm", CHyprColor{1.0, 0.2, 0.2, 1.0}, 5000); - throw std::runtime_error("[hs] Algorithm registration failed"); + HyprlandAPI::addNotification(PHANDLE, "[hyprscrolling] Failure in initialization: failed to register dispatchers", CHyprColor{1.0, 0.2, 0.2, 1.0}, 5000); + throw std::runtime_error("[hs] Dispatchers failed"); } return {"hyprscrolling", "A plugin to add a scrolling layout to hyprland", "Vaxry", "1.0"}; } APICALL EXPORT void PLUGIN_EXIT() { - HyprlandAPI::removeAlgo(PHANDLE, "scrolling"); + HyprlandAPI::removeLayout(PHANDLE, g_pScrollingLayout.get()); + g_pScrollingLayout.reset(); } diff --git a/hyprtrails/globals.hpp b/hyprtrails/globals.hpp index 0829b85..87bc18f 100644 --- a/hyprtrails/globals.hpp +++ b/hyprtrails/globals.hpp @@ -2,11 +2,17 @@ #include #include +#include + +class CEventLoopTimer; +class CTrail; inline HANDLE PHANDLE = nullptr; struct SGlobalState { - CShader trailShader; + CShader trailShader; + SP tick; + std::vector trails; }; inline UP g_pGlobalState; diff --git a/hyprtrails/main.cpp b/hyprtrails/main.cpp index 39f3470..cec5f09 100644 --- a/hyprtrails/main.cpp +++ b/hyprtrails/main.cpp @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include "globals.hpp" #include "shaders.hpp" @@ -23,11 +25,21 @@ void onNewWindow(PHLWINDOW PWINDOW) { HyprlandAPI::addWindowDecoration(PHANDLE, PWINDOW, makeUnique(PWINDOW)); } +static void onTick(SP self, void* data) { + tickTrails(); + + const int timeoutMs = g_pHyprRenderer->m_mostHzMonitor ? 1000.0 / g_pHyprRenderer->m_mostHzMonitor->m_refreshRate : 16; + self->updateTimeout(std::chrono::milliseconds(timeoutMs)); +} + void initGlobal() { Render::GL::g_pHyprOpenGL->makeEGLCurrent(); if (!g_pGlobalState->trailShader.createProgram(QUADTRAIL, FRAGTRAIL, true, false)) throw std::runtime_error("[ht] Failed to create trail shader"); + + g_pGlobalState->tick = makeShared(std::chrono::milliseconds(1), onTick, nullptr); + g_pEventLoopManager->addTimer(g_pGlobalState->tick); } APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) { @@ -69,5 +81,10 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) { } APICALL EXPORT void PLUGIN_EXIT() { + if (g_pGlobalState && g_pGlobalState->tick) { + g_pGlobalState->tick->cancel(); + g_pEventLoopManager->removeTimer(g_pGlobalState->tick); + } + g_pHyprRenderer->m_renderPass.removeAllOfType("CTrailPassElement"); } diff --git a/hyprtrails/trail.cpp b/hyprtrails/trail.cpp index c4783a7..ebf1553 100644 --- a/hyprtrails/trail.cpp +++ b/hyprtrails/trail.cpp @@ -1,5 +1,6 @@ #include "trail.hpp" +#include #include #include #include @@ -13,6 +14,9 @@ void CTrail::onTick() { static auto* const PHISTORYSTEP = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprtrails:history_step")->getDataStaticPtr(); static auto* const PHISTORYPOINTS = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprtrails:history_points")->getDataStaticPtr(); + if (!validMapped(m_pWindow)) + return; + m_iTimer++; const auto PWINDOW = m_pWindow.lock(); @@ -36,10 +40,16 @@ void CTrail::onTick() { CTrail::CTrail(PHLWINDOW pWindow) : IHyprWindowDecoration(pWindow), m_pWindow(pWindow) { m_lastWindowPos = pWindow->m_realPosition->value(); m_lastWindowSize = pWindow->m_realSize->value(); + + if (g_pGlobalState) + g_pGlobalState->trails.push_back(this); } CTrail::~CTrail() { damageEntire(); + + if (g_pGlobalState) + std::erase(g_pGlobalState->trails, this); } SDecorationPositioningInfo CTrail::getPositioningInfo() { @@ -83,8 +93,6 @@ void CTrail::draw(PHLMONITOR pMonitor, const float& a) { if (!validMapped(m_pWindow)) return; - onTick(); - const auto PWINDOW = m_pWindow.lock(); if (!PWINDOW->m_ruleApplicator->decorate().valueOrDefault()) @@ -335,3 +343,13 @@ void CTrail::damageEntire() { sc(m_lastWindowSize.x + m_seExtents.topLeft.x + m_seExtents.bottomRight.x), sc(m_seExtents.topLeft.y)}; g_pHyprRenderer->damageBox(dm); } + +void tickTrails() { + if (!g_pGlobalState) + return; + + for (auto* const trail : g_pGlobalState->trails) { + if (trail) + trail->onTick(); + } +} diff --git a/hyprtrails/trail.hpp b/hyprtrails/trail.hpp index 0b36b04..e2e259d 100644 --- a/hyprtrails/trail.hpp +++ b/hyprtrails/trail.hpp @@ -64,4 +64,7 @@ class CTrail : public IHyprWindowDecoration { bool m_bNeedsDamage = false; friend class CTrailPassElement; + friend void tickTrails(); }; + +void tickTrails(); diff --git a/hyprwinwrap/main.cpp b/hyprwinwrap/main.cpp index 6bc280a..afb475d 100644 --- a/hyprwinwrap/main.cpp +++ b/hyprwinwrap/main.cpp @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include #include #include @@ -37,6 +39,23 @@ typedef void (*origCommitSubsurface)(Desktop::View::CSubsurface* thisptr); typedef void (*origCommit)(void* owner, void* data); std::vector bgWindows; +std::vector> bgRules; + +static SP makeWindowRule(const std::string& name, const Desktop::Rule::eRuleProperty prop, const std::string& match) { + auto rule = makeShared(name); + rule->registerMatch(prop, "^(" + match + ")$"); + rule->addEffect(Desktop::Rule::WINDOW_RULE_EFFECT_FLOAT, "1"); + rule->addEffect(Desktop::Rule::WINDOW_RULE_EFFECT_SIZE, "100% 100%"); + return rule; +} + +static void clearWindowRules() { + for (auto& rule : bgRules) { + if (rule) + Desktop::Rule::ruleEngine()->unregisterRule(rule); + } + bgRules.clear(); +} void onNewWindow(PHLWINDOW pWindow) { static auto* const PCLASS = (Hyprlang::STRING const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprwinwrap:class")->getDataStaticPtr(); @@ -175,19 +194,25 @@ void onCommit(void* owner, void* data) { } void onConfigReloaded() { + clearWindowRules(); + static auto* const PCLASS = (Hyprlang::STRING const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprwinwrap:class")->getDataStaticPtr(); const std::string classRule(*PCLASS); if (!classRule.empty()) { - HyprlandAPI::invokeHyprctlCommand("keyword", std::string{"windowrulev2 float, class:^("} + classRule + ")$"); - HyprlandAPI::invokeHyprctlCommand("keyword", std::string{"windowrulev2 size 100% 100%, class:^("} + classRule + ")$"); + auto rule = makeWindowRule("hyprwinwrap-class", Desktop::Rule::RULE_PROP_CLASS, classRule); + bgRules.emplace_back(rule); + Desktop::Rule::ruleEngine()->registerRule(SP{rule}); } static auto* const PTITLE = (Hyprlang::STRING const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprwinwrap:title")->getDataStaticPtr(); const std::string titleRule(*PTITLE); if (!titleRule.empty()) { - HyprlandAPI::invokeHyprctlCommand("keyword", std::string{"windowrulev2 float, title:^("} + titleRule + ")$"); - HyprlandAPI::invokeHyprctlCommand("keyword", std::string{"windowrulev2 size 100% 100%, title:^("} + titleRule + ")$"); + auto rule = makeWindowRule("hyprwinwrap-title", Desktop::Rule::RULE_PROP_TITLE, titleRule); + bgRules.emplace_back(rule); + Desktop::Rule::ruleEngine()->registerRule(SP{rule}); } + + Desktop::Rule::ruleEngine()->updateAllRules(); } APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) { @@ -231,11 +256,13 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) { HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprwinwrap:pos_x", Hyprlang::STRING{"0"}); HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprwinwrap:pos_y", Hyprlang::STRING{"0"}); + onConfigReloaded(); + HyprlandAPI::addNotification(PHANDLE, "[hyprwinwrap] Initialized successfully!", CHyprColor{0.2, 1.0, 0.2, 1.0}, 5000); return {"hyprwinwrap", "A clone of xwinwrap for Hyprland", "Vaxry", "1.0"}; } APICALL EXPORT void PLUGIN_EXIT() { - ; + clearWindowRules(); }