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 405aa42de8.

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
This commit is contained in:
Maximilian Seidler 2024-07-02 00:45:06 +02:00 committed by GitHub
parent 0f66aeac03
commit 88b9ce48ed
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 54 additions and 61 deletions

View file

@ -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<CSessionLockSurface>(POUTPUT);
g_pRenderer->asyncResourceGatherer->recheckDMAFramesFor(POUTPUT);
}
}

View file

@ -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<CTimer> 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<CTimer> 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) {

View file

@ -64,6 +64,7 @@ class CHyprlock {
std::optional<std::string> passwordLastFailReason();
void renderOutput(const std::string& stringPort);
void renderAllOutputs();
size_t getPasswordBufferLen();
size_t getPasswordBufferDisplayLen();

View file

@ -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<Hyprlang::STRING>(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<CDMAFrame>(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;
}

View file

@ -49,7 +49,6 @@ class CAsyncResourceGatherer {
void unloadAsset(SPreloadedAsset* asset);
void notify();
void await();
void recheckDMAFramesFor(COutput* output);
private:
std::thread asyncLoopThread;

View file

@ -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() {

View file

@ -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;
};

View file

@ -4,6 +4,7 @@
#include "../helpers/Color.hpp"
#include "../core/Output.hpp"
#include "../core/hyprlock.hpp"
#include "../renderer/DMAFrame.hpp"
#include "mtx.hpp"
#include <GLES3/gl32.h>
@ -320,9 +321,15 @@ std::vector<std::unique_ptr<IWidget>>* CRenderer::getOrCreateWidgetsFor(const CS
const std::string PATH = std::any_cast<Hyprlang::STRING>(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<CBackground>(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);
}

View file

@ -41,6 +41,8 @@ class CRenderer {
void pushFb(GLint fb);
void popFb();
void removeWidgetsFor(const CSessionLockSurface* surf);
private:
widgetMap_t widgets;