From 8ec245ac667f2066a65b6fe89a9a2a7eff6df81b Mon Sep 17 00:00:00 2001 From: outfoxxed Date: Tue, 18 Jun 2024 20:34:16 -0700 Subject: [PATCH] wayland/lock: initialize lock content before starting lock Reduces any chances of the compositor displaying a blank frame first. --- src/wayland/session_lock.cpp | 127 +++++++++++++--------- src/wayland/session_lock.hpp | 3 +- src/wayland/session_lock/session_lock.cpp | 2 + src/wayland/session_lock/session_lock.hpp | 4 +- 4 files changed, 79 insertions(+), 57 deletions(-) diff --git a/src/wayland/session_lock.cpp b/src/wayland/session_lock.cpp index bb5ed13..251d7b8 100644 --- a/src/wayland/session_lock.cpp +++ b/src/wayland/session_lock.cpp @@ -46,60 +46,72 @@ void WlSessionLock::onReload(QObject* oldInstance) { } // clang-format on - if (this->lockTarget) { - if (!this->manager->lock()) this->lockTarget = false; - this->updateSurfaces(old); - } else { - this->setLocked(false); + this->realizeLockTarget(old); +} + +void WlSessionLock::updateSurfaces(bool show, WlSessionLock* old) { + auto screens = QGuiApplication::screens(); + + auto map = this->surfaces.toStdMap(); + for (auto& [screen, surface]: map) { + if (!screens.contains(screen)) { + this->surfaces.remove(screen); + surface->deleteLater(); + } + } + + for (auto* screen: screens) { + if (!this->surfaces.contains(screen)) { + auto* instanceObj = + this->mSurfaceComponent->create(QQmlEngine::contextForObject(this->mSurfaceComponent)); + auto* instance = qobject_cast(instanceObj); + + if (instance == nullptr) { + qWarning( + ) << "WlSessionLock.surface does not create a WlSessionLockSurface. Aborting lock."; + if (instanceObj != nullptr) instanceObj->deleteLater(); + this->unlock(); + return; + } + + instance->setParent(this); + instance->setScreen(screen); + + auto* oldInstance = old == nullptr ? nullptr : old->surfaces.value(screen, nullptr); + instance->reload(oldInstance); + + this->surfaces[screen] = instance; + } + } + + if (show) { + if (!this->manager->isLocked()) { + qFatal() << "Tried to show lockscreen surfaces without active lock"; + } + + for (auto* surface: this->surfaces.values()) { + surface->show(); + } } } -void WlSessionLock::updateSurfaces(WlSessionLock* old) { - if (this->manager->isLocked()) { - auto screens = QGuiApplication::screens(); - - auto map = this->surfaces.toStdMap(); - for (auto& [screen, surface]: map) { - if (!screens.contains(screen)) { - this->surfaces.remove(screen); - surface->deleteLater(); - } - } - +void WlSessionLock::realizeLockTarget(WlSessionLock* old) { + if (this->lockTarget) { if (this->mSurfaceComponent == nullptr) { qWarning() << "WlSessionLock.surface is null. Aborting lock."; this->unlock(); return; } - for (auto* screen: screens) { - if (!this->surfaces.contains(screen)) { - auto* instanceObj = - this->mSurfaceComponent->create(QQmlEngine::contextForObject(this->mSurfaceComponent)); - auto* instance = qobject_cast(instanceObj); + // preload initial surfaces to make the chance of the compositor displaying a blank + // frame before the lock surfaces are shown as low as possible. + this->updateSurfaces(false); - if (instance == nullptr) { - qWarning( - ) << "WlSessionLock.surface does not create a WlSessionLockSurface. Aborting lock."; - if (instanceObj != nullptr) instanceObj->deleteLater(); - this->unlock(); - return; - } + if (!this->manager->lock()) this->lockTarget = false; - instance->setParent(this); - instance->setScreen(screen); - - auto* oldInstance = old == nullptr ? nullptr : old->surfaces.value(screen, nullptr); - instance->reload(oldInstance); - instance->attach(); - - this->surfaces[screen] = instance; - } - - for (auto* surface: this->surfaces.values()) { - surface->show(); - } - } + this->updateSurfaces(true, old); + } else { + this->unlock(); // emits lockStateChanged } } @@ -118,7 +130,7 @@ void WlSessionLock::unlock() { } } -void WlSessionLock::onScreensChanged() { this->updateSurfaces(); } +void WlSessionLock::onScreensChanged() { this->updateSurfaces(true); } bool WlSessionLock::isLocked() const { return this->manager == nullptr ? this->lockTarget : this->manager->isLocked(); @@ -137,18 +149,17 @@ void WlSessionLock::setLocked(bool locked) { return; } - if (locked) { - if (!this->manager->lock()) this->lockTarget = false; - this->updateSurfaces(); - if (this->lockTarget) emit this->lockStateChanged(); - } else { - this->unlock(); // emits lockStateChanged - } + this->realizeLockTarget(); } QQmlComponent* WlSessionLock::surfaceComponent() const { return this->mSurfaceComponent; } void WlSessionLock::setSurfaceComponent(QQmlComponent* surfaceComponent) { + if (this->manager != nullptr && this->manager->isLocked()) { + qCritical() << "WlSessionLock.surfaceComponent cannot be changed while the lock is active."; + return; + } + if (this->mSurfaceComponent != nullptr) this->mSurfaceComponent->deleteLater(); if (surfaceComponent != nullptr) surfaceComponent->setParent(this); @@ -202,6 +213,8 @@ void WlSessionLockSurface::onReload(QObject* oldInstance) { } void WlSessionLockSurface::attach() { + if (this->ext->isAttached()) return; + if (auto* parent = qobject_cast(this->parent())) { if (!this->ext->attach(this->window, parent->manager)) { qFatal() << "Failed to attach WlSessionLockSurface"; @@ -220,7 +233,10 @@ QQuickWindow* WlSessionLockSurface::disownWindow() { return window; } -void WlSessionLockSurface::show() { this->ext->setVisible(); } +void WlSessionLockSurface::show() { + this->attach(); + this->ext->setVisible(); +} QQuickItem* WlSessionLockSurface::contentItem() const { return this->mContentItem; } @@ -257,8 +273,11 @@ void WlSessionLockSurface::setScreen(QScreen* qscreen) { QObject::connect(qscreen, &QObject::destroyed, this, &WlSessionLockSurface::onScreenDestroyed); } - if (this->window == nullptr) this->mScreen = qscreen; - else this->window->setScreen(qscreen); + if (this->window == nullptr) { + this->mScreen = qscreen; + } else { + this->window->setScreen(qscreen); + } emit this->screenChanged(); } diff --git a/src/wayland/session_lock.hpp b/src/wayland/session_lock.hpp index 9e35cd4..a44dae0 100644 --- a/src/wayland/session_lock.hpp +++ b/src/wayland/session_lock.hpp @@ -97,7 +97,8 @@ private slots: void onScreensChanged(); private: - void updateSurfaces(WlSessionLock* old = nullptr); + void updateSurfaces(bool show, WlSessionLock* old = nullptr); + void realizeLockTarget(WlSessionLock* old = nullptr); SessionLockManager* manager = nullptr; QQmlComponent* mSurfaceComponent = nullptr; diff --git a/src/wayland/session_lock/session_lock.cpp b/src/wayland/session_lock/session_lock.cpp index 1f2f1ce..50e8818 100644 --- a/src/wayland/session_lock/session_lock.cpp +++ b/src/wayland/session_lock/session_lock.cpp @@ -63,6 +63,8 @@ LockWindowExtension* LockWindowExtension::get(QWindow* window) { } } +bool LockWindowExtension::isAttached() const { return this->surface != nullptr; } + bool LockWindowExtension::attach(QWindow* window, SessionLockManager* manager) { if (this->surface != nullptr) qFatal() << "Cannot change the attached window of a LockWindowExtension"; diff --git a/src/wayland/session_lock/session_lock.hpp b/src/wayland/session_lock/session_lock.hpp index 7e3968b..1ad6ae9 100644 --- a/src/wayland/session_lock/session_lock.hpp +++ b/src/wayland/session_lock/session_lock.hpp @@ -61,6 +61,8 @@ public: ~LockWindowExtension() override; Q_DISABLE_COPY_MOVE(LockWindowExtension); + [[nodiscard]] bool isAttached() const; + // Attach this lock extension to the given window. // The extension is reparented to the window and replaces any existing lock extension. // Returns false if the window cannot be used. @@ -70,8 +72,6 @@ public: // To make a window invisible, destroy it as it cannot be recovered. void setVisible(); - [[nodiscard]] bool isLocked() const; - static LockWindowExtension* get(QWindow* window); signals: