From 6ca74885afb2e4781748d2768c6486d4dd755766 Mon Sep 17 00:00:00 2001 From: Aitor42 Date: Tue, 23 Jun 2026 20:43:22 +0200 Subject: [PATCH 1/4] fix(session): wait for lock screen to render before suspending --- src/shell/lockscreen/lock_screen.cpp | 20 ++++++++++++++++++-- src/shell/lockscreen/lock_screen.h | 1 + src/shell/lockscreen/lock_surface.cpp | 8 ++++++++ src/shell/lockscreen/lock_surface.h | 6 ++++++ 4 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/shell/lockscreen/lock_screen.cpp b/src/shell/lockscreen/lock_screen.cpp index 31cf0d729b..e8d96275cf 100644 --- a/src/shell/lockscreen/lock_screen.cpp +++ b/src/shell/lockscreen/lock_screen.cpp @@ -354,7 +354,7 @@ void LockScreen::runAfterSessionLocked(std::function fn) { if (fn == nullptr) { return; } - if (m_locked) { + if (m_locked && allSurfacesReady()) { DeferredCall::callLater(std::move(fn)); return; } @@ -388,7 +388,7 @@ void LockScreen::handleLocked(void* data, ext_session_lock_v1* /*lock*/) { if (self->m_onSessionLocked) { self->m_onSessionLocked(); } - if (self->m_pendingAfterLocked) { + if (self->m_pendingAfterLocked && self->allSurfacesReady()) { auto pending = std::move(self->m_pendingAfterLocked); self->m_pendingAfterLocked = {}; DeferredCall::callLater(std::move(pending)); @@ -456,6 +456,15 @@ bool LockScreen::shouldUseBlurredDesktop() const { && m_wayland->hasScreencopy(); } +bool LockScreen::allSurfacesReady() const { + for (const auto& instance : m_instances) { + if (instance.surface != nullptr && !instance.surface->firstFrameRendered()) { + return false; + } + } + return true; +} + void LockScreen::primeDesktopCaptures() { if (m_configService != nullptr && !m_configService->isLockScreenEnabled()) { return; @@ -609,6 +618,13 @@ void LockScreen::createInstance(const WaylandOutput& output) { surface->setDesktopCapture(std::move(captureIt->second)); m_desktopCaptures.erase(captureIt); } + surface->setRenderCallback([this]() { + if (m_locked && m_pendingAfterLocked && allSurfacesReady()) { + auto pending = std::move(m_pendingAfterLocked); + m_pendingAfterLocked = {}; + DeferredCall::callLater(std::move(pending)); + } + }); surface->setOnLogin([this]() { tryAuthenticate(); }); surface->setOnPasswordChanged([this](const std::string& value) { handlePasswordEdited(value); }); surface->setPromptState(m_user, m_password, m_status, m_statusIsError, m_authenticating); diff --git a/src/shell/lockscreen/lock_screen.h b/src/shell/lockscreen/lock_screen.h index ef7f33ad69..ed02f83fda 100644 --- a/src/shell/lockscreen/lock_screen.h +++ b/src/shell/lockscreen/lock_screen.h @@ -81,6 +81,7 @@ class LockScreen { void syncInstances(); void captureDesktopSnapshots(); [[nodiscard]] bool shouldUseBlurredDesktop() const; + [[nodiscard]] bool allSurfacesReady() const; void applyLockscreenStyle(LockSurface& surface) const; void applyOutputRestriction(); void applyWallpaperStyleToSurfaces(); diff --git a/src/shell/lockscreen/lock_surface.cpp b/src/shell/lockscreen/lock_surface.cpp index 02cb63e187..96ea987c1d 100644 --- a/src/shell/lockscreen/lock_surface.cpp +++ b/src/shell/lockscreen/lock_surface.cpp @@ -830,3 +830,11 @@ void LockSurface::onGpuResourcesInvalidated() { m_wallpaperDirty = true; requestLayout(); } + +void LockSurface::render() { + Surface::render(); + m_firstFrameRendered = true; + if (m_renderCallback) { + m_renderCallback(); + } +} diff --git a/src/shell/lockscreen/lock_surface.h b/src/shell/lockscreen/lock_surface.h index 8cd5731bac..08c0c6d866 100644 --- a/src/shell/lockscreen/lock_surface.h +++ b/src/shell/lockscreen/lock_surface.h @@ -62,6 +62,9 @@ class LockSurface : public Surface { void setOutputKey(std::string outputKey) { m_outputKey = std::move(outputKey); } void setWidgetsHost(LockscreenWidgetsHost* host) noexcept { m_widgetsHost = host; } + [[nodiscard]] bool firstFrameRendered() const noexcept { return m_firstFrameRendered; } + void setRenderCallback(std::function callback) { m_renderCallback = std::move(callback); } + static void handleConfigure( void* data, ext_session_lock_surface_v1* lockSurface, std::uint32_t serial, std::uint32_t width, std::uint32_t height @@ -69,6 +72,7 @@ class LockSurface : public Surface { private: void prepareFrame(bool needsUpdate, bool needsLayout); + void render() override; void applyWallpaperTexture(); void applyBlurredDesktopTexture(); void releaseWallpaperTextureRef(const std::string& path); @@ -119,4 +123,6 @@ class LockSurface : public Surface { bool m_authenticating = false; std::string m_outputKey; LockscreenWidgetsHost* m_widgetsHost = nullptr; + bool m_firstFrameRendered = false; + std::function m_renderCallback; }; From bd0f382e52f9621e4c267e61f253319468b736ae Mon Sep 17 00:00:00 2001 From: Aitor42 Date: Tue, 23 Jun 2026 20:58:59 +0200 Subject: [PATCH 2/4] fix(session): call render callback once and handle empty instances --- src/shell/lockscreen/lock_screen.cpp | 3 +++ src/shell/lockscreen/lock_surface.cpp | 8 +++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/shell/lockscreen/lock_screen.cpp b/src/shell/lockscreen/lock_screen.cpp index e8d96275cf..b10ad2bef4 100644 --- a/src/shell/lockscreen/lock_screen.cpp +++ b/src/shell/lockscreen/lock_screen.cpp @@ -457,6 +457,9 @@ bool LockScreen::shouldUseBlurredDesktop() const { } bool LockScreen::allSurfacesReady() const { + if (m_instances.empty()) { + return false; + } for (const auto& instance : m_instances) { if (instance.surface != nullptr && !instance.surface->firstFrameRendered()) { return false; diff --git a/src/shell/lockscreen/lock_surface.cpp b/src/shell/lockscreen/lock_surface.cpp index 96ea987c1d..f6fdd98bfe 100644 --- a/src/shell/lockscreen/lock_surface.cpp +++ b/src/shell/lockscreen/lock_surface.cpp @@ -833,8 +833,10 @@ void LockSurface::onGpuResourcesInvalidated() { void LockSurface::render() { Surface::render(); - m_firstFrameRendered = true; - if (m_renderCallback) { - m_renderCallback(); + if (!m_firstFrameRendered) { + m_firstFrameRendered = true; + if (m_renderCallback) { + m_renderCallback(); + } } } From adccdef76c330bd517e473dcebdf05ebef367e55 Mon Sep 17 00:00:00 2001 From: Aitor42 Date: Thu, 25 Jun 2026 14:38:21 +0200 Subject: [PATCH 3/4] fix(session): add fallback timeout for lock suspend and deduplicate readiness flush --- src/shell/lockscreen/lock_screen.cpp | 43 +++++++++++++++++---------- src/shell/lockscreen/lock_screen.h | 3 ++ src/shell/lockscreen/lock_surface.cpp | 1 + src/shell/lockscreen/lock_surface.h | 4 ++- 4 files changed, 35 insertions(+), 16 deletions(-) diff --git a/src/shell/lockscreen/lock_screen.cpp b/src/shell/lockscreen/lock_screen.cpp index b10ad2bef4..6c8da81964 100644 --- a/src/shell/lockscreen/lock_screen.cpp +++ b/src/shell/lockscreen/lock_screen.cpp @@ -152,6 +152,7 @@ void LockScreen::unlock() { } m_pendingAfterLocked = {}; + m_suspendTimeoutTimer.stop(); invalidatePendingAuthentication(); stopFingerprint(); @@ -350,15 +351,25 @@ bool LockScreen::isActive() const noexcept { return m_lockPending || m_locked; } bool LockScreen::isSessionLocked() const noexcept { return m_locked; } +bool LockScreen::tryFlushPendingAfterLocked() { + if (m_locked && m_pendingAfterLocked && allSurfacesReady()) { + auto pending = std::move(m_pendingAfterLocked); + m_pendingAfterLocked = {}; + m_suspendTimeoutTimer.stop(); + DeferredCall::callLater(std::move(pending)); + return true; + } + return false; +} + void LockScreen::runAfterSessionLocked(std::function fn) { if (fn == nullptr) { return; } - if (m_locked && allSurfacesReady()) { - DeferredCall::callLater(std::move(fn)); + m_pendingAfterLocked = std::move(fn); + if (tryFlushPendingAfterLocked()) { return; } - m_pendingAfterLocked = std::move(fn); if (isActive()) { return; } @@ -382,17 +393,24 @@ void LockScreen::handleLocked(void* data, ext_session_lock_v1* /*lock*/) { instance.surface->setLockedState(true); instance.surface->setOnLogin([self]() { self->tryAuthenticate(); }); } + + // Start the fallback timer (3 seconds) to trigger suspend anyway if surfaces take too long to render + self->m_suspendTimeoutTimer.start(std::chrono::seconds(3), [self]() { + if (self->m_pendingAfterLocked) { + kLog.warn("Lock screen surfaces took too long to render; suspending fallback triggered"); + auto pending = std::move(self->m_pendingAfterLocked); + self->m_pendingAfterLocked = {}; + DeferredCall::callLater(std::move(pending)); + } + }); + self->updatePromptOnSurfaces(); self->startFingerprint(); kLog.info("session is locked"); if (self->m_onSessionLocked) { self->m_onSessionLocked(); } - if (self->m_pendingAfterLocked && self->allSurfacesReady()) { - auto pending = std::move(self->m_pendingAfterLocked); - self->m_pendingAfterLocked = {}; - DeferredCall::callLater(std::move(pending)); - } + self->tryFlushPendingAfterLocked(); } void LockScreen::handleFinished(void* data, ext_session_lock_v1* /*lock*/) { @@ -621,13 +639,7 @@ void LockScreen::createInstance(const WaylandOutput& output) { surface->setDesktopCapture(std::move(captureIt->second)); m_desktopCaptures.erase(captureIt); } - surface->setRenderCallback([this]() { - if (m_locked && m_pendingAfterLocked && allSurfacesReady()) { - auto pending = std::move(m_pendingAfterLocked); - m_pendingAfterLocked = {}; - DeferredCall::callLater(std::move(pending)); - } - }); + surface->setRenderCallback([this]() { tryFlushPendingAfterLocked(); }); surface->setOnLogin([this]() { tryAuthenticate(); }); surface->setOnPasswordChanged([this](const std::string& value) { handlePasswordEdited(value); }); surface->setPromptState(m_user, m_password, m_status, m_statusIsError, m_authenticating); @@ -651,6 +663,7 @@ void LockScreen::createInstance(const WaylandOutput& output) { void LockScreen::resetLockState() { m_pendingAfterLocked = {}; + m_suspendTimeoutTimer.stop(); m_lockDeferred = false; if (m_lock == nullptr) { m_lockPending = false; diff --git a/src/shell/lockscreen/lock_screen.h b/src/shell/lockscreen/lock_screen.h index ed02f83fda..c9519c3ca4 100644 --- a/src/shell/lockscreen/lock_screen.h +++ b/src/shell/lockscreen/lock_screen.h @@ -2,6 +2,7 @@ #include "auth/pam_authenticator.h" #include "capture/screencopy_capture.h" +#include "core/timer_manager.h" #include #include @@ -82,6 +83,7 @@ class LockScreen { void captureDesktopSnapshots(); [[nodiscard]] bool shouldUseBlurredDesktop() const; [[nodiscard]] bool allSurfacesReady() const; + bool tryFlushPendingAfterLocked(); void applyLockscreenStyle(LockSurface& surface) const; void applyOutputRestriction(); void applyWallpaperStyleToSurfaces(); @@ -125,4 +127,5 @@ class LockScreen { std::function m_onSessionLocked; std::function m_onSessionUnlocked; std::function m_onLockEngaged; + Timer m_suspendTimeoutTimer; }; diff --git a/src/shell/lockscreen/lock_surface.cpp b/src/shell/lockscreen/lock_surface.cpp index f6fdd98bfe..e193e51133 100644 --- a/src/shell/lockscreen/lock_surface.cpp +++ b/src/shell/lockscreen/lock_surface.cpp @@ -406,6 +406,7 @@ void LockSurface::handleConfigure( std::uint32_t height ) { auto* self = static_cast(data); + self->m_firstFrameRendered = false; ext_session_lock_surface_v1_ack_configure(lockSurface, serial); self->Surface::onConfigure(width, height); } diff --git a/src/shell/lockscreen/lock_surface.h b/src/shell/lockscreen/lock_surface.h index 08c0c6d866..57c3a21cbf 100644 --- a/src/shell/lockscreen/lock_surface.h +++ b/src/shell/lockscreen/lock_surface.h @@ -70,9 +70,11 @@ class LockSurface : public Surface { std::uint32_t height ); +protected: + void render() override; + private: void prepareFrame(bool needsUpdate, bool needsLayout); - void render() override; void applyWallpaperTexture(); void applyBlurredDesktopTexture(); void releaseWallpaperTextureRef(const std::string& path); From d259388b945b8371c4cf6560a8eda608711258e0 Mon Sep 17 00:00:00 2001 From: Aitor42 Date: Thu, 25 Jun 2026 14:44:35 +0200 Subject: [PATCH 4/4] fix(session): only reset frame render status when configure changes geometry --- src/shell/lockscreen/lock_surface.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/shell/lockscreen/lock_surface.cpp b/src/shell/lockscreen/lock_surface.cpp index e193e51133..d16fcd255c 100644 --- a/src/shell/lockscreen/lock_surface.cpp +++ b/src/shell/lockscreen/lock_surface.cpp @@ -406,7 +406,9 @@ void LockSurface::handleConfigure( std::uint32_t height ) { auto* self = static_cast(data); - self->m_firstFrameRendered = false; + if (self->width() != width || self->height() != height) { + self->m_firstFrameRendered = false; + } ext_session_lock_surface_v1_ack_configure(lockSurface, serial); self->Surface::onConfigure(width, height); }