Port hyprexpo to Lua Hyprland branch

This commit is contained in:
Ivan Malison 2026-04-28 15:00:26 -07:00
parent d09549230a
commit 9aaa4c3023
5 changed files with 105 additions and 86 deletions

View file

@ -8,17 +8,16 @@ class COverviewPassElement : public IPassElement {
COverviewPassElement();
virtual ~COverviewPassElement() = default;
virtual std::vector<UP<IPassElement>> draw() override;
virtual bool needsLiveBlur() override;
virtual bool needsPrecomputeBlur() override;
virtual std::optional<CBox> boundingBox() override;
virtual CRegion opaqueRegion() override;
virtual const char* passName() override {
return "COverviewPassElement";
}
virtual ePassElementType type() override {
virtual void draw(const CRegion& damage);
virtual bool needsLiveBlur();
virtual bool needsPrecomputeBlur();
virtual std::optional<CBox> boundingBox();
virtual CRegion opaqueRegion();
virtual ePassElementType type() {
return EK_CUSTOM;
}
virtual const char* passName() {
return "COverviewPassElement";
}
};

View file

@ -57,4 +57,3 @@ off | hides the overview
disable | same as `off`
on | displays the overview
enable | same as `on`

View file

@ -6,12 +6,18 @@
#include <hyprland/src/desktop/state/FocusState.hpp>
#include <hyprland/src/desktop/view/Window.hpp>
#include <hyprland/src/config/ConfigManager.hpp>
#include <hyprland/src/config/shared/actions/ConfigActions.hpp>
#include <hyprland/src/config/values/types/ColorValue.hpp>
#include <hyprland/src/config/values/types/IntValue.hpp>
#include <hyprland/src/config/values/types/StringValue.hpp>
#include <hyprland/src/desktop/DesktopTypes.hpp>
#include <hyprland/src/helpers/time/Time.hpp>
#include <hyprland/src/render/Renderer.hpp>
#include <hyprland/src/managers/input/trackpad/GestureTypes.hpp>
#include <hyprland/src/managers/input/trackpad/TrackpadGestures.hpp>
#include <hyprland/src/event/EventBus.hpp>
#include <lua.hpp>
#include <hyprutils/string/ConstVarList.hpp>
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<Config::Values::CIntValue>("plugin:hyprexpo:columns", "columns", 3));
HyprlandAPI::addConfigValueV2(PHANDLE, makeShared<Config::Values::CIntValue>("plugin:hyprexpo:gap_size", "gap size", 5));
HyprlandAPI::addConfigValueV2(PHANDLE, makeShared<Config::Values::CColorValue>("plugin:hyprexpo:bg_col", "background color", 0xFF111111));
HyprlandAPI::addConfigValueV2(PHANDLE, makeShared<Config::Values::CStringValue>("plugin:hyprexpo:workspace_method", "workspace method", "center current"));
HyprlandAPI::addConfigValueV2(PHANDLE, makeShared<Config::Values::CIntValue>("plugin:hyprexpo:skip_empty", "skip empty workspaces", 0));
HyprlandAPI::addConfigValueV2(PHANDLE, makeShared<Config::Values::CIntValue>("plugin:hyprexpo:gesture_distance", "gesture distance", 200));
HyprlandAPI::reloadConfig();

View file

@ -1,63 +1,80 @@
#include "overview.hpp"
#include <algorithm>
#include <any>
#include <sstream>
#define private public
#define protected public
#include <hyprland/src/render/Renderer.hpp>
#undef protected
#include "overview.hpp"
#include <hyprland/src/render/gl/GLFramebuffer.hpp>
#include <hyprland/src/render/pass/ClearPassElement.hpp>
#include <hyprland/src/config/shared/animation/AnimationTree.hpp>
#include <hyprland/src/config/shared/actions/ConfigActions.hpp>
#include <hyprutils/string/ConstVarList.hpp>
#include <hyprland/src/Compositor.hpp>
#include <hyprland/src/desktop/state/FocusState.hpp>
#include <hyprland/src/config/ConfigValue.hpp>
#include <hyprland/src/config/ConfigManager.hpp>
#include <hyprland/src/config/shared/actions/ConfigActions.hpp>
#include <hyprland/src/config/shared/animation/AnimationTree.hpp>
#include <hyprland/src/managers/animation/AnimationManager.hpp>
#include <hyprland/src/managers/animation/DesktopAnimationManager.hpp>
#include <hyprland/src/managers/cursor/CursorShapeOverrideController.hpp>
#include <hyprland/src/managers/input/InputManager.hpp>
#include <hyprland/src/managers/eventLoop/EventLoopManager.hpp>
#include <hyprland/src/helpers/time/Time.hpp>
#include <hyprland/src/helpers/varlist/VarList.hpp>
#include <hyprland/src/render/OpenGL.hpp>
#undef private
#undef protected
#include "OverviewPassElement.hpp"
using namespace Hyprutils::String;
using namespace Render;
using namespace Render::GL;
static const CConfigValue<Config::INTEGER> PCOLUMNS("plugin:hyprexpo:columns");
static const CConfigValue<Config::INTEGER> PGAPS("plugin:hyprexpo:gap_size");
static const CConfigValue<Config::INTEGER> PCOL("plugin:hyprexpo:bg_col");
static const CConfigValue<Config::INTEGER> PSKIP("plugin:hyprexpo:skip_empty");
static const CConfigValue<Config::STRING> PMETHOD("plugin:hyprexpo:workspace_method");
static const CConfigValue<Config::INTEGER> 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<Hyprutils::Animation::CBaseAnimatedVariable> 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<CGLFramebuffer>();
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<CGLFramebuffer>();
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;

View file

@ -4,17 +4,11 @@
#include "globals.hpp"
#include <hyprland/src/desktop/DesktopTypes.hpp>
#include <hyprutils/memory/SharedPtr.hpp>
#include <hyprland/src/render/Framebuffer.hpp>
#include <hyprland/src/helpers/AnimatedVariable.hpp>
#include <hyprland/src/event/EventBus.hpp>
#include <vector>
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<Render::IFramebuffer> 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;