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 <paul.tvete@qt.io>
This commit is contained in:
parent
9d84dc61ba
commit
49fc676e8b
@ -263,10 +263,22 @@ uint Pointer::sendEnter(Surface *surface, const QPointF &position)
|
|||||||
wl_client *client = surface->resource()->client();
|
wl_client *client = surface->resource()->client();
|
||||||
const auto pointerResources = resourceMap().values(client);
|
const auto pointerResources = resourceMap().values(client);
|
||||||
for (auto *r : pointerResources)
|
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;
|
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
|
// Make sure you call enter, frame etc. first
|
||||||
void Pointer::sendMotion(wl_client *client, const QPointF &position)
|
void Pointer::sendMotion(wl_client *client, const QPointF &position)
|
||||||
{
|
{
|
||||||
|
@ -275,7 +275,10 @@ public:
|
|||||||
explicit Pointer(Seat *seat) : m_seat(seat) {}
|
explicit Pointer(Seat *seat) : m_seat(seat) {}
|
||||||
Surface *cursorSurface();
|
Surface *cursorSurface();
|
||||||
CursorRole* m_cursorRole = nullptr; //TODO: cleanup
|
CursorRole* m_cursorRole = nullptr; //TODO: cleanup
|
||||||
|
void send_enter() = delete;
|
||||||
uint sendEnter(Surface *surface, const QPointF &position);
|
uint sendEnter(Surface *surface, const QPointF &position);
|
||||||
|
void send_leave() = delete;
|
||||||
|
uint sendLeave(Surface *surface);
|
||||||
void sendMotion(wl_client *client, const QPointF &position);
|
void sendMotion(wl_client *client, const QPointF &position);
|
||||||
uint sendButton(wl_client *client, uint button, uint state);
|
uint sendButton(wl_client *client, uint button, uint state);
|
||||||
void sendAxis(wl_client *client, axis axis, qreal value);
|
void sendAxis(wl_client *client, axis axis, qreal value);
|
||||||
|
@ -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)
|
void XdgSurface::xdg_surface_get_popup(Resource *resource, uint32_t id, wl_resource *parent, wl_resource *positioner)
|
||||||
{
|
{
|
||||||
Q_UNUSED(parent);
|
|
||||||
Q_UNUSED(positioner);
|
Q_UNUSED(positioner);
|
||||||
QVERIFY(!m_toplevel);
|
QVERIFY(!m_toplevel);
|
||||||
QVERIFY(!m_popup);
|
QVERIFY(!m_popup);
|
||||||
m_popup = new XdgPopup(this, id, resource->version());
|
auto *p = fromResource<XdgSurface>(parent);
|
||||||
|
m_popup = new XdgPopup(this, p, id, resource->version());
|
||||||
}
|
}
|
||||||
|
|
||||||
void XdgSurface::xdg_surface_destroy_resource(Resource *resource)
|
void XdgSurface::xdg_surface_destroy_resource(Resource *resource)
|
||||||
@ -135,6 +135,12 @@ void XdgSurface::xdg_surface_destroy_resource(Resource *resource)
|
|||||||
delete this;
|
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)
|
void XdgSurface::xdg_surface_set_window_geometry(Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height)
|
||||||
{
|
{
|
||||||
Q_UNUSED(resource);
|
Q_UNUSED(resource);
|
||||||
@ -189,10 +195,14 @@ void XdgToplevel::xdg_toplevel_set_min_size(Resource *resource, int32_t width, i
|
|||||||
m_pending.minSize = size;
|
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)
|
: QtWaylandServer::xdg_popup(xdgSurface->resource()->client(), id, version)
|
||||||
, m_xdgSurface(xdgSurface)
|
, 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)
|
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());
|
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)
|
void XdgPopup::xdg_popup_grab(QtWaylandServer::xdg_popup::Resource *resource, wl_resource *seat, uint32_t serial)
|
||||||
{
|
{
|
||||||
Q_UNUSED(resource);
|
Q_UNUSED(resource);
|
||||||
Q_UNUSED(seat); // TODO: verify correct seat as well
|
Q_UNUSED(seat); // TODO: verify correct seat as well
|
||||||
//TODO: verify no other popup has grabbed
|
|
||||||
QVERIFY(!m_grabbed);
|
QVERIFY(!m_grabbed);
|
||||||
|
QVERIFY(m_parentXdgSurface->isValidPopupGrabParent());
|
||||||
|
m_xdgSurface->m_xdgWmBase->m_topmostGrabbingPopup = this;
|
||||||
m_grabbed = true;
|
m_grabbed = true;
|
||||||
m_grabSerial = serial;
|
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
|
} // namespace MockCompositor
|
||||||
|
@ -51,6 +51,7 @@ public:
|
|||||||
QVector<XdgSurface *> m_xdgSurfaces;
|
QVector<XdgSurface *> m_xdgSurfaces;
|
||||||
XdgToplevel *toplevel(int i = 0);
|
XdgToplevel *toplevel(int i = 0);
|
||||||
XdgPopup *popup(int i = 0);
|
XdgPopup *popup(int i = 0);
|
||||||
|
XdgPopup *m_topmostGrabbingPopup = nullptr;
|
||||||
CoreCompositor *m_compositor = nullptr;
|
CoreCompositor *m_compositor = nullptr;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
@ -75,8 +76,13 @@ public:
|
|||||||
void send_configure(uint serial) = delete; // Use the one below instead, as it tracks state
|
void send_configure(uint serial) = delete; // Use the one below instead, as it tracks state
|
||||||
void sendConfigure(uint serial);
|
void sendConfigure(uint serial);
|
||||||
uint sendConfigure();
|
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;
|
XdgToplevel *m_toplevel = nullptr;
|
||||||
XdgPopup *m_popup = nullptr;
|
XdgPopup *m_popup = nullptr;
|
||||||
|
|
||||||
XdgWmBase *m_xdgWmBase = nullptr;
|
XdgWmBase *m_xdgWmBase = nullptr;
|
||||||
Surface *m_surface = nullptr;
|
Surface *m_surface = nullptr;
|
||||||
bool m_configureSent = false;
|
bool m_configureSent = false;
|
||||||
@ -86,6 +92,7 @@ public:
|
|||||||
struct DoubleBufferedState {
|
struct DoubleBufferedState {
|
||||||
QRect windowGeometry = {0, 0, 0, 0};
|
QRect windowGeometry = {0, 0, 0, 0};
|
||||||
} m_pending, m_committed;
|
} m_pending, m_committed;
|
||||||
|
QVector<XdgPopup *> m_popups;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void verifyConfigured() { QVERIFY(m_configureSent); }
|
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_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_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 *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_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;
|
void xdg_surface_ack_configure(Resource *resource, uint32_t serial) override;
|
||||||
};
|
};
|
||||||
@ -126,14 +133,17 @@ protected:
|
|||||||
class XdgPopup : public QtWaylandServer::xdg_popup
|
class XdgPopup : public QtWaylandServer::xdg_popup
|
||||||
{
|
{
|
||||||
public:
|
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);
|
void sendConfigure(const QRect &geometry);
|
||||||
|
uint sendCompleteConfigure(const QRect &geometry);
|
||||||
Surface *surface() { return m_xdgSurface->m_surface; }
|
Surface *surface() { return m_xdgSurface->m_surface; }
|
||||||
XdgSurface *m_xdgSurface = nullptr;
|
XdgSurface *m_xdgSurface = nullptr;
|
||||||
|
XdgSurface *m_parentXdgSurface = nullptr;
|
||||||
bool m_grabbed = false;
|
bool m_grabbed = false;
|
||||||
uint m_grabSerial = 0;
|
uint m_grabSerial = 0;
|
||||||
protected:
|
protected:
|
||||||
void xdg_popup_grab(Resource *resource, ::wl_resource *seat, uint32_t serial) override;
|
void xdg_popup_grab(Resource *resource, ::wl_resource *seat, uint32_t serial) override;
|
||||||
|
void xdg_popup_destroy(Resource *resource) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace MockCompositor
|
} // namespace MockCompositor
|
||||||
|
@ -42,6 +42,7 @@ private slots:
|
|||||||
void configureSize();
|
void configureSize();
|
||||||
void configureStates();
|
void configureStates();
|
||||||
void popup();
|
void popup();
|
||||||
|
void tooltipOnPopup();
|
||||||
void pongs();
|
void pongs();
|
||||||
void minMaxSize();
|
void minMaxSize();
|
||||||
void windowGeometry();
|
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()
|
void tst_xdgshell::pongs()
|
||||||
{
|
{
|
||||||
QSignalSpy pongSpy(exec([=] { return get<XdgWmBase>(); }), &XdgWmBase::pong);
|
QSignalSpy pongSpy(exec([=] { return get<XdgWmBase>(); }), &XdgWmBase::pong);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user