diff --git a/autotests/client/test_xdg_shell.cpp b/autotests/client/test_xdg_shell.cpp --- a/autotests/client/test_xdg_shell.cpp +++ b/autotests/client/test_xdg_shell.cpp @@ -408,9 +408,20 @@ QSignalSpy pingSpy(m_xdgShellInterface, &XdgShellInterface::pongReceived); QVERIFY(pingSpy.isValid()); - m_xdgShellInterface->ping(); + quint32 serial = m_xdgShellInterface->ping(); QVERIFY(pingSpy.wait()); QCOMPARE(pingSpy.count(), 1); + QCOMPARE(pingSpy.takeFirst().at(0).value(), serial); + + // test of a ping failure + // disconnecting the connection thread to the queue will break the connection and pings will do a timeout + disconnect(m_connection, &ConnectionThread::eventsRead, m_queue, &EventQueue::dispatch); + m_xdgShellInterface->ping(); + QSignalSpy pingDelayedSpy(m_xdgShellInterface, &XdgShellInterface::pingDelayed); + QVERIFY(pingDelayedSpy.wait()); + + QSignalSpy pingTimeoutSpy(m_xdgShellInterface, &XdgShellInterface::pingTimeout); + QVERIFY(pingTimeoutSpy.wait()); } void XdgShellTest::testClose() diff --git a/src/server/xdgshell_interface.h b/src/server/xdgshell_interface.h --- a/src/server/xdgshell_interface.h +++ b/src/server/xdgshell_interface.h @@ -111,14 +111,15 @@ **/ XdgShellSurfaceInterface *getSurface(wl_resource *native); - /* + /** * Confirm the client is still alive and responding * * Will result in pong being emitted * + * @returns unique identifier for this request * @since XDGMERGE_VERSION */ - void ping(); + quint32 ping(); Q_SIGNALS: void surfaceCreated(KWayland::Server::XdgShellSurfaceInterface *surface); @@ -150,10 +151,29 @@ /* * Emitted in response to a ping request * - * @param surface The popup xdg shell surface which got created + * @param serial unique identifier for the request + * @since XDGMERGE_VERSION + */ + void pongReceived(quint32 serial); + + /* + * Emitted when the application takes more than expected + * to answer to a ping, this will always be emitted before + * eventuallt pingTimeout gets emitted + * + * @param serial unique identifier for the request + * @since XDGMERGE_VERSION + */ + void pingDelayed(quint32 serial); + + /* + * Emitted when the application doesn't answer to a ping + * and the serve gave up on it + * + * @param serial unique identifier for the request * @since XDGMERGE_VERSION */ - void pongReceived(); + void pingTimeout(quint32 serial); protected: class Private; diff --git a/src/server/xdgshell_interface.cpp b/src/server/xdgshell_interface.cpp --- a/src/server/xdgshell_interface.cpp +++ b/src/server/xdgshell_interface.cpp @@ -27,11 +27,32 @@ XdgShellInterface::Private::Private(XdgShellInterfaceVersion interfaceVersion, XdgShellInterface *q, Display *d, const wl_interface *interface, quint32 version) : Global::Private(d, interface, version) , interfaceVersion(interfaceVersion) - , pingTimer(new QTimer) , q(q) { - pingTimer->setSingleShot(true); +} + +void XdgShellInterface::Private::setupTimer(quint32 serial) +{ + QTimer *pingTimer = new QTimer(); + pingTimer->setSingleShot(false); pingTimer->setInterval(1000); + int attempt = 0; + connect(pingTimer, &QTimer::timeout, q, [this, serial, attempt]() mutable { + ++attempt; + if (attempt == 1) { + emit q->pingDelayed(serial); + } else { + emit q->pingTimeout(serial); + auto timerIt = pingTimers.find(serial); + if (timerIt != pingTimers.end()) { + delete timerIt.value(); + pingTimers.erase(timerIt); + } + } + }); + + pingTimers.insert(serial, pingTimer); + pingTimer->start(); } XdgShellInterface::XdgShellInterface(Private *d, QObject *parent) @@ -53,9 +74,9 @@ return d->interfaceVersion; } -void XdgShellInterface::ping() +quint32 XdgShellInterface::ping() { - d_func()->ping(); + return d_func()->ping(); } XdgShellInterface::Private *XdgShellInterface::d_func() const diff --git a/src/server/xdgshell_interface_p.h b/src/server/xdgshell_interface_p.h --- a/src/server/xdgshell_interface_p.h +++ b/src/server/xdgshell_interface_p.h @@ -36,9 +36,10 @@ public: XdgShellInterfaceVersion interfaceVersion; - virtual void ping() = 0; - quint32 pingSerial = 0; - QScopedPointer pingTimer; + virtual quint32 ping() = 0; + void setupTimer(quint32 serial); + //pingserial/timer correspondence + QHash pingTimers; protected: Private(XdgShellInterfaceVersion interfaceVersion, XdgShellInterface *q, Display *d, const wl_interface *interface, quint32 version); diff --git a/src/server/xdgshell_v5_interface.cpp b/src/server/xdgshell_v5_interface.cpp --- a/src/server/xdgshell_v5_interface.cpp +++ b/src/server/xdgshell_v5_interface.cpp @@ -45,7 +45,7 @@ void createSurface(wl_client *client, uint32_t version, uint32_t id, SurfaceInterface *surface, wl_resource *parentResource); void createPopup(wl_client *client, uint32_t version, uint32_t id, SurfaceInterface *surface, SurfaceInterface *parent, SeatInterface *seat, quint32 serial, const QPoint &pos, wl_resource *parentResource); void bind(wl_client *client, uint32_t version, uint32_t id) override; - void ping() override; + quint32 ping() override; static void unbind(wl_resource *resource); static Private *cast(wl_resource *r) { @@ -166,9 +166,11 @@ { Q_UNUSED(client) auto s = cast(resource); - if (s->pingTimer->isActive() && serial == s->pingSerial) { - s->pingTimer->stop(); - emit s->q->pongReceived(); + auto timerIt = s->pingTimers.find(serial); + if (timerIt != s->pingTimers.end() && timerIt.value()->isActive()) { + delete timerIt.value(); + s->pingTimers.erase(timerIt); + emit s->q->pongReceived(serial); } } @@ -213,14 +215,16 @@ return nullptr; } -void XdgShellV5Interface::Private::ping() +quint32 XdgShellV5Interface::Private::ping() { - if (!resource || pingTimer->isActive()) { - return; + if (!resource) { + return -1; } - pingSerial = display->nextSerial(); + const quint32 pingSerial = display->nextSerial(); xdg_shell_send_ping(resource, pingSerial); - pingTimer->start(); + + setupTimer(pingSerial); + return pingSerial; } XdgShellV5Interface::Private *XdgShellV5Interface::d_func() const diff --git a/src/server/xdgshell_v6_interface.cpp b/src/server/xdgshell_v6_interface.cpp --- a/src/server/xdgshell_v6_interface.cpp +++ b/src/server/xdgshell_v6_interface.cpp @@ -50,7 +50,7 @@ void bind(wl_client *client, uint32_t version, uint32_t id) override; - void ping() override; + quint32 ping() override; static void unbind(wl_resource *resource); static Private *cast(wl_resource *r) { @@ -279,9 +279,11 @@ { Q_UNUSED(client) auto s = cast(resource); - if (s->pingTimer->isActive() && serial == s->pingSerial) { - s->pingTimer->stop(); - emit s->q->pongReceived(); + auto timerIt = s->pingTimers.find(serial); + if (timerIt != s->pingTimers.end() && timerIt.value()->isActive()) { + delete timerIt.value(); + s->pingTimers.erase(timerIt); + emit s->q->pongReceived(serial); } } @@ -354,14 +356,16 @@ return nullptr; } -void XdgShellV6Interface::Private::ping() +quint32 XdgShellV6Interface::Private::ping() { - if (!resource || pingTimer->isActive()) { - return; + if (!resource) { + return -1; } - pingSerial = display->nextSerial(); + const quint32 pingSerial = display->nextSerial(); zxdg_shell_v6_send_ping(resource, pingSerial); - pingTimer->start(); + + setupTimer(pingSerial); + return pingSerial; } XdgShellV6Interface::Private *XdgShellV6Interface::d_func() const