mirror of
https://github.com/hyprwm/hyprland-plugins.git
synced 2024-12-22 06:59:48 +01:00
hyprexpo: add
This commit is contained in:
parent
4334510363
commit
58aa63baf0
7 changed files with 728 additions and 0 deletions
|
@ -11,6 +11,7 @@ This repo houses official plugins for Hyprland.
|
|||
- borders-plus-plus -> adds one or two additional borders to windows
|
||||
- csgo-vulkan-fix -> fixes custom resolutions on CS:GO with `-vulkan`
|
||||
- hyprbars -> adds title bars to windows
|
||||
- hyprexpo -> adds an expo-like workspace overview
|
||||
- hyprtrails -> adds smooth trails behind moving windows
|
||||
- hyprwinwrap -> clone of xwinwrap, allows you to put any app as a wallpaper
|
||||
|
||||
|
|
4
hyprexpo/Makefile
Normal file
4
hyprexpo/Makefile
Normal file
|
@ -0,0 +1,4 @@
|
|||
all:
|
||||
$(CXX) -shared -fPIC --no-gnu-unique main.cpp overview.cpp -o hyprexpo.so -g `pkg-config --cflags pixman-1 libdrm hyprland` -std=c++2b -Wno-narrowing
|
||||
clean:
|
||||
rm ./hyprexpo.so
|
19
hyprexpo/README.md
Normal file
19
hyprexpo/README.md
Normal file
|
@ -0,0 +1,19 @@
|
|||
# hyprexpo
|
||||
|
||||
Overview plugin like gnome kde or wf.
|
||||
|
||||
## Config
|
||||
|
||||
```ini
|
||||
|
||||
plugin {
|
||||
hyprexpo {
|
||||
columns = 3
|
||||
gap_size = 5
|
||||
bg_col = rgb(111111)
|
||||
workspace_method = center current # [center/first] [workspace] e.g. first 1 or center m+1
|
||||
enable_gesture = true # laptop touchpad, 4 fingers
|
||||
}
|
||||
}
|
||||
|
||||
```
|
5
hyprexpo/globals.hpp
Normal file
5
hyprexpo/globals.hpp
Normal file
|
@ -0,0 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include <hyprland/src/plugins/PluginAPI.hpp>
|
||||
|
||||
inline HANDLE PHANDLE = nullptr;
|
211
hyprexpo/main.cpp
Normal file
211
hyprexpo/main.cpp
Normal file
|
@ -0,0 +1,211 @@
|
|||
#define WLR_USE_UNSTABLE
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <hyprland/src/Compositor.hpp>
|
||||
#include <hyprland/src/desktop/Window.hpp>
|
||||
#include <hyprland/src/config/ConfigManager.hpp>
|
||||
#include <hyprland/src/desktop/DesktopTypes.hpp>
|
||||
|
||||
#include "globals.hpp"
|
||||
#include "overview.hpp"
|
||||
|
||||
// Methods
|
||||
inline CFunctionHook* g_pRenderWorkspaceHook = nullptr;
|
||||
inline CFunctionHook* g_pAddDamageHookA = nullptr;
|
||||
inline CFunctionHook* g_pAddDamageHookB = nullptr;
|
||||
inline CFunctionHook* g_pSwipeBeginHook = nullptr;
|
||||
inline CFunctionHook* g_pSwipeEndHook = nullptr;
|
||||
inline CFunctionHook* g_pSwipeUpdateHook = nullptr;
|
||||
typedef void (*origRenderWorkspace)(void*, CMonitor*, PHLWORKSPACE, timespec*, const CBox&);
|
||||
typedef void (*origAddDamageA)(void*, const CBox*);
|
||||
typedef void (*origAddDamageB)(void*, const pixman_region32_t*);
|
||||
typedef void (*origSwipeBegin)(void*, wlr_pointer_swipe_begin_event*);
|
||||
typedef void (*origSwipeEnd)(void*, wlr_pointer_swipe_end_event*);
|
||||
typedef void (*origSwipeUpdate)(void*, wlr_pointer_swipe_update_event*);
|
||||
|
||||
// Do NOT change this function.
|
||||
APICALL EXPORT std::string PLUGIN_API_VERSION() {
|
||||
return HYPRLAND_API_VERSION;
|
||||
}
|
||||
|
||||
static bool renderingOverview = false;
|
||||
|
||||
//
|
||||
static void hkRenderWorkspace(void* thisptr, CMonitor* pMonitor, PHLWORKSPACE pWorkspace, timespec* now, const CBox& geometry) {
|
||||
if (!g_pOverview || renderingOverview || g_pOverview->blockOverviewRendering)
|
||||
((origRenderWorkspace)(g_pRenderWorkspaceHook->m_pOriginal))(thisptr, pMonitor, pWorkspace, now, geometry);
|
||||
else
|
||||
g_pOverview->render();
|
||||
}
|
||||
|
||||
static void hkAddDamageA(void* thisptr, const CBox* box) {
|
||||
const auto PMONITOR = (CMonitor*)thisptr;
|
||||
|
||||
if (!g_pOverview || g_pOverview->pMonitor != PMONITOR || g_pOverview->blockDamageReporting) {
|
||||
((origAddDamageA)g_pAddDamageHookA->m_pOriginal)(thisptr, box);
|
||||
return;
|
||||
}
|
||||
|
||||
g_pOverview->onDamageReported();
|
||||
}
|
||||
|
||||
static void hkAddDamageB(void* thisptr, const pixman_region32_t* rg) {
|
||||
const auto PMONITOR = (CMonitor*)thisptr;
|
||||
|
||||
if (!g_pOverview || g_pOverview->pMonitor != PMONITOR || g_pOverview->blockDamageReporting) {
|
||||
((origAddDamageB)g_pAddDamageHookB->m_pOriginal)(thisptr, rg);
|
||||
return;
|
||||
}
|
||||
|
||||
g_pOverview->onDamageReported();
|
||||
}
|
||||
|
||||
static float gestured = 0;
|
||||
|
||||
static void hkSwipeBegin(void* thisptr, wlr_pointer_swipe_begin_event* e) {
|
||||
static auto* const* PENABLE = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:enable_gesture")->getDataStaticPtr();
|
||||
|
||||
if (g_pOverview)
|
||||
return;
|
||||
|
||||
if (!**PENABLE || e->fingers != 4) {
|
||||
((origSwipeBegin)g_pSwipeBeginHook->m_pOriginal)(thisptr, e);
|
||||
return;
|
||||
}
|
||||
|
||||
renderingOverview = true;
|
||||
g_pOverview = std::make_unique<COverview>(g_pCompositor->m_pLastMonitor->activeWorkspace, true);
|
||||
renderingOverview = false;
|
||||
|
||||
gestured = 0;
|
||||
}
|
||||
|
||||
static void hkSwipeUpdate(void* thisptr, wlr_pointer_swipe_update_event* e) {
|
||||
if (!g_pOverview) {
|
||||
((origSwipeUpdate)g_pSwipeUpdateHook->m_pOriginal)(thisptr, e);
|
||||
return;
|
||||
}
|
||||
|
||||
gestured += e->dy;
|
||||
|
||||
g_pOverview->onSwipeUpdate(gestured);
|
||||
}
|
||||
|
||||
static void hkSwipeEnd(void* thisptr, wlr_pointer_swipe_end_event* e) {
|
||||
if (!g_pOverview) {
|
||||
((origSwipeEnd)g_pSwipeEndHook->m_pOriginal)(thisptr, e);
|
||||
return;
|
||||
}
|
||||
|
||||
g_pOverview->onSwipeEnd();
|
||||
}
|
||||
|
||||
static void onExpoDispatcher(std::string arg) {
|
||||
|
||||
if (arg == "toggle") {
|
||||
if (g_pOverview)
|
||||
g_pOverview->close();
|
||||
else {
|
||||
renderingOverview = true;
|
||||
g_pOverview = std::make_unique<COverview>(g_pCompositor->m_pLastMonitor->activeWorkspace);
|
||||
renderingOverview = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (arg == "off" || arg == "close" || arg == "disable") {
|
||||
if (g_pOverview)
|
||||
g_pOverview->close();
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_pOverview)
|
||||
return;
|
||||
|
||||
renderingOverview = true;
|
||||
g_pOverview = std::make_unique<COverview>(g_pCompositor->m_pLastMonitor->activeWorkspace);
|
||||
renderingOverview = false;
|
||||
}
|
||||
|
||||
APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
|
||||
PHANDLE = handle;
|
||||
|
||||
const std::string HASH = __hyprland_api_get_hash();
|
||||
|
||||
if (HASH != GIT_COMMIT_HASH) {
|
||||
HyprlandAPI::addNotification(PHANDLE, "[hyprexpo] Failure in initialization: Version mismatch (headers ver is not equal to running hyprland ver)",
|
||||
CColor{1.0, 0.2, 0.2, 1.0}, 5000);
|
||||
throw std::runtime_error("[he] Version mismatch");
|
||||
}
|
||||
|
||||
auto FNS = HyprlandAPI::findFunctionsByName(PHANDLE, "renderWorkspace");
|
||||
if (FNS.empty())
|
||||
throw std::runtime_error("[he] No fns for hook renderWorkspace");
|
||||
|
||||
g_pRenderWorkspaceHook = HyprlandAPI::createFunctionHook(PHANDLE, FNS[0].address, (void*)hkRenderWorkspace);
|
||||
|
||||
FNS = HyprlandAPI::findFunctionsByName(PHANDLE, "addDamageEPK15pixman_region32");
|
||||
if (FNS.empty())
|
||||
throw std::runtime_error("[he] No fns for hook addDamageEPK15pixman_region32");
|
||||
|
||||
g_pAddDamageHookB = HyprlandAPI::createFunctionHook(PHANDLE, FNS[0].address, (void*)hkAddDamageB);
|
||||
|
||||
FNS = HyprlandAPI::findFunctionsByName(PHANDLE, "addDamageEPK4CBox");
|
||||
if (FNS.empty())
|
||||
throw std::runtime_error("[he] No fns for hook addDamageEPK4CBox");
|
||||
|
||||
g_pAddDamageHookA = HyprlandAPI::createFunctionHook(PHANDLE, FNS[0].address, (void*)hkAddDamageA);
|
||||
|
||||
FNS = HyprlandAPI::findFunctionsByName(PHANDLE, "onSwipeBegin");
|
||||
if (FNS.empty())
|
||||
throw std::runtime_error("[he] No fns for hook onSwipeBegin");
|
||||
|
||||
g_pSwipeBeginHook = HyprlandAPI::createFunctionHook(PHANDLE, FNS[0].address, (void*)hkSwipeBegin);
|
||||
|
||||
FNS = HyprlandAPI::findFunctionsByName(PHANDLE, "onSwipeEnd");
|
||||
if (FNS.empty())
|
||||
throw std::runtime_error("[he] No fns for hook onSwipeEnd");
|
||||
|
||||
g_pSwipeEndHook = HyprlandAPI::createFunctionHook(PHANDLE, FNS[0].address, (void*)hkSwipeEnd);
|
||||
|
||||
FNS = HyprlandAPI::findFunctionsByName(PHANDLE, "onSwipeUpdate");
|
||||
if (FNS.empty())
|
||||
throw std::runtime_error("[he] No fns for hook onSwipeUpdate");
|
||||
|
||||
g_pSwipeUpdateHook = HyprlandAPI::createFunctionHook(PHANDLE, FNS[0].address, (void*)hkSwipeUpdate);
|
||||
|
||||
bool success = g_pRenderWorkspaceHook->hook();
|
||||
success = success && g_pAddDamageHookA->hook();
|
||||
success = success && g_pAddDamageHookB->hook();
|
||||
// mega buggy, I'll have to fix it one day.
|
||||
// success = success && g_pSwipeBeginHook->hook();
|
||||
// success = success && g_pSwipeEndHook->hook();
|
||||
// success = success && g_pSwipeUpdateHook->hook();
|
||||
|
||||
if (!success)
|
||||
throw std::runtime_error("[he] Failed initializing hooks");
|
||||
|
||||
HyprlandAPI::registerCallbackDynamic(PHANDLE, "preRender", [](void* self, SCallbackInfo& info, std::any param) {
|
||||
if (!g_pOverview)
|
||||
return;
|
||||
g_pOverview->onPreRender();
|
||||
});
|
||||
|
||||
HyprlandAPI::addDispatcher(PHANDLE, "hyprexpo:expo", onExpoDispatcher);
|
||||
|
||||
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:enable_gesture", Hyprlang::INT{1});
|
||||
|
||||
HyprlandAPI::reloadConfig();
|
||||
|
||||
return {"hyprexpo", "A plugin for an overview", "Vaxry", "1.0"};
|
||||
}
|
||||
|
||||
APICALL EXPORT void PLUGIN_EXIT() {
|
||||
;
|
||||
}
|
411
hyprexpo/overview.cpp
Normal file
411
hyprexpo/overview.cpp
Normal file
|
@ -0,0 +1,411 @@
|
|||
#include "overview.hpp"
|
||||
#include <any>
|
||||
#define private public
|
||||
#include <hyprland/src/render/Renderer.hpp>
|
||||
#include <hyprland/src/Compositor.hpp>
|
||||
#include <hyprland/src/config/ConfigValue.hpp>
|
||||
#undef private
|
||||
|
||||
static void damageMonitor(void*) {
|
||||
g_pOverview->damage();
|
||||
}
|
||||
|
||||
static void removeOverview(void*) {
|
||||
g_pOverview.reset();
|
||||
}
|
||||
|
||||
COverview::~COverview() {
|
||||
g_pHookSystem->unhook(mouseButtonHook);
|
||||
g_pHookSystem->unhook(mouseMoveHook);
|
||||
g_pInputManager->unsetCursorImage();
|
||||
g_pHyprOpenGL->markBlurDirtyForMonitor(pMonitor);
|
||||
}
|
||||
|
||||
COverview::COverview(PHLWORKSPACE startedOn_, bool swipe_) : startedOn(startedOn_), swipe(swipe_) {
|
||||
const auto PMONITOR = g_pCompositor->m_pLastMonitor;
|
||||
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* PMETHOD = (Hyprlang::STRING const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:workspace_method")->getDataStaticPtr();
|
||||
|
||||
SIDE_LENGTH = **PCOLUMNS;
|
||||
GAP_WIDTH = **PGAPS;
|
||||
BG_COLOR = **PCOL;
|
||||
|
||||
// process the method
|
||||
bool methodCenter = true;
|
||||
int methodStartID = pMonitor->activeWorkspaceID();
|
||||
CVarList method{*PMETHOD, 0, 's', true};
|
||||
if (method.size() < 2)
|
||||
Debug::log(ERR, "[he] invalid workspace_method");
|
||||
else {
|
||||
methodCenter = method[0] == "center";
|
||||
std::string s;
|
||||
methodStartID = getWorkspaceIDFromString(method[1], s);
|
||||
if (methodStartID == WORKSPACE_INVALID)
|
||||
methodStartID = pMonitor->activeWorkspaceID();
|
||||
}
|
||||
|
||||
images.resize(SIDE_LENGTH * SIDE_LENGTH);
|
||||
|
||||
if (methodCenter) {
|
||||
int currentID = methodStartID;
|
||||
int firstID = currentID;
|
||||
|
||||
int backtracked = 0;
|
||||
|
||||
for (size_t i = 1; i < images.size() / 2; ++i) {
|
||||
std::string s;
|
||||
currentID = getWorkspaceIDFromString("r-" + std::to_string(i), s);
|
||||
if (currentID >= firstID)
|
||||
break;
|
||||
|
||||
backtracked++;
|
||||
firstID = currentID;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < SIDE_LENGTH * SIDE_LENGTH; ++i) {
|
||||
auto& image = images[i];
|
||||
std::string s;
|
||||
currentID =
|
||||
getWorkspaceIDFromString("r" + ((int64_t)i - backtracked < 0 ? std::to_string((int64_t)i - backtracked) : "+" + std::to_string((int64_t)i - backtracked)), s);
|
||||
image.workspaceID = currentID;
|
||||
}
|
||||
} else {
|
||||
int currentID = methodStartID;
|
||||
images[0].workspaceID = currentID;
|
||||
for (size_t i = 1; i < SIDE_LENGTH * SIDE_LENGTH; ++i) {
|
||||
auto& image = images[i];
|
||||
std::string s;
|
||||
currentID = getWorkspaceIDFromString("r+" + std::to_string(i), s);
|
||||
image.workspaceID = currentID;
|
||||
}
|
||||
}
|
||||
|
||||
g_pHyprRenderer->makeEGLCurrent();
|
||||
|
||||
timespec now;
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
|
||||
Vector2D tileSize = pMonitor->vecSize / SIDE_LENGTH;
|
||||
Vector2D tileRenderSize = (pMonitor->vecSize - Vector2D{GAP_WIDTH * pMonitor->scale, GAP_WIDTH * pMonitor->scale} * (SIDE_LENGTH - 1)) / SIDE_LENGTH;
|
||||
CBox monbox{0, 0, tileSize.x * 2, tileSize.y * 2};
|
||||
|
||||
int currentid = 0;
|
||||
|
||||
PHLWORKSPACE openSpecial = PMONITOR->activeSpecialWorkspace;
|
||||
if (openSpecial)
|
||||
PMONITOR->activeSpecialWorkspace.reset();
|
||||
|
||||
g_pHyprRenderer->m_bBlockSurfaceFeedback = true;
|
||||
|
||||
startedOn->m_bVisible = false;
|
||||
|
||||
for (size_t i = 0; i < SIDE_LENGTH * SIDE_LENGTH; ++i) {
|
||||
COverview::SWorkspaceImage& image = images[i];
|
||||
image.fb.alloc(monbox.w, monbox.h, PMONITOR->drmFormat);
|
||||
|
||||
CRegion fakeDamage{0, 0, INT16_MAX, INT16_MAX};
|
||||
g_pHyprRenderer->beginRender(PMONITOR, fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, &image.fb);
|
||||
|
||||
g_pHyprOpenGL->clear(CColor{0, 0, 0, 1.0});
|
||||
|
||||
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(image.workspaceID);
|
||||
|
||||
if (PWORKSPACE == startedOn)
|
||||
currentid = i;
|
||||
|
||||
if (PWORKSPACE) {
|
||||
image.pWorkspace = PWORKSPACE;
|
||||
PMONITOR->activeWorkspace = PWORKSPACE;
|
||||
PWORKSPACE->startAnim(true, true, true);
|
||||
PWORKSPACE->m_bVisible = true;
|
||||
|
||||
if (PWORKSPACE == startedOn)
|
||||
PMONITOR->activeSpecialWorkspace = openSpecial;
|
||||
|
||||
g_pHyprRenderer->renderWorkspace(PMONITOR, PWORKSPACE, &now, monbox);
|
||||
|
||||
PWORKSPACE->m_bVisible = false;
|
||||
PWORKSPACE->startAnim(false, false, true);
|
||||
|
||||
if (PWORKSPACE == startedOn)
|
||||
PMONITOR->activeSpecialWorkspace.reset();
|
||||
} else
|
||||
g_pHyprRenderer->renderWorkspace(PMONITOR, PWORKSPACE, &now, monbox);
|
||||
|
||||
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_pHyprRenderer->endRender();
|
||||
}
|
||||
|
||||
g_pHyprRenderer->m_bBlockSurfaceFeedback = false;
|
||||
|
||||
PMONITOR->activeSpecialWorkspace = openSpecial;
|
||||
PMONITOR->activeWorkspace = startedOn;
|
||||
startedOn->m_bVisible = true;
|
||||
startedOn->startAnim(true, true, true);
|
||||
|
||||
// zoom on the current workspace.
|
||||
const auto& TILE = images[std::clamp(currentid, 0, SIDE_LENGTH * SIDE_LENGTH)];
|
||||
|
||||
size.create(pMonitor->vecSize * pMonitor->vecSize / tileSize, g_pConfigManager->getAnimationPropertyConfig("windowsMove"), AVARDAMAGE_NONE);
|
||||
pos.create((-((pMonitor->vecSize / (double)SIDE_LENGTH) * Vector2D{currentid % SIDE_LENGTH, currentid / SIDE_LENGTH}) * pMonitor->scale) * (pMonitor->vecSize / tileSize),
|
||||
g_pConfigManager->getAnimationPropertyConfig("windowsMove"), AVARDAMAGE_NONE);
|
||||
|
||||
size.setUpdateCallback(damageMonitor);
|
||||
pos.setUpdateCallback(damageMonitor);
|
||||
|
||||
if (!swipe) {
|
||||
size = pMonitor->vecSize;
|
||||
pos = {0, 0};
|
||||
|
||||
size.setCallbackOnEnd([this](void*) { redrawAll(true); });
|
||||
}
|
||||
|
||||
openedID = currentid;
|
||||
|
||||
g_pInputManager->setCursorImageUntilUnset("left_ptr");
|
||||
|
||||
lastMousePosLocal = g_pInputManager->getMouseCoordsInternal() - pMonitor->vecPosition;
|
||||
|
||||
mouseMoveHook = g_pHookSystem->hookDynamic("mouseMove", [this](void* self, SCallbackInfo& info, std::any param) {
|
||||
if (closing)
|
||||
return;
|
||||
|
||||
info.cancelled = true;
|
||||
lastMousePosLocal = g_pInputManager->getMouseCoordsInternal() - pMonitor->vecPosition;
|
||||
});
|
||||
|
||||
mouseButtonHook = g_pHookSystem->hookDynamic("mouseButton", [this](void* self, SCallbackInfo& info, std::any param) {
|
||||
if (closing)
|
||||
return;
|
||||
|
||||
info.cancelled = true;
|
||||
|
||||
// get tile x,y
|
||||
int x = lastMousePosLocal.x / pMonitor->vecSize.x * SIDE_LENGTH;
|
||||
int y = lastMousePosLocal.y / pMonitor->vecSize.y * SIDE_LENGTH;
|
||||
|
||||
closeOnID = x + y * SIDE_LENGTH;
|
||||
|
||||
close();
|
||||
});
|
||||
}
|
||||
|
||||
void COverview::redrawID(int id, bool forcelowres) {
|
||||
blockOverviewRendering = true;
|
||||
|
||||
g_pHyprRenderer->makeEGLCurrent();
|
||||
|
||||
id = std::clamp(id, 0, SIDE_LENGTH * SIDE_LENGTH);
|
||||
|
||||
Vector2D tileSize = pMonitor->vecSize / SIDE_LENGTH;
|
||||
Vector2D tileRenderSize = (pMonitor->vecSize - Vector2D{GAP_WIDTH, GAP_WIDTH} * (SIDE_LENGTH - 1)) / SIDE_LENGTH;
|
||||
CBox monbox{0, 0, tileSize.x * 2, tileSize.y * 2};
|
||||
|
||||
if (!forcelowres && (size.value() != pMonitor->vecSize || closing))
|
||||
monbox = {{0, 0}, pMonitor->vecPixelSize};
|
||||
|
||||
if (!ENABLE_LOWRES)
|
||||
monbox = {{0, 0}, pMonitor->vecPixelSize};
|
||||
|
||||
timespec now;
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
|
||||
auto& image = images[id];
|
||||
|
||||
if (image.fb.m_vSize != monbox.size()) {
|
||||
image.fb.release();
|
||||
image.fb.m_pStencilTex = nullptr;
|
||||
image.fb.alloc(monbox.w, monbox.h, pMonitor->drmFormat);
|
||||
}
|
||||
|
||||
CRegion fakeDamage{0, 0, INT16_MAX, INT16_MAX};
|
||||
g_pHyprRenderer->beginRender(pMonitor, fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, &image.fb);
|
||||
|
||||
g_pHyprOpenGL->clear(CColor{0, 0, 0, 1.0});
|
||||
|
||||
const auto PWORKSPACE = image.pWorkspace;
|
||||
|
||||
PHLWORKSPACE openSpecial = pMonitor->activeSpecialWorkspace;
|
||||
if (openSpecial)
|
||||
pMonitor->activeSpecialWorkspace.reset();
|
||||
|
||||
startedOn->m_bVisible = false;
|
||||
|
||||
if (PWORKSPACE) {
|
||||
pMonitor->activeWorkspace = PWORKSPACE;
|
||||
PWORKSPACE->startAnim(true, true, true);
|
||||
PWORKSPACE->m_bVisible = true;
|
||||
|
||||
if (PWORKSPACE == startedOn)
|
||||
pMonitor->activeSpecialWorkspace = openSpecial;
|
||||
|
||||
g_pHyprRenderer->renderWorkspace(pMonitor, PWORKSPACE, &now, monbox);
|
||||
|
||||
PWORKSPACE->m_bVisible = false;
|
||||
PWORKSPACE->startAnim(false, false, true);
|
||||
|
||||
if (PWORKSPACE == startedOn)
|
||||
pMonitor->activeSpecialWorkspace.reset();
|
||||
} else
|
||||
g_pHyprRenderer->renderWorkspace(pMonitor, PWORKSPACE, &now, monbox);
|
||||
|
||||
g_pHyprRenderer->endRender();
|
||||
|
||||
pMonitor->activeSpecialWorkspace = openSpecial;
|
||||
pMonitor->activeWorkspace = startedOn;
|
||||
startedOn->m_bVisible = true;
|
||||
startedOn->startAnim(true, true, true);
|
||||
|
||||
blockOverviewRendering = false;
|
||||
}
|
||||
|
||||
void COverview::redrawAll(bool forcelowres) {
|
||||
for (size_t i = 0; i < SIDE_LENGTH * SIDE_LENGTH; ++i) {
|
||||
redrawID(i, forcelowres);
|
||||
}
|
||||
}
|
||||
|
||||
void COverview::damage() {
|
||||
blockDamageReporting = true;
|
||||
g_pHyprRenderer->damageMonitor(pMonitor);
|
||||
blockDamageReporting = false;
|
||||
}
|
||||
|
||||
void COverview::onDamageReported() {
|
||||
damageDirty = true;
|
||||
|
||||
Vector2D SIZE = size.value();
|
||||
|
||||
Vector2D tileSize = (SIZE / SIDE_LENGTH);
|
||||
Vector2D tileRenderSize = (SIZE - Vector2D{GAP_WIDTH, GAP_WIDTH} * (SIDE_LENGTH - 1)) / SIDE_LENGTH;
|
||||
const auto& TILE = images[std::clamp(openedID, 0, SIDE_LENGTH * SIDE_LENGTH)];
|
||||
CBox texbox = CBox{(openedID % SIDE_LENGTH) * tileRenderSize.x + (openedID % SIDE_LENGTH) * GAP_WIDTH,
|
||||
(openedID / SIDE_LENGTH) * tileRenderSize.y + (openedID / SIDE_LENGTH) * GAP_WIDTH, tileRenderSize.x, tileRenderSize.y}
|
||||
.translate(pMonitor->vecPosition);
|
||||
|
||||
damage();
|
||||
|
||||
blockDamageReporting = true;
|
||||
g_pHyprRenderer->damageBox(&texbox);
|
||||
blockDamageReporting = false;
|
||||
g_pCompositor->scheduleFrameForMonitor(pMonitor);
|
||||
}
|
||||
|
||||
void COverview::close() {
|
||||
if (closing)
|
||||
return;
|
||||
|
||||
const int ID = closeOnID == -1 ? openedID : closeOnID;
|
||||
|
||||
const auto& TILE = images[std::clamp(ID, 0, SIDE_LENGTH * SIDE_LENGTH)];
|
||||
|
||||
Vector2D tileSize = (pMonitor->vecSize / SIDE_LENGTH);
|
||||
|
||||
size = pMonitor->vecSize * pMonitor->vecSize / tileSize;
|
||||
pos = (-((pMonitor->vecSize / (double)SIDE_LENGTH) * Vector2D{ID % SIDE_LENGTH, ID / SIDE_LENGTH}) * pMonitor->scale) * (pMonitor->vecSize / tileSize);
|
||||
|
||||
size.setCallbackOnEnd(removeOverview);
|
||||
|
||||
closing = true;
|
||||
|
||||
redrawAll();
|
||||
|
||||
if (TILE.workspaceID != pMonitor->activeWorkspaceID()) {
|
||||
pMonitor->setSpecialWorkspace(0);
|
||||
|
||||
const auto NEWIDWS = g_pCompositor->getWorkspaceByID(TILE.workspaceID);
|
||||
|
||||
if (!NEWIDWS)
|
||||
g_pKeybindManager->changeworkspace(std::to_string(TILE.workspaceID));
|
||||
else
|
||||
g_pKeybindManager->changeworkspace(NEWIDWS->getConfigName());
|
||||
|
||||
pMonitor->activeWorkspace->startAnim(true, true, true);
|
||||
|
||||
startedOn = pMonitor->activeWorkspace;
|
||||
}
|
||||
}
|
||||
|
||||
void COverview::onPreRender() {
|
||||
if (damageDirty) {
|
||||
damageDirty = false;
|
||||
redrawID(openedID);
|
||||
}
|
||||
}
|
||||
|
||||
void COverview::onWorkspaceChange() {
|
||||
startedOn->startAnim(false, false, true);
|
||||
startedOn = pMonitor->activeWorkspace;
|
||||
for (size_t i = 0; i < SIDE_LENGTH * SIDE_LENGTH; ++i) {
|
||||
if (images[i].workspaceID != pMonitor->activeWorkspaceID())
|
||||
continue;
|
||||
|
||||
openedID = i;
|
||||
break;
|
||||
}
|
||||
startedOn->startAnim(true, true, true);
|
||||
}
|
||||
|
||||
void COverview::render() {
|
||||
|
||||
const auto GAPSIZE = (closing ? (1.0 - size.getPercent()) : size.getPercent()) * GAP_WIDTH;
|
||||
|
||||
if (pMonitor->activeWorkspace != startedOn) {
|
||||
// likely user changed.
|
||||
onWorkspaceChange();
|
||||
}
|
||||
|
||||
Vector2D SIZE = size.value();
|
||||
|
||||
Vector2D tileSize = (SIZE / SIDE_LENGTH);
|
||||
Vector2D tileRenderSize = (SIZE - Vector2D{GAPSIZE, GAPSIZE} * (SIDE_LENGTH - 1)) / SIDE_LENGTH;
|
||||
|
||||
g_pHyprOpenGL->clear(CColor{0.1, 0.1, 0.1, 1.0});
|
||||
|
||||
for (size_t y = 0; y < SIDE_LENGTH; ++y) {
|
||||
for (size_t x = 0; x < SIDE_LENGTH; ++x) {
|
||||
CBox texbox = {x * tileRenderSize.x + x * GAPSIZE, y * tileRenderSize.y + y * GAPSIZE, tileRenderSize.x, tileRenderSize.y};
|
||||
texbox.scale(pMonitor->scale).translate(pos.value());
|
||||
texbox.round();
|
||||
CRegion damage{0, 0, INT16_MAX, INT16_MAX};
|
||||
g_pHyprOpenGL->renderTextureInternalWithDamage(images[x + y * SIDE_LENGTH].fb.m_cTex, &texbox, 1.0, &damage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static float lerp(const float& from, const float& to, const float perc) {
|
||||
return (to - from) * perc + from;
|
||||
}
|
||||
|
||||
static Vector2D lerp(const Vector2D& from, const Vector2D& to, const float perc) {
|
||||
return Vector2D{lerp(from.x, to.x, perc), lerp(from.y, to.y, perc)};
|
||||
}
|
||||
|
||||
void COverview::onSwipeUpdate(double delta) {
|
||||
const float PERC = std::clamp(delta / 300.0, 0.0, 1.0);
|
||||
|
||||
Vector2D tileSize = (pMonitor->vecSize / SIDE_LENGTH);
|
||||
|
||||
const auto SIZEMAX = pMonitor->vecSize * pMonitor->vecSize / tileSize;
|
||||
const auto POSMAX =
|
||||
(-((pMonitor->vecSize / (double)SIDE_LENGTH) * Vector2D{openedID % SIDE_LENGTH, openedID / SIDE_LENGTH}) * pMonitor->scale) * (pMonitor->vecSize / tileSize);
|
||||
|
||||
const auto SIZEMIN = pMonitor->vecSize;
|
||||
const auto POSMIN = Vector2D{0, 0};
|
||||
|
||||
size.setValueAndWarp(lerp(SIZEMIN, SIZEMAX, PERC));
|
||||
pos.setValueAndWarp(lerp(POSMIN, POSMAX, PERC));
|
||||
}
|
||||
|
||||
void COverview::onSwipeEnd() {
|
||||
size = pMonitor->vecSize;
|
||||
pos = {0, 0};
|
||||
|
||||
size.setCallbackOnEnd([this](void*) { redrawAll(true); });
|
||||
}
|
77
hyprexpo/overview.hpp
Normal file
77
hyprexpo/overview.hpp
Normal file
|
@ -0,0 +1,77 @@
|
|||
#pragma once
|
||||
|
||||
#define WLR_USE_UNSTABLE
|
||||
|
||||
#include "globals.hpp"
|
||||
#include <hyprland/src/desktop/DesktopTypes.hpp>
|
||||
#include <hyprland/src/render/Framebuffer.hpp>
|
||||
#include <hyprland/src/helpers/AnimatedVariable.hpp>
|
||||
#include <hyprland/src/managers/HookSystemManager.hpp>
|
||||
#include <vector>
|
||||
|
||||
// saves on resources, but is a bit broken rn with blur.
|
||||
// hyprland's fault, but cba to fix.
|
||||
constexpr bool ENABLE_LOWRES = false;
|
||||
|
||||
class CMonitor;
|
||||
|
||||
class COverview {
|
||||
public:
|
||||
COverview(PHLWORKSPACE startedOn_, bool swipe = false);
|
||||
~COverview();
|
||||
|
||||
void render();
|
||||
void damage();
|
||||
void onDamageReported();
|
||||
void onPreRender();
|
||||
|
||||
void onSwipeUpdate(double delta);
|
||||
void onSwipeEnd();
|
||||
|
||||
// close without a selection
|
||||
void close();
|
||||
|
||||
bool blockOverviewRendering = false;
|
||||
bool blockDamageReporting = false;
|
||||
|
||||
CMonitor* pMonitor = nullptr;
|
||||
|
||||
private:
|
||||
void redrawID(int id, bool forcelowres = false);
|
||||
void redrawAll(bool forcelowres = false);
|
||||
void onWorkspaceChange();
|
||||
|
||||
int SIDE_LENGTH = 3;
|
||||
int GAP_WIDTH = 5;
|
||||
CColor BG_COLOR = CColor{0.1, 0.1, 0.1, 1.0};
|
||||
|
||||
bool damageDirty = false;
|
||||
|
||||
struct SWorkspaceImage {
|
||||
CFramebuffer fb;
|
||||
int64_t workspaceID = -1;
|
||||
PHLWORKSPACE pWorkspace;
|
||||
CBox box;
|
||||
};
|
||||
|
||||
Vector2D lastMousePosLocal = Vector2D{};
|
||||
|
||||
int openedID = -1;
|
||||
int closeOnID = -1;
|
||||
|
||||
std::vector<SWorkspaceImage> images;
|
||||
|
||||
PHLWORKSPACE startedOn;
|
||||
|
||||
CAnimatedVariable<Vector2D> size;
|
||||
CAnimatedVariable<Vector2D> pos;
|
||||
|
||||
bool closing = false;
|
||||
|
||||
HOOK_CALLBACK_FN* mouseMoveHook = nullptr;
|
||||
HOOK_CALLBACK_FN* mouseButtonHook = nullptr;
|
||||
|
||||
bool swipe = false;
|
||||
};
|
||||
|
||||
inline std::unique_ptr<COverview> g_pOverview;
|
Loading…
Reference in a new issue