Skip to content

Commit aeb347b

Browse files
committed
wayland/toplevel: add pending state for outputs entered before qscreen init
Fixes a crash in sway, and potentially other compositors, when a toplevel enters an output before Qt has created a QScreen for it.
1 parent c3ed3b0 commit aeb347b

File tree

2 files changed

+65
-4
lines changed

2 files changed

+65
-4
lines changed

src/wayland/toplevel_management/handle.cpp

Lines changed: 63 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <private/qwaylandscreen_p.h>
88
#include <private/qwaylandwindow_p.h>
99
#include <qcontainerfwd.h>
10+
#include <qguiapplication.h>
1011
#include <qlogging.h>
1112
#include <qloggingcategory.h>
1213
#include <qobject.h>
@@ -181,22 +182,58 @@ void ToplevelHandle::zwlr_foreign_toplevel_handle_v1_state(wl_array* stateArray)
181182

182183
void ToplevelHandle::zwlr_foreign_toplevel_handle_v1_output_enter(wl_output* output) {
183184
auto* display = QtWaylandClient::QWaylandIntegration::instance()->display();
184-
auto* screen = display->screenForOutput(output)->screen();
185+
186+
auto* platformScreen = display->screenForOutput(output);
187+
if (!platformScreen) {
188+
qCDebug(logToplevelManagement) << this << "got pending output enter" << output;
189+
190+
if (this->mPendingVisibleScreens.isEmpty()) {
191+
QObject::connect(
192+
static_cast<QGuiApplication*>(QGuiApplication::instance()), // NOLINT
193+
&QGuiApplication::screenAdded,
194+
this,
195+
&ToplevelHandle::onScreenAdded
196+
);
197+
}
198+
199+
this->mPendingVisibleScreens.append(output);
200+
return;
201+
}
202+
203+
auto* screen = platformScreen->screen();
185204

186205
qCDebug(logToplevelManagement) << this << "got output enter" << screen;
187206

188-
this->mVisibleScreens.push_back(screen);
207+
this->mVisibleScreens.append(screen);
189208
emit this->visibleScreenAdded(screen);
190209
}
191210

192211
void ToplevelHandle::zwlr_foreign_toplevel_handle_v1_output_leave(wl_output* output) {
193212
auto* display = QtWaylandClient::QWaylandIntegration::instance()->display();
194-
auto* screen = display->screenForOutput(output)->screen();
213+
auto* platformScreen = display->screenForOutput(output);
214+
215+
if (!this->mPendingVisibleScreens.isEmpty()) {
216+
this->mPendingVisibleScreens.removeOne(output);
217+
218+
if (this->mPendingVisibleScreens.isEmpty()) {
219+
qCDebug(logToplevelManagement) << this << "got pending output leave" << output;
220+
221+
QObject::disconnect(
222+
static_cast<QGuiApplication*>(QGuiApplication::instance()), // NOLINT
223+
nullptr,
224+
this,
225+
nullptr
226+
);
227+
}
228+
}
229+
230+
if (!platformScreen) return;
231+
auto* screen = platformScreen->screen();
195232

196233
qCDebug(logToplevelManagement) << this << "got output leave" << screen;
197234

198-
emit this->visibleScreenRemoved(screen);
199235
this->mVisibleScreens.removeOne(screen);
236+
emit this->visibleScreenRemoved(screen);
200237
}
201238

202239
void ToplevelHandle::zwlr_foreign_toplevel_handle_v1_parent(
@@ -225,4 +262,26 @@ void ToplevelHandle::onParentClosed() {
225262
emit this->parentChanged();
226263
}
227264

265+
void ToplevelHandle::onScreenAdded(QScreen* screen) {
266+
auto* waylandScreen = dynamic_cast<QtWaylandClient::QWaylandScreen*>(screen->handle());
267+
if (!waylandScreen) return;
268+
269+
auto* output = waylandScreen->output();
270+
271+
if (this->mPendingVisibleScreens.removeOne(output)) {
272+
qCDebug(logToplevelManagement) << this << "got pending entered output init" << screen;
273+
this->mVisibleScreens.append(screen);
274+
emit this->visibleScreenAdded(screen);
275+
}
276+
277+
if (this->mPendingVisibleScreens.isEmpty()) {
278+
QObject::disconnect(
279+
static_cast<QGuiApplication*>(QGuiApplication::instance()), // NOLINT
280+
nullptr,
281+
this,
282+
nullptr
283+
);
284+
}
285+
}
286+
228287
} // namespace qs::wayland::toplevel_management::impl

src/wayland/toplevel_management/handle.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ class ToplevelHandle
5151
private slots:
5252
void onParentClosed();
5353
void onRectWindowDestroyed();
54+
void onScreenAdded(QScreen* screen);
5455

5556
private:
5657
void zwlr_foreign_toplevel_handle_v1_done() override;
@@ -66,6 +67,7 @@ private slots:
6667
QString mAppId;
6768
QString mTitle;
6869
QVector<QScreen*> mVisibleScreens;
70+
QVector<wl_output*> mPendingVisibleScreens;
6971
ToplevelHandle* mParent = nullptr;
7072
bool mActivated = false;
7173
bool mMaximized = false;

0 commit comments

Comments
 (0)