diff --git a/shell_client.h b/shell_client.h --- a/shell_client.h +++ b/shell_client.h @@ -41,6 +41,10 @@ { Q_OBJECT public: + enum PingReason { + CloseWindow = 0, + FocusWindow + }; ShellClient(KWayland::Server::ShellSurfaceInterface *surface); ShellClient(KWayland::Server::XdgShellSurfaceInterface *surface); ShellClient(KWayland::Server::XdgShellPopupInterface *surface); @@ -218,6 +222,7 @@ bool m_transient = false; bool m_hidden = false; bool m_internal; + bool m_hasPopupGrab = false; qreal m_opacity = 1.0; class RequestGeometryBlocker { @@ -243,6 +248,7 @@ int m_requestGeometryBlockCounter = 0; QRect m_blockedRequestGeometry; QString m_caption; + QHash m_pingSerials; QString m_captionSuffix; bool m_compositingSetup = false; diff --git a/shell_client.cpp b/shell_client.cpp --- a/shell_client.cpp +++ b/shell_client.cpp @@ -228,8 +228,45 @@ connect(s, &SurfaceInterface::destroyed, this, &ShellClient::destroyClient); if (m_shellSurface) { initSurface(m_shellSurface); + // TODO: verify grab serial + m_hasPopupGrab = m_shellSurface->isPopup(); } else if (m_xdgShellSurface) { initSurface(m_xdgShellSurface); + + auto global = static_cast(m_xdgShellSurface->global()); + connect(global, &XdgShellInterface::pingDelayed, + this, [this](qint32 serial) { + auto it = m_pingSerials.find(serial); + if (it != m_pingSerials.end()) { + qCDebug(KWIN_CORE) << "First ping timeout:" << caption(); + setUnresponsive(true); + } + }); + + connect(global, &XdgShellInterface::pingTimeout, + this, [this](qint32 serial) { + auto it = m_pingSerials.find(serial); + if (it != m_pingSerials.end()) { + if (it.value() == CloseWindow) { + qCDebug(KWIN_CORE) << "Final ping timeout on a close attempt, asking to kill:" << caption(); + killWindow(); + } + m_pingSerials.erase(it); + } + }); + + connect(global, &XdgShellInterface::pongReceived, + this, [this](qint32 serial){ + auto it = m_pingSerials.find(serial); + if (it != m_pingSerials.end()) { + setUnresponsive(false); + if (it.value() == CloseWindow && m_xdgShellSurface) { + m_xdgShellSurface->close(); + } + m_pingSerials.erase(it); + } + }); + connect(m_xdgShellSurface, &XdgShellSurfaceInterface::windowMenuRequested, this, [this] (SeatInterface *seat, quint32 serial, const QPoint &surfacePos) { // TODO: check serial on seat @@ -249,10 +286,21 @@ } m_xdgShellSurface->configure(xdgSurfaceStates()); }; + configure(); connect(this, &AbstractClient::activeChanged, this, configure); connect(this, &AbstractClient::clientStartUserMovedResized, this, configure); connect(this, &AbstractClient::clientFinishUserMovedResized, this, configure); } else if (m_xdgShellPopup) { + connect(m_xdgShellPopup, &XdgShellPopupInterface::grabRequested, this, [this](SeatInterface *seat, quint32 serial) { + Q_UNUSED(seat) + Q_UNUSED(serial) + //TODO - should check the parent had focus + m_hasPopupGrab = true; + }); + + QRect position = QRect(m_xdgShellPopup->transientOffset(), m_xdgShellPopup->initialSize()); + m_xdgShellPopup->configure(position); + connect(m_xdgShellPopup, &XdgShellPopupInterface::destroyed, this, &ShellClient::destroyClient); } @@ -587,13 +635,11 @@ void ShellClient::closeWindow() { if (m_xdgShellSurface && isCloseable()) { - m_xdgShellSurface->close(); - return; - } - if (m_qtExtendedSurface && isCloseable()) { + const qint32 pingSerial = static_cast(m_xdgShellSurface->global())->ping(m_xdgShellSurface); + m_pingSerials.insert(pingSerial, CloseWindow); + } else if (m_qtExtendedSurface && isCloseable()) { m_qtExtendedSurface->close(); - } - if (m_internalWindow) { + } else if (m_internalWindow) { m_internalWindow->hide(); } } @@ -845,7 +891,9 @@ void ShellClient::takeFocus() { - if (rules()->checkAcceptFocus(wantsInput())) { + if (m_xdgShellSurface && rules()->checkAcceptFocus(wantsInput())) { + const qint32 pingSerial = static_cast(m_xdgShellSurface->global())->ping(m_xdgShellSurface); + m_pingSerials.insert(pingSerial, FocusWindow); setActive(true); } @@ -1032,6 +1080,15 @@ if (m_xdgShellSurface) { m_xdgShellSurface->configure(xdgSurfaceStates(), size); } + if (m_xdgShellPopup) { + auto parent = transientFor(); + if (parent) { + const QPoint globalClientContentPos = parent->geometry().topLeft() + parent->clientPos(); + const QPoint relativeOffset = rect.topLeft() -globalClientContentPos; + m_xdgShellPopup->configure(QRect(relativeOffset, rect.size())); + } + } + m_blockedRequestGeometry = QRect(); if (m_internal) { m_internalWindow->setGeometry(QRect(rect.topLeft() + QPoint(borderLeft(), borderTop()), rect.size() - QSize(borderLeft() + borderRight(), borderTop() + borderBottom()))); @@ -1414,6 +1471,9 @@ if (isLockScreen()) { return false; } + if (m_xdgShellPopup) { + return false; + } if (m_shellSurface) { if (m_shellSurface->isTransient() && !m_shellSurface->acceptsKeyboardFocus()) { return false; @@ -1520,18 +1580,17 @@ bool ShellClient::hasPopupGrab() const { - if (m_shellSurface) { - // TODO: verify grab serial - return m_shellSurface->isPopup(); - } - return false; + return m_hasPopupGrab; } void ShellClient::popupDone() { if (m_shellSurface) { m_shellSurface->popupDone(); } + if (m_xdgShellPopup) { + m_xdgShellPopup->popupDone(); + } } void ShellClient::updateClientOutputs() diff --git a/wayland_server.h b/wayland_server.h --- a/wayland_server.h +++ b/wayland_server.h @@ -205,6 +205,7 @@ KWayland::Server::SeatInterface *m_seat = nullptr; KWayland::Server::ShellInterface *m_shell = nullptr; KWayland::Server::XdgShellInterface *m_xdgShell = nullptr; + KWayland::Server::XdgShellInterface *m_xdgShell6 = nullptr; KWayland::Server::PlasmaShellInterface *m_plasmaShell = nullptr; KWayland::Server::PlasmaWindowManagementInterface *m_windowManagement = nullptr; KWayland::Server::QtSurfaceExtensionInterface *m_qtExtendedSurface = nullptr; diff --git a/wayland_server.cpp b/wayland_server.cpp --- a/wayland_server.cpp +++ b/wayland_server.cpp @@ -201,6 +201,13 @@ connect(m_xdgShell, &XdgShellInterface::surfaceCreated, this, &WaylandServer::createSurface); // TODO: verify seat and serial connect(m_xdgShell, &XdgShellInterface::popupCreated, this, &WaylandServer::createSurface); + + m_xdgShell6 = m_display->createXdgShell(XdgShellInterfaceVersion::UnstableV6, m_display); + m_xdgShell6->create(); + connect(m_xdgShell6, &XdgShellInterface::surfaceCreated, this, &WaylandServer::createSurface); + connect(m_xdgShell6, &XdgShellInterface::xdgPopupCreated, this, &WaylandServer::createSurface); + + m_display->createShm(); m_seat = m_display->createSeat(m_display); m_seat->create();