Client: Announce an output after receiving more complete state

Output initialization is not atomic, meaning that the compositor may
process a wl_output bind request in one event loop cycle, and the
xdg_output_manager.get_xdg_output in another event loop cycle.

This means that xdg_output properties may arrive in another wl_output
done frame. Prior to xdg-output v3, that wasn't an issue because the
compositor is required to send an xdg_output.done event after sending
xdg_output properties.

Starting with v3, the compositor may choose not to send an
xdg_output.done event after sending xdg_output properties. Therefore,
as is, QtWayland may announce an output with bad logical geometry or
even worse without name assigned by the compositor.

Unfortunately, that breaks applications such as plasmashell. Plasma uses
output names as a criterion to determine what kind of contents should be
displayed on a particular output.

In order to fix the initialization sequence, this change makes every
QWaylandScreen track processed events. After all required events have
been received, the screen can be announced to the rest of Qt.

Change-Id: If5da747edd7af277ec1364cbea105c6994f47402
Reviewed-by: David Edmundson <davidedmundson@kde.org>
This commit is contained in:
Vlad Zahorodnii 2021-05-05 20:49:26 +03:00
parent 6a244c5e13
commit 703af489cf
2 changed files with 31 additions and 11 deletions

View File

@ -71,7 +71,7 @@ QWaylandScreen::QWaylandScreen(QWaylandDisplay *waylandDisplay, int version, uin
qCWarning(lcQpaWayland) << "wl_output done event not supported by compositor,"
<< "QScreen may not work correctly";
mWaylandDisplay->forceRoundTrip(); // Give the compositor a chance to send geometry etc.
mOutputDone = true; // Fake the done event
mProcessedEvents |= OutputDoneEvent; // Fake the done event
maybeInitialize();
}
}
@ -82,14 +82,25 @@ QWaylandScreen::~QWaylandScreen()
zxdg_output_v1::destroy();
}
uint QWaylandScreen::requiredEvents() const
{
uint ret = OutputDoneEvent;
if (mWaylandDisplay->xdgOutputManager()) {
ret |= XdgOutputNameEvent;
if (mWaylandDisplay->xdgOutputManager()->version() < 3)
ret |= XdgOutputDoneEvent;
}
return ret;
}
void QWaylandScreen::maybeInitialize()
{
Q_ASSERT(!mInitialized);
if (!mOutputDone)
return;
if (mWaylandDisplay->xdgOutputManager() && !mXdgOutputDone)
const uint requiredEvents = this->requiredEvents();
if ((mProcessedEvents & requiredEvents) != requiredEvents)
return;
mInitialized = true;
@ -265,9 +276,8 @@ void QWaylandScreen::output_scale(int32_t factor)
void QWaylandScreen::output_done()
{
mOutputDone = true;
if (zxdg_output_v1::isInitialized() && mWaylandDisplay->xdgOutputManager()->version() >= 3)
mXdgOutputDone = true;
mProcessedEvents |= OutputDoneEvent;
if (mInitialized) {
updateOutputProperties();
if (zxdg_output_v1::isInitialized())
@ -328,7 +338,7 @@ void QWaylandScreen::zxdg_output_v1_done()
if (Q_UNLIKELY(mWaylandDisplay->xdgOutputManager()->version() >= 3))
qWarning(lcQpaWayland) << "zxdg_output_v1.done received on version 3 or newer, this is most likely a bug in the compositor";
mXdgOutputDone = true;
mProcessedEvents |= XdgOutputDoneEvent;
if (mInitialized)
updateXdgOutputProperties();
else
@ -337,7 +347,11 @@ void QWaylandScreen::zxdg_output_v1_done()
void QWaylandScreen::zxdg_output_v1_name(const QString &name)
{
if (Q_UNLIKELY(mInitialized))
qWarning(lcQpaWayland) << "zxdg_output_v1.name received after output has been initialized, this is most likely a bug in the compositor";
mOutputName = name;
mProcessedEvents |= XdgOutputNameEvent;
}
void QWaylandScreen::updateXdgOutputProperties()

View File

@ -111,6 +111,13 @@ public:
static QWaylandScreen *fromWlOutput(::wl_output *output);
protected:
enum Event : uint {
XdgOutputDoneEvent = 0x1,
OutputDoneEvent = 0x2,
XdgOutputNameEvent = 0x4,
};
uint requiredEvents() const;
void output_mode(uint32_t flags, int width, int height, int refresh) override;
void output_geometry(int32_t x, int32_t y,
int32_t width, int32_t height,
@ -143,8 +150,7 @@ protected:
QSize mPhysicalSize;
QString mOutputName;
Qt::ScreenOrientation m_orientation = Qt::PrimaryOrientation;
bool mOutputDone = false;
bool mXdgOutputDone = false;
uint mProcessedEvents = 0;
bool mInitialized = false;
};