Skip to content

Commit 79f088d

Browse files
committed
wayland: resolve session lock crashes on unlock, sleep, and output changes
- Destroys Wayland lock surface roles synchronously prior to sending unlock_and_destroy. - Avoids creating lock surfaces on invalid/placeholder/removed outputs to prevent early initialization failures. - Checks and stops server-side destruction of surfaces if the compositor has already unlocked or if the target output is invalid/placeholder. - Fixes Qt 6.6.3 compilation compatibility when QPointer uses incomplete forward-declared types. - Reverts memory cleanup to asynchronous deleteLater() for QML safety.
1 parent b66495f commit 79f088d

6 files changed

Lines changed: 67 additions & 9 deletions

File tree

changelog/next.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@
44
- Fixed WlSessionLockSurface.visible crashing if accessed before backing surface creation.
55
- Fixed mpris players returning `rate` for `minRate` and `maxRate`.
66
- Fixed missing/wrong change signals on various properties.
7+
- Fixed multiple session lock client crashes on sleep, wake, DPMS, and unlocking.

src/wayland/session_lock.cpp

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,12 @@ void WlSessionLock::updateSurfaces(bool show, WlSessionLock* old) {
5656
auto screens = QGuiApplication::screens();
5757

5858
screens.removeIf([](QScreen* screen) {
59-
if (dynamic_cast<QtWaylandClient::QWaylandScreen*>(screen->handle()) == nullptr) {
59+
auto* waylandScreen = dynamic_cast<QtWaylandClient::QWaylandScreen*>(screen->handle());
60+
if (waylandScreen == nullptr || waylandScreen->isPlaceholder()
61+
|| waylandScreen->output() == nullptr)
62+
{
6063
qDebug() << "Not creating lock surface for screen" << screen
61-
<< "as it is not backed by a wayland screen.";
64+
<< "as it is not backed by a valid wayland output.";
6265

6366
return true;
6467
}
@@ -139,14 +142,20 @@ void WlSessionLock::realizeLockTarget(WlSessionLock* old) {
139142
void WlSessionLock::unlock() {
140143
if (this->isLocked()) {
141144
this->lockTarget = false;
142-
this->manager->unlock();
143145

144146
for (auto* surface: this->surfaces) {
145-
surface->deleteLater();
147+
if (surface->ext != nullptr) {
148+
surface->ext->destroySurface();
149+
}
146150
}
147151

152+
for (auto* surface: this->surfaces) {
153+
surface->deleteLater();
154+
}
148155
this->surfaces.clear();
149156

157+
this->manager->unlock();
158+
150159
emit this->lockStateChanged();
151160
}
152161
}
@@ -206,7 +215,12 @@ WlSessionLockSurface::WlSessionLockSurface(QObject* parent)
206215
}
207216

208217
WlSessionLockSurface::~WlSessionLockSurface() {
218+
if (this->ext != nullptr) {
219+
this->ext->destroySurface();
220+
}
221+
209222
if (this->window != nullptr) {
223+
this->window->destroy();
210224
this->window->deleteLater();
211225
}
212226
}

src/wayland/session_lock.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,4 +185,6 @@ private slots:
185185
QScreen* mScreen = nullptr;
186186
QColor mColor = Qt::white;
187187
LockWindowExtension* ext;
188+
189+
friend class WlSessionLock;
188190
};

src/wayland/session_lock/session_lock.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <qlogging.h>
55
#include <qobject.h>
66
#include <qwindow.h>
7+
#include <wayland-client.h> // NOLINT(misc-include-cleaner)
78

89
#include "lock.hpp"
910
#include "manager.hpp"
@@ -117,3 +118,13 @@ void LockWindowExtension::setVisible() {
117118
if (this->surface == nullptr) this->immediatelyVisible = true;
118119
else this->surface->setVisible();
119120
}
121+
122+
void LockWindowExtension::destroySurface() {
123+
if (this->surface != nullptr) {
124+
auto* lockObj = qobject_cast<QSWaylandSessionLock*>(this->lock.data());
125+
if (lockObj != nullptr && lockObj->active()) {
126+
this->surface->destroy();
127+
this->surface = nullptr;
128+
}
129+
}
130+
}

src/wayland/session_lock/session_lock.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#pragma once
22

33
#include <qobject.h>
4+
#include <qpointer.h>
45
#include <qtclasshelpermacros.h>
56
#include <qtmetamacros.h>
67
#include <qwindow.h>
@@ -73,6 +74,7 @@ class LockWindowExtension: public QObject {
7374
// This must be called in place of QWindow::setVisible. Calling QWindow::setVisible will result in a crash.
7475
// To make a window invisible, destroy it as it cannot be recovered.
7576
void setVisible();
77+
void destroySurface();
7678

7779
static LockWindowExtension* get(QWindow* window);
7880

@@ -87,7 +89,7 @@ class LockWindowExtension: public QObject {
8789

8890
private:
8991
QSWaylandSessionLockSurface* surface = nullptr;
90-
QSWaylandSessionLock* lock = nullptr;
92+
QPointer<QObject> lock = nullptr;
9193
bool immediatelyVisible = false;
9294

9395
friend class QSWaylandSessionLockSurface;

src/wayland/session_lock/surface.cpp

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ QSWaylandSessionLockSurface::QSWaylandSessionLockSurface(QtWaylandClient::QWayla
2121
qFatal() << "QSWaylandSessionLockSurface created with null LockWindowExtension";
2222
}
2323

24-
if (this->ext->lock == nullptr) {
24+
auto* lock = qobject_cast<QSWaylandSessionLock*>(this->ext->lock.data());
25+
if (lock == nullptr) {
2526
qFatal() << "QSWaylandSessionLock for QSWaylandSessionLockSurface died";
2627
}
2728

@@ -34,12 +35,39 @@ QSWaylandSessionLockSurface::QSWaylandSessionLockSurface(QtWaylandClient::QWayla
3435
qFatal() << "Session lock screen does not correspond to a real screen. Force closing window";
3536
}
3637

37-
this->init(this->ext->lock->get_lock_surface(window->waylandSurface()->object(), output));
38+
this->init(lock->get_lock_surface(window->waylandSurface()->object(), output));
3839
}
3940

4041
QSWaylandSessionLockSurface::~QSWaylandSessionLockSurface() {
41-
if (this->ext != nullptr) this->ext->surface = nullptr;
42-
this->destroy();
42+
if (this->object() == nullptr) {
43+
return;
44+
}
45+
46+
bool shouldDestroyOnServer = true;
47+
48+
if (this->ext != nullptr) {
49+
this->ext->surface = nullptr;
50+
auto* lock = qobject_cast<QSWaylandSessionLock*>(this->ext->lock.data());
51+
if (lock == nullptr || !lock->active()) {
52+
shouldDestroyOnServer = false;
53+
} else {
54+
auto* qwindow = this->window() ? this->window()->window() : nullptr;
55+
auto* screen = qwindow ? qwindow->screen() : nullptr;
56+
auto* waylandScreen =
57+
screen ? dynamic_cast<QtWaylandClient::QWaylandScreen*>(screen->handle()) : nullptr;
58+
if (waylandScreen == nullptr || waylandScreen->isPlaceholder()
59+
|| waylandScreen->output() == nullptr)
60+
{
61+
shouldDestroyOnServer = false;
62+
}
63+
}
64+
} else {
65+
shouldDestroyOnServer = false;
66+
}
67+
68+
if (shouldDestroyOnServer) {
69+
this->destroy();
70+
}
4371
}
4472

4573
bool QSWaylandSessionLockSurface::isExposed() const { return this->configured; }

0 commit comments

Comments
 (0)