mirror of
https://github.com/quickshell-mirror/quickshell.git
synced 2026-10-27 02:54:36 +00:00
wayland/lock: guard against reentrancy during surface creation
This commit is contained in:
4 files changed
+59
-41
No files matched your search
@@ -1,3 +1,4 @@
|
||||
## Bug Fixes
|
||||
|
||||
- Fixed main process crashes on pam subprocess misbehavior.
|
||||
- Fixed crashes when attempting to create or modify session locks reentrantly.
|
||||
@@ -113,70 +113,84 @@ void WlSessionLock::updateSurfaces(bool show, WlSessionLock* old) {
|
||||
}
|
||||
|
||||
void WlSessionLock::realizeLockTarget(WlSessionLock* old) {
|
||||
if (this->lockTarget) {
|
||||
if (!SessionLockManager::lockAvailable()) {
|
||||
qCritical() << "Cannot start session lock: The current compositor does not support the "
|
||||
"ext-session-lock-v1 protocol.";
|
||||
this->unlock();
|
||||
return;
|
||||
if (this->realizing || !this->manager) {
|
||||
this->awaitingRealization = true;
|
||||
return;
|
||||
}
|
||||
|
||||
this->realizing = true;
|
||||
|
||||
[&, this]() {
|
||||
if (this->lockTarget) {
|
||||
if (!SessionLockManager::lockAvailable()) {
|
||||
qCritical() << "Cannot start session lock: The current compositor does not support the "
|
||||
"ext-session-lock-v1 protocol.";
|
||||
this->unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->mSurfaceComponent == nullptr) {
|
||||
qWarning() << "WlSessionLock.surface is null. Aborting lock.";
|
||||
this->unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
auto wasLocked = this->isLocked();
|
||||
if (!this->manager->lock()) {
|
||||
qWarning() << "Failed to acquire session lock.";
|
||||
this->unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
this->updateSurfaces(true, old);
|
||||
if (!wasLocked) emit this->lockStateChanged();
|
||||
} else {
|
||||
this->unlock(); // emits lockStateChanged
|
||||
}
|
||||
}();
|
||||
|
||||
if (this->mSurfaceComponent == nullptr) {
|
||||
qWarning() << "WlSessionLock.surface is null. Aborting lock.";
|
||||
this->unlock();
|
||||
return;
|
||||
}
|
||||
this->realizing = false;
|
||||
|
||||
// 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 (!this->manager->lock()) this->lockTarget = false;
|
||||
|
||||
this->updateSurfaces(true, old);
|
||||
} else {
|
||||
this->unlock(); // emits lockStateChanged
|
||||
if (this->awaitingRealization) {
|
||||
this->awaitingRealization = false;
|
||||
this->realizeLockTarget();
|
||||
}
|
||||
}
|
||||
|
||||
void WlSessionLock::unlock() {
|
||||
if (this->isLocked()) {
|
||||
this->lockTarget = false;
|
||||
this->manager->unlock();
|
||||
this->lockTarget = false;
|
||||
|
||||
for (auto* surface: this->surfaces) {
|
||||
surface->deleteLater();
|
||||
}
|
||||
|
||||
this->surfaces.clear();
|
||||
|
||||
emit this->lockStateChanged();
|
||||
for (auto* surface: this->surfaces) {
|
||||
surface->deleteLater();
|
||||
}
|
||||
|
||||
this->surfaces.clear();
|
||||
|
||||
if (this->manager) this->manager->unlock();
|
||||
if (this->isLocked()) emit this->lockStateChanged();
|
||||
}
|
||||
|
||||
void WlSessionLock::onScreensChanged() {
|
||||
if (this->manager != nullptr && this->manager->isLocked()) {
|
||||
// Theoretically screens shouldnt be invalidated while realizing, instead in later event loop cycles.
|
||||
if (this->isLocked() && !this->realizing) {
|
||||
this->updateSurfaces(true);
|
||||
}
|
||||
}
|
||||
|
||||
bool WlSessionLock::isLocked() const {
|
||||
return this->manager == nullptr ? this->lockTarget : this->manager->isLocked();
|
||||
}
|
||||
bool WlSessionLock::isLocked() const { return this->manager && this->manager->isLocked(); }
|
||||
|
||||
bool WlSessionLock::isSecure() const {
|
||||
return this->manager != nullptr && SessionLockManager::isSecure();
|
||||
}
|
||||
|
||||
void WlSessionLock::setLocked(bool locked) {
|
||||
if (this->isLocked() == locked) return;
|
||||
if (this->lockTarget == locked) return;
|
||||
this->lockTarget = locked;
|
||||
|
||||
if (this->manager == nullptr) {
|
||||
emit this->lockStateChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
this->realizeLockTarget();
|
||||
}
|
||||
|
||||
|
||||
@@ -101,6 +101,8 @@ private:
|
||||
QQmlComponent* mSurfaceComponent = nullptr;
|
||||
QMap<QScreen*, WlSessionLockSurface*> surfaces;
|
||||
bool lockTarget = false;
|
||||
bool realizing = false;
|
||||
bool awaitingRealization = false;
|
||||
|
||||
friend class WlSessionLockSurface;
|
||||
};
|
||||
|
||||
@@ -25,7 +25,8 @@ QSWaylandSessionLockManager* manager() {
|
||||
bool SessionLockManager::lockAvailable() { return manager()->isActive(); }
|
||||
|
||||
bool SessionLockManager::lock() {
|
||||
if (this->isLocked() || SessionLockManager::sessionLocked()) return false;
|
||||
if (this->isLocked()) return true;
|
||||
if (SessionLockManager::sessionLocked()) return false;
|
||||
this->mLock = manager()->acquireLock();
|
||||
this->mLock->setParent(this);
|
||||
|
||||
|
||||
Reference in new issue
Block a user