diff --git a/src/plugins/platforms/wayland/qwaylandbuffer.cpp b/src/plugins/platforms/wayland/qwaylandbuffer.cpp index a0fcc532f5b..076a0d57d74 100644 --- a/src/plugins/platforms/wayland/qwaylandbuffer.cpp +++ b/src/plugins/platforms/wayland/qwaylandbuffer.cpp @@ -66,7 +66,7 @@ void QWaylandBuffer::init(wl_buffer *buf) void QWaylandBuffer::release(void *data, wl_buffer *) { - static_cast(data)->mBusy = false; + static_cast(data)->mBusy--; } const wl_buffer_listener QWaylandBuffer::listener = { diff --git a/src/plugins/platforms/wayland/qwaylandbuffer_p.h b/src/plugins/platforms/wayland/qwaylandbuffer_p.h index 9e8cba2e490..b3513d1515e 100644 --- a/src/plugins/platforms/wayland/qwaylandbuffer_p.h +++ b/src/plugins/platforms/wayland/qwaylandbuffer_p.h @@ -73,14 +73,14 @@ public: virtual QSize size() const = 0; virtual int scale() const { return 1; } - void setBusy() { mBusy = true; } - bool busy() const { return mBusy; } + void setBusy() { mBusy++; } + bool busy() const { return mBusy > 0; } protected: struct wl_buffer *mBuffer; private: - bool mBusy; + int mBusy; static void release(void *data, wl_buffer *); static const wl_buffer_listener listener; diff --git a/src/plugins/platforms/wayland/qwaylandwindow.cpp b/src/plugins/platforms/wayland/qwaylandwindow.cpp index cac92a7e114..cff380e8fa2 100644 --- a/src/plugins/platforms/wayland/qwaylandwindow.cpp +++ b/src/plugins/platforms/wayland/qwaylandwindow.cpp @@ -261,6 +261,8 @@ void QWaylandWindow::reset(bool sendDestroyEvent) wl_callback_destroy(mFrameCallback); mFrameCallback = nullptr; } + + mMask = QRegion(); } QWaylandWindow *QWaylandWindow::fromWlSurface(::wl_surface *surface) @@ -790,22 +792,27 @@ QWaylandAbstractDecoration *QWaylandWindow::decoration() const return mWindowDecoration; } -static QWindow *topLevelWindow(QWindow *window) +static QWaylandWindow *closestShellSurfaceWindow(QWindow *window) { - while (QWindow *parent = window->parent()) - window = parent; - return window; + while (window) { + auto w = static_cast(window->handle()); + if (w->shellSurface()) + return w; + window = window->transientParent() ? window->transientParent() : window->parent(); + } + return nullptr; } QWaylandWindow *QWaylandWindow::transientParent() const { - // Take the top level window here, since the transient parent may be a QWidgetWindow - // or some other window without a shell surface, which is then not able to get mouse - // events. - if (auto transientParent = window()->transientParent()) - return static_cast(topLevelWindow(transientParent)->handle()); - else if (QGuiApplication::focusWindow() && (window()->type() == Qt::ToolTip || window()->type() == Qt::Popup)) - return static_cast(topLevelWindow(QGuiApplication::focusWindow())->handle()); + // Take the closest window with a shell surface, since the transient parent may be a + // QWidgetWindow or some other window without a shell surface, which is then not able to + // get mouse events. + if (auto transientParent = closestShellSurfaceWindow(window()->transientParent())) + return transientParent; + + if (QGuiApplication::focusWindow() && (window()->type() == Qt::ToolTip || window()->type() == Qt::Popup)) + return closestShellSurfaceWindow(QGuiApplication::focusWindow()); return nullptr; } diff --git a/src/plugins/platforms/wayland/qwaylandwlshellsurface.cpp b/src/plugins/platforms/wayland/qwaylandwlshellsurface.cpp index 77434e98b6c..92223f45ee6 100644 --- a/src/plugins/platforms/wayland/qwaylandwlshellsurface.cpp +++ b/src/plugins/platforms/wayland/qwaylandwlshellsurface.cpp @@ -185,6 +185,7 @@ void QWaylandWlShellSurface::updateTransientParent(QWindow *parent) || testShowWithoutActivating(m_window->window())) flags |= WL_SHELL_SURFACE_TRANSIENT_INACTIVE; + Q_ASSERT(parent_wayland_window->object()); set_transient(parent_wayland_window->object(), transientPos.x(), transientPos.y(), @@ -211,15 +212,16 @@ void QWaylandWlShellSurface::setPopup(QWaylandWindow *parent, QWaylandInputDevic transientPos.setY(transientPos.y() + parent_wayland_window->decoration()->margins().top()); } + Q_ASSERT(parent_wayland_window->object()); set_popup(device->wl_seat(), serial, parent_wayland_window->object(), transientPos.x(), transientPos.y(), 0); } void QWaylandWlShellSurface::setType(Qt::WindowType type, QWaylandWindow *transientParent) { - if (type == Qt::Popup && transientParent) + if (type == Qt::Popup && transientParent && transientParent->object()) setPopup(transientParent, m_window->display()->lastInputDevice(), m_window->display()->lastInputSerial()); - else if (transientParent) + else if (transientParent && transientParent->object()) updateTransientParent(transientParent->window()); else setTopLevel(); diff --git a/tests/auto/wayland/client/tst_client.cpp b/tests/auto/wayland/client/tst_client.cpp index 8acddfbe740..1eee90f49db 100644 --- a/tests/auto/wayland/client/tst_client.cpp +++ b/tests/auto/wayland/client/tst_client.cpp @@ -143,6 +143,8 @@ private slots: void touchDrag(); void mouseDrag(); void dontCrashOnMultipleCommits(); + void hiddenTransientParent(); + void hiddenPopupParent(); private: MockCompositor *compositor; @@ -360,6 +362,48 @@ void tst_WaylandClient::dontCrashOnMultipleCommits() QTRY_VERIFY(!compositor->surface()); } +void tst_WaylandClient::hiddenTransientParent() +{ + QWindow parent; + QWindow transient; + + transient.setTransientParent(&parent); + + parent.show(); + QTRY_VERIFY(compositor->surface()); + + parent.hide(); + QTRY_VERIFY(!compositor->surface()); + + transient.show(); + QTRY_VERIFY(compositor->surface()); +} + +void tst_WaylandClient::hiddenPopupParent() +{ + TestWindow toplevel; + toplevel.show(); + + // wl_shell relies on a mouse event in order to send a serial and seat + // with the set_popup request. + QSharedPointer surface; + QTRY_VERIFY(surface = compositor->surface()); + QPoint mousePressPos(16, 16); + QCOMPARE(toplevel.mousePressEventCount, 0); + compositor->sendMousePress(surface, mousePressPos); + QTRY_COMPARE(toplevel.mousePressEventCount, 1); + + QWindow popup; + popup.setTransientParent(&toplevel); + popup.setFlag(Qt::Popup, true); + + toplevel.hide(); + QTRY_VERIFY(!compositor->surface()); + + popup.show(); + QTRY_VERIFY(compositor->surface()); +} + int main(int argc, char **argv) { setenv("XDG_RUNTIME_DIR", ".", 1);