diff options
-rw-r--r-- | tests/auto/client/shared/xdgshell.cpp | 1 | ||||
-rw-r--r-- | tests/auto/client/shared/xdgshell.h | 5 | ||||
-rw-r--r-- | tests/auto/client/xdgshell/tst_xdgshell.cpp | 89 |
3 files changed, 94 insertions, 1 deletions
diff --git a/tests/auto/client/shared/xdgshell.cpp b/tests/auto/client/shared/xdgshell.cpp index 9437688a..13acc01e 100644 --- a/tests/auto/client/shared/xdgshell.cpp +++ b/tests/auto/client/shared/xdgshell.cpp @@ -236,6 +236,7 @@ void XdgPopup::xdg_popup_destroy(Resource *resource) { } m_xdgSurface->m_popup = nullptr; m_parentXdgSurface->m_popups.removeAll(this); + emit destroyRequested(); } } // namespace MockCompositor diff --git a/tests/auto/client/shared/xdgshell.h b/tests/auto/client/shared/xdgshell.h index ca19c293..618babde 100644 --- a/tests/auto/client/shared/xdgshell.h +++ b/tests/auto/client/shared/xdgshell.h @@ -130,8 +130,9 @@ protected: void xdg_toplevel_set_min_size(Resource *resource, int32_t width, int32_t height) override; }; -class XdgPopup : public QtWaylandServer::xdg_popup +class XdgPopup : public QObject, public QtWaylandServer::xdg_popup { + Q_OBJECT public: explicit XdgPopup(XdgSurface *xdgSurface, XdgSurface *parent, int id, int version = 1); void sendConfigure(const QRect &geometry); @@ -141,6 +142,8 @@ public: XdgSurface *m_parentXdgSurface = nullptr; bool m_grabbed = false; uint m_grabSerial = 0; +signals: + void destroyRequested(); protected: void xdg_popup_grab(Resource *resource, ::wl_resource *seat, uint32_t serial) override; void xdg_popup_destroy(Resource *resource) override; diff --git a/tests/auto/client/xdgshell/tst_xdgshell.cpp b/tests/auto/client/xdgshell/tst_xdgshell.cpp index 32b62689..6efffc8a 100644 --- a/tests/auto/client/xdgshell/tst_xdgshell.cpp +++ b/tests/auto/client/xdgshell/tst_xdgshell.cpp @@ -43,6 +43,7 @@ private slots: void configureStates(); void popup(); void tooltipOnPopup(); + void switchPopups(); void pongs(); void minMaxSize(); void windowGeometry(); @@ -332,6 +333,94 @@ void tst_xdgshell::tooltipOnPopup() QCOMPOSITOR_TRY_COMPARE(xdgPopup(0), nullptr); } +// QTBUG-65680 +void tst_xdgshell::switchPopups() +{ + class Popup : public QRasterWindow { + public: + explicit Popup(QWindow *parent) { + setTransientParent(parent); + setFlags(Qt::Popup); + resize(10, 10); + show(); + } + }; + + class Window : public QRasterWindow { + public: + void mousePressEvent(QMouseEvent *event) override { + QRasterWindow::mousePressEvent(event); + if (!m_popups.empty()) + m_popups.last()->setVisible(false); + } + // The bug this checks for, is the case where there is a flushWindowSystemEvents() call + // somewhere within setVisible(false) before the grab has been released. + // This leads to the the release event below—including its show() call—to be handled + // At a time where there is still an active grab, making it illegal for the new popup to + // grab. + void mouseReleaseEvent(QMouseEvent *event) override { + QRasterWindow::mouseReleaseEvent(event); + m_popups << new Popup(this); + } + ~Window() override { qDeleteAll(m_popups); } + QVector<Popup *> m_popups; + }; + + Window window; + window.resize(200, 200); + window.show(); + + QCOMPOSITOR_TRY_VERIFY(xdgToplevel()); + exec([=] { xdgToplevel()->sendCompleteConfigure(); }); + QCOMPOSITOR_TRY_VERIFY(xdgToplevel()->m_xdgSurface->m_committedConfigureSerial); + + exec([=] { + auto *surface = xdgToplevel()->surface(); + auto *p = pointer(); + p->sendEnter(surface, {100, 100}); +// p->sendFrame(); //TODO: uncomment when we support seat v5 + p->sendButton(client(), BTN_LEFT, Pointer::button_state_pressed); + p->sendButton(client(), BTN_LEFT, Pointer::button_state_released); +// p->sendFrame(); + p->sendLeave(surface); +// p->sendFrame(); + }); + + QCOMPOSITOR_TRY_VERIFY(xdgPopup()); + exec([=] { xdgPopup()->sendCompleteConfigure(QRect(100, 100, 100, 100)); }); + QCOMPOSITOR_TRY_VERIFY(xdgPopup()->m_xdgSurface->m_committedConfigureSerial); + QCOMPOSITOR_TRY_VERIFY(xdgPopup()->m_grabbed); + + QSignalSpy firstDestroyed(exec([=] { return xdgPopup(); }), &XdgPopup::destroyRequested); + + exec([=] { + auto *surface = xdgToplevel()->surface(); + auto *p = pointer(); + p->sendEnter(surface, {100, 100}); +// p->sendFrame(); + p->sendButton(client(), BTN_LEFT, Pointer::button_state_pressed); + p->sendButton(client(), BTN_LEFT, Pointer::button_state_released); +// p->sendFrame(); + }); + + // The client will now hide one popup and then show another + firstDestroyed.wait(); + + QCOMPOSITOR_TRY_VERIFY(xdgPopup()); + QCOMPOSITOR_TRY_VERIFY(!xdgPopup(1)); + + // Verify we still grabbed in case the client has checks to avoid illegal grabs + QCOMPOSITOR_TRY_VERIFY(xdgPopup()->m_grabbed); + + // Sometimes the popup will select another parent if one is illegal at the time it tries to + // grab. So we verify we got the intended parent on the compositor side. + QCOMPOSITOR_TRY_VERIFY(xdgPopup()->m_parentXdgSurface == xdgToplevel()->m_xdgSurface); + + // For good measure just check that configuring works as usual + exec([=] { xdgPopup()->sendCompleteConfigure(QRect(100, 100, 100, 100)); }); + QCOMPOSITOR_TRY_VERIFY(xdgPopup()->m_xdgSurface->m_committedConfigureSerial); +} + void tst_xdgshell::pongs() { QSignalSpy pongSpy(exec([=] { return get<XdgWmBase>(); }), &XdgWmBase::pong); |