Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions src/surface/surfacewrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ void SurfaceWrapper::invalidate()
{
Q_ASSERT_X(!m_wrapperAboutToRemove, Q_FUNC_INFO, "Can't call `invalidate` twice!");
m_wrapperAboutToRemove = true;
m_pendingPrelaunchXWaylandStackSync = false;
Q_EMIT aboutToBeInvalidated();

if (!m_skipDockPreView)
Expand Down Expand Up @@ -479,6 +480,10 @@ void SurfaceWrapper::setup()
updateX11SkipFlags();
updateSizeCapabilities();
});
connect(xwaylandSurface,
&WXWaylandSurface::x11MapCompleted,
this,
&SurfaceWrapper::syncPrelaunchXWaylandStacking);
updateX11SkipFlags();
}
// Connect DConfig windowRadius change so QML bindings re-evaluate radius()
Expand Down Expand Up @@ -546,6 +551,8 @@ void SurfaceWrapper::setActivate(bool activate)

Q_ASSERT(!activate || hasActiveCapability());
m_isActivated = activate;
if (activate)
syncPrelaunchXWaylandStacking();

if (m_attention && m_isActivated)
setAttention(false);
Expand Down Expand Up @@ -732,6 +739,8 @@ void SurfaceWrapper::completeSplashTransition(const QSizeF &targetImplicitSize,
m_decoration->stackBefore(m_surfaceItem);
}

requestPrelaunchXWaylandStackSync();

m_surfaceItem->setVisible(true);
if (m_type == Type::XWayland
&& (m_surfaceState == State::Maximized || m_surfaceState == State::Fullscreen)) {
Expand Down Expand Up @@ -760,6 +769,51 @@ void SurfaceWrapper::completeSplashTransition(const QSizeF &targetImplicitSize,
updateHasActiveCapability(ActiveControlState::MappedOrSplash, surface() && surface()->mapped());
}

void SurfaceWrapper::requestPrelaunchXWaylandStackSync()
{
if (m_isProxy || m_type != Type::XWayland)
return;

auto *xwaylandSurface = qobject_cast<WXWaylandSurface *>(m_shellSurface);
if (!xwaylandSurface || xwaylandSurface->isBypassManager())
return;

m_pendingPrelaunchXWaylandStackSync = true;
if (!xwaylandSurface->isX11Mapped()) {
qCDebug(lcTlSurface)
<< "Deferring prelaunch XWayland stacking until X11 map completes for" << appId();
return;
}

syncPrelaunchXWaylandStacking();
}

void SurfaceWrapper::syncPrelaunchXWaylandStacking()
{
if (!m_pendingPrelaunchXWaylandStackSync)
return;

auto *xwaylandSurface = qobject_cast<WXWaylandSurface *>(m_shellSurface);
if (m_wrapperAboutToRemove || m_isProxy || m_type != Type::XWayland || !xwaylandSurface
|| xwaylandSurface->isBypassManager()) {
m_pendingPrelaunchXWaylandStackSync = false;
return;
}

if (!m_isActivated || !xwaylandSurface->isX11Mapped())
return;

// wlroots initially places a managed XWayland window at the bottom of the native X11
// stack. A prelaunch wrapper is already activated, so the normal activation path cannot
// observe a wrapper change and raise the newly attached X11 window. Synchronize both
// stacks after wlroots has finished handling XCB_MAP_NOTIFY.
stackToLast();
xwaylandSurface->restack(nullptr, WXWaylandSurface::XCB_STACK_MODE_ABOVE);
m_pendingPrelaunchXWaylandStackSync = false;
qCDebug(lcTlSurface)
<< "Synchronized active prelaunch XWayland stacking after X11 map for" << appId();
}

WSurface *SurfaceWrapper::surface() const
{
if (!m_shellSurface)
Expand Down
3 changes: 3 additions & 0 deletions src/surface/surfacewrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,8 @@ public Q_SLOTS:
void updateActivateCapability();
void updateFocusCapability();
void completeSplashTransition(const QSizeF &targetImplicitSize, bool hideDecoration = false);
void requestPrelaunchXWaylandStackSync();
void syncPrelaunchXWaylandStacking();

// wayland set by treeland-dde-shell, x11 set by bypassManager/windowTypes
void setSkipDockPreView(bool skip);
Expand Down Expand Up @@ -518,6 +520,7 @@ public Q_SLOTS:

bool m_socketEnabled{ false };
bool m_windowAnimationEnabled{ true };
bool m_pendingPrelaunchXWaylandStackSync{ false };
const QString m_appId;
};

Expand Down
2 changes: 2 additions & 0 deletions waylib/src/server/protocols/private/wxwaylandsurface_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ class Q_DECL_HIDDEN WXWaylandSurfacePrivate : public WToplevelSurfacePrivate
uint minimized:1;
uint fullscreen:1;
uint activated:1;
bool x11Mapped = false;
quint64 x11MapGeneration = 0;

private:
// XWayland owns this handle and destroys it after notifying the
Expand Down
36 changes: 32 additions & 4 deletions waylib/src/server/protocols/wxwayland.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ class Q_DECL_HIDDEN WXWaylandPrivate : public WWaylandResourcePrivate

void xcbPollReplies();
void xcbAsyncTimeoutForWindow(xcb_window_t windowId);
void setX11Mapped(xcb_window_t windowId, bool mapped);

W_DECLARE_PUBLIC(WXWayland)

Expand All @@ -89,17 +90,30 @@ bool xwayland_user_event_handler(wlr_xwayland *xwayland, xcb_generic_event_t *ev
return false;

const uint8_t response_type = event->response_type & ~0x80;
if (response_type != XCB_PROPERTY_NOTIFY)
return false;

auto *pe = reinterpret_cast<const xcb_property_notify_event_t *>(event);
auto *self = WXWayland::fromHandle(xwayland);

if (!self)
return false;

auto *d = self->d_func();

if (response_type == XCB_MAP_NOTIFY) {
auto *me = reinterpret_cast<const xcb_map_notify_event_t *>(event);
d->setX11Mapped(me->window, true);
return false;
}

if (response_type == XCB_UNMAP_NOTIFY) {
auto *ue = reinterpret_cast<const xcb_unmap_notify_event_t *>(event);
d->setX11Mapped(ue->window, false);
return false;
}

if (response_type != XCB_PROPERTY_NOTIFY)
return false;

auto *pe = reinterpret_cast<const xcb_property_notify_event_t *>(event);

// Trigger async property reading infrastructure if this window is being tracked.
if (!d->asyncProps.isEmpty()) {
d->xcbPollReplies();
Expand Down Expand Up @@ -139,6 +153,20 @@ bool xwayland_user_event_handler(wlr_xwayland *xwayland, xcb_generic_event_t *ev
return false;
}

void WXWaylandPrivate::setX11Mapped(xcb_window_t windowId, bool mapped)
{
for (auto *surface : std::as_const(surfaceList)) {
QPointer<WXWaylandSurface> guard(surface);
if (!guard)
continue;
if (guard->handle()->window_id != windowId)
continue;

guard->setX11Mapped(mapped);
break;
}
}

void WXWaylandPrivate::init()
{
W_Q(WXWayland);
Expand Down
37 changes: 37 additions & 0 deletions waylib/src/server/protocols/wxwaylandsurface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ void WXWaylandSurfacePrivate::init()
});
q->listeners()->add(&m_handle->events.dissociate, this, [this, q] (void *) {
Q_ASSERT(surface);
q->setX11Mapped(false);
Q_EMIT q->aboutToDissociate();
delete surface;
surface = nullptr;
Expand Down Expand Up @@ -486,6 +487,12 @@ bool WXWaylandSurface::isBypassManager() const
return d->handle()->override_redirect;
}

bool WXWaylandSurface::isX11Mapped() const
{
W_DC(WXWaylandSurface);
return d->x11Mapped;
}

WXWaylandSurface::WindowTypes WXWaylandSurface::windowTypes() const
{
W_DC(WXWaylandSurface);
Expand Down Expand Up @@ -605,4 +612,34 @@ void WXWaylandSurface::restack(WXWaylandSurface *sibling, StackMode mode)
wlr_xwayland_surface_restack(handle(), nullptr, static_cast<xcb_stack_mode_t>(mode));
}

void WXWaylandSurface::setX11Mapped(bool mapped)
{
W_D(WXWaylandSurface);

if (d->x11Mapped == mapped)
return;

d->x11Mapped = mapped;
const quint64 generation = ++d->x11MapGeneration;
if (!mapped)
return;

// wlroots invokes the user event handler before its own XCB_MAP_NOTIFY handler.
// Defer notification so consumers run after wlroots performs the initial X11 restack.
QPointer<WXWaylandSurface> guard(this);
QMetaObject::invokeMethod(
this,
[guard, generation] {
if (!guard)
return;

const auto *d = guard->d_func();
if (!d->x11Mapped || d->x11MapGeneration != generation)
return;

Q_EMIT guard->x11MapCompleted();
},
Qt::QueuedConnection);
}

WAYLIB_SERVER_END_NAMESPACE
8 changes: 8 additions & 0 deletions waylib/src/server/protocols/wxwaylandsurface.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
WAYLIB_SERVER_BEGIN_NAMESPACE

class WXWayland;
class WXWaylandPrivate;
class WSeat;
class WXWaylandSurfacePrivate;
class WAYLIB_SERVER_EXPORT WXWaylandSurface : public WToplevelSurface
Expand Down Expand Up @@ -108,6 +109,7 @@ class WAYLIB_SERVER_EXPORT WXWaylandSurface : public WToplevelSurface
ConfigureFlags requestConfigureFlags() const;

bool isBypassManager() const;
bool isX11Mapped() const;
WindowTypes windowTypes() const;
DecorationsFlags decorationsFlags() const;

Expand All @@ -127,6 +129,8 @@ public Q_SLOTS:
void associated();
// Emitted before WXWaylandSurfacePrivate handles notify_dissociate cleanup.
void aboutToDissociate();
// Emitted asynchronously after wlroots finishes handling XCB_MAP_NOTIFY.
void x11MapCompleted();

void parentXWaylandSurfaceChanged();
void childrenChanged();
Expand All @@ -139,6 +143,10 @@ public Q_SLOTS:

void requestConfigure(QRect geometry, ConfigureFlags flags);
void requestActivate();

private:
friend class WXWaylandPrivate;
void setX11Mapped(bool mapped);
};

WAYLIB_SERVER_END_NAMESPACE
Expand Down
Loading