From 88b9ce48ed0c561c44c3a09cd6cef0e1bebaf59f Mon Sep 17 00:00:00 2001 From: Maximilian Seidler <78690852+PaideiaDilemma@users.noreply.github.com> Date: Tue, 2 Jul 2024 00:45:06 +0200 Subject: [PATCH] core: improve dynamic output handling (#386) * core: check for sessionLockSurface before calling render This is needed, because when a new monitor is added via `onGlobal` the order of the events is not guaranteed. Meaning that render for a particular monitor might get called before a `CSessionLockSurface` for that monitor exists. * renderer: remove widgets for destroyed lockSurfaces * asyncResourceGatherer: don't create duplicate dma frames for a specific stringPort * core: remove renderer widgets in ~CSessionLockSurface instead of in onGlobalRemoved * Revert "core: remove renderer widgets in ~CSessionLockSurface instead of in onGlobalRemoved" This reverts commit 405aa42de883a7080d3e02200e91e76484b3a97b. Because of destruction order, it is safer to do it in `onGlobalRemoved`. * core: ditch dynamic DMAFrame generation * core: identify DMAFrames via output size and stringPort * core: fallback to background color for dynamic outputs with background:path=screenshot * core: remove output pointer from DMAFrame --- src/core/Output.cpp | 4 +-- src/core/hyprlock.cpp | 47 ++++++++++++++------------ src/core/hyprlock.hpp | 1 + src/renderer/AsyncResourceGatherer.cpp | 26 +------------- src/renderer/AsyncResourceGatherer.hpp | 1 - src/renderer/DMAFrame.cpp | 11 +++--- src/renderer/DMAFrame.hpp | 6 ++-- src/renderer/Renderer.cpp | 17 ++++++++-- src/renderer/Renderer.hpp | 2 ++ 9 files changed, 54 insertions(+), 61 deletions(-) diff --git a/src/core/Output.cpp b/src/core/Output.cpp index 2282e5c..3b0a8a4 100644 --- a/src/core/Output.cpp +++ b/src/core/Output.cpp @@ -25,11 +25,9 @@ static void handleDone(void* data, wl_output* output) { const auto POUTPUT = (COutput*)data; Debug::log(LOG, "output {} done", POUTPUT->name); if (g_pHyprlock->m_bLocked && !POUTPUT->sessionLockSurface) { - // if we are already locked, create a surface dynamically after a small timeout - // we also need to request a dma frame for screenshots + // if we are already locked, create a surface dynamically Debug::log(LOG, "Creating a surface dynamically for output as we are already locked"); POUTPUT->sessionLockSurface = std::make_unique(POUTPUT); - g_pRenderer->asyncResourceGatherer->recheckDMAFramesFor(POUTPUT); } } diff --git a/src/core/hyprlock.cpp b/src/core/hyprlock.cpp index eedc963..05eda53 100644 --- a/src/core/hyprlock.cpp +++ b/src/core/hyprlock.cpp @@ -300,7 +300,11 @@ void CHyprlock::onGlobal(void* data, struct wl_registry* registry, uint32_t name void CHyprlock::onGlobalRemoved(void* data, struct wl_registry* registry, uint32_t name) { Debug::log(LOG, " | removed iface {}", name); - std::erase_if(m_vOutputs, [name](const auto& other) { return other->name == name; }); + auto outputIt = std::find_if(m_vOutputs.begin(), m_vOutputs.end(), [name](const auto& other) { return other->name == name; }); + if (outputIt != m_vOutputs.end()) { + g_pRenderer->removeWidgetsFor(outputIt->get()->sessionLockSurface.get()); + m_vOutputs.erase(outputIt); + } } // end wl_registry @@ -570,9 +574,7 @@ void CHyprlock::unlock() { m_tFadeEnds = std::chrono::system_clock::now() + std::chrono::milliseconds(500); m_bFadeStarted = true; - for (auto& o : m_vOutputs) { - o->sessionLockSurface->render(); - } + renderAllOutputs(); } // wl_seat @@ -758,9 +760,7 @@ static const ext_session_lock_v1_listener sessionLockListener = { static void displayFailTextTimerCallback(std::shared_ptr self, void* data) { g_pAuth->m_bDisplayFailText = false; - for (auto& o : g_pHyprlock->m_vOutputs) { - o->sessionLockSurface->render(); - } + g_pHyprlock->renderAllOutputs(); } void CHyprlock::onPasswordCheckTimer() { @@ -779,9 +779,7 @@ void CHyprlock::onPasswordCheckTimer() { g_pAuth->start(); - for (auto& o : m_vOutputs) { - o->sessionLockSurface->render(); - } + renderAllOutputs(); } } @@ -790,9 +788,8 @@ void CHyprlock::clearPasswordBuffer() { return; m_sPasswordState.passBuffer = ""; - for (auto& o : m_vOutputs) { - o->sessionLockSurface->render(); - } + + renderAllOutputs(); } void CHyprlock::renderOutput(const std::string& stringPort) { @@ -803,9 +800,21 @@ void CHyprlock::renderOutput(const std::string& stringPort) { const auto PMONITOR = MON->get(); + if (!PMONITOR->sessionLockSurface) + return; + PMONITOR->sessionLockSurface->render(); } +void CHyprlock::renderAllOutputs() { + for (auto& o : m_vOutputs) { + if (!o->sessionLockSurface) + continue; + + o->sessionLockSurface->render(); + } +} + void CHyprlock::startKeyRepeat(xkb_keysym_t sym) { if (m_pKeyRepeatTimer) { m_pKeyRepeatTimer->cancel(); @@ -830,9 +839,7 @@ void CHyprlock::repeatKey(xkb_keysym_t sym) { m_pKeyRepeatTimer = addTimer( std::chrono::milliseconds(m_iKeebRepeatRate), [sym](std::shared_ptr self, void* data) { g_pHyprlock->repeatKey(sym); }, nullptr); - for (auto& o : m_vOutputs) { - o->sessionLockSurface->render(); - } + renderAllOutputs(); } void CHyprlock::onKey(uint32_t key, bool down) { @@ -863,9 +870,7 @@ void CHyprlock::onKey(uint32_t key, bool down) { } if (g_pAuth->checkWaiting()) { - for (auto& o : m_vOutputs) { - o->sessionLockSurface->render(); - } + renderAllOutputs(); return; } @@ -881,9 +886,7 @@ void CHyprlock::onKey(uint32_t key, bool down) { startKeyRepeat(SYM); } - for (auto& o : m_vOutputs) { - o->sessionLockSurface->render(); - } + renderAllOutputs(); } void CHyprlock::handleKeySym(xkb_keysym_t sym) { diff --git a/src/core/hyprlock.hpp b/src/core/hyprlock.hpp index 52344b9..5a2efb7 100644 --- a/src/core/hyprlock.hpp +++ b/src/core/hyprlock.hpp @@ -64,6 +64,7 @@ class CHyprlock { std::optional passwordLastFailReason(); void renderOutput(const std::string& stringPort); + void renderAllOutputs(); size_t getPasswordBufferLen(); size_t getPasswordBufferDisplayLen(); diff --git a/src/renderer/AsyncResourceGatherer.cpp b/src/renderer/AsyncResourceGatherer.cpp index 465511c..19781d8 100644 --- a/src/renderer/AsyncResourceGatherer.cpp +++ b/src/renderer/AsyncResourceGatherer.cpp @@ -56,30 +56,6 @@ CAsyncResourceGatherer::CAsyncResourceGatherer() { } } -void CAsyncResourceGatherer::recheckDMAFramesFor(COutput* output) { - const auto CWIDGETS = g_pConfigManager->getWidgetConfigs(); - - bool shouldMake = false; - - for (auto& c : CWIDGETS) { - if (c.type != "background") - continue; - - if (std::string{std::any_cast(c.values.at("path"))} != "screenshot") - continue; - - if (c.monitor.empty() || c.monitor == output->stringPort || output->stringDesc.starts_with(c.monitor)) { - shouldMake = true; - break; - } - } - - if (!shouldMake) - return; - - dmas.emplace_back(std::make_unique(output)); -} - SPreloadedAsset* CAsyncResourceGatherer::getAssetByID(const std::string& id) { for (auto& a : assets) { if (a.first == id) @@ -94,7 +70,7 @@ SPreloadedAsset* CAsyncResourceGatherer::getAssetByID(const std::string& id) { }; for (auto& dma : dmas) { - if (id == "dma:" + dma->name) + if (id == dma->resourceID) return dma->asset.ready ? &dma->asset : nullptr; } diff --git a/src/renderer/AsyncResourceGatherer.hpp b/src/renderer/AsyncResourceGatherer.hpp index d94cb85..8e8f5a1 100644 --- a/src/renderer/AsyncResourceGatherer.hpp +++ b/src/renderer/AsyncResourceGatherer.hpp @@ -49,7 +49,6 @@ class CAsyncResourceGatherer { void unloadAsset(SPreloadedAsset* asset); void notify(); void await(); - void recheckDMAFramesFor(COutput* output); private: std::thread asyncLoopThread; diff --git a/src/renderer/DMAFrame.cpp b/src/renderer/DMAFrame.cpp index 08d9ff9..c67c6ac 100644 --- a/src/renderer/DMAFrame.cpp +++ b/src/renderer/DMAFrame.cpp @@ -86,7 +86,12 @@ static const zwlr_screencopy_frame_v1_listener wlrFrameListener = { .buffer_done = wlrOnBufferDone, }; -CDMAFrame::CDMAFrame(COutput* output_) : output(output_) { +std::string CDMAFrame::getResourceId(COutput* output) { + return std::format("dma:{}-{}x{}", output->stringPort, output->size.x, output->size.y); +} + +CDMAFrame::CDMAFrame(COutput* output_) { + resourceID = getResourceId(output_); if (!glEGLImageTargetTexture2DOES) { glEGLImageTargetTexture2DOES = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)eglGetProcAddress("glEGLImageTargetTexture2DOES"); @@ -100,13 +105,11 @@ CDMAFrame::CDMAFrame(COutput* output_) : output(output_) { eglQueryDmaBufModifiersEXT = (PFNEGLQUERYDMABUFMODIFIERSEXTPROC)eglGetProcAddress("eglQueryDmaBufModifiersEXT"); // firstly, plant a listener for the frame - frameCb = zwlr_screencopy_manager_v1_capture_output(g_pHyprlock->getScreencopy(), false, output->output); + frameCb = zwlr_screencopy_manager_v1_capture_output(g_pHyprlock->getScreencopy(), false, output_->output); scdata.frame = this; zwlr_screencopy_frame_v1_add_listener(frameCb, &wlrFrameListener, &scdata); - - name = output->stringPort; } CDMAFrame::~CDMAFrame() { diff --git a/src/renderer/DMAFrame.hpp b/src/renderer/DMAFrame.hpp index cb13d24..7309432 100644 --- a/src/renderer/DMAFrame.hpp +++ b/src/renderer/DMAFrame.hpp @@ -19,6 +19,8 @@ struct SScreencopyData { class CDMAFrame { public: + static std::string getResourceId(COutput* output); + CDMAFrame(COutput* mon); ~CDMAFrame(); @@ -27,7 +29,7 @@ class CDMAFrame { wl_buffer* wlBuffer = nullptr; - std::string name; + std::string resourceID; SPreloadedAsset asset; @@ -42,7 +44,5 @@ class CDMAFrame { zwlr_screencopy_frame_v1* frameCb = nullptr; SScreencopyData scdata; - COutput* output = nullptr; - EGLImage image = nullptr; }; \ No newline at end of file diff --git a/src/renderer/Renderer.cpp b/src/renderer/Renderer.cpp index 08a15c9..bd19238 100644 --- a/src/renderer/Renderer.cpp +++ b/src/renderer/Renderer.cpp @@ -4,6 +4,7 @@ #include "../helpers/Color.hpp" #include "../core/Output.hpp" #include "../core/hyprlock.hpp" +#include "../renderer/DMAFrame.hpp" #include "mtx.hpp" #include @@ -320,9 +321,15 @@ std::vector>* CRenderer::getOrCreateWidgetsFor(const CS const std::string PATH = std::any_cast(c.values.at("path")); std::string resourceID = ""; - if (PATH == "screenshot") - resourceID = "dma:" + surf->output->stringPort; - else if (!PATH.empty()) + if (PATH == "screenshot") { + resourceID = CDMAFrame::getResourceId(surf->output); + // When the initial gather of the asyncResourceGatherer is completed (ready), all DMAFrames are available. + // Dynamic ones are tricky, because a screencopy would copy hyprlock itself. + if (asyncResourceGatherer->ready) { + if (!asyncResourceGatherer->getAssetByID(resourceID)) + resourceID = ""; // Fallback to solid color (background:color) + } + } else if (!PATH.empty()) resourceID = "background:" + PATH; widgets[surf].emplace_back(std::make_unique(surf->size, surf->output, resourceID, c.values, PATH == "screenshot")); @@ -512,3 +519,7 @@ void CRenderer::popFb() { boundFBs.pop_back(); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, boundFBs.empty() ? 0 : boundFBs.back()); } + +void CRenderer::removeWidgetsFor(const CSessionLockSurface* surf) { + widgets.erase(surf); +} diff --git a/src/renderer/Renderer.hpp b/src/renderer/Renderer.hpp index 6599740..d3fc2b6 100644 --- a/src/renderer/Renderer.hpp +++ b/src/renderer/Renderer.hpp @@ -41,6 +41,8 @@ class CRenderer { void pushFb(GLint fb); void popFb(); + void removeWidgetsFor(const CSessionLockSurface* surf); + private: widgetMap_t widgets;