From 60d6d83ea0c43462d5f8e17aaa654ab6f625dd6e Mon Sep 17 00:00:00 2001 From: Johan Klokkhammer Helsing Date: Fri, 21 Dec 2018 14:31:58 +0100 Subject: Client xdg-shell: Add test for tooltips on popups This used to cause protocol errors. Task-number: QTBUG-71734 Change-Id: Ic937210fc42c93f1d411fb0fb4f269de01f07b5b Reviewed-by: Paul Olav Tvete --- tests/auto/client/shared/coreprotocol.cpp | 14 ++++- tests/auto/client/shared/coreprotocol.h | 3 ++ tests/auto/client/shared/xdgshell.cpp | 36 +++++++++++-- tests/auto/client/shared/xdgshell.h | 14 ++++- tests/auto/client/xdgshell/tst_xdgshell.cpp | 80 +++++++++++++++++++++++++++++ 5 files changed, 140 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/tests/auto/client/shared/coreprotocol.cpp b/tests/auto/client/shared/coreprotocol.cpp index 54725e812..6f51a9793 100644 --- a/tests/auto/client/shared/coreprotocol.cpp +++ b/tests/auto/client/shared/coreprotocol.cpp @@ -263,10 +263,22 @@ uint Pointer::sendEnter(Surface *surface, const QPointF &position) wl_client *client = surface->resource()->client(); const auto pointerResources = resourceMap().values(client); for (auto *r : pointerResources) - send_enter(r->handle, m_enterSerial, surface->resource()->handle, x ,y); + wl_pointer::send_enter(r->handle, m_enterSerial, surface->resource()->handle, x ,y); return m_enterSerial; } +uint Pointer::sendLeave(Surface *surface) +{ + m_enterSerial = 0; + uint serial = m_seat->m_compositor->nextSerial(); + + wl_client *client = surface->resource()->client(); + const auto pointerResources = resourceMap().values(client); + for (auto *r : pointerResources) + wl_pointer::send_leave(r->handle, serial, surface->resource()->handle); + return serial; +} + // Make sure you call enter, frame etc. first void Pointer::sendMotion(wl_client *client, const QPointF &position) { diff --git a/tests/auto/client/shared/coreprotocol.h b/tests/auto/client/shared/coreprotocol.h index 11b35eb4d..249c16f42 100644 --- a/tests/auto/client/shared/coreprotocol.h +++ b/tests/auto/client/shared/coreprotocol.h @@ -275,7 +275,10 @@ public: explicit Pointer(Seat *seat) : m_seat(seat) {} Surface *cursorSurface(); CursorRole* m_cursorRole = nullptr; //TODO: cleanup + void send_enter() = delete; uint sendEnter(Surface *surface, const QPointF &position); + void send_leave() = delete; + uint sendLeave(Surface *surface); void sendMotion(wl_client *client, const QPointF &position); uint sendButton(wl_client *client, uint button, uint state); void sendAxis(wl_client *client, axis axis, qreal value); diff --git a/tests/auto/client/shared/xdgshell.cpp b/tests/auto/client/shared/xdgshell.cpp index 6bc303020..9437688ad 100644 --- a/tests/auto/client/shared/xdgshell.cpp +++ b/tests/auto/client/shared/xdgshell.cpp @@ -120,11 +120,11 @@ void XdgSurface::xdg_surface_get_toplevel(Resource *resource, uint32_t id) void XdgSurface::xdg_surface_get_popup(Resource *resource, uint32_t id, wl_resource *parent, wl_resource *positioner) { - Q_UNUSED(parent); Q_UNUSED(positioner); QVERIFY(!m_toplevel); QVERIFY(!m_popup); - m_popup = new XdgPopup(this, id, resource->version()); + auto *p = fromResource(parent); + m_popup = new XdgPopup(this, p, id, resource->version()); } void XdgSurface::xdg_surface_destroy_resource(Resource *resource) @@ -135,6 +135,12 @@ void XdgSurface::xdg_surface_destroy_resource(Resource *resource) delete this; } +void XdgSurface::xdg_surface_destroy(QtWaylandServer::xdg_surface::Resource *resource) +{ + QVERIFY(m_popups.empty()); + wl_resource_destroy(resource->handle); +} + void XdgSurface::xdg_surface_set_window_geometry(Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { Q_UNUSED(resource); @@ -189,10 +195,14 @@ void XdgToplevel::xdg_toplevel_set_min_size(Resource *resource, int32_t width, i m_pending.minSize = size; } -XdgPopup::XdgPopup(XdgSurface *xdgSurface, int id, int version) +XdgPopup::XdgPopup(XdgSurface *xdgSurface, XdgSurface *parent, int id, int version) : QtWaylandServer::xdg_popup(xdgSurface->resource()->client(), id, version) , m_xdgSurface(xdgSurface) + , m_parentXdgSurface(parent) { + Q_ASSERT(m_xdgSurface); + Q_ASSERT(m_parentXdgSurface); + m_parentXdgSurface->m_popups << this; } void XdgPopup::sendConfigure(const QRect &geometry) @@ -200,14 +210,32 @@ void XdgPopup::sendConfigure(const QRect &geometry) send_configure(geometry.x(), geometry.y(), geometry.width(), geometry.height()); } +uint XdgPopup::sendCompleteConfigure(const QRect &geometry) +{ + sendConfigure(geometry); + return m_xdgSurface->sendConfigure(); +} + void XdgPopup::xdg_popup_grab(QtWaylandServer::xdg_popup::Resource *resource, wl_resource *seat, uint32_t serial) { Q_UNUSED(resource); Q_UNUSED(seat); // TODO: verify correct seat as well - //TODO: verify no other popup has grabbed QVERIFY(!m_grabbed); + QVERIFY(m_parentXdgSurface->isValidPopupGrabParent()); + m_xdgSurface->m_xdgWmBase->m_topmostGrabbingPopup = this; m_grabbed = true; m_grabSerial = serial; } +void XdgPopup::xdg_popup_destroy(Resource *resource) { + Q_UNUSED(resource); + if (m_grabbed) { + auto *base = m_xdgSurface->m_xdgWmBase; + QCOMPARE(base->m_topmostGrabbingPopup, this); + base->m_topmostGrabbingPopup = this->m_parentXdgSurface->m_popup; + } + m_xdgSurface->m_popup = nullptr; + m_parentXdgSurface->m_popups.removeAll(this); +} + } // namespace MockCompositor diff --git a/tests/auto/client/shared/xdgshell.h b/tests/auto/client/shared/xdgshell.h index 1a01ea1ea..ca19c293f 100644 --- a/tests/auto/client/shared/xdgshell.h +++ b/tests/auto/client/shared/xdgshell.h @@ -51,6 +51,7 @@ public: QVector m_xdgSurfaces; XdgToplevel *toplevel(int i = 0); XdgPopup *popup(int i = 0); + XdgPopup *m_topmostGrabbingPopup = nullptr; CoreCompositor *m_compositor = nullptr; signals: @@ -75,8 +76,13 @@ public: void send_configure(uint serial) = delete; // Use the one below instead, as it tracks state void sendConfigure(uint serial); uint sendConfigure(); + bool isTopmostGrabbingPopup() const { return m_popup && m_xdgWmBase->m_topmostGrabbingPopup == m_popup; } + bool isValidPopupGrabParent() const { return isTopmostGrabbingPopup() || (m_toplevel && !m_xdgWmBase->m_topmostGrabbingPopup); } + + // Role objects XdgToplevel *m_toplevel = nullptr; XdgPopup *m_popup = nullptr; + XdgWmBase *m_xdgWmBase = nullptr; Surface *m_surface = nullptr; bool m_configureSent = false; @@ -86,6 +92,7 @@ public: struct DoubleBufferedState { QRect windowGeometry = {0, 0, 0, 0}; } m_pending, m_committed; + QVector m_popups; public slots: void verifyConfigured() { QVERIFY(m_configureSent); } @@ -98,7 +105,7 @@ protected: void xdg_surface_get_toplevel(Resource *resource, uint32_t id) override; void xdg_surface_get_popup(Resource *resource, uint32_t id, ::wl_resource *parent, ::wl_resource *positioner) override; void xdg_surface_destroy_resource(Resource *resource) override; - void xdg_surface_destroy(Resource *resource) override { wl_resource_destroy(resource->handle); } + void xdg_surface_destroy(Resource *resource) override; void xdg_surface_set_window_geometry(Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) override; void xdg_surface_ack_configure(Resource *resource, uint32_t serial) override; }; @@ -126,14 +133,17 @@ protected: class XdgPopup : public QtWaylandServer::xdg_popup { public: - explicit XdgPopup(XdgSurface *xdgSurface, int id, int version = 1); + explicit XdgPopup(XdgSurface *xdgSurface, XdgSurface *parent, int id, int version = 1); void sendConfigure(const QRect &geometry); + uint sendCompleteConfigure(const QRect &geometry); Surface *surface() { return m_xdgSurface->m_surface; } XdgSurface *m_xdgSurface = nullptr; + XdgSurface *m_parentXdgSurface = nullptr; bool m_grabbed = false; uint m_grabSerial = 0; protected: void xdg_popup_grab(Resource *resource, ::wl_resource *seat, uint32_t serial) override; + void xdg_popup_destroy(Resource *resource) override; }; } // namespace MockCompositor diff --git a/tests/auto/client/xdgshell/tst_xdgshell.cpp b/tests/auto/client/xdgshell/tst_xdgshell.cpp index de0341014..32b62689e 100644 --- a/tests/auto/client/xdgshell/tst_xdgshell.cpp +++ b/tests/auto/client/xdgshell/tst_xdgshell.cpp @@ -42,6 +42,7 @@ private slots: void configureSize(); void configureStates(); void popup(); + void tooltipOnPopup(); void pongs(); void minMaxSize(); void windowGeometry(); @@ -252,6 +253,85 @@ void tst_xdgshell::popup() }); } +void tst_xdgshell::tooltipOnPopup() +{ + class Popup : public QRasterWindow { + public: + explicit Popup(QWindow *parent) { + setTransientParent(parent); + setFlags(Qt::Popup); + resize(100, 100); + show(); + } + void mousePressEvent(QMouseEvent *event) override { + QRasterWindow::mousePressEvent(event); + m_tooltip = new QRasterWindow; + m_tooltip->setTransientParent(this); + m_tooltip->setFlags(Qt::ToolTip); + m_tooltip->resize(100, 100); + m_tooltip->show(); + } + QWindow *m_tooltip = nullptr; + }; + + class Window : public QRasterWindow { + public: + void mousePressEvent(QMouseEvent *event) override { + QRasterWindow::mousePressEvent(event); + m_popup = new Popup(this); + } + Popup *m_popup = nullptr; + }; + + 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); + + exec([=] { + auto *surface = xdgPopup()->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(); + }); + + QCOMPOSITOR_TRY_VERIFY(xdgPopup(1)); + exec([=] { xdgPopup(1)->sendCompleteConfigure(QRect(100, 100, 100, 100)); }); + QCOMPOSITOR_TRY_VERIFY(xdgPopup(1)->m_xdgSurface->m_committedConfigureSerial); + QCOMPOSITOR_TRY_VERIFY(!xdgPopup(1)->m_grabbed); + + // Close the middle popup (according protocol this should also destroy any nested popups) + window.m_popup->close(); + + // Close order is verified in XdgSurface::xdg_surface_destroy + + QCOMPOSITOR_TRY_COMPARE(xdgPopup(1), nullptr); + QCOMPOSITOR_TRY_COMPARE(xdgPopup(0), nullptr); +} + void tst_xdgshell::pongs() { QSignalSpy pongSpy(exec([=] { return get(); }), &XdgWmBase::pong); -- cgit v1.2.3