wayland: Reset Toplevel::surfaceId after surface is created

Xwayland will re-create the wl_surface object if the X11 window is
unmapped and mapped. That, and the fact that the order in which the
WL_SURFACE_ID client message event is received and the wl_surface object
is created is undefined can cause the following bug:

* WL_SURFACE_ID is received
* the old wl_surface object is destroyed, m_surfaceId is reset to 0
* new wl_surface is created but because m_surfaceId is 0, it won't be
  associated with the x11 window

This change ensures that kwin will associate the wl_surface with x11
window by making it not reset cached surface id when the old wl_surface
is destroyed.

However, we cannot leave m_surfaceId as is because wayland aggressively
re-uses object ids so kwin can associate wrong surface with x11 window.

To prevent that, this change also makes Toplevel::setSurface() reset
cached surface id.

CCBUG: 442936
CCBUG: 426069
This commit is contained in:
Vlad Zahorodnii 2021-09-29 11:28:47 +03:00
parent 61eb8ce75b
commit 52c9dbb487
4 changed files with 9 additions and 22 deletions

View file

@ -1285,13 +1285,12 @@ void Toplevel::propertyNotifyEvent(xcb_property_notify_event_t *e)
void Toplevel::clientMessageEvent(xcb_client_message_event_t *e) void Toplevel::clientMessageEvent(xcb_client_message_event_t *e)
{ {
if (e->type == atoms->wl_surface_id) { if (e->type == atoms->wl_surface_id) {
m_surfaceId = e->data.data32[0]; m_pendingSurfaceId = e->data.data32[0];
if (auto w = waylandServer()) { if (auto w = waylandServer()) {
if (auto s = KWaylandServer::SurfaceInterface::get(m_surfaceId, w->xWaylandConnection())) { if (auto s = KWaylandServer::SurfaceInterface::get(m_pendingSurfaceId, w->xWaylandConnection())) {
setSurface(s); setSurface(s);
} }
} }
Q_EMIT surfaceIdChanged(m_surfaceId);
} }
} }

View file

@ -615,11 +615,10 @@ void Toplevel::setSurface(KWaylandServer::SurfaceInterface *surface)
return; return;
} }
m_surface = surface; m_surface = surface;
m_pendingSurfaceId = 0;
connect(m_surface, &KWaylandServer::SurfaceInterface::destroyed, this, [this]() { connect(m_surface, &KWaylandServer::SurfaceInterface::destroyed, this, [this]() {
m_surface = nullptr; m_surface = nullptr;
m_surfaceId = 0;
}); });
m_surfaceId = surface->id();
Q_EMIT surfaceChanged(); Q_EMIT surfaceChanged();
} }

View file

@ -255,12 +255,6 @@ class KWIN_EXPORT Toplevel : public QObject
*/ */
Q_PROPERTY(bool skipsCloseAnimation READ skipsCloseAnimation WRITE setSkipCloseAnimation NOTIFY skipCloseAnimationChanged) Q_PROPERTY(bool skipsCloseAnimation READ skipsCloseAnimation WRITE setSkipCloseAnimation NOTIFY skipCloseAnimationChanged)
/**
* The Id of the Wayland Surface associated with this Toplevel.
* On X11 only setups the value is @c 0.
*/
Q_PROPERTY(quint32 surfaceId READ surfaceId NOTIFY surfaceIdChanged)
/** /**
* Interface to the Wayland Surface. * Interface to the Wayland Surface.
* Relevant only in Wayland, in X11 it will be nullptr * Relevant only in Wayland, in X11 it will be nullptr
@ -494,7 +488,7 @@ public:
bool skipsCloseAnimation() const; bool skipsCloseAnimation() const;
void setSkipCloseAnimation(bool set); void setSkipCloseAnimation(bool set);
quint32 surfaceId() const; quint32 pendingSurfaceId() const;
KWaylandServer::SurfaceInterface *surface() const; KWaylandServer::SurfaceInterface *surface() const;
void setSurface(KWaylandServer::SurfaceInterface *surface); void setSurface(KWaylandServer::SurfaceInterface *surface);
@ -615,11 +609,6 @@ Q_SIGNALS:
* @since 5.0 * @since 5.0
*/ */
void windowClassChanged(); void windowClassChanged();
/**
* Emitted when a Wayland Surface gets associated with this Toplevel.
* @since 5.3
*/
void surfaceIdChanged(quint32);
/** /**
* @since 5.4 * @since 5.4
*/ */
@ -728,7 +717,7 @@ private:
mutable bool m_shapeRegionIsValid = false; mutable bool m_shapeRegionIsValid = false;
AbstractOutput *m_output = nullptr; AbstractOutput *m_output = nullptr;
bool m_skipCloseAnimation; bool m_skipCloseAnimation;
quint32 m_surfaceId = 0; quint32 m_pendingSurfaceId = 0;
KWaylandServer::SurfaceInterface *m_surface = nullptr; KWaylandServer::SurfaceInterface *m_surface = nullptr;
// when adding new data members, check also copyToDeleted() // when adding new data members, check also copyToDeleted()
qreal m_screenScale = 1.0; qreal m_screenScale = 1.0;
@ -969,9 +958,9 @@ inline const ClientMachine *Toplevel::clientMachine() const
return m_clientMachine; return m_clientMachine;
} }
inline quint32 Toplevel::surfaceId() const inline quint32 Toplevel::pendingSurfaceId() const
{ {
return m_surfaceId; return m_pendingSurfaceId;
} }
inline KWaylandServer::SurfaceInterface *Toplevel::surface() const inline KWaylandServer::SurfaceInterface *Toplevel::surface() const

View file

@ -403,7 +403,7 @@ bool WaylandServer::init(InitializationFlags flags)
} }
X11Client *client = ws->findClient([surface](const X11Client *client) { X11Client *client = ws->findClient([surface](const X11Client *client) {
return client->surfaceId() == surface->id(); return client->pendingSurfaceId() == surface->id();
}); });
if (client) { if (client) {
client->setSurface(surface); client->setSurface(surface);
@ -411,7 +411,7 @@ bool WaylandServer::init(InitializationFlags flags)
} }
Unmanaged *unmanaged = ws->findUnmanaged([surface](const Unmanaged *unmanaged) { Unmanaged *unmanaged = ws->findUnmanaged([surface](const Unmanaged *unmanaged) {
return unmanaged->surfaceId() == surface->id(); return unmanaged->pendingSurfaceId() == surface->id();
}); });
if (unmanaged) { if (unmanaged) {
unmanaged->setSurface(surface); unmanaged->setSurface(surface);