diff --git a/src/shell/lockscreen/lock_screen.cpp b/src/shell/lockscreen/lock_screen.cpp index 31cf0d729b..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) { - 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) { - 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*/) { @@ -456,6 +474,18 @@ bool LockScreen::shouldUseBlurredDesktop() const { && m_wayland->hasScreencopy(); } +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; + } + } + return true; +} + void LockScreen::primeDesktopCaptures() { if (m_configService != nullptr && !m_configService->isLockScreenEnabled()) { return; @@ -609,6 +639,7 @@ void LockScreen::createInstance(const WaylandOutput& output) { surface->setDesktopCapture(std::move(captureIt->second)); m_desktopCaptures.erase(captureIt); } + 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); @@ -632,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 ef7f33ad69..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 @@ -81,6 +82,8 @@ class LockScreen { void syncInstances(); void captureDesktopSnapshots(); [[nodiscard]] bool shouldUseBlurredDesktop() const; + [[nodiscard]] bool allSurfacesReady() const; + bool tryFlushPendingAfterLocked(); void applyLockscreenStyle(LockSurface& surface) const; void applyOutputRestriction(); void applyWallpaperStyleToSurfaces(); @@ -124,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 02cb63e187..d16fcd255c 100644 --- a/src/shell/lockscreen/lock_surface.cpp +++ b/src/shell/lockscreen/lock_surface.cpp @@ -406,6 +406,9 @@ void LockSurface::handleConfigure( std::uint32_t height ) { auto* self = static_cast(data); + 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); } @@ -830,3 +833,13 @@ void LockSurface::onGpuResourcesInvalidated() { m_wallpaperDirty = true; requestLayout(); } + +void LockSurface::render() { + Surface::render(); + if (!m_firstFrameRendered) { + 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..57c3a21cbf 100644 --- a/src/shell/lockscreen/lock_surface.h +++ b/src/shell/lockscreen/lock_surface.h @@ -62,11 +62,17 @@ 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 ); +protected: + void render() override; + private: void prepareFrame(bool needsUpdate, bool needsLayout); void applyWallpaperTexture(); @@ -119,4 +125,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; };