wayland: Reset surface on QWindow type change

Changing the role between toplevel and popups is illegal on wayland even
if the window is not visible. We need to reset to the wl_surface.

This meant we cannot call setWindowFlags in the constructor.

QWindow::flags / QWindow::windowType changes after
QPlatformWindow::setFlags has finished, so we need to use our own flags
rather in XdgShell rather than pulling them from the QWindow.

Task-number: QTBUG-136110
Change-Id: I8b54b7ea8a768a539178395e53cc63a64fd80232
Reviewed-by: David Edmundson <davidedmundson@kde.org>
This commit is contained in:
David Edmundson 2025-04-25 10:49:01 +02:00
parent 7838a57d29
commit 70e75851bf
4 changed files with 66 additions and 6 deletions

View File

@ -311,7 +311,7 @@ QWaylandXdgSurface::QWaylandXdgSurface(QWaylandXdgShell *shell, ::xdg_surface *s
, m_window(window) , m_window(window)
{ {
QWaylandDisplay *display = window->display(); QWaylandDisplay *display = window->display();
Qt::WindowType type = window->window()->type(); Qt::WindowType type = static_cast<Qt::WindowType>(int(window->windowFlags() & Qt::WindowType_Mask));
auto *transientParent = window->transientParent(); auto *transientParent = window->transientParent();
if (type == Qt::ToolTip) { if (type == Qt::ToolTip) {

View File

@ -62,6 +62,7 @@ QWaylandWindow::QWaylandWindow(QWindow *window, QWaylandDisplay *display)
} }
initializeWlSurface(); initializeWlSurface();
mFlags = window->flags();
setWindowIcon(window->icon()); setWindowIcon(window->icon());
@ -180,6 +181,9 @@ void QWaylandWindow::initWindow()
} }
} }
createDecoration();
updateInputRegion();
// Enable high-dpi rendering. Scale() returns the screen scale factor and will // Enable high-dpi rendering. Scale() returns the screen scale factor and will
// typically be integer 1 (normal-dpi) or 2 (high-dpi). Call set_buffer_scale() // typically be integer 1 (normal-dpi) or 2 (high-dpi). Call set_buffer_scale()
// to inform the compositor that high-resolution buffers will be provided. // to inform the compositor that high-resolution buffers will be provided.
@ -188,7 +192,6 @@ void QWaylandWindow::initWindow()
else if (mSurface->version() >= 3) else if (mSurface->version() >= 3)
mSurface->set_buffer_scale(std::ceil(scale())); mSurface->set_buffer_scale(std::ceil(scale()));
setWindowFlags(window()->flags());
QRect geometry = windowGeometry(); QRect geometry = windowGeometry();
QRect defaultGeometry = this->defaultGeometry(); QRect defaultGeometry = this->defaultGeometry();
if (geometry.width() <= 0) if (geometry.width() <= 0)
@ -198,10 +201,11 @@ void QWaylandWindow::initWindow()
setGeometry_helper(geometry); setGeometry_helper(geometry);
setMask(window()->mask()); setMask(window()->mask());
if (mShellSurface) if (mShellSurface) {
mShellSurface->requestWindowStates(window()->windowStates()); mShellSurface->requestWindowStates(window()->windowStates());
mShellSurface->setWindowFlags(mFlags);
}
handleContentOrientationChange(window()->contentOrientation()); handleContentOrientationChange(window()->contentOrientation());
mFlags = window()->flags();
if (mShellSurface && mShellSurface->commitSurfaceRole()) if (mShellSurface && mShellSurface->commitSurfaceRole())
mSurface->commit(); mSurface->commit();
@ -1046,16 +1050,33 @@ void QWaylandWindow::setWindowState(Qt::WindowStates states)
void QWaylandWindow::setWindowFlags(Qt::WindowFlags flags) void QWaylandWindow::setWindowFlags(Qt::WindowFlags flags)
{ {
if (mShellSurface) const bool wasPopup = mFlags.testFlag(Qt::Popup);
mShellSurface->setWindowFlags(flags); const bool isPopup = flags.testFlag(Qt::Popup);
mFlags = flags; mFlags = flags;
// changing role is not allowed on XdgShell on the same wl_surface
if (wasPopup != isPopup) {
reset();
initializeWlSurface();
if (window()->isVisible()) {
initWindow();
}
} else {
if (mShellSurface)
mShellSurface->setWindowFlags(flags);
}
createDecoration(); createDecoration();
QReadLocker locker(&mSurfaceLock); QReadLocker locker(&mSurfaceLock);
updateInputRegion(); updateInputRegion();
} }
Qt::WindowFlags QWaylandWindow::windowFlags() const
{
return mFlags;
}
bool QWaylandWindow::createDecoration() bool QWaylandWindow::createDecoration()
{ {
Q_ASSERT_X(QThread::currentThreadId() == QThreadData::get2(thread())->threadId.loadRelaxed(), Q_ASSERT_X(QThread::currentThreadId() == QThreadData::get2(thread())->threadId.loadRelaxed(),

View File

@ -156,6 +156,7 @@ public:
Qt::WindowStates windowStates() const; Qt::WindowStates windowStates() const;
void setWindowState(Qt::WindowStates states) override; void setWindowState(Qt::WindowStates states) override;
void setWindowFlags(Qt::WindowFlags flags) override; void setWindowFlags(Qt::WindowFlags flags) override;
Qt::WindowFlags windowFlags() const;
void handleWindowStatesChanged(Qt::WindowStates states); void handleWindowStatesChanged(Qt::WindowStates states);
void raise() override; void raise() override;

View File

@ -24,6 +24,7 @@ private slots:
void popup(); void popup();
void tooltipOnPopup(); void tooltipOnPopup();
void tooltipAndSiblingPopup(); void tooltipAndSiblingPopup();
void windowTypeChanges();
void switchPopups(); void switchPopups();
void hidePopupParent(); void hidePopupParent();
void popupsWithoutParent(); void popupsWithoutParent();
@ -475,6 +476,43 @@ void tst_xdgshell::tooltipAndSiblingPopup()
QCOMPOSITOR_TRY_COMPARE(xdgPopup(0), nullptr); QCOMPOSITOR_TRY_COMPARE(xdgPopup(0), nullptr);
} }
void tst_xdgshell::windowTypeChanges()
{
QRasterWindow parentWindow;
parentWindow.resize(200, 200);
parentWindow.show();
QCOMPOSITOR_TRY_VERIFY(xdgToplevel());
exec([&] { xdgToplevel()->sendCompleteConfigure(); });
QCOMPOSITOR_TRY_VERIFY(xdgToplevel()->m_xdgSurface->m_committedConfigureSerial);
// show a toplevel
QRasterWindow window;
window.resize(100, 100);
window.setTransientParent(&parentWindow);
window.show();
QCOMPOSITOR_TRY_VERIFY(xdgToplevel(1));
exec([&] { xdgToplevel(1)->sendCompleteConfigure(); });
QCOMPOSITOR_TRY_VERIFY(xdgToplevel(1)->m_xdgSurface->m_committedConfigureSerial);
// now change it to a popup
window.setFlag(Qt::ToolTip, true);
QCOMPOSITOR_TRY_VERIFY(!xdgToplevel(1));
QCOMPOSITOR_TRY_VERIFY(xdgPopup());
exec([&] { xdgPopup()->sendCompleteConfigure(QRect(100, 100, 100, 100)); });
QCOMPOSITOR_TRY_VERIFY(xdgPopup()->m_xdgSurface->m_committedConfigureSerial);
window.hide();
QCOMPOSITOR_TRY_VERIFY(!xdgPopup());
// change to a toplevel again this time whilst hidden
window.setFlag(Qt::ToolTip, false);
window.show();
QCOMPOSITOR_TRY_VERIFY(xdgToplevel(1));
exec([&] { xdgToplevel(1)->sendCompleteConfigure(); });
QCOMPOSITOR_TRY_VERIFY(xdgToplevel(1)->m_xdgSurface->m_committedConfigureSerial);
}
// QTBUG-65680 // QTBUG-65680
void tst_xdgshell::switchPopups() void tst_xdgshell::switchPopups()
{ {