platforms/wayland: Delay setup until first configure

As per the xdg-shell specification, the compositor can only attach
a wl_buffer to an xdg_surface once the compositor has sent an
xdg_surface.configure event. Previously, there was a race condition
in which WaylandOutput objects were added/enabled on creation, making
it possible for the EglWaylandOutputs to be set up and for
EglWaylandBackend to draw a frame (thereby attaching a buffer), all
before the xdg_surface.configure configure event was received.
Compositors other than kwin would then send an unconfigured buffer
error and close the connection.

This commit avoids the problem by not marking the WaylandBackend
ready for use until all the output surfaces constructed at startup
have received their initial configure event. (In practice, this
takes one additional roundtrip.)
This commit is contained in:
Manuel Stoeckl 2021-08-07 22:30:14 -04:00 committed by David Edmundson
parent 1bf901cfbf
commit 9be3513348
4 changed files with 32 additions and 7 deletions

View file

@ -739,12 +739,11 @@ void WaylandBackend::createOutputs()
});
logicalWidthSum += logicalWidth;
m_outputs << waylandOutput;
Q_EMIT outputAdded(waylandOutput);
Q_EMIT outputEnabled(waylandOutput);
// The output will only actually be added when it receives its first
// configure event, and buffers can start being attached
m_pendingInitialOutputs++;
}
setReady(true);
Q_EMIT screensQueried();
}
void WaylandBackend::destroyOutputs()
@ -852,6 +851,22 @@ Outputs WaylandBackend::enabledOutputs() const
return m_outputs;
}
void WaylandBackend::addConfiguredOutput(WaylandOutput *output)
{
m_outputs << output;
Q_EMIT outputAdded(output);
Q_EMIT outputEnabled(output);
m_pendingInitialOutputs--;
if (m_pendingInitialOutputs == 0) {
// Mark as ready once all the initial set of screens has arrived
// (i.e, received their first configure and it is now safe to commit
// buffers to them)
setReady(true);
Q_EMIT screensQueried();
}
}
DmaBufTexture *WaylandBackend::createDmaBufTexture(const QSize& size)
{
#if HAVE_GBM && HAVE_WAYLAND_EGL

View file

@ -200,6 +200,7 @@ public:
QVector<WaylandOutput*> waylandOutputs() const {
return m_outputs;
}
void addConfiguredOutput(WaylandOutput *output);
void createDpmsFilter();
void clearDpmsFilter();
@ -235,6 +236,7 @@ private:
QThread *m_connectionThread;
QVector<WaylandOutput*> m_outputs;
int m_pendingInitialOutputs = 0;
WaylandCursor *m_waylandCursor = nullptr;

View file

@ -145,11 +145,18 @@ XdgShellOutput::~XdgShellOutput()
void XdgShellOutput::handleConfigure(const QSize &size, XdgShellSurface::States states, quint32 serial)
{
Q_UNUSED(states);
m_xdgShellSurface->ackConfigure(serial);
if (size.width() > 0 && size.height() > 0) {
setGeometry(geometry().topLeft(), size);
Q_EMIT sizeChanged(size);
if (m_hasBeenConfigured) {
Q_EMIT sizeChanged(size);
}
}
if (!m_hasBeenConfigured) {
m_hasBeenConfigured = true;
backend()->addConfiguredOutput(this);
}
m_xdgShellSurface->ackConfigure(serial);
}
void XdgShellOutput::updateWindowTitle()

View file

@ -110,6 +110,7 @@ private:
int m_number;
KWayland::Client::LockedPointer *m_pointerLock = nullptr;
bool m_hasPointerLock = false;
bool m_hasBeenConfigured = false;
};
}