From d09549230abf44f9571122edf999d3f7bb6c8b77 Mon Sep 17 00:00:00 2001 From: Ivan Malison Date: Wed, 29 Apr 2026 08:03:46 -0700 Subject: [PATCH 01/12] fix CI against latest Hyprland --- borders-plus-plus/BorderppPassElement.cpp | 8 +- borders-plus-plus/BorderppPassElement.hpp | 14 ++-- borders-plus-plus/borderDeco.cpp | 1 + flake.lock | 94 +++++++++++------------ hyprexpo/OverviewPassElement.cpp | 3 +- hyprexpo/OverviewPassElement.hpp | 18 +++-- hyprexpo/main.cpp | 2 +- hyprexpo/overview.cpp | 61 ++++++++------- hyprexpo/overview.hpp | 10 ++- hyprscrolling/CMakeLists.txt | 4 +- hyprscrolling/Makefile | 2 +- hyprscrolling/main.cpp | 34 ++------ hyprtrails/TrailPassElement.cpp | 8 +- hyprtrails/TrailPassElement.hpp | 14 ++-- hyprtrails/globals.hpp | 4 +- hyprtrails/main.cpp | 82 ++------------------ hyprtrails/trail.cpp | 37 +++++---- hyprtrails/trail.hpp | 1 - hyprwinwrap/main.cpp | 21 +++-- xtra-dispatchers/main.cpp | 6 +- 20 files changed, 185 insertions(+), 239 deletions(-) diff --git a/borders-plus-plus/BorderppPassElement.cpp b/borders-plus-plus/BorderppPassElement.cpp index 295e88def..53772a478 100644 --- a/borders-plus-plus/BorderppPassElement.cpp +++ b/borders-plus-plus/BorderppPassElement.cpp @@ -1,13 +1,15 @@ #include "BorderppPassElement.hpp" #include +#include #include "borderDeco.hpp" CBorderPPPassElement::CBorderPPPassElement(const CBorderPPPassElement::SBorderPPData& data_) : data(data_) { ; } -void CBorderPPPassElement::draw(const CRegion& damage) { - data.deco->drawPass(g_pHyprOpenGL->m_renderData.pMonitor.lock(), data.a); +std::vector> CBorderPPPassElement::draw() { + data.deco->drawPass(g_pHyprRenderer->m_renderData.pMonitor.lock(), data.a); + return {}; } bool CBorderPPPassElement::needsLiveBlur() { @@ -16,4 +18,4 @@ bool CBorderPPPassElement::needsLiveBlur() { bool CBorderPPPassElement::needsPrecomputeBlur() { return false; -} \ No newline at end of file +} diff --git a/borders-plus-plus/BorderppPassElement.hpp b/borders-plus-plus/BorderppPassElement.hpp index 12b858aa4..6d8abe5e3 100644 --- a/borders-plus-plus/BorderppPassElement.hpp +++ b/borders-plus-plus/BorderppPassElement.hpp @@ -13,14 +13,18 @@ class CBorderPPPassElement : public IPassElement { CBorderPPPassElement(const SBorderPPData& data_); virtual ~CBorderPPPassElement() = default; - virtual void draw(const CRegion& damage); - virtual bool needsLiveBlur(); - virtual bool needsPrecomputeBlur(); + virtual std::vector> draw() override; + virtual bool needsLiveBlur() override; + virtual bool needsPrecomputeBlur() override; - virtual const char* passName() { + virtual const char* passName() override { return "CBorderPPPassElement"; } + virtual ePassElementType type() override { + return EK_CUSTOM; + } + private: SBorderPPData data; -}; \ No newline at end of file +}; diff --git a/borders-plus-plus/borderDeco.cpp b/borders-plus-plus/borderDeco.cpp index 9a2abfa6a..dbb1855a1 100644 --- a/borders-plus-plus/borderDeco.cpp +++ b/borders-plus-plus/borderDeco.cpp @@ -5,6 +5,7 @@ #include #include using namespace Hyprutils::Memory; +using namespace Render::GL; #include "BorderppPassElement.hpp" #include "globals.hpp" diff --git a/flake.lock b/flake.lock index c291026e4..1dcaed11c 100644 --- a/flake.lock +++ b/flake.lock @@ -20,11 +20,11 @@ ] }, "locked": { - "lastModified": 1765900596, - "narHash": "sha256-+hn8v9jkkLP9m+o0Nm5SiEq10W0iWDSotH2XfjU45fA=", + "lastModified": 1776876344, + "narHash": "sha256-Ubqb/agkuMJK+k19gjQgHux/eOYRc1sRGoOZOho8+VY=", "owner": "hyprwm", "repo": "aquamarine", - "rev": "d83c97f8f5c0aae553c1489c7d9eff3eadcadace", + "rev": "648a13d0ee1e03a843b3e145b8ece15393058701", "type": "github" }, "original": { @@ -36,15 +36,15 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1761588595, - "narHash": "sha256-XKUZz9zewJNUj46b4AJdiRZJAvSZ0Dqj2BNfXvFlJC4=", - "owner": "edolstra", + "lastModified": 1767039857, + "narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=", + "owner": "NixOS", "repo": "flake-compat", - "rev": "f387cd2afec9419c8ee37694406ca490c3f34ee5", + "rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab", "type": "github" }, "original": { - "owner": "edolstra", + "owner": "NixOS", "repo": "flake-compat", "type": "github" } @@ -87,11 +87,11 @@ ] }, "locked": { - "lastModified": 1753964049, - "narHash": "sha256-lIqabfBY7z/OANxHoPeIrDJrFyYy9jAM4GQLzZ2feCM=", + "lastModified": 1776511930, + "narHash": "sha256-fCpwFiTW0rT7oKJqr3cqHMnkwypSwQKpbtUEtxdkgrM=", "owner": "hyprwm", "repo": "hyprcursor", - "rev": "44e91d467bdad8dcf8bbd2ac7cf49972540980a5", + "rev": "39435900785d0c560c6ae8777d29f28617d031ef", "type": "github" }, "original": { @@ -116,11 +116,11 @@ ] }, "locked": { - "lastModified": 1763733840, - "narHash": "sha256-JnET78yl5RvpGuDQy3rCycOCkiKoLr5DN1fPhRNNMco=", + "lastModified": 1776426399, + "narHash": "sha256-RUESLKNikIeEq9ymGJ6nmcDXiSFQpUW1IhJ245nL3xM=", "owner": "hyprwm", "repo": "hyprgraphics", - "rev": "8f1bec691b2d198c60cccabca7a94add2df4ed1a", + "rev": "68d064434787cf1ed4a2fe257c03c5f52f33cf84", "type": "github" }, "original": { @@ -146,11 +146,11 @@ "xdph": "xdph" }, "locked": { - "lastModified": 1767201430, - "narHash": "sha256-2FF66EaIbsc7CL1jKHbRFslSePDq40fzlTTbUlm5v3k=", + "lastModified": 1777469508, + "narHash": "sha256-2j6wX8051O5KcLw03t9x/RsFUOIGmQ2BPMMEO//iF1o=", "owner": "hyprwm", "repo": "Hyprland", - "rev": "48a024e0322bbd7c4c88126498ec478444ec4cb2", + "rev": "202cf48ecf627839c0a7433cbeb018c744214390", "type": "github" }, "original": { @@ -192,11 +192,11 @@ ] }, "locked": { - "lastModified": 1765643131, - "narHash": "sha256-CCGohW5EBIRy4B7vTyBMqPgsNcaNenVad/wszfddET0=", + "lastModified": 1776426575, + "narHash": "sha256-KI6nIfVihn/DPaeB5Et46Xg3dkNHrrEtUd5LBBVomB0=", "owner": "hyprwm", "repo": "hyprland-guiutils", - "rev": "e50ae912813bdfa8372d62daf454f48d6df02297", + "rev": "a968d211048e3ed538e47b84cb3649299578f19d", "type": "github" }, "original": { @@ -217,11 +217,11 @@ ] }, "locked": { - "lastModified": 1765214753, - "narHash": "sha256-P9zdGXOzToJJgu5sVjv7oeOGPIIwrd9hAUAP3PsmBBs=", + "lastModified": 1772460177, + "narHash": "sha256-/6G/MsPvtn7bc4Y32pserBT/Z4SUUdBd4XYJpOEKVR4=", "owner": "hyprwm", "repo": "hyprland-protocols", - "rev": "3f3860b869014c00e8b9e0528c7b4ddc335c21ab", + "rev": "1cb6db5fd6bb8aee419f4457402fa18293ace917", "type": "github" }, "original": { @@ -246,11 +246,11 @@ ] }, "locked": { - "lastModified": 1764612430, - "narHash": "sha256-54ltTSbI6W+qYGMchAgCR6QnC1kOdKXN6X6pJhOWxFg=", + "lastModified": 1776426736, + "narHash": "sha256-rl7i4aY+9p8LysJp7o8uRWahCkpFznCgGHXszlTw7b0=", "owner": "hyprwm", "repo": "hyprlang", - "rev": "0d00dc118981531aa731150b6ea551ef037acddd", + "rev": "7833ff33b2e82d3406337b5dcf0d1cec595d83e9", "type": "github" }, "original": { @@ -298,11 +298,11 @@ ] }, "locked": { - "lastModified": 1764592794, - "narHash": "sha256-7CcO+wbTJ1L1NBQHierHzheQGPWwkIQug/w+fhTAVuU=", + "lastModified": 1772462885, + "narHash": "sha256-5pHXrQK9zasMnIo6yME6EOXmWGFMSnCITcfKshhKJ9I=", "owner": "hyprwm", "repo": "hyprtoolkit", - "rev": "5cfe0743f0e608e1462972303778d8a0859ee63e", + "rev": "9af245a69fa6b286b88ddfc340afd288e00a6998", "type": "github" }, "original": { @@ -323,11 +323,11 @@ ] }, "locked": { - "lastModified": 1766160771, - "narHash": "sha256-roINUGikWRqqgKrD4iotKbGj3ZKJl3hjMz5l/SyKrHw=", + "lastModified": 1777148223, + "narHash": "sha256-PTf7kRFFzCW6rIYxLH2fWfVJmj86FSYe3k6L8B+IM9o=", "owner": "hyprwm", "repo": "hyprutils", - "rev": "5ac060bfcf2f12b3a6381156ebbc13826a05b09f", + "rev": "fa3992be2dfebe4ab06d753c6ca59bea298e798f", "type": "github" }, "original": { @@ -348,11 +348,11 @@ ] }, "locked": { - "lastModified": 1763640274, - "narHash": "sha256-Uan1Nl9i4TF/kyFoHnTq1bd/rsWh4GAK/9/jDqLbY5A=", + "lastModified": 1777148232, + "narHash": "sha256-Uv0WZLhu89SafuSOmYDA7akrPt4wBRmsa1ucasO5aXg=", "owner": "hyprwm", "repo": "hyprwayland-scanner", - "rev": "f6cf414ca0e16a4d30198fd670ec86df3c89f671", + "rev": "fec9cf1abcc1011e46f0a0986f46bf93c6bf8b92", "type": "github" }, "original": { @@ -377,11 +377,11 @@ ] }, "locked": { - "lastModified": 1766253200, - "narHash": "sha256-26qPwrd3od+xoYVywSB7hC2cz9ivN46VPLlrsXyGxvE=", + "lastModified": 1776728575, + "narHash": "sha256-z9eGphrArEBpl1O/GCH0wlY6z4K9vA6yWh2gAS6qytU=", "owner": "hyprwm", "repo": "hyprwire", - "rev": "1079777525b30a947c8d657fac158e00ae85de9d", + "rev": "f3a80888783702a39691b684d099e16b83ed4702", "type": "github" }, "original": { @@ -392,11 +392,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1766070988, - "narHash": "sha256-G/WVghka6c4bAzMhTwT2vjLccg/awmHkdKSd2JrycLc=", + "lastModified": 1776877367, + "narHash": "sha256-EHq1/OX139R1RvBzOJ0aMRT3xnWyqtHBRUBuO1gFzjI=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "c6245e83d836d0433170a16eb185cefe0572f8b8", + "rev": "0726a0ecb6d4e08f6adced58726b95db924cef57", "type": "github" }, "original": { @@ -416,11 +416,11 @@ ] }, "locked": { - "lastModified": 1765911976, - "narHash": "sha256-t3T/xm8zstHRLx+pIHxVpQTiySbKqcQbK+r+01XVKc0=", + "lastModified": 1776796298, + "narHash": "sha256-PcRvlWayisPSjd0UcRQbhG8Oqw78AcPE6x872cPRHN8=", "owner": "cachix", "repo": "git-hooks.nix", - "rev": "b68b780b69702a090c8bb1b973bab13756cc7a27", + "rev": "3cfd774b0a530725a077e17354fbdb87ea1c4aad", "type": "github" }, "original": { @@ -485,11 +485,11 @@ ] }, "locked": { - "lastModified": 1761431178, - "narHash": "sha256-xzjC1CV3+wpUQKNF+GnadnkeGUCJX+vgaWIZsnz9tzI=", + "lastModified": 1777035886, + "narHash": "sha256-m1TNuBoSXUBSKhD9UVMkU90M0wFTPTfvIOOltO8IM8A=", "owner": "hyprwm", "repo": "xdg-desktop-portal-hyprland", - "rev": "4b8801228ff958d028f588f0c2b911dbf32297f9", + "rev": "ecfcdcc781f48821d83e1e2a0e30d7beca0eeb5e", "type": "github" }, "original": { diff --git a/hyprexpo/OverviewPassElement.cpp b/hyprexpo/OverviewPassElement.cpp index 1fc88e73a..ef40486e5 100644 --- a/hyprexpo/OverviewPassElement.cpp +++ b/hyprexpo/OverviewPassElement.cpp @@ -6,8 +6,9 @@ COverviewPassElement::COverviewPassElement() { ; } -void COverviewPassElement::draw(const CRegion& damage) { +std::vector> COverviewPassElement::draw() { g_pOverview->fullRender(); + return {}; } bool COverviewPassElement::needsLiveBlur() { diff --git a/hyprexpo/OverviewPassElement.hpp b/hyprexpo/OverviewPassElement.hpp index f9d5a373a..428b257d4 100644 --- a/hyprexpo/OverviewPassElement.hpp +++ b/hyprexpo/OverviewPassElement.hpp @@ -8,13 +8,17 @@ class COverviewPassElement : public IPassElement { COverviewPassElement(); virtual ~COverviewPassElement() = default; - virtual void draw(const CRegion& damage); - virtual bool needsLiveBlur(); - virtual bool needsPrecomputeBlur(); - virtual std::optional boundingBox(); - virtual CRegion opaqueRegion(); + virtual std::vector> draw() override; + virtual bool needsLiveBlur() override; + virtual bool needsPrecomputeBlur() override; + virtual std::optional boundingBox() override; + virtual CRegion opaqueRegion() override; - virtual const char* passName() { + virtual const char* passName() override { return "COverviewPassElement"; } -}; \ No newline at end of file + + virtual ePassElementType type() override { + return EK_CUSTOM; + } +}; diff --git a/hyprexpo/main.cpp b/hyprexpo/main.cpp index 1f74f3801..3b3dff1f6 100644 --- a/hyprexpo/main.cpp +++ b/hyprexpo/main.cpp @@ -263,5 +263,5 @@ APICALL EXPORT void PLUGIN_EXIT() { g_unloading = true; - g_pConfigManager->reload(); // we need to reload now to clear all the gestures + Config::mgr()->reload(); // we need to reload now to clear all the gestures } diff --git a/hyprexpo/overview.cpp b/hyprexpo/overview.cpp index 00bb8c91f..26c716228 100644 --- a/hyprexpo/overview.cpp +++ b/hyprexpo/overview.cpp @@ -1,7 +1,14 @@ -#include "overview.hpp" #include -#define private public +#include +#define protected public #include +#undef protected +#include "overview.hpp" +#include +#include +#include +#include +#include #include #include #include @@ -12,18 +19,20 @@ #include #include #include -#undef private #include "OverviewPassElement.hpp" +using namespace Hyprutils::String; +using namespace Render; +using namespace Render::GL; + static void damageMonitor(WP thisptr) { g_pOverview->damage(); } COverview::~COverview() { - g_pHyprRenderer->makeEGLCurrent(); images.clear(); // otherwise we get a vram leak Cursor::overrideController->unsetOverride(Cursor::CURSOR_OVERRIDE_UNKNOWN); - g_pHyprOpenGL->markBlurDirtyForMonitor(pMonitor.lock()); + g_pHyprRenderer->damageMonitor(pMonitor.lock()); } COverview::COverview(PHLWORKSPACE startedOn_, bool swipe_) : startedOn(startedOn_), swipe(swipe_) { @@ -43,12 +52,12 @@ COverview::COverview(PHLWORKSPACE startedOn_, bool swipe_) : startedOn(startedOn // process the method bool methodCenter = true; int methodStartID = pMonitor->activeWorkspaceID(); - CVarList method{*PMETHOD, 0, 's', true}; + CConstVarList method{*PMETHOD, 0, 's', true}; if (method.size() < 2) Log::logger->log(Log::ERR, "[he] invalid workspace_method"); else { methodCenter = method[0] == "center"; - methodStartID = getWorkspaceIDNameFromString(method[1]).id; + methodStartID = getWorkspaceIDNameFromString(std::string{method[1]}).id; if (methodStartID == WORKSPACE_INVALID) methodStartID = pMonitor->activeWorkspaceID(); } @@ -120,8 +129,6 @@ COverview::COverview(PHLWORKSPACE startedOn_, bool swipe_) : startedOn(startedOn pMonitor->m_activeWorkspace = startedOn; } - g_pHyprRenderer->makeEGLCurrent(); - Vector2D tileSize = pMonitor->m_size / SIDE_LENGTH; Vector2D tileRenderSize = (pMonitor->m_size - Vector2D{GAP_WIDTH * pMonitor->m_scale, GAP_WIDTH * pMonitor->m_scale} * (SIDE_LENGTH - 1)) / SIDE_LENGTH; CBox monbox{0, 0, tileSize.x * 2, tileSize.y * 2}; @@ -141,12 +148,13 @@ COverview::COverview(PHLWORKSPACE startedOn_, bool swipe_) : startedOn(startedOn for (size_t i = 0; i < (size_t)(SIDE_LENGTH * SIDE_LENGTH); ++i) { COverview::SWorkspaceImage& image = images[i]; - image.fb.alloc(monbox.w, monbox.h, PMONITOR->m_output->state->state().drmFormat); + image.fb = makeShared(); + image.fb->alloc(monbox.w, monbox.h, PMONITOR->m_output->state->state().drmFormat); CRegion fakeDamage{0, 0, INT16_MAX, INT16_MAX}; - g_pHyprRenderer->beginRender(PMONITOR, fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, &image.fb); + g_pHyprRenderer->beginRender(PMONITOR, fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, image.fb); - g_pHyprOpenGL->clear(CHyprColor{0, 0, 0, 1.0}); + g_pHyprRenderer->draw(CClearPassElement::SClearData{CHyprColor{0, 0, 0, 1.0}}); const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(image.workspaceID); @@ -175,7 +183,7 @@ COverview::COverview(PHLWORKSPACE startedOn_, bool swipe_) : startedOn(startedOn image.box = {(i % SIDE_LENGTH) * tileRenderSize.x + (i % SIDE_LENGTH) * GAP_WIDTH, (i / SIDE_LENGTH) * tileRenderSize.y + (i / SIDE_LENGTH) * GAP_WIDTH, tileRenderSize.x, tileRenderSize.y}; - g_pHyprOpenGL->m_renderData.blockScreenShader = true; + g_pHyprRenderer->m_renderData.blockScreenShader = true; g_pHyprRenderer->endRender(); } @@ -189,10 +197,10 @@ COverview::COverview(PHLWORKSPACE startedOn_, bool swipe_) : startedOn(startedOn // zoom on the current workspace. // const auto& TILE = images[std::clamp(currentid, 0, SIDE_LENGTH * SIDE_LENGTH)]; - g_pAnimationManager->createAnimation(pMonitor->m_size * pMonitor->m_size / tileSize, size, g_pConfigManager->getAnimationPropertyConfig("windowsMove"), AVARDAMAGE_NONE); + g_pAnimationManager->createAnimation(pMonitor->m_size * pMonitor->m_size / tileSize, size, Config::animationTree()->getAnimationPropertyConfig("windowsMove"), AVARDAMAGE_NONE); g_pAnimationManager->createAnimation((-((pMonitor->m_size / (double)SIDE_LENGTH) * Vector2D{currentid % SIDE_LENGTH, currentid / SIDE_LENGTH}) * pMonitor->m_scale) * (pMonitor->m_size / tileSize), - pos, g_pConfigManager->getAnimationPropertyConfig("windowsMove"), AVARDAMAGE_NONE); + pos, Config::animationTree()->getAnimationPropertyConfig("windowsMove"), AVARDAMAGE_NONE); size->setUpdateCallback(damageMonitor); pos->setUpdateCallback(damageMonitor); @@ -257,8 +265,6 @@ void COverview::redrawID(int id, bool forcelowres) { blockOverviewRendering = true; - g_pHyprRenderer->makeEGLCurrent(); - id = std::clamp(id, 0, SIDE_LENGTH * SIDE_LENGTH); Vector2D tileSize = pMonitor->m_size / SIDE_LENGTH; @@ -273,15 +279,16 @@ void COverview::redrawID(int id, bool forcelowres) { auto& image = images[id]; - if (image.fb.m_size != monbox.size()) { - image.fb.release(); - image.fb.alloc(monbox.w, monbox.h, pMonitor->m_output->state->state().drmFormat); + if (image.fb->m_size != monbox.size()) { + image.fb->release(); + image.fb = makeShared(); + image.fb->alloc(monbox.w, monbox.h, pMonitor->m_output->state->state().drmFormat); } CRegion fakeDamage{0, 0, INT16_MAX, INT16_MAX}; - g_pHyprRenderer->beginRender(pMonitor.lock(), fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, &image.fb); + g_pHyprRenderer->beginRender(pMonitor.lock(), fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, image.fb); - g_pHyprOpenGL->clear(CHyprColor{0, 0, 0, 1.0}); + g_pHyprRenderer->draw(CClearPassElement::SClearData{CHyprColor{0, 0, 0, 1.0}}); const auto PWORKSPACE = image.pWorkspace; @@ -309,7 +316,7 @@ void COverview::redrawID(int id, bool forcelowres) { } else g_pHyprRenderer->renderWorkspace(pMonitor.lock(), PWORKSPACE, Time::steadyNow(), monbox); - g_pHyprOpenGL->m_renderData.blockScreenShader = true; + g_pHyprRenderer->m_renderData.blockScreenShader = true; g_pHyprRenderer->endRender(); pMonitor->m_activeSpecialWorkspace = openSpecial; @@ -387,9 +394,9 @@ void COverview::close() { const auto OLDWS = pMonitor->m_activeWorkspace; if (!NEWIDWS) - g_pKeybindManager->changeworkspace(std::to_string(NEWID)); + Config::Actions::changeWorkspace(std::to_string(NEWID)); else - g_pKeybindManager->changeworkspace(NEWIDWS->getConfigName()); + Config::Actions::changeWorkspace(NEWIDWS->getConfigName()); g_pDesktopAnimationManager->startAnimation(pMonitor->m_activeWorkspace, CDesktopAnimationManager::ANIMATION_TYPE_IN, true, true); g_pDesktopAnimationManager->startAnimation(OLDWS, CDesktopAnimationManager::ANIMATION_TYPE_OUT, false, true); @@ -443,7 +450,7 @@ void COverview::fullRender() { Vector2D tileSize = (SIZE / SIDE_LENGTH); Vector2D tileRenderSize = (SIZE - Vector2D{GAPSIZE, GAPSIZE} * (SIDE_LENGTH - 1)) / SIDE_LENGTH; - g_pHyprOpenGL->clear(BG_COLOR.stripA()); + g_pHyprRenderer->draw(CClearPassElement::SClearData{BG_COLOR.stripA()}); for (size_t y = 0; y < (size_t)SIDE_LENGTH; ++y) { for (size_t x = 0; x < (size_t)SIDE_LENGTH; ++x) { @@ -451,7 +458,7 @@ void COverview::fullRender() { texbox.scale(pMonitor->m_scale).translate(pos->value()); texbox.round(); CRegion damage{0, 0, INT16_MAX, INT16_MAX}; - g_pHyprOpenGL->renderTextureInternal(images[x + y * SIDE_LENGTH].fb.getTexture(), texbox, {.damage = &damage, .a = 1.0}); + g_pHyprOpenGL->renderTexture(images[x + y * SIDE_LENGTH].fb->getTexture(), texbox, {.damage = &damage, .a = 1.0}); } } } diff --git a/hyprexpo/overview.hpp b/hyprexpo/overview.hpp index 144add970..7ecc64fde 100644 --- a/hyprexpo/overview.hpp +++ b/hyprexpo/overview.hpp @@ -4,11 +4,17 @@ #include "globals.hpp" #include -#include +#include #include #include #include +using namespace Hyprutils::Memory; + +namespace Render { + class IFramebuffer; +} + // saves on resources, but is a bit broken rn with blur. // hyprland's fault, but cba to fix. constexpr bool ENABLE_LOWRES = false; @@ -54,7 +60,7 @@ class COverview { bool damageDirty = false; struct SWorkspaceImage { - CFramebuffer fb; + SP fb; int64_t workspaceID = -1; PHLWORKSPACE pWorkspace; CBox box; diff --git a/hyprscrolling/CMakeLists.txt b/hyprscrolling/CMakeLists.txt index 2a05e9f02..4e0349710 100644 --- a/hyprscrolling/CMakeLists.txt +++ b/hyprscrolling/CMakeLists.txt @@ -7,9 +7,7 @@ project(hyprscrolling set(CMAKE_CXX_STANDARD 23) -file(GLOB_RECURSE SRC "*.cpp") - -add_library(hyprscrolling SHARED ${SRC}) +add_library(hyprscrolling SHARED main.cpp) find_package(PkgConfig REQUIRED) pkg_check_modules(deps REQUIRED IMPORTED_TARGET diff --git a/hyprscrolling/Makefile b/hyprscrolling/Makefile index 147191286..193b4542d 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 Scrolling.cpp -o hyprscrolling.so `pkg-config --cflags pixman-1 libdrm hyprland pangocairo libinput libudev wayland-server xkbcommon` + $(CXX) $(CXXFLAGS) $(LDFLAGS) $(EXTRA_FLAGS) main.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 4f09173d2..80e31bd23 100644 --- a/hyprscrolling/main.cpp +++ b/hyprscrolling/main.cpp @@ -1,33 +1,15 @@ #define WLR_USE_UNSTABLE -#include - #include -#include - -#define private public -#include -#include -#include -#include -#include -#undef private - -#include -using namespace Hyprutils::String; +#include #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; @@ -40,9 +22,9 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) { throw std::runtime_error("[hs] Version mismatch"); } - bool success = true; - - g_pScrollingLayout = makeUnique(); + const bool success = HyprlandAPI::addTiledAlgo(PHANDLE, "scrolling", &typeid(Layout::Tiled::CScrollingAlgorithm), []() -> UP { + return makeUnique(); + }); HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprscrolling:fullscreen_on_one_column", Hyprlang::INT{0}); HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprscrolling:column_width", Hyprlang::FLOAT{0.5F}); @@ -50,17 +32,15 @@ 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 dispatchers", CHyprColor{1.0, 0.2, 0.2, 1.0}, 5000); - throw std::runtime_error("[hs] Dispatchers failed"); + 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"); } return {"hyprscrolling", "A plugin to add a scrolling layout to hyprland", "Vaxry", "1.0"}; } APICALL EXPORT void PLUGIN_EXIT() { - HyprlandAPI::removeLayout(PHANDLE, g_pScrollingLayout.get()); - g_pScrollingLayout.reset(); + HyprlandAPI::removeAlgo(PHANDLE, "scrolling"); } diff --git a/hyprtrails/TrailPassElement.cpp b/hyprtrails/TrailPassElement.cpp index a2734f624..c5bf78155 100644 --- a/hyprtrails/TrailPassElement.cpp +++ b/hyprtrails/TrailPassElement.cpp @@ -1,13 +1,15 @@ #include "TrailPassElement.hpp" #include +#include #include "trail.hpp" CTrailPassElement::CTrailPassElement(const CTrailPassElement::STrailData& data_) : data(data_) { ; } -void CTrailPassElement::draw(const CRegion& damage) { - data.deco->renderPass(g_pHyprOpenGL->m_renderData.pMonitor.lock(), data.a); +std::vector> CTrailPassElement::draw() { + data.deco->renderPass(g_pHyprRenderer->m_renderData.pMonitor.lock(), data.a); + return {}; } bool CTrailPassElement::needsLiveBlur() { @@ -16,4 +18,4 @@ bool CTrailPassElement::needsLiveBlur() { bool CTrailPassElement::needsPrecomputeBlur() { return false; -} \ No newline at end of file +} diff --git a/hyprtrails/TrailPassElement.hpp b/hyprtrails/TrailPassElement.hpp index b9e998d51..ff33ff139 100644 --- a/hyprtrails/TrailPassElement.hpp +++ b/hyprtrails/TrailPassElement.hpp @@ -13,14 +13,18 @@ class CTrailPassElement : public IPassElement { CTrailPassElement(const STrailData& data_); virtual ~CTrailPassElement() = default; - virtual void draw(const CRegion& damage); - virtual bool needsLiveBlur(); - virtual bool needsPrecomputeBlur(); + virtual std::vector> draw() override; + virtual bool needsLiveBlur() override; + virtual bool needsPrecomputeBlur() override; - virtual const char* passName() { + virtual const char* passName() override { return "CTrailPassElement"; } + virtual ePassElementType type() override { + return EK_CUSTOM; + } + private: STrailData data; -}; \ No newline at end of file +}; diff --git a/hyprtrails/globals.hpp b/hyprtrails/globals.hpp index 5abe3915d..0829b8574 100644 --- a/hyprtrails/globals.hpp +++ b/hyprtrails/globals.hpp @@ -1,12 +1,12 @@ #pragma once #include +#include inline HANDLE PHANDLE = nullptr; struct SGlobalState { - SShader trailShader; - wl_event_source* tick = nullptr; + CShader trailShader; }; inline UP g_pGlobalState; diff --git a/hyprtrails/main.cpp b/hyprtrails/main.cpp index 76201a8c6..39f34704c 100644 --- a/hyprtrails/main.cpp +++ b/hyprtrails/main.cpp @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include "globals.hpp" #include "shaders.hpp" @@ -19,82 +19,15 @@ APICALL EXPORT std::string PLUGIN_API_VERSION() { return HYPRLAND_API_VERSION; } -void onNewWindow(void* self, std::any data) { - // data is guaranteed - const auto PWINDOW = std::any_cast(data); - +void onNewWindow(PHLWINDOW PWINDOW) { HyprlandAPI::addWindowDecoration(PHANDLE, PWINDOW, makeUnique(PWINDOW)); } -GLuint CompileShader(const GLuint& type, std::string src) { - auto shader = glCreateShader(type); - - auto shaderSource = src.c_str(); - - glShaderSource(shader, 1, (const GLchar**)&shaderSource, nullptr); - glCompileShader(shader); - - GLint ok; - glGetShaderiv(shader, GL_COMPILE_STATUS, &ok); - - if (ok == GL_FALSE) - throw std::runtime_error("compileShader() failed!"); - - return shader; -} - -GLuint CreateProgram(const std::string& vert, const std::string& frag) { - auto vertCompiled = CompileShader(GL_VERTEX_SHADER, vert); - - if (!vertCompiled) - throw std::runtime_error("Compiling vshader failed."); - - auto fragCompiled = CompileShader(GL_FRAGMENT_SHADER, frag); - - if (!fragCompiled) - throw std::runtime_error("Compiling fshader failed."); - - auto prog = glCreateProgram(); - glAttachShader(prog, vertCompiled); - glAttachShader(prog, fragCompiled); - glLinkProgram(prog); - - glDetachShader(prog, vertCompiled); - glDetachShader(prog, fragCompiled); - glDeleteShader(vertCompiled); - glDeleteShader(fragCompiled); - - GLint ok; - glGetProgramiv(prog, GL_LINK_STATUS, &ok); - - if (ok == GL_FALSE) - throw std::runtime_error("createProgram() failed! GL_LINK_STATUS not OK!"); - - return prog; -} - -int onTick(void* data) { - EMIT_HOOK_EVENT("trailTick", nullptr); - - const int TIMEOUT = g_pHyprRenderer->m_mostHzMonitor ? 1000.0 / g_pHyprRenderer->m_mostHzMonitor->m_refreshRate : 16; - wl_event_source_timer_update(g_pGlobalState->tick, TIMEOUT); - - return 0; -} - void initGlobal() { - g_pHyprRenderer->makeEGLCurrent(); - - GLuint prog = CreateProgram(QUADTRAIL, FRAGTRAIL); - g_pGlobalState->trailShader.program = prog; - g_pGlobalState->trailShader.uniformLocations[SHADER_PROJ] = glGetUniformLocation(prog, "proj"); - g_pGlobalState->trailShader.uniformLocations[SHADER_TEX] = glGetUniformLocation(prog, "tex"); - g_pGlobalState->trailShader.uniformLocations[SHADER_COLOR] = glGetUniformLocation(prog, "color"); - g_pGlobalState->trailShader.uniformLocations[SHADER_POS_ATTRIB] = glGetAttribLocation(prog, "pos"); - g_pGlobalState->trailShader.uniformLocations[SHADER_GRADIENT] = glGetUniformLocation(prog, "snapshots"); - - g_pGlobalState->tick = wl_event_loop_add_timer(g_pCompositor->m_wlEventLoop, &onTick, nullptr); - wl_event_source_timer_update(g_pGlobalState->tick, 1); + Render::GL::g_pHyprOpenGL->makeEGLCurrent(); + + if (!g_pGlobalState->trailShader.createProgram(QUADTRAIL, FRAGTRAIL, true, false)) + throw std::runtime_error("[ht] Failed to create trail shader"); } APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) { @@ -115,7 +48,7 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) { HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprtrails:history_step", Hyprlang::INT{2}); HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprtrails:color", Hyprlang::INT{*configStringToInt("rgba(ffaa00ff)")}); - static auto P = HyprlandAPI::registerCallbackDynamic(PHANDLE, "openWindow", [&](void* self, SCallbackInfo& info, std::any data) { onNewWindow(self, data); }); + static auto P = Event::bus()->m_events.window.open.listen([&](PHLWINDOW w) { onNewWindow(w); }); g_pGlobalState = makeUnique(); initGlobal(); @@ -136,6 +69,5 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) { } APICALL EXPORT void PLUGIN_EXIT() { - wl_event_source_remove(g_pGlobalState->tick); g_pHyprRenderer->m_renderPass.removeAllOfType("CTrailPassElement"); } diff --git a/hyprtrails/trail.cpp b/hyprtrails/trail.cpp index 8598449fe..c4783a753 100644 --- a/hyprtrails/trail.cpp +++ b/hyprtrails/trail.cpp @@ -5,6 +5,7 @@ #include #include using namespace Hyprutils::Memory; +using namespace Render::GL; #include "globals.hpp" #include "TrailPassElement.hpp" @@ -35,13 +36,10 @@ void CTrail::onTick() { CTrail::CTrail(PHLWINDOW pWindow) : IHyprWindowDecoration(pWindow), m_pWindow(pWindow) { m_lastWindowPos = pWindow->m_realPosition->value(); m_lastWindowSize = pWindow->m_realSize->value(); - - pTickCb = HyprlandAPI::registerCallbackDynamic(PHANDLE, "trailTick", [this](void* self, SCallbackInfo& info, std::any data) { this->onTick(); }); } CTrail::~CTrail() { damageEntire(); - HyprlandAPI::unregisterCallback(PHANDLE, pTickCb); } SDecorationPositioningInfo CTrail::getPositioningInfo() { @@ -85,6 +83,8 @@ 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()) @@ -127,14 +127,13 @@ void CTrail::renderPass(PHLMONITOR pMonitor, const float& a) { glStencilFunc(GL_NOTEQUAL, 1, -1); glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); - CBox monbox = {0, 0, g_pHyprOpenGL->m_renderData.pMonitor->m_transformedSize.x, g_pHyprOpenGL->m_renderData.pMonitor->m_transformedSize.y}; + CBox monbox = {0, 0, g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.x, g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.y}; - Mat3x3 matrix = g_pHyprOpenGL->m_renderData.monitorProjection.projectBox(monbox, Math::wlTransformToHyprutils(Math::invertTransform(WL_OUTPUT_TRANSFORM_NORMAL)), monbox.rot); - Mat3x3 glMatrix = g_pHyprOpenGL->m_renderData.projection.copy().multiply(matrix); + Mat3x3 glMatrix = g_pHyprRenderer->projectBoxToTarget(monbox, Math::wlTransformToHyprutils(Math::invertTransform(WL_OUTPUT_TRANSFORM_NORMAL))); g_pHyprOpenGL->blend(true); - glUseProgram(g_pGlobalState->trailShader.program); + glUseProgram(g_pGlobalState->trailShader.program()); glMatrix.transpose(); g_pGlobalState->trailShader.setUniformMatrix3fv(SHADER_PROJ, 1, GL_FALSE, glMatrix.getMatrix()); @@ -246,21 +245,21 @@ void CTrail::renderPass(PHLMONITOR pMonitor, const float& a) { sc((PWINDOW->m_realPosition->value().y - pMonitor->m_position.y) / pMonitor->m_size.y), sc((PWINDOW->m_realPosition->value().x + PWINDOW->m_realSize->value().x) / pMonitor->m_size.x), sc((PWINDOW->m_realPosition->value().y + PWINDOW->m_realSize->value().y) / pMonitor->m_size.y)}; - glUniform4f(g_pGlobalState->trailShader.uniformLocations[SHADER_GRADIENT], thisboxopengl.x, thisboxopengl.y, thisboxopengl.w, thisboxopengl.h); - glUniform4f(g_pGlobalState->trailShader.uniformLocations[SHADER_COLOR], COLOR.r, COLOR.g, COLOR.b, COLOR.a); + glUniform4f(g_pGlobalState->trailShader.getUniformLocation(SHADER_GRADIENT), thisboxopengl.x, thisboxopengl.y, thisboxopengl.w, thisboxopengl.h); + glUniform4f(g_pGlobalState->trailShader.getUniformLocation(SHADER_COLOR), COLOR.r, COLOR.g, COLOR.b, COLOR.a); CBox transformedBox = monbox; - transformedBox.transform(Math::wlTransformToHyprutils(Math::invertTransform(g_pHyprOpenGL->m_renderData.pMonitor->m_transform)), g_pHyprOpenGL->m_renderData.pMonitor->m_transformedSize.x, - g_pHyprOpenGL->m_renderData.pMonitor->m_transformedSize.y); + transformedBox.transform(Math::wlTransformToHyprutils(Math::invertTransform(g_pHyprRenderer->m_renderData.pMonitor->m_transform)), g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.x, + g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.y); - glVertexAttribPointer(g_pGlobalState->trailShader.uniformLocations[SHADER_POS_ATTRIB], 2, GL_FLOAT, GL_FALSE, 0, (float*)points.data()); + glVertexAttribPointer(g_pGlobalState->trailShader.getUniformLocation(SHADER_POS_ATTRIB), 2, GL_FLOAT, GL_FALSE, 0, (float*)points.data()); - glEnableVertexAttribArray(g_pGlobalState->trailShader.uniformLocations[SHADER_POS_ATTRIB]); + glEnableVertexAttribArray(g_pGlobalState->trailShader.getUniformLocation(SHADER_POS_ATTRIB)); - if (g_pHyprOpenGL->m_renderData.clipBox.width != 0 && g_pHyprOpenGL->m_renderData.clipBox.height != 0) { - CRegion damageClip{g_pHyprOpenGL->m_renderData.clipBox.x, g_pHyprOpenGL->m_renderData.clipBox.y, g_pHyprOpenGL->m_renderData.clipBox.width, - g_pHyprOpenGL->m_renderData.clipBox.height}; - damageClip.intersect(g_pHyprOpenGL->m_renderData.damage); + if (g_pHyprRenderer->m_renderData.clipBox.width != 0 && g_pHyprRenderer->m_renderData.clipBox.height != 0) { + CRegion damageClip{g_pHyprRenderer->m_renderData.clipBox.x, g_pHyprRenderer->m_renderData.clipBox.y, g_pHyprRenderer->m_renderData.clipBox.width, + g_pHyprRenderer->m_renderData.clipBox.height}; + damageClip.intersect(g_pHyprRenderer->m_renderData.damage); if (!damageClip.empty()) { for (auto& RECT : damageClip.getRects()) { @@ -269,13 +268,13 @@ void CTrail::renderPass(PHLMONITOR pMonitor, const float& a) { } } } else { - for (auto& RECT : g_pHyprOpenGL->m_renderData.damage.getRects()) { + for (auto& RECT : g_pHyprRenderer->m_renderData.damage.getRects()) { g_pHyprOpenGL->scissor(&RECT); glDrawArrays(GL_TRIANGLE_STRIP, 0, points.size()); } } - glDisableVertexAttribArray(g_pGlobalState->trailShader.uniformLocations[SHADER_POS_ATTRIB]); + glDisableVertexAttribArray(g_pGlobalState->trailShader.getUniformLocation(SHADER_POS_ATTRIB)); glClearStencil(0); glClear(GL_STENCIL_BUFFER_BIT); diff --git a/hyprtrails/trail.hpp b/hyprtrails/trail.hpp index 956775f3a..0b36b0486 100644 --- a/hyprtrails/trail.hpp +++ b/hyprtrails/trail.hpp @@ -46,7 +46,6 @@ class CTrail : public IHyprWindowDecoration { virtual void damageEntire(); private: - SP pTickCb; void onTick(); void renderPass(PHLMONITOR pMonitor, const float& a); diff --git a/hyprwinwrap/main.cpp b/hyprwinwrap/main.cpp index 5cb2d004a..6bc280a32 100644 --- a/hyprwinwrap/main.cpp +++ b/hyprwinwrap/main.cpp @@ -8,6 +8,7 @@ #include #define private public +#define protected public #include #include #include @@ -17,6 +18,10 @@ #include #include #undef private +#undef protected + +using namespace Render; +using namespace Render::GL; #include "globals.hpp" @@ -121,13 +126,13 @@ void onRenderStage(eRenderStage stage) { for (auto& bg : bgWindows) { const auto bgw = bg.lock(); - if (bgw->m_monitor != g_pHyprOpenGL->m_renderData.pMonitor) + if (bgw->m_monitor != g_pHyprRenderer->m_renderData.pMonitor) continue; // cant use setHidden cuz that sends suspended and shit too that would be laggy bgw->m_hidden = false; - g_pHyprRenderer->renderWindow(bgw, g_pHyprOpenGL->m_renderData.pMonitor.lock(), Time::steadyNow(), false, RENDER_PASS_ALL, false, true); + g_pHyprRenderer->renderWindow(bgw, g_pHyprRenderer->m_renderData.pMonitor.lock(), Time::steadyNow(), false, RENDER_PASS_ALL, false, true); bgw->m_hidden = true; } @@ -146,7 +151,7 @@ void onCommitSubsurface(Desktop::View::CSubsurface* thisptr) { ((origCommitSubsurface)subsurfaceHook->m_original)(thisptr); if (const auto MON = PWINDOW->m_monitor.lock(); MON) - g_pHyprOpenGL->markBlurDirtyForMonitor(MON); + g_pHyprRenderer->damageMonitor(MON); PWINDOW->m_hidden = true; } @@ -164,7 +169,7 @@ void onCommit(void* owner, void* data) { ((origCommit)commitHook->m_original)(owner, data); if (const auto MON = PWINDOW->m_monitor.lock(); MON) - g_pHyprOpenGL->markBlurDirtyForMonitor(MON); + g_pHyprRenderer->damageMonitor(MON); PWINDOW->m_hidden = true; } @@ -173,15 +178,15 @@ void onConfigReloaded() { static auto* const PCLASS = (Hyprlang::STRING const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprwinwrap:class")->getDataStaticPtr(); const std::string classRule(*PCLASS); if (!classRule.empty()) { - g_pConfigManager->parseKeyword("windowrulev2", std::string{"float, class:^("} + classRule + ")$"); - g_pConfigManager->parseKeyword("windowrulev2", std::string{"size 100\% 100\%, class:^("} + classRule + ")$"); + HyprlandAPI::invokeHyprctlCommand("keyword", std::string{"windowrulev2 float, class:^("} + classRule + ")$"); + HyprlandAPI::invokeHyprctlCommand("keyword", std::string{"windowrulev2 size 100% 100%, class:^("} + classRule + ")$"); } static auto* const PTITLE = (Hyprlang::STRING const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprwinwrap:title")->getDataStaticPtr(); const std::string titleRule(*PTITLE); if (!titleRule.empty()) { - g_pConfigManager->parseKeyword("windowrulev2", std::string{"float, title:^("} + titleRule + ")$"); - g_pConfigManager->parseKeyword("windowrulev2", std::string{"size 100\% 100\%, title:^("} + titleRule + ")$"); + HyprlandAPI::invokeHyprctlCommand("keyword", std::string{"windowrulev2 float, title:^("} + titleRule + ")$"); + HyprlandAPI::invokeHyprctlCommand("keyword", std::string{"windowrulev2 size 100% 100%, title:^("} + titleRule + ")$"); } } diff --git a/xtra-dispatchers/main.cpp b/xtra-dispatchers/main.cpp index 35095813b..9a2fb73fe 100644 --- a/xtra-dispatchers/main.cpp +++ b/xtra-dispatchers/main.cpp @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include #include #undef private @@ -38,7 +40,7 @@ static SDispatchResult moveOrExec(std::string in) { const auto PWINDOW = g_pCompositor->getWindowByRegex(vars[0]); if (!PWINDOW) - g_pKeybindManager->spawn(vars[1]); + Config::Supplementary::executor()->spawn(vars[1]); else { if (monitor->m_activeWorkspace != PWINDOW->m_workspace) g_pCompositor->moveWindowToWorkspaceSafe(PWINDOW, monitor->m_activeWorkspace); @@ -122,7 +124,7 @@ static SDispatchResult closeUnfocused(std::string in) { if (w->m_workspace != monitor->m_activeWorkspace || w->m_monitor != monitor || !w->m_isMapped || w == window) continue; - g_pCompositor->closeWindow(w); + Config::Actions::closeWindow(w); } return SDispatchResult{}; From 9aaa4c3023007be6739f9b06ab2f1d964e55e3a3 Mon Sep 17 00:00:00 2001 From: Ivan Malison Date: Tue, 28 Apr 2026 15:00:26 -0700 Subject: [PATCH 02/12] Port hyprexpo to Lua Hyprland branch --- hyprexpo/OverviewPassElement.hpp | 19 +++--- hyprexpo/README.md | 1 - hyprexpo/main.cpp | 31 +++++++--- hyprexpo/overview.cpp | 100 ++++++++++++++++++------------- hyprexpo/overview.hpp | 38 +++++------- 5 files changed, 104 insertions(+), 85 deletions(-) diff --git a/hyprexpo/OverviewPassElement.hpp b/hyprexpo/OverviewPassElement.hpp index 428b257d4..61da4bdf5 100644 --- a/hyprexpo/OverviewPassElement.hpp +++ b/hyprexpo/OverviewPassElement.hpp @@ -8,17 +8,16 @@ class COverviewPassElement : public IPassElement { COverviewPassElement(); virtual ~COverviewPassElement() = default; - virtual std::vector> draw() override; - virtual bool needsLiveBlur() override; - virtual bool needsPrecomputeBlur() override; - virtual std::optional boundingBox() override; - virtual CRegion opaqueRegion() override; - - virtual const char* passName() override { - return "COverviewPassElement"; + virtual void draw(const CRegion& damage); + virtual bool needsLiveBlur(); + virtual bool needsPrecomputeBlur(); + virtual std::optional boundingBox(); + virtual CRegion opaqueRegion(); + virtual ePassElementType type() { + return EK_CUSTOM; } - virtual ePassElementType type() override { - return EK_CUSTOM; + virtual const char* passName() { + return "COverviewPassElement"; } }; diff --git a/hyprexpo/README.md b/hyprexpo/README.md index 97bd1d482..09267dfca 100644 --- a/hyprexpo/README.md +++ b/hyprexpo/README.md @@ -57,4 +57,3 @@ off | hides the overview disable | same as `off` on | displays the overview enable | same as `on` - diff --git a/hyprexpo/main.cpp b/hyprexpo/main.cpp index 3b3dff1f6..32c1c02e7 100644 --- a/hyprexpo/main.cpp +++ b/hyprexpo/main.cpp @@ -6,12 +6,18 @@ #include #include #include +#include +#include +#include +#include #include +#include #include #include #include #include +#include #include using namespace Hyprutils::String; @@ -23,7 +29,7 @@ using namespace Hyprutils::String; inline CFunctionHook* g_pRenderWorkspaceHook = nullptr; inline CFunctionHook* g_pAddDamageHookA = nullptr; inline CFunctionHook* g_pAddDamageHookB = nullptr; -typedef void (*origRenderWorkspace)(void*, PHLMONITOR, PHLWORKSPACE, timespec*, const CBox&); +typedef void (*origRenderWorkspace)(void*, PHLMONITOR, PHLWORKSPACE, const Time::steady_tp&, const CBox&); typedef void (*origAddDamageA)(void*, const CBox&); typedef void (*origAddDamageB)(void*, const pixman_region32_t*); @@ -39,7 +45,7 @@ static bool renderingOverview = false; const std::string KEYWORD_EXPO_GESTURE = "hyprexpo-gesture"; // -static void hkRenderWorkspace(void* thisptr, PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace, timespec* now, const CBox& geometry) { +static void hkRenderWorkspace(void* thisptr, PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace, const Time::steady_tp& now, const CBox& geometry) { if (!g_pOverview || renderingOverview || g_pOverview->blockOverviewRendering || g_pOverview->pMonitor != pMonitor) ((origRenderWorkspace)(g_pRenderWorkspaceHook->m_original))(thisptr, pMonitor, pWorkspace, now, geometry); else @@ -106,6 +112,13 @@ static SDispatchResult onExpoDispatcher(std::string arg) { return {}; } +static int luaExpo(lua_State* L) { + const auto RESULT = onExpoDispatcher(luaL_optstring(L, 1, "toggle")); + if (!RESULT.success) + return luaL_error(L, "%s", RESULT.error.c_str()); + return 0; +} + static void failNotif(const std::string& reason) { HyprlandAPI::addNotification(PHANDLE, "[hyprexpo] Failure in initialization: " + reason, CHyprColor{1.0, 0.2, 0.2, 1.0}, 5000); } @@ -242,16 +255,16 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) { }); HyprlandAPI::addDispatcherV2(PHANDLE, "hyprexpo:expo", ::onExpoDispatcher); + HyprlandAPI::addLuaFunction(PHANDLE, "hyprexpo", "expo", ::luaExpo); HyprlandAPI::addConfigKeyword(PHANDLE, KEYWORD_EXPO_GESTURE, ::expoGestureKeyword, {true}); - HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprexpo:columns", Hyprlang::INT{3}); - HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprexpo:gap_size", Hyprlang::INT{5}); - HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprexpo:bg_col", Hyprlang::INT{0xFF111111}); - HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprexpo:workspace_method", Hyprlang::STRING{"center current"}); - HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprexpo:skip_empty", Hyprlang::INT{0}); - - HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprexpo:gesture_distance", Hyprlang::INT{200}); + HyprlandAPI::addConfigValueV2(PHANDLE, makeShared("plugin:hyprexpo:columns", "columns", 3)); + HyprlandAPI::addConfigValueV2(PHANDLE, makeShared("plugin:hyprexpo:gap_size", "gap size", 5)); + HyprlandAPI::addConfigValueV2(PHANDLE, makeShared("plugin:hyprexpo:bg_col", "background color", 0xFF111111)); + HyprlandAPI::addConfigValueV2(PHANDLE, makeShared("plugin:hyprexpo:workspace_method", "workspace method", "center current")); + HyprlandAPI::addConfigValueV2(PHANDLE, makeShared("plugin:hyprexpo:skip_empty", "skip empty workspaces", 0)); + HyprlandAPI::addConfigValueV2(PHANDLE, makeShared("plugin:hyprexpo:gesture_distance", "gesture distance", 200)); HyprlandAPI::reloadConfig(); diff --git a/hyprexpo/overview.cpp b/hyprexpo/overview.cpp index 26c716228..f21a81e2e 100644 --- a/hyprexpo/overview.cpp +++ b/hyprexpo/overview.cpp @@ -1,63 +1,80 @@ +#include "overview.hpp" +#include #include -#include +#define private public #define protected public #include -#undef protected -#include "overview.hpp" -#include -#include -#include -#include -#include #include #include #include #include +#include +#include #include #include #include #include #include #include +#include +#include +#undef private +#undef protected #include "OverviewPassElement.hpp" using namespace Hyprutils::String; -using namespace Render; -using namespace Render::GL; + +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 PMETHOD("plugin:hyprexpo:workspace_method"); +static const CConfigValue PDISTANCE("plugin:hyprexpo:gesture_distance"); + +static void clearWithColor(const CHyprColor& color) { + glClearColor(color.r, color.g, color.b, color.a); + glClear(GL_COLOR_BUFFER_BIT); +} + +static void ensureFramebuffer(COverview::SWorkspaceImage& image, const CBox& monbox, uint32_t drmFormat) { + if (!image.fb) + image.fb = g_pHyprRenderer->createFB("hyprexpo"); + + if (image.fb->m_size != monbox.size()) { + image.fb->release(); + image.fb->alloc(monbox.w, monbox.h, drmFormat); + } +} static void damageMonitor(WP thisptr) { g_pOverview->damage(); } COverview::~COverview() { + Render::GL::g_pHyprOpenGL->makeEGLCurrent(); images.clear(); // otherwise we get a vram leak Cursor::overrideController->unsetOverride(Cursor::CURSOR_OVERRIDE_UNKNOWN); - g_pHyprRenderer->damageMonitor(pMonitor.lock()); + if (pMonitor) + pMonitor->m_blurFBDirty = true; } COverview::COverview(PHLWORKSPACE startedOn_, bool swipe_) : startedOn(startedOn_), swipe(swipe_) { const auto PMONITOR = Desktop::focusState()->monitor(); pMonitor = PMONITOR; - static auto* const* PCOLUMNS = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:columns")->getDataStaticPtr(); - static auto* const* PGAPS = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:gap_size")->getDataStaticPtr(); - static auto* const* PCOL = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:bg_col")->getDataStaticPtr(); - static auto* const* PSKIP = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:skip_empty")->getDataStaticPtr(); - static auto const* PMETHOD = (Hyprlang::STRING const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:workspace_method")->getDataStaticPtr(); - - SIDE_LENGTH = **PCOLUMNS; - GAP_WIDTH = **PGAPS; - BG_COLOR = **PCOL; + SIDE_LENGTH = *PCOLUMNS; + GAP_WIDTH = *PGAPS; + BG_COLOR = CHyprColor(*PCOL); // process the method bool methodCenter = true; int methodStartID = pMonitor->activeWorkspaceID(); - CConstVarList 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 { methodCenter = method[0] == "center"; - methodStartID = getWorkspaceIDNameFromString(std::string{method[1]}).id; + methodStartID = getWorkspaceIDNameFromString(method[1]).id; if (methodStartID == WORKSPACE_INVALID) methodStartID = pMonitor->activeWorkspaceID(); } @@ -65,7 +82,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; @@ -129,6 +146,8 @@ COverview::COverview(PHLWORKSPACE startedOn_, bool swipe_) : startedOn(startedOn pMonitor->m_activeWorkspace = startedOn; } + Render::GL::g_pHyprOpenGL->makeEGLCurrent(); + Vector2D tileSize = pMonitor->m_size / SIDE_LENGTH; Vector2D tileRenderSize = (pMonitor->m_size - Vector2D{GAP_WIDTH * pMonitor->m_scale, GAP_WIDTH * pMonitor->m_scale} * (SIDE_LENGTH - 1)) / SIDE_LENGTH; CBox monbox{0, 0, tileSize.x * 2, tileSize.y * 2}; @@ -148,13 +167,12 @@ COverview::COverview(PHLWORKSPACE startedOn_, bool swipe_) : startedOn(startedOn for (size_t i = 0; i < (size_t)(SIDE_LENGTH * SIDE_LENGTH); ++i) { COverview::SWorkspaceImage& image = images[i]; - image.fb = makeShared(); - image.fb->alloc(monbox.w, monbox.h, PMONITOR->m_output->state->state().drmFormat); + ensureFramebuffer(image, monbox, PMONITOR->m_output->state->state().drmFormat); CRegion fakeDamage{0, 0, INT16_MAX, INT16_MAX}; - g_pHyprRenderer->beginRender(PMONITOR, fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, image.fb); + g_pHyprRenderer->beginRender(PMONITOR, fakeDamage, Render::RENDER_MODE_FULL_FAKE, nullptr, image.fb); - g_pHyprRenderer->draw(CClearPassElement::SClearData{CHyprColor{0, 0, 0, 1.0}}); + clearWithColor(CHyprColor{0, 0, 0, 1.0}); const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(image.workspaceID); @@ -265,6 +283,8 @@ void COverview::redrawID(int id, bool forcelowres) { blockOverviewRendering = true; + Render::GL::g_pHyprOpenGL->makeEGLCurrent(); + id = std::clamp(id, 0, SIDE_LENGTH * SIDE_LENGTH); Vector2D tileSize = pMonitor->m_size / SIDE_LENGTH; @@ -279,16 +299,12 @@ void COverview::redrawID(int id, bool forcelowres) { auto& image = images[id]; - if (image.fb->m_size != monbox.size()) { - image.fb->release(); - image.fb = makeShared(); - image.fb->alloc(monbox.w, monbox.h, pMonitor->m_output->state->state().drmFormat); - } + ensureFramebuffer(image, monbox, pMonitor->m_output->state->state().drmFormat); CRegion fakeDamage{0, 0, INT16_MAX, INT16_MAX}; - g_pHyprRenderer->beginRender(pMonitor.lock(), fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, image.fb); + g_pHyprRenderer->beginRender(pMonitor.lock(), fakeDamage, Render::RENDER_MODE_FULL_FAKE, nullptr, image.fb); - g_pHyprRenderer->draw(CClearPassElement::SClearData{CHyprColor{0, 0, 0, 1.0}}); + clearWithColor(CHyprColor{0, 0, 0, 1.0}); const auto PWORKSPACE = image.pWorkspace; @@ -450,7 +466,7 @@ void COverview::fullRender() { Vector2D tileSize = (SIZE / SIDE_LENGTH); Vector2D tileRenderSize = (SIZE - Vector2D{GAPSIZE, GAPSIZE} * (SIDE_LENGTH - 1)) / SIDE_LENGTH; - g_pHyprRenderer->draw(CClearPassElement::SClearData{BG_COLOR.stripA()}); + clearWithColor(BG_COLOR.stripA()); for (size_t y = 0; y < (size_t)SIDE_LENGTH; ++y) { for (size_t x = 0; x < (size_t)SIDE_LENGTH; ++x) { @@ -458,7 +474,7 @@ void COverview::fullRender() { texbox.scale(pMonitor->m_scale).translate(pos->value()); texbox.round(); CRegion damage{0, 0, INT16_MAX, INT16_MAX}; - g_pHyprOpenGL->renderTexture(images[x + y * SIDE_LENGTH].fb->getTexture(), texbox, {.damage = &damage, .a = 1.0}); + Render::GL::g_pHyprOpenGL->renderTextureInternal(images[x + y * SIDE_LENGTH].fb->getTexture(), texbox, {.damage = &damage, .a = 1.0}); } } } @@ -482,15 +498,13 @@ void COverview::resetSwipe() { void COverview::onSwipeUpdate(double delta) { m_isSwiping = true; - static auto* const* PDISTANCE = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:gesture_distance")->getDataStaticPtr(); - - 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; + 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); + Vector2D tileSize = (pMonitor->m_size / SIDE_LENGTH); - const auto SIZEMAX = pMonitor->m_size * pMonitor->m_size / tileSize; - const auto POSMAX = (-((pMonitor->m_size / (double)SIDE_LENGTH) * Vector2D{WORKSPACE_FOCUS_ID % SIDE_LENGTH, WORKSPACE_FOCUS_ID / SIDE_LENGTH}) * pMonitor->m_scale) * + const auto SIZEMAX = pMonitor->m_size * pMonitor->m_size / tileSize; + const auto POSMAX = (-((pMonitor->m_size / (double)SIDE_LENGTH) * Vector2D{WORKSPACE_FOCUS_ID % SIDE_LENGTH, WORKSPACE_FOCUS_ID / SIDE_LENGTH}) * pMonitor->m_scale) * (pMonitor->m_size / tileSize); const auto SIZEMIN = pMonitor->m_size; diff --git a/hyprexpo/overview.hpp b/hyprexpo/overview.hpp index 7ecc64fde..799df0b05 100644 --- a/hyprexpo/overview.hpp +++ b/hyprexpo/overview.hpp @@ -4,17 +4,11 @@ #include "globals.hpp" #include -#include +#include #include #include #include -using namespace Hyprutils::Memory; - -namespace Render { - class IFramebuffer; -} - // saves on resources, but is a bit broken rn with blur. // hyprland's fault, but cba to fix. constexpr bool ENABLE_LOWRES = false; @@ -47,25 +41,25 @@ class COverview { PHLMONITORREF pMonitor; bool m_isSwiping = false; - private: - void redrawID(int id, bool forcelowres = false); - void redrawAll(bool forcelowres = false); - void onWorkspaceChange(); - void fullRender(); - - int SIDE_LENGTH = 3; - int GAP_WIDTH = 5; - CHyprColor BG_COLOR = CHyprColor{0.1, 0.1, 0.1, 1.0}; - - bool damageDirty = false; - struct SWorkspaceImage { SP fb; - int64_t workspaceID = -1; - PHLWORKSPACE pWorkspace; - CBox box; + int64_t workspaceID = -1; + PHLWORKSPACE pWorkspace; + CBox box; }; + private: + void redrawID(int id, bool forcelowres = false); + void redrawAll(bool forcelowres = false); + void onWorkspaceChange(); + void fullRender(); + + int SIDE_LENGTH = 3; + int GAP_WIDTH = 5; + CHyprColor BG_COLOR = CHyprColor{0.1, 0.1, 0.1, 1.0}; + + bool damageDirty = false; + Vector2D lastMousePosLocal = Vector2D{}; int openedID = -1; From 54ec6d630ee964b761e994373ceff1d2ab74b230 Mon Sep 17 00:00:00 2001 From: Ivan Malison Date: Tue, 28 Apr 2026 15:00:54 -0700 Subject: [PATCH 03/12] Restore custom hyprexpo overview behavior --- hyprexpo/README.md | 3 + hyprexpo/main.cpp | 52 ++++++++++++++++++ hyprexpo/overview.cpp | 124 ++++++++++++++++++++++++++++++++++++++++-- hyprexpo/overview.hpp | 11 +++- 4 files changed, 181 insertions(+), 9 deletions(-) diff --git a/hyprexpo/README.md b/hyprexpo/README.md index 09267dfca..084f02bac 100644 --- a/hyprexpo/README.md +++ b/hyprexpo/README.md @@ -28,6 +28,8 @@ gap_size | number | gap between desktops | `5` bg_col | color | color in gaps (between desktops) | `rgb(000000)` workspace_method | [center/first] [workspace] | position of the desktops | `center current` skip_empty | boolean | whether the grid displays workspaces sequentially by id using selector "r" (`false`) or skips empty workspaces using selector "m" (`true`) | `false` +show_workspace_numbers | boolean | show numeric labels for workspaces | `false` +workspace_number_color | color | color of workspace number labels | `rgb(ffffff)` gesture_distance | number | how far is the max for the gesture | `300` ### Keywords @@ -53,6 +55,7 @@ Here are a list of options you can use: | --- | --- | toggle | displays if hidden, hide if displayed select | selects the hovered desktop +bring | brings a window from the hovered desktop to the current desktop off | hides the overview disable | same as `off` on | displays the overview diff --git a/hyprexpo/main.cpp b/hyprexpo/main.cpp index 32c1c02e7..9e86ffb7c 100644 --- a/hyprexpo/main.cpp +++ b/hyprexpo/main.cpp @@ -74,6 +74,47 @@ static void hkAddDamageB(void* thisptr, const pixman_region32_t* rg) { g_pOverview->onDamageReported(); } +static PHLWINDOW windowToBringFromWorkspace(const PHLWORKSPACE& workspace) { + if (!workspace) + return nullptr; + + for (auto it = g_pCompositor->m_windows.rbegin(); it != g_pCompositor->m_windows.rend(); ++it) { + const auto& w = *it; + if (!w || w->m_workspace != workspace || !w->m_isMapped || w->isHidden()) + continue; + + return w; + } + + return nullptr; +} + +static SDispatchResult bringWindowFromWorkspace(int64_t sourceWorkspaceID) { + if (sourceWorkspaceID == WORKSPACE_INVALID) + return {.success = false, .error = "selected workspace is empty"}; + + const auto FOCUSSTATE = Desktop::focusState(); + const auto MONITOR = FOCUSSTATE->monitor(); + if (!MONITOR || !MONITOR->m_activeWorkspace) + return {.success = false, .error = "no active monitor/workspace"}; + + if (sourceWorkspaceID == MONITOR->activeWorkspaceID()) + return {}; + + const auto SOURCEWORKSPACE = g_pCompositor->getWorkspaceByID(sourceWorkspaceID); + if (!SOURCEWORKSPACE) + return {.success = false, .error = "selected workspace is not open"}; + + const auto WINDOW = windowToBringFromWorkspace(SOURCEWORKSPACE); + if (!WINDOW) + return {.success = false, .error = "selected workspace has no mapped windows"}; + + g_pCompositor->moveWindowToWorkspaceSafe(WINDOW, MONITOR->m_activeWorkspace); + FOCUSSTATE->fullWindowFocus(WINDOW, Desktop::FOCUS_REASON_KEYBIND); + g_pCompositor->warpCursorTo(WINDOW->middle()); + return {}; +} + static SDispatchResult onExpoDispatcher(std::string arg) { if (g_pOverview && g_pOverview->m_isSwiping) @@ -86,6 +127,15 @@ static SDispatchResult onExpoDispatcher(std::string arg) { } return {}; } + if (arg == "bring") { + if (g_pOverview) { + g_pOverview->selectHoveredWorkspace(); + const auto BRINGRESULT = bringWindowFromWorkspace(g_pOverview->selectedWorkspaceID()); + g_pOverview->close(false); + return BRINGRESULT; + } + return {}; + } if (arg == "toggle") { if (g_pOverview) g_pOverview->close(); @@ -264,6 +314,8 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) { HyprlandAPI::addConfigValueV2(PHANDLE, makeShared("plugin:hyprexpo:bg_col", "background color", 0xFF111111)); HyprlandAPI::addConfigValueV2(PHANDLE, makeShared("plugin:hyprexpo:workspace_method", "workspace method", "center current")); HyprlandAPI::addConfigValueV2(PHANDLE, makeShared("plugin:hyprexpo:skip_empty", "skip empty workspaces", 0)); + HyprlandAPI::addConfigValueV2(PHANDLE, makeShared("plugin:hyprexpo:show_workspace_numbers", "show workspace numbers", 0)); + HyprlandAPI::addConfigValueV2(PHANDLE, makeShared("plugin:hyprexpo:workspace_number_color", "workspace number color", 0xFFFFFFFF)); HyprlandAPI::addConfigValueV2(PHANDLE, makeShared("plugin:hyprexpo:gesture_distance", "gesture distance", 200)); HyprlandAPI::reloadConfig(); diff --git a/hyprexpo/overview.cpp b/hyprexpo/overview.cpp index f21a81e2e..7ba0ec8d3 100644 --- a/hyprexpo/overview.cpp +++ b/hyprexpo/overview.cpp @@ -1,6 +1,8 @@ #include "overview.hpp" #include #include +#include +#include #define private public #define protected public #include @@ -28,6 +30,8 @@ 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"); @@ -46,6 +50,84 @@ 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(); } @@ -62,9 +144,10 @@ 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); + SIDE_LENGTH = *PCOLUMNS; + GAP_WIDTH = *PGAPS; + BG_COLOR = CHyprColor(*PCOL); + showWorkspaceNumbers = *PSHOWNUM; // process the method bool methodCenter = true; @@ -152,6 +235,17 @@ COverview::COverview(PHLWORKSPACE startedOn_, bool swipe_) : startedOn(startedOn Vector2D tileRenderSize = (pMonitor->m_size - Vector2D{GAP_WIDTH * pMonitor->m_scale, GAP_WIDTH * pMonitor->m_scale} * (SIDE_LENGTH - 1)) / SIDE_LENGTH; CBox monbox{0, 0, tileSize.x * 2, tileSize.y * 2}; + if (showWorkspaceNumbers) { + 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); + } + } + if (!ENABLE_LOWRES) monbox = {{0, 0}, pMonitor->m_pixelSize}; @@ -377,7 +471,15 @@ void COverview::onDamageReported() { g_pCompositor->scheduleFrameForMonitor(pMonitor.lock()); } -void COverview::close() { +int64_t COverview::selectedWorkspaceID() const { + const int ID = closeOnID == -1 ? openedID : closeOnID; + if (ID < 0 || ID >= (int)images.size()) + return WORKSPACE_INVALID; + + return images[ID].workspaceID; +} + +void COverview::close(bool switchToSelection) { if (closing) return; @@ -397,7 +499,7 @@ void COverview::close() { redrawAll(); - if (TILE.workspaceID != pMonitor->activeWorkspaceID()) { + if (switchToSelection && TILE.workspaceID != pMonitor->activeWorkspaceID()) { pMonitor->setSpecialWorkspace(0); // If this tile's workspace was WORKSPACE_INVALID, move to the next @@ -474,7 +576,17 @@ void COverview::fullRender() { texbox.scale(pMonitor->m_scale).translate(pos->value()); texbox.round(); CRegion damage{0, 0, INT16_MAX, INT16_MAX}; - Render::GL::g_pHyprOpenGL->renderTextureInternal(images[x + y * SIDE_LENGTH].fb->getTexture(), texbox, {.damage = &damage, .a = 1.0}); + 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; + 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()); + labelBox.round(); + Render::GL::g_pHyprOpenGL->renderTexture(image.labelTex, labelBox, {.a = 1.0}); + } } } } diff --git a/hyprexpo/overview.hpp b/hyprexpo/overview.hpp index 799df0b05..ecad6c59b 100644 --- a/hyprexpo/overview.hpp +++ b/hyprexpo/overview.hpp @@ -5,6 +5,7 @@ #include "globals.hpp" #include #include +#include #include #include #include @@ -32,8 +33,9 @@ class COverview { void onSwipeEnd(); // close without a selection - void close(); + void close(bool switchToSelection = true); void selectHoveredWorkspace(); + int64_t selectedWorkspaceID() const; bool blockOverviewRendering = false; bool blockDamageReporting = false; @@ -46,6 +48,8 @@ class COverview { int64_t workspaceID = -1; PHLWORKSPACE pWorkspace; CBox box; + SP labelTex; + Vector2D labelSizePx; }; private: @@ -79,8 +83,9 @@ class COverview { CHyprSignalListener touchMoveHook; CHyprSignalListener touchDownHook; - bool swipe = false; - bool swipeWasCommenced = false; + bool swipe = false; + bool swipeWasCommenced = false; + bool showWorkspaceNumbers = false; friend class COverviewPassElement; }; From 8fc97eec03803c602a054d014579b39ed096f9a3 Mon Sep 17 00:00:00 2001 From: Ivan Malison Date: Wed, 29 Apr 2026 07:12:40 -0700 Subject: [PATCH 04/12] hyprexpo: defer Lua config value lookups --- hyprexpo/main.cpp | 2 -- hyprexpo/overview.cpp | 78 ++++++++++++++++++++++++++++++------------- 2 files changed, 55 insertions(+), 25 deletions(-) diff --git a/hyprexpo/main.cpp b/hyprexpo/main.cpp index 9e86ffb7c..f203cce15 100644 --- a/hyprexpo/main.cpp +++ b/hyprexpo/main.cpp @@ -318,8 +318,6 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) { HyprlandAPI::addConfigValueV2(PHANDLE, makeShared("plugin:hyprexpo:workspace_number_color", "workspace number color", 0xFFFFFFFF)); HyprlandAPI::addConfigValueV2(PHANDLE, makeShared("plugin:hyprexpo:gesture_distance", "gesture distance", 200)); - HyprlandAPI::reloadConfig(); - return {"hyprexpo", "A plugin for an overview", "Vaxry", "1.0"}; } diff --git a/hyprexpo/overview.cpp b/hyprexpo/overview.cpp index 7ba0ec8d3..b6f8319c8 100644 --- a/hyprexpo/overview.cpp +++ b/hyprexpo/overview.cpp @@ -26,14 +26,45 @@ using namespace Hyprutils::String; -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 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 void clearWithColor(const CHyprColor& color) { glClearColor(color.r, color.g, color.b, color.a); @@ -144,15 +175,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 { @@ -165,7 +196,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; @@ -236,7 +267,7 @@ 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) @@ -361,9 +392,9 @@ void COverview::selectHoveredWorkspace() { return; // get tile x,y - int x = lastMousePosLocal.x / pMonitor->m_size.x * SIDE_LENGTH; - int y = lastMousePosLocal.y / pMonitor->m_size.y * SIDE_LENGTH; - closeOnID = x + y * SIDE_LENGTH; + int x = std::clamp((int)(lastMousePosLocal.x / pMonitor->m_size.x * SIDE_LENGTH), 0, SIDE_LENGTH - 1); + int y = std::clamp((int)(lastMousePosLocal.y / pMonitor->m_size.y * SIDE_LENGTH), 0, SIDE_LENGTH - 1); + closeOnID = std::clamp(x + y * SIDE_LENGTH, 0, SIDE_LENGTH * SIDE_LENGTH - 1); } void COverview::redrawID(int id, bool forcelowres) { @@ -379,7 +410,7 @@ void COverview::redrawID(int id, bool forcelowres) { Render::GL::g_pHyprOpenGL->makeEGLCurrent(); - id = std::clamp(id, 0, SIDE_LENGTH * SIDE_LENGTH); + id = std::clamp(id, 0, SIDE_LENGTH * SIDE_LENGTH - 1); Vector2D tileSize = pMonitor->m_size / SIDE_LENGTH; Vector2D tileRenderSize = (pMonitor->m_size - Vector2D{GAP_WIDTH, GAP_WIDTH} * (SIDE_LENGTH - 1)) / SIDE_LENGTH; @@ -483,9 +514,10 @@ void COverview::close(bool switchToSelection) { if (closing) return; - const int ID = closeOnID == -1 ? openedID : closeOnID; + const int ID = closeOnID == -1 ? openedID : closeOnID; + const int SAFEID = std::clamp(ID, 0, SIDE_LENGTH * SIDE_LENGTH - 1); - const auto& TILE = images[std::clamp(ID, 0, SIDE_LENGTH * SIDE_LENGTH)]; + const auto& TILE = images[SAFEID]; Vector2D tileSize = (pMonitor->m_size / SIDE_LENGTH); @@ -493,7 +525,7 @@ void COverview::close(bool switchToSelection) { pos->warp(); *size = pMonitor->m_size * pMonitor->m_size / tileSize; - *pos = (-((pMonitor->m_size / (double)SIDE_LENGTH) * Vector2D{ID % SIDE_LENGTH, ID / SIDE_LENGTH}) * pMonitor->m_scale) * (pMonitor->m_size / tileSize); + *pos = (-((pMonitor->m_size / (double)SIDE_LENGTH) * Vector2D{SAFEID % SIDE_LENGTH, SAFEID / SIDE_LENGTH}) * pMonitor->m_scale) * (pMonitor->m_size / tileSize); closing = true; @@ -610,7 +642,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); From 05aa3a62e18ed70edfd6138d026577b006a63df3 Mon Sep 17 00:00:00 2001 From: Ivan Malison Date: Wed, 29 Apr 2026 07:41:45 -0700 Subject: [PATCH 05/12] hyprexpo: use alpha format for overview framebuffers --- hyprexpo/overview.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/hyprexpo/overview.cpp b/hyprexpo/overview.cpp index b6f8319c8..a909ca57e 100644 --- a/hyprexpo/overview.cpp +++ b/hyprexpo/overview.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -66,6 +67,11 @@ static const CConfigValue& PDISTANCE() { return VALUE; } +static uint32_t framebufferFormatWithAlpha(uint32_t drmFormat) { + const auto alphaFormat = NFormatUtils::alphaFormat(drmFormat); + return alphaFormat == 0 ? DRM_FORMAT_ABGR8888 : alphaFormat; +} + static void clearWithColor(const CHyprColor& color) { glClearColor(color.r, color.g, color.b, color.a); glClear(GL_COLOR_BUFFER_BIT); @@ -292,7 +298,7 @@ COverview::COverview(PHLWORKSPACE startedOn_, bool swipe_) : startedOn(startedOn for (size_t i = 0; i < (size_t)(SIDE_LENGTH * SIDE_LENGTH); ++i) { COverview::SWorkspaceImage& image = images[i]; - ensureFramebuffer(image, monbox, PMONITOR->m_output->state->state().drmFormat); + ensureFramebuffer(image, monbox, framebufferFormatWithAlpha(PMONITOR->m_output->state->state().drmFormat)); CRegion fakeDamage{0, 0, INT16_MAX, INT16_MAX}; g_pHyprRenderer->beginRender(PMONITOR, fakeDamage, Render::RENDER_MODE_FULL_FAKE, nullptr, image.fb); @@ -424,7 +430,7 @@ void COverview::redrawID(int id, bool forcelowres) { auto& image = images[id]; - ensureFramebuffer(image, monbox, pMonitor->m_output->state->state().drmFormat); + ensureFramebuffer(image, monbox, framebufferFormatWithAlpha(pMonitor->m_output->state->state().drmFormat)); CRegion fakeDamage{0, 0, INT16_MAX, INT16_MAX}; g_pHyprRenderer->beginRender(pMonitor.lock(), fakeDamage, Render::RENDER_MODE_FULL_FAKE, nullptr, image.fb); From d3e87c8893508fcda29044ce4c1d9d345a34ee7d Mon Sep 17 00:00:00 2001 From: Ivan Malison Date: Wed, 29 Apr 2026 08:49:26 -0700 Subject: [PATCH 06/12] hyprexpo: avoid eager config binding during init --- hyprexpo/main.cpp | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/hyprexpo/main.cpp b/hyprexpo/main.cpp index f203cce15..e1402c6c1 100644 --- a/hyprexpo/main.cpp +++ b/hyprexpo/main.cpp @@ -173,6 +173,16 @@ static void failNotif(const std::string& reason) { HyprlandAPI::addNotification(PHANDLE, "[hyprexpo] Failure in initialization: " + reason, CHyprColor{1.0, 0.2, 0.2, 1.0}, 5000); } +static bool addConfigValue(SP value) { + const auto RET = Config::mgr()->registerPluginValue(PHANDLE, value); + if (!RET) { + Log::logger->log(Log::ERR, "[hyprexpo] failed to register plugin value \"{}\": {}", value->name(), RET.error()); + return false; + } + + return true; +} + static Hyprlang::CParseResult expoGestureKeyword(const char* LHS, const char* RHS) { Hyprlang::CParseResult result; @@ -309,14 +319,14 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) { HyprlandAPI::addConfigKeyword(PHANDLE, KEYWORD_EXPO_GESTURE, ::expoGestureKeyword, {true}); - HyprlandAPI::addConfigValueV2(PHANDLE, makeShared("plugin:hyprexpo:columns", "columns", 3)); - HyprlandAPI::addConfigValueV2(PHANDLE, makeShared("plugin:hyprexpo:gap_size", "gap size", 5)); - HyprlandAPI::addConfigValueV2(PHANDLE, makeShared("plugin:hyprexpo:bg_col", "background color", 0xFF111111)); - HyprlandAPI::addConfigValueV2(PHANDLE, makeShared("plugin:hyprexpo:workspace_method", "workspace method", "center current")); - HyprlandAPI::addConfigValueV2(PHANDLE, makeShared("plugin:hyprexpo:skip_empty", "skip empty workspaces", 0)); - HyprlandAPI::addConfigValueV2(PHANDLE, makeShared("plugin:hyprexpo:show_workspace_numbers", "show workspace numbers", 0)); - HyprlandAPI::addConfigValueV2(PHANDLE, makeShared("plugin:hyprexpo:workspace_number_color", "workspace number color", 0xFFFFFFFF)); - HyprlandAPI::addConfigValueV2(PHANDLE, makeShared("plugin:hyprexpo:gesture_distance", "gesture distance", 200)); + addConfigValue(makeShared("plugin:hyprexpo:columns", "columns", 3)); + addConfigValue(makeShared("plugin:hyprexpo:gap_size", "gap size", 5)); + addConfigValue(makeShared("plugin:hyprexpo:bg_col", "background color", 0xFF111111)); + addConfigValue(makeShared("plugin:hyprexpo:workspace_method", "workspace method", "center current")); + addConfigValue(makeShared("plugin:hyprexpo:skip_empty", "skip empty workspaces", 0)); + addConfigValue(makeShared("plugin:hyprexpo:show_workspace_numbers", "show workspace numbers", 0)); + addConfigValue(makeShared("plugin:hyprexpo:workspace_number_color", "workspace number color", 0xFFFFFFFF)); + addConfigValue(makeShared("plugin:hyprexpo:gesture_distance", "gesture distance", 200)); return {"hyprexpo", "A plugin for an overview", "Vaxry", "1.0"}; } From ca1992973b5fb8ab95e88e8ffba16792c41568ae Mon Sep 17 00:00:00 2001 From: Ivan Malison Date: Wed, 29 Apr 2026 12:52:20 -0700 Subject: [PATCH 07/12] hyprexpo: keep custom pass draw override current --- hyprexpo/OverviewPassElement.hpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/hyprexpo/OverviewPassElement.hpp b/hyprexpo/OverviewPassElement.hpp index 61da4bdf5..3a364ec53 100644 --- a/hyprexpo/OverviewPassElement.hpp +++ b/hyprexpo/OverviewPassElement.hpp @@ -8,16 +8,16 @@ class COverviewPassElement : public IPassElement { COverviewPassElement(); virtual ~COverviewPassElement() = default; - virtual void draw(const CRegion& damage); - virtual bool needsLiveBlur(); - virtual bool needsPrecomputeBlur(); - virtual std::optional boundingBox(); - virtual CRegion opaqueRegion(); - virtual ePassElementType type() { + virtual std::vector> draw() override; + virtual bool needsLiveBlur() override; + virtual bool needsPrecomputeBlur() override; + virtual std::optional boundingBox() override; + virtual CRegion opaqueRegion() override; + virtual ePassElementType type() override { return EK_CUSTOM; } - virtual const char* passName() { + virtual const char* passName() override { return "COverviewPassElement"; } }; From d22114866f6ac93d5613d791715ce369f8ff839e Mon Sep 17 00:00:00 2001 From: Ivan Malison Date: Wed, 29 Apr 2026 17:08:03 -0700 Subject: [PATCH 08/12] Address PR review comments --- flake.nix | 1 - hyprexpo/overview.cpp | 149 +++++------------------------------ hyprexpo/overview.hpp | 1 - hyprscrolling/CMakeLists.txt | 4 +- hyprscrolling/Makefile | 2 +- hyprscrolling/main.cpp | 34 ++++++-- hyprtrails/globals.hpp | 8 +- hyprtrails/main.cpp | 17 ++++ hyprtrails/trail.cpp | 22 +++++- hyprtrails/trail.hpp | 3 + hyprwinwrap/main.cpp | 37 +++++++-- 11 files changed, 129 insertions(+), 149 deletions(-) diff --git a/flake.nix b/flake.nix index 1ff5a3e37..0f598d7d2 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 a909ca57e..10a7ef5d0 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 ecad6c59b..2aa9baebc 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 4e0349710..2a05e9f02 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 193b4542d..147191286 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 80e31bd23..4f09173d2 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 0829b8574..87bc18f90 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 39f34704c..cec5f09a2 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 c4783a753..ebf1553fb 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 0b36b0486..e2e259d2c 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 6bc280a32..afb475df8 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(); } From e3ad819f666eefd39a9954c6765e108b07723678 Mon Sep 17 00:00:00 2001 From: Ivan Malison Date: Wed, 29 Apr 2026 22:28:12 -0700 Subject: [PATCH 09/12] hyprexpo: cap generated workspace numbers --- hyprexpo/README.md | 1 + hyprexpo/main.cpp | 1 + hyprexpo/overview.cpp | 18 +++++++++++++++--- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/hyprexpo/README.md b/hyprexpo/README.md index 084f02bac..7ba3cda4b 100644 --- a/hyprexpo/README.md +++ b/hyprexpo/README.md @@ -28,6 +28,7 @@ gap_size | number | gap between desktops | `5` bg_col | color | color in gaps (between desktops) | `rgb(000000)` workspace_method | [center/first] [workspace] | position of the desktops | `center current` skip_empty | boolean | whether the grid displays workspaces sequentially by id using selector "r" (`false`) or skips empty workspaces using selector "m" (`true`) | `false` +max_workspace | number | highest normal workspace to show when `skip_empty` is `false`; `0` disables the limit | `0` show_workspace_numbers | boolean | show numeric labels for workspaces | `false` workspace_number_color | color | color of workspace number labels | `rgb(ffffff)` gesture_distance | number | how far is the max for the gesture | `300` diff --git a/hyprexpo/main.cpp b/hyprexpo/main.cpp index e1402c6c1..d601f9ca5 100644 --- a/hyprexpo/main.cpp +++ b/hyprexpo/main.cpp @@ -324,6 +324,7 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) { addConfigValue(makeShared("plugin:hyprexpo:bg_col", "background color", 0xFF111111)); addConfigValue(makeShared("plugin:hyprexpo:workspace_method", "workspace method", "center current")); addConfigValue(makeShared("plugin:hyprexpo:skip_empty", "skip empty workspaces", 0)); + addConfigValue(makeShared("plugin:hyprexpo:max_workspace", "maximum normal workspace shown, or 0 for unlimited", 0)); addConfigValue(makeShared("plugin:hyprexpo:show_workspace_numbers", "show workspace numbers", 0)); addConfigValue(makeShared("plugin:hyprexpo:workspace_number_color", "workspace number color", 0xFFFFFFFF)); addConfigValue(makeShared("plugin:hyprexpo:gesture_distance", "gesture distance", 200)); diff --git a/hyprexpo/overview.cpp b/hyprexpo/overview.cpp index 10a7ef5d0..b702dd4c3 100644 --- a/hyprexpo/overview.cpp +++ b/hyprexpo/overview.cpp @@ -30,6 +30,7 @@ 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 PMAXWS("plugin:hyprexpo:max_workspace"); 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"); @@ -92,9 +93,20 @@ 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"; - - if (methodCenter) { + const bool skipEmpty = *PSKIP; + const int64_t maxWorkspace = *PMAXWS; + std::string selector = skipEmpty ? "m" : "r"; + + if (!skipEmpty && maxWorkspace > 0) { + const int64_t tileCount = SIDE_LENGTH * SIDE_LENGTH; + const int64_t maxStart = std::max(1, maxWorkspace - tileCount + 1); + const int64_t startID = methodCenter ? std::clamp(methodStartID - tileCount / 2, 1, maxStart) : std::clamp(methodStartID, 1, maxStart); + + for (size_t i = 0; i < images.size(); ++i) { + const int64_t workspaceID = startID + i; + images[i].workspaceID = workspaceID <= maxWorkspace ? workspaceID : WORKSPACE_INVALID; + } + } else if (methodCenter) { int currentID = methodStartID; int firstID = currentID; From c50ed7b75e9c99b5a01de3ac17aa91d767af6dda Mon Sep 17 00:00:00 2001 From: Ivan Malison Date: Wed, 29 Apr 2026 22:53:28 -0700 Subject: [PATCH 10/12] hyprexpo: defer config value binding --- hyprexpo/overview.cpp | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/hyprexpo/overview.cpp b/hyprexpo/overview.cpp index b702dd4c3..1dbe98fc3 100644 --- a/hyprexpo/overview.cpp +++ b/hyprexpo/overview.cpp @@ -26,22 +26,12 @@ using namespace Hyprutils::String; -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 PMAXWS("plugin:hyprexpo:max_workspace"); -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); return alphaFormat == 0 ? DRM_FORMAT_ABGR8888 : alphaFormat; } -static void clearWithColor(const CHyprColor& color) { +static void clearWithColor(const CHyprColor& color) { glClearColor(color.r, color.g, color.b, color.a); glClear(GL_COLOR_BUFFER_BIT); } @@ -72,6 +62,15 @@ COverview::COverview(PHLWORKSPACE startedOn_, bool swipe_) : startedOn(startedOn const auto PMONITOR = Desktop::focusState()->monitor(); pMonitor = PMONITOR; + 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 PMAXWS("plugin:hyprexpo:max_workspace"); + 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"); + SIDE_LENGTH = *PCOLUMNS; GAP_WIDTH = *PGAPS; BG_COLOR = CHyprColor(*PCOL); @@ -549,13 +548,15 @@ 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 auto WORKSPACE_FOCUS_ID = closing && closeOnID != -1 ? closeOnID : openedID; + static const CConfigValue PDISTANCE("plugin:hyprexpo:gesture_distance"); - Vector2D tileSize = (pMonitor->m_size / SIDE_LENGTH); + 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); - const auto SIZEMAX = pMonitor->m_size * pMonitor->m_size / tileSize; - const auto POSMAX = (-((pMonitor->m_size / (double)SIDE_LENGTH) * Vector2D{WORKSPACE_FOCUS_ID % SIDE_LENGTH, WORKSPACE_FOCUS_ID / SIDE_LENGTH}) * pMonitor->m_scale) * + const auto SIZEMAX = pMonitor->m_size * pMonitor->m_size / tileSize; + const auto POSMAX = (-((pMonitor->m_size / (double)SIDE_LENGTH) * Vector2D{WORKSPACE_FOCUS_ID % SIDE_LENGTH, WORKSPACE_FOCUS_ID / SIDE_LENGTH}) * pMonitor->m_scale) * (pMonitor->m_size / tileSize); const auto SIZEMIN = pMonitor->m_size; From e84a326bd3507c0c4e78a45df0d5db282ba18767 Mon Sep 17 00:00:00 2001 From: Ivan Malison Date: Fri, 1 May 2026 10:10:03 -0700 Subject: [PATCH 11/12] docs: add Lua config examples --- csgo-vulkan-fix/README.md | 20 +++++++++++++++++--- hyprbars/README.md | 30 ++++++++++++++++++++++++++++++ hyprexpo/README.md | 22 ++++++++++++++++++++++ 3 files changed, 69 insertions(+), 3 deletions(-) diff --git a/csgo-vulkan-fix/README.md b/csgo-vulkan-fix/README.md index 072757605..66158381b 100644 --- a/csgo-vulkan-fix/README.md +++ b/csgo-vulkan-fix/README.md @@ -18,9 +18,9 @@ CS2 launch options: ``` example plugin config: -``` +```ini plugin { - csgo-vulkan-fix { + csgo_vulkan_fix { # Whether to fix the mouse position. A select few apps might be wonky with this. fix_mouse = true @@ -31,4 +31,18 @@ plugin { } ``` -fullscreen the game manually and enjoy. \ No newline at end of file +Lua config: +```lua +hl.config({ + plugin = { + csgo_vulkan_fix = { + fix_mouse = true, + }, + }, +}) + +hl.plugin.csgo_vulkan_fix.vkfix_app({ app = "cs2", w = 1650, h = 1050 }) +hl.plugin.csgo_vulkan_fix.vkfix_app({ app = "myapp", w = 1920, h = 1080 }) +``` + +fullscreen the game manually and enjoy. diff --git a/hyprbars/README.md b/hyprbars/README.md index bded3bbb6..7224107ea 100644 --- a/hyprbars/README.md +++ b/hyprbars/README.md @@ -55,6 +55,36 @@ hyprbars-button = bgcolor, size, icon, on-click, fgcolor Please note it _has_ to be inside `plugin { hyprbars { } }`. +For Lua config, use `hl.config` for plugin options and +`hl.plugin.hyprbars.add_button` for buttons: + +```lua +hl.config({ + plugin = { + hyprbars = { + bar_height = 20, + on_double_click = "hyprctl dispatch fullscreen 1", + }, + }, +}) + +hl.plugin.hyprbars.add_button({ + bg_color = "rgb(ff4040)", + fg_color = "rgb(ffffff)", + size = 10, + icon = "X", + action = "hyprctl dispatch killactive", +}) + +hl.plugin.hyprbars.add_button({ + bg_color = "rgb(eeee11)", + fg_color = "rgb(000000)", + size = 10, + icon = "_", + action = "hyprctl dispatch fullscreen 1", +}) +``` + ## Window rules Hyprbars supports the following _dynamic_ [window rules](https://wiki.hypr.land/Configuring/Window-Rules/): diff --git a/hyprexpo/README.md b/hyprexpo/README.md index 7ba3cda4b..4bf7db49c 100644 --- a/hyprexpo/README.md +++ b/hyprexpo/README.md @@ -51,6 +51,28 @@ Example: bind = SUPER, g, hyprexpo:expo, toggle ``` +Lua config: +```lua +hl.config({ + plugin = { + hyprexpo = { + columns = 3, + gap_size = 5, + bg_col = "rgb(111111)", + workspace_method = "center current", + skip_empty = false, + show_workspace_numbers = false, + workspace_number_color = "rgb(ffffff)", + gesture_distance = 300, + }, + }, +}) + +hl.bind("SUPER + g", function() + hl.plugin.hyprexpo.expo("toggle") +end) +``` + Here are a list of options you can use: | option | description | | --- | --- | From b3c656e93451f12daeb907320f69c190efd50e04 Mon Sep 17 00:00:00 2001 From: Ivan Malison Date: Fri, 1 May 2026 11:19:47 -0700 Subject: [PATCH 12/12] hyprexpo: remove workspace number feature from PR --- hyprexpo/README.md | 5 ----- hyprexpo/main.cpp | 3 --- hyprexpo/overview.cpp | 46 ++++++------------------------------------- hyprexpo/overview.hpp | 7 ++----- 4 files changed, 8 insertions(+), 53 deletions(-) diff --git a/hyprexpo/README.md b/hyprexpo/README.md index 4bf7db49c..cbc9d077b 100644 --- a/hyprexpo/README.md +++ b/hyprexpo/README.md @@ -28,9 +28,6 @@ gap_size | number | gap between desktops | `5` bg_col | color | color in gaps (between desktops) | `rgb(000000)` workspace_method | [center/first] [workspace] | position of the desktops | `center current` skip_empty | boolean | whether the grid displays workspaces sequentially by id using selector "r" (`false`) or skips empty workspaces using selector "m" (`true`) | `false` -max_workspace | number | highest normal workspace to show when `skip_empty` is `false`; `0` disables the limit | `0` -show_workspace_numbers | boolean | show numeric labels for workspaces | `false` -workspace_number_color | color | color of workspace number labels | `rgb(ffffff)` gesture_distance | number | how far is the max for the gesture | `300` ### Keywords @@ -61,8 +58,6 @@ hl.config({ bg_col = "rgb(111111)", workspace_method = "center current", skip_empty = false, - show_workspace_numbers = false, - workspace_number_color = "rgb(ffffff)", gesture_distance = 300, }, }, diff --git a/hyprexpo/main.cpp b/hyprexpo/main.cpp index d601f9ca5..65521f48e 100644 --- a/hyprexpo/main.cpp +++ b/hyprexpo/main.cpp @@ -324,9 +324,6 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) { addConfigValue(makeShared("plugin:hyprexpo:bg_col", "background color", 0xFF111111)); addConfigValue(makeShared("plugin:hyprexpo:workspace_method", "workspace method", "center current")); addConfigValue(makeShared("plugin:hyprexpo:skip_empty", "skip empty workspaces", 0)); - addConfigValue(makeShared("plugin:hyprexpo:max_workspace", "maximum normal workspace shown, or 0 for unlimited", 0)); - addConfigValue(makeShared("plugin:hyprexpo:show_workspace_numbers", "show workspace numbers", 0)); - addConfigValue(makeShared("plugin:hyprexpo:workspace_number_color", "workspace number color", 0xFFFFFFFF)); addConfigValue(makeShared("plugin:hyprexpo:gesture_distance", "gesture distance", 200)); return {"hyprexpo", "A plugin for an overview", "Vaxry", "1.0"}; diff --git a/hyprexpo/overview.cpp b/hyprexpo/overview.cpp index 1dbe98fc3..097280c55 100644 --- a/hyprexpo/overview.cpp +++ b/hyprexpo/overview.cpp @@ -66,15 +66,11 @@ COverview::COverview(PHLWORKSPACE startedOn_, bool swipe_) : startedOn(startedOn 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 PMAXWS("plugin:hyprexpo:max_workspace"); - 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"); - SIDE_LENGTH = *PCOLUMNS; - GAP_WIDTH = *PGAPS; - BG_COLOR = CHyprColor(*PCOL); - showWorkspaceNumbers = *PSHOWNUM; + SIDE_LENGTH = *PCOLUMNS; + GAP_WIDTH = *PGAPS; + BG_COLOR = CHyprColor(*PCOL); // process the method bool methodCenter = true; @@ -92,20 +88,9 @@ COverview::COverview(PHLWORKSPACE startedOn_, bool swipe_) : startedOn(startedOn images.resize(SIDE_LENGTH * SIDE_LENGTH); // r includes empty workspaces; m skips over them - const bool skipEmpty = *PSKIP; - const int64_t maxWorkspace = *PMAXWS; - std::string selector = skipEmpty ? "m" : "r"; - - if (!skipEmpty && maxWorkspace > 0) { - const int64_t tileCount = SIDE_LENGTH * SIDE_LENGTH; - const int64_t maxStart = std::max(1, maxWorkspace - tileCount + 1); - const int64_t startID = methodCenter ? std::clamp(methodStartID - tileCount / 2, 1, maxStart) : std::clamp(methodStartID, 1, maxStart); - - for (size_t i = 0; i < images.size(); ++i) { - const int64_t workspaceID = startID + i; - images[i].workspaceID = workspaceID <= maxWorkspace ? workspaceID : WORKSPACE_INVALID; - } - } else if (methodCenter) { + std::string selector = *PSKIP ? "m" : "r"; + + if (methodCenter) { int currentID = methodStartID; int firstID = currentID; @@ -173,16 +158,6 @@ COverview::COverview(PHLWORKSPACE startedOn_, bool swipe_) : startedOn(startedOn Vector2D tileRenderSize = (pMonitor->m_size - Vector2D{GAP_WIDTH * pMonitor->m_scale, GAP_WIDTH * pMonitor->m_scale} * (SIDE_LENGTH - 1)) / SIDE_LENGTH; CBox monbox{0, 0, tileSize.x * 2, tileSize.y * 2}; - if (showWorkspaceNumbers) { - 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->renderText(std::to_string(image.workspaceID), numberColor, fontSizePx, false, "Sans Bold", tileRenderSize.x * pMonitor->m_scale); - } - } - if (!ENABLE_LOWRES) monbox = {{0, 0}, pMonitor->m_pixelSize}; @@ -516,15 +491,6 @@ void COverview::fullRender() { CRegion damage{0, 0, INT16_MAX, INT16_MAX}; 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()) { - 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()); - labelBox.round(); - Render::GL::g_pHyprOpenGL->renderTexture(image.labelTex, labelBox, {.a = 1.0}); - } } } } diff --git a/hyprexpo/overview.hpp b/hyprexpo/overview.hpp index 2aa9baebc..1cbf2df8b 100644 --- a/hyprexpo/overview.hpp +++ b/hyprexpo/overview.hpp @@ -5,7 +5,6 @@ #include "globals.hpp" #include #include -#include #include #include #include @@ -48,7 +47,6 @@ class COverview { int64_t workspaceID = -1; PHLWORKSPACE pWorkspace; CBox box; - SP labelTex; }; private: @@ -82,9 +80,8 @@ class COverview { CHyprSignalListener touchMoveHook; CHyprSignalListener touchDownHook; - bool swipe = false; - bool swipeWasCommenced = false; - bool showWorkspaceNumbers = false; + bool swipe = false; + bool swipeWasCommenced = false; friend class COverviewPassElement; };