diff --git a/hyprexpo/ExpoGesture.cpp b/hyprexpo/ExpoGesture.cpp index cc1254b..d18eaab 100644 --- a/hyprexpo/ExpoGesture.cpp +++ b/hyprexpo/ExpoGesture.cpp @@ -12,7 +12,7 @@ void CExpoGesture::begin(const ITrackpadGesture::STrackpadGestureBegin& e) { m_firstUpdate = true; if (!g_pOverview) - g_pOverview = std::make_unique(g_pCompositor->m_lastMonitor->m_activeWorkspace); + g_pOverview = makeShared(g_pCompositor->m_lastMonitor->m_activeWorkspace); else { g_pOverview->selectHoveredWorkspace(); g_pOverview->setClosing(true); diff --git a/hyprexpo/IOverview.hpp b/hyprexpo/IOverview.hpp new file mode 100644 index 0000000..af839ef --- /dev/null +++ b/hyprexpo/IOverview.hpp @@ -0,0 +1,32 @@ +#pragma once +#include + +class IOverview { + public: + IOverview() = default; + virtual ~IOverview() = default; + + virtual void render() = 0; + virtual void damage() = 0; + virtual void onDamageReported() = 0; + virtual void onPreRender() = 0; + + virtual void setClosing(bool closing) = 0; + + virtual void resetSwipe() = 0; + virtual void onSwipeUpdate(double delta) = 0; + virtual void onSwipeEnd() = 0; + + virtual void close() = 0; + virtual void selectHoveredWorkspace() = 0; + + virtual void fullRender() = 0; + + bool blockOverviewRendering = false; + bool blockDamageReporting = false; + + PHLMONITORREF pMonitor; + bool m_isSwiping = false; +}; + +inline SP g_pOverview; diff --git a/hyprexpo/Makefile b/hyprexpo/Makefile index 9cd944c..3fc6c50 100644 --- a/hyprexpo/Makefile +++ b/hyprexpo/Makefile @@ -6,6 +6,6 @@ else endif all: - $(CXX) -shared -fPIC $(EXTRA_FLAGS) main.cpp overview.cpp ExpoGesture.cpp OverviewPassElement.cpp -o hyprexpo.so -g `pkg-config --cflags pixman-1 libdrm hyprland pangocairo libinput libudev wayland-server xkbcommon` -std=c++2b -Wno-narrowing + $(CXX) -shared -fPIC $(EXTRA_FLAGS) main.cpp overview.cpp ExpoGesture.cpp OverviewPassElement.cpp scrollOverview.cpp -o hyprexpo.so -g `pkg-config --cflags pixman-1 libdrm hyprland pangocairo libinput libudev wayland-server xkbcommon` -std=c++2b -Wno-narrowing clean: rm ./hyprexpo.so diff --git a/hyprexpo/globals.hpp b/hyprexpo/globals.hpp index 2257475..f5132f3 100644 --- a/hyprexpo/globals.hpp +++ b/hyprexpo/globals.hpp @@ -2,4 +2,5 @@ #include -inline HANDLE PHANDLE = nullptr; \ No newline at end of file +inline HANDLE PHANDLE = nullptr; +inline bool IS_SCROLLING = false; \ No newline at end of file diff --git a/hyprexpo/main.cpp b/hyprexpo/main.cpp index e53b8ac..39d4fd0 100644 --- a/hyprexpo/main.cpp +++ b/hyprexpo/main.cpp @@ -9,12 +9,14 @@ #include #include #include +#include #include using namespace Hyprutils::String; #include "globals.hpp" #include "overview.hpp" +#include "scrollOverview.hpp" #include "ExpoGesture.hpp" // Methods @@ -66,6 +68,8 @@ static void hkAddDamageB(void* thisptr, const pixman_region32_t* rg) { static SDispatchResult onExpoDispatcher(std::string arg) { + IS_SCROLLING = g_pLayoutManager->getCurrentLayout()->getLayoutName() == "scrolling"; + if (g_pOverview && g_pOverview->m_isSwiping) return {.success = false, .error = "already swiping"}; @@ -81,7 +85,10 @@ static SDispatchResult onExpoDispatcher(std::string arg) { g_pOverview->close(); else { renderingOverview = true; - g_pOverview = std::make_unique(g_pCompositor->m_lastMonitor->m_activeWorkspace); + if (IS_SCROLLING) + g_pOverview = makeShared(g_pCompositor->m_lastMonitor->m_activeWorkspace); + else + makeShared(g_pCompositor->m_lastMonitor->m_activeWorkspace); renderingOverview = false; } return {}; @@ -97,7 +104,10 @@ static SDispatchResult onExpoDispatcher(std::string arg) { return {}; renderingOverview = true; - g_pOverview = std::make_unique(g_pCompositor->m_lastMonitor->m_activeWorkspace); + if (IS_SCROLLING) + g_pOverview = makeShared(g_pCompositor->m_lastMonitor->m_activeWorkspace); + else + makeShared(g_pCompositor->m_lastMonitor->m_activeWorkspace); renderingOverview = false; return {}; } @@ -107,7 +117,7 @@ static void failNotif(const std::string& reason) { } static Hyprlang::CParseResult expoGestureKeyword(const char* LHS, const char* RHS) { - Hyprlang::CParseResult result; + Hyprlang::CParseResult result; if (g_unloading) return result; diff --git a/hyprexpo/overview.cpp b/hyprexpo/overview.cpp index 9d84387..f92a192 100644 --- a/hyprexpo/overview.cpp +++ b/hyprexpo/overview.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #undef private #include "OverviewPassElement.hpp" diff --git a/hyprexpo/overview.hpp b/hyprexpo/overview.hpp index 4b02400..9e47865 100644 --- a/hyprexpo/overview.hpp +++ b/hyprexpo/overview.hpp @@ -9,43 +9,40 @@ #include #include +#include "IOverview.hpp" + // 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 { +class COverview : public IOverview { public: COverview(PHLWORKSPACE startedOn_, bool swipe = false); - ~COverview(); + virtual ~COverview(); - void render(); - void damage(); - void onDamageReported(); - void onPreRender(); + virtual void render(); + virtual void damage(); + virtual void onDamageReported(); + virtual void onPreRender(); - void setClosing(bool closing); + virtual void setClosing(bool closing); - void resetSwipe(); - void onSwipeUpdate(double delta); - void onSwipeEnd(); + virtual void resetSwipe(); + virtual void onSwipeUpdate(double delta); + virtual void onSwipeEnd(); // close without a selection - void close(); - void selectHoveredWorkspace(); + virtual void close(); + virtual void selectHoveredWorkspace(); - bool blockOverviewRendering = false; - bool blockDamageReporting = false; - - PHLMONITORREF pMonitor; - bool m_isSwiping = false; + virtual void fullRender(); 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; @@ -84,5 +81,3 @@ class COverview { friend class COverviewPassElement; }; - -inline std::unique_ptr g_pOverview; diff --git a/hyprexpo/scrollOverview.cpp b/hyprexpo/scrollOverview.cpp new file mode 100644 index 0000000..e4876a2 --- /dev/null +++ b/hyprexpo/scrollOverview.cpp @@ -0,0 +1,393 @@ +#include "scrollOverview.hpp" +#include +#define private public +#include +#include +#include +#include +#include +#include +#include +#include +#include +#undef private +#include "OverviewPassElement.hpp" + +static void damageMonitor(WP thisptr) { + g_pOverview->damage(); +} + +static void removeOverview(WP thisptr) { + g_pOverview.reset(); +} + +CScrollOverview::~CScrollOverview() { + g_pHyprRenderer->makeEGLCurrent(); + images.clear(); // otherwise we get a vram leak + g_pInputManager->unsetCursorImage(); + g_pHyprOpenGL->markBlurDirtyForMonitor(pMonitor.lock()); +} + +CScrollOverview::CScrollOverview(PHLWORKSPACE startedOn_, bool swipe_) : startedOn(startedOn_), swipe(swipe_) { + const auto PMONITOR = g_pCompositor->m_lastMonitor.lock(); + pMonitor = PMONITOR; + + for (const auto& w : g_pCompositor->getWorkspaces()) { + if (w && w->m_monitor == pMonitor && !w->m_isSpecialWorkspace) + images.emplace_back(makeShared(w.lock())); + } + + std::sort(images.begin(), images.end(), [](const auto& a, const auto& b) { return a->pWorkspace->m_id < b->pWorkspace->m_id; }); + + g_pAnimationManager->createAnimation(1.F, scale, g_pConfigManager->getAnimationPropertyConfig("windowsMove"), AVARDAMAGE_NONE); + g_pAnimationManager->createAnimation({}, viewOffset, g_pConfigManager->getAnimationPropertyConfig("windowsMove"), AVARDAMAGE_NONE); + + scale->setUpdateCallback(damageMonitor); + viewOffset->setUpdateCallback(damageMonitor); + + if (!swipe) + *scale = 0.5F; + + lastMousePosLocal = g_pInputManager->getMouseCoordsInternal() - pMonitor->m_position; + + auto onCursorMove = [this](void* self, SCallbackInfo& info, std::any param) { + if (closing) + return; + + info.cancelled = true; + lastMousePosLocal = g_pInputManager->getMouseCoordsInternal() - pMonitor->m_position; + + // highlightHoverDebug(); + }; + + auto onCursorSelect = [this](void* self, SCallbackInfo& info, std::any param) { + if (closing) + return; + + info.cancelled = true; + + selectHoveredWorkspace(); + + close(); + }; + + auto onMouseAxis = [this](void* self, SCallbackInfo& info, std::any param) { + if (closing) + return; + + info.cancelled = true; + + auto data = std::any_cast>(param); + auto e = std::any_cast(data["event"]); + + const auto VAL = std::clamp(sc(scale->value() + e.delta / -500.F), 0.05F, 0.95F); + *scale = VAL; + }; + + mouseMoveHook = g_pHookSystem->hookDynamic("mouseMove", onCursorMove); + touchMoveHook = g_pHookSystem->hookDynamic("touchMove", onCursorMove); + mouseAxisHook = g_pHookSystem->hookDynamic("mouseAxis", onMouseAxis); + + mouseButtonHook = g_pHookSystem->hookDynamic("mouseButton", onCursorSelect); + touchDownHook = g_pHookSystem->hookDynamic("touchDown", onCursorSelect); + + g_pInputManager->setCursorImageUntilUnset("left_ptr"); + + redrawAll(); +} + +void CScrollOverview::selectHoveredWorkspace() { + size_t activeIdx = 0; + for (size_t i = 0; i < images.size(); ++i) { + if (images[i]->pWorkspace && images[i]->pWorkspace == startedOn) { + activeIdx = i; + break; + } + } + + const auto VIEWPORT_CENTER = CBox{{}, pMonitor->m_size}.middle(); + + float yoff = -(float)activeIdx * pMonitor->m_size.y * scale->value(); + bool found = false; + for (const auto& wimg : images) { + for (const auto& img : wimg->windowImages) { + CBox texbox = {img->pWindow->m_realPosition->value() - pMonitor->m_position, img->pWindow->m_realSize->value()}; + + // scale the box to the viewport center + texbox.translate(-VIEWPORT_CENTER).scale(scale->value()).translate(VIEWPORT_CENTER).translate(-viewOffset->value() * scale->value()); + + texbox.translate({0.F, yoff}); + + // texbox.scale(pMonitor->m_scale).round(); + + if (texbox.containsPoint(lastMousePosLocal)) { + closeOnWindow = img->pWindow; + // *viewOffset = CBox{img->pWindow->m_realPosition->value(), img->pWindow->m_realSize->value()}.translate({0.F, yoff / scale->value()}).middle() - + // CBox{pMonitor->m_position, pMonitor->m_size}.middle(); + found = true; + break; + } + } + if (found) + break; + yoff += pMonitor->m_size.y * scale->value(); + } +} + +void CScrollOverview::highlightHoverDebug() { + size_t activeIdx = 0; + for (size_t i = 0; i < images.size(); ++i) { + if (images[i]->pWorkspace && images[i]->pWorkspace == startedOn) { + activeIdx = i; + break; + } + } + + const auto VIEWPORT_CENTER = CBox{{}, pMonitor->m_size}.middle(); + + float yoff = -(float)activeIdx * pMonitor->m_size.y * scale->value(); + for (const auto& wimg : images) { + for (const auto& img : wimg->windowImages) { + CBox texbox = {img->pWindow->m_realPosition->value() - pMonitor->m_position, img->pWindow->m_realSize->value()}; + + // scale the box to the viewport center + texbox.translate(-VIEWPORT_CENTER).scale(scale->value()).translate(VIEWPORT_CENTER).translate(-viewOffset->value() * scale->value()); + + texbox.translate({0.F, yoff}); + + // texbox.scale(pMonitor->m_scale).round(); + + if (texbox.containsPoint(lastMousePosLocal)) { + img->highlight = true; + continue; + } + + img->highlight = false; + } + yoff += pMonitor->m_size.y * scale->value(); + } +} + +SP CScrollOverview::imageForWorkspace(PHLWORKSPACE w) { + for (const auto& i : images) { + if (i->pWorkspace == w) + return i; + } + return nullptr; +} + +void CScrollOverview::redrawWorkspace(PHLWORKSPACE workspace, bool forcelowres) { + if (pMonitor->m_activeWorkspace != startedOn && !closing) { + // likely user changed. + onWorkspaceChange(); + } + + blockOverviewRendering = true; + + g_pHyprRenderer->makeEGLCurrent(); + + auto image = imageForWorkspace(workspace); + + if (!image) + return; + + // get all tiled windows on the workspace and max dim + // TODO: float + std::vector windows; + for (const auto& w : g_pCompositor->m_windows) { + if (!validMapped(w) || w->m_isFloating || w->m_workspace != workspace) + continue; + windows.emplace_back(w); + } + + for (const auto& w : windows) { + auto img = image->windowImages.emplace_back(makeShared()); + img->pWindow = w; + img->fb.alloc(pMonitor->m_pixelSize.x, pMonitor->m_pixelSize.y, 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, &img->fb); + + g_pHyprOpenGL->clear(CHyprColor{0, 0, 0, 0}); + + g_pHyprRenderer->renderWindow(w, pMonitor.lock(), Time::steadyNow(), true, RENDER_PASS_ALL, true, true); + + g_pHyprOpenGL->m_renderData.blockScreenShader = true; + g_pHyprRenderer->endRender(); + } + + blockOverviewRendering = false; +} + +void CScrollOverview::redrawAll(bool forcelowres) { + + for (const auto& img : images) { + redrawWorkspace(img->pWorkspace); + } + + // redraw bg + if (backgroundFb.m_size != pMonitor->m_pixelSize) { + backgroundFb.release(); + backgroundFb.alloc(pMonitor->m_pixelSize.x, pMonitor->m_pixelSize.y, 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, &backgroundFb); + + g_pHyprOpenGL->clear(CHyprColor{0, 0, 0, 1.0}); + + g_pHyprRenderer->renderAllClientsForWorkspace(pMonitor.lock(), nullptr, Time::steadyNow()); + + g_pHyprOpenGL->m_renderData.blockScreenShader = true; + g_pHyprRenderer->endRender(); +} + +void CScrollOverview::damage() { + blockDamageReporting = true; + g_pHyprRenderer->damageMonitor(pMonitor.lock()); + blockDamageReporting = false; +} + +void CScrollOverview::onDamageReported() { + ; // TODO: +} + +void CScrollOverview::close() { + + if (closing) + return; + + closing = true; + + if (!closeOnWindow) + closeOnWindow = g_pCompositor->m_lastWindow; + + if (closeOnWindow == g_pCompositor->m_lastWindow) + *viewOffset = Vector2D{}; + else { + + if (closeOnWindow->m_workspace != pMonitor->m_activeWorkspace) { + g_pDesktopAnimationManager->startAnimation(pMonitor->m_activeWorkspace, CDesktopAnimationManager::ANIMATION_TYPE_OUT, true, true); + g_pDesktopAnimationManager->startAnimation(closeOnWindow->m_workspace, CDesktopAnimationManager::ANIMATION_TYPE_IN, false, true); + pMonitor->changeWorkspace(closeOnWindow->m_workspace, true, true, true); + } + + g_pCompositor->focusWindow(closeOnWindow.lock()); + + size_t activeIdx = 0; + for (size_t i = 0; i < images.size(); ++i) { + if (images[i]->pWorkspace && images[i]->pWorkspace == startedOn) { + activeIdx = i; + break; + } + } + + float yoff = -(float)activeIdx * pMonitor->m_size.y * scale->value(); + bool found = false; + for (const auto& wimg : images) { + for (const auto& img : wimg->windowImages) { + if (img->pWindow == closeOnWindow && closeOnWindow) { + Vector2D middleOfWindow = CBox{img->pWindow->m_realPosition->value(), img->pWindow->m_realSize->value()}.translate({0.F, yoff / scale->value()}).middle() - + CBox{pMonitor->m_position, pMonitor->m_size}.middle(); + + // we need to do this because the window doesnt have to be centered after click + *viewOffset = middleOfWindow + + (CBox{pMonitor->m_position, pMonitor->m_size}.middle() - CBox{img->pWindow->m_realPosition->value(), img->pWindow->m_realSize->value()}.middle()); + found = true; + break; + } + } + if (found) + break; + yoff += pMonitor->m_size.y * scale->value(); + } + } + + *scale = 1.F; + + scale->setCallbackOnEnd(removeOverview); +} + +void CScrollOverview::onPreRender() { + ; +} + +void CScrollOverview::onWorkspaceChange() { + ; // TODO: +} + +void CScrollOverview::render() { + g_pHyprRenderer->m_renderPass.add(makeUnique()); +} + +void CScrollOverview::fullRender() { + + g_pHyprOpenGL->clear(CHyprColor{0, 0, 0, 1}); + + CBox texbox = {{}, pMonitor->m_size}; + texbox.scale(pMonitor->m_scale); + texbox.round(); + CRegion damage{0, 0, INT16_MAX, INT16_MAX}; + g_pHyprOpenGL->renderTextureInternal(backgroundFb.getTexture(), texbox, {.damage = &damage, .a = 1.0}); + + const auto VIEWPORT_CENTER = CBox{{}, pMonitor->m_size}.middle(); + + size_t activeIdx = 0; + for (size_t i = 0; i < images.size(); ++i) { + if (images[i]->pWorkspace && images[i]->pWorkspace == startedOn) { + activeIdx = i; + break; + } + } + + // render all views + float yoff = -(float)activeIdx * pMonitor->m_size.y * scale->value(); + for (const auto& wimg : images) { + for (const auto& img : wimg->windowImages) { + CBox texbox = CBox{img->pWindow->m_realPosition->value() - pMonitor->m_position, pMonitor->m_size}; + + // scale the box to the viewport center + texbox.translate(-VIEWPORT_CENTER).scale(scale->value()).translate(VIEWPORT_CENTER).translate(-viewOffset->value() * scale->value()); + + texbox.translate({0.F, yoff}); + + texbox.scale(pMonitor->m_scale).round(); + CRegion damage{0, 0, INT16_MAX, INT16_MAX}; + g_pHyprOpenGL->renderTextureInternal(img->fb.getTexture(), texbox, {.damage = &damage, .a = 1.0}); + + if (img->highlight) { + CBox texbox2 = CBox{img->pWindow->m_realPosition->value(), img->pWindow->m_realSize->value()} + .translate(-VIEWPORT_CENTER) + .scale(scale->value()) + .translate(VIEWPORT_CENTER) + .translate({0.F, yoff}); + g_pHyprOpenGL->renderRect(texbox2, CHyprColor{0.5, 0.0, 0.0, 0.5}, CHyprOpenGLImpl::SRectRenderData{.round = 5}); + } + } + yoff += pMonitor->m_size.y * scale->value(); + } +} + +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 CScrollOverview::setClosing(bool closing_) { + // TODO: +} + +void CScrollOverview::resetSwipe() { + // TODO: +} + +void CScrollOverview::onSwipeUpdate(double delta) { + // TODO: +} + +void CScrollOverview::onSwipeEnd() { + // TODO: +} diff --git a/hyprexpo/scrollOverview.hpp b/hyprexpo/scrollOverview.hpp new file mode 100644 index 0000000..d844b99 --- /dev/null +++ b/hyprexpo/scrollOverview.hpp @@ -0,0 +1,86 @@ +#pragma once + +#define WLR_USE_UNSTABLE + +#include "globals.hpp" +#include +#include +#include +#include +#include + +#include "IOverview.hpp" + +class CMonitor; + +class CScrollOverview : public IOverview { + public: + CScrollOverview(PHLWORKSPACE startedOn_, bool swipe = false); + virtual ~CScrollOverview(); + + virtual void render(); + virtual void damage(); + virtual void onDamageReported(); + virtual void onPreRender(); + + virtual void setClosing(bool closing); + + virtual void resetSwipe(); + virtual void onSwipeUpdate(double delta); + virtual void onSwipeEnd(); + + // close without a selection + virtual void close(); + virtual void selectHoveredWorkspace(); + + virtual void fullRender(); + + private: + void redrawWorkspace(PHLWORKSPACE w, bool forcelowres = false); + void redrawAll(bool forcelowres = false); + void onWorkspaceChange(); + void highlightHoverDebug(); + + bool damageDirty = false; + + struct SWindowImage { + PHLWINDOWREF pWindow; + CFramebuffer fb; + bool highlight = false; + }; + + struct SWorkspaceImage { + PHLWORKSPACE pWorkspace; + CBox box; + std::vector> windowImages; + }; + + CFramebuffer backgroundFb; + + Vector2D lastMousePosLocal = Vector2D{}; + + PHLWINDOWREF closeOnWindow; + + std::vector> images; + SP imageForWorkspace(PHLWORKSPACE w); + + PHLWORKSPACE startedOn; + + PHLANIMVAR scale; + PHLANIMVAR viewOffset; + + bool closing = false; + + SP mouseMoveHook; + SP mouseButtonHook; + SP touchMoveHook; + SP touchDownHook; + SP mouseAxisHook; + + bool swipe = false; + bool swipeWasCommenced = false; + + friend class CScrollOverviewPassElement; +}; + +inline std::unique_ptr g_pScrollOverview;