From de9fdb3fb720de999d30c7ced16df0013082fd2a Mon Sep 17 00:00:00 2001 From: David Edmundson Date: Mon, 6 Nov 2023 12:41:20 +0000 Subject: [PATCH] Support XDG activation QWaylandLayerShellIntegration has virtual hooks for Xdg Activation. This is important to hook up in layer shell because activation using requestActivate in Qt will go through this path. It also means we have support for us to drop the implementation in KWindowSystem in favour of calling into Qt. --- src/CMakeLists.txt | 12 +++++- src/qwaylandlayershellintegration.cpp | 2 + src/qwaylandlayershellintegration_p.h | 5 +++ src/qwaylandlayersurface.cpp | 56 ++++++++++++++++++++++++++- src/qwaylandlayersurface_p.h | 10 ++++- src/qwaylandxdgactivationv1.cpp | 44 +++++++++++++++++++++ src/qwaylandxdgactivationv1_p.h | 48 +++++++++++++++++++++++ 7 files changed, 173 insertions(+), 4 deletions(-) create mode 100644 src/qwaylandxdgactivationv1.cpp create mode 100644 src/qwaylandxdgactivationv1_p.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1605134..3cbbe39 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -6,10 +6,11 @@ remove_definitions(-DQT_NO_SIGNALS_SLOTS_KEYWORDS) add_library(LayerShellQtInterface) qt6_generate_wayland_protocol_client_sources(LayerShellQtInterface FILES ${WaylandProtocols_DATADIR}/stable/xdg-shell/xdg-shell.xml + ${WaylandProtocols_DATADIR}/staging/xdg-activation/xdg-activation-v1.xml ${CMAKE_CURRENT_SOURCE_DIR}/wlr-layer-shell-unstable-v1.xml ) -ecm_qt_declare_logging_category(LAYER_SHELL_SOURCES +ecm_qt_declare_logging_category(LayerShellQtInterface HEADER layershellqt_logging.h IDENTIFIER @@ -18,7 +19,14 @@ ecm_qt_declare_logging_category(LAYER_SHELL_SOURCES layershellqt ) -target_sources(LayerShellQtInterface PRIVATE qwaylandlayersurface.cpp interfaces/window.cpp interfaces/shell.cpp qwaylandlayershellintegration.cpp ${LAYER_SHELL_SOURCES}) +target_sources(LayerShellQtInterface PRIVATE + qwaylandxdgactivationv1.cpp + qwaylandlayersurface.cpp + qwaylandlayershellintegration.cpp + interfaces/window.cpp + interfaces/shell.cpp +) + target_link_libraries(LayerShellQtInterface PUBLIC Qt::Gui) target_link_libraries(LayerShellQtInterface PRIVATE Qt::WaylandClientPrivate Wayland::Client PkgConfig::XKBCOMMON) if (TARGET Qt::XkbCommonSupportPrivate) diff --git a/src/qwaylandlayershellintegration.cpp b/src/qwaylandlayershellintegration.cpp index 5d92a23..8d90d26 100644 --- a/src/qwaylandlayershellintegration.cpp +++ b/src/qwaylandlayershellintegration.cpp @@ -7,6 +7,7 @@ #include "qwaylandlayershellintegration_p.h" #include "qwaylandlayersurface_p.h" +#include "qwaylandxdgactivationv1_p.h" #include #include @@ -15,6 +16,7 @@ namespace LayerShellQt { QWaylandLayerShellIntegration::QWaylandLayerShellIntegration() : QWaylandShellIntegrationTemplate(4) + , m_xdgActivation(new QWaylandXdgActivationV1) { } diff --git a/src/qwaylandlayershellintegration_p.h b/src/qwaylandlayershellintegration_p.h index 08c5699..43e8c8c 100644 --- a/src/qwaylandlayershellintegration_p.h +++ b/src/qwaylandlayershellintegration_p.h @@ -13,6 +13,8 @@ #include #include +class QWaylandXdgActivationV1; + namespace LayerShellQt { @@ -22,7 +24,10 @@ public: QWaylandLayerShellIntegration(); ~QWaylandLayerShellIntegration() override; + QWaylandXdgActivationV1 *activation() const { return m_xdgActivation.data(); } QtWaylandClient::QWaylandShellSurface *createShellSurface(QtWaylandClient::QWaylandWindow *window) override; +private: + QScopedPointer m_xdgActivation; }; } diff --git a/src/qwaylandlayersurface.cpp b/src/qwaylandlayersurface.cpp index 29fb7ba..ed57f4a 100644 --- a/src/qwaylandlayersurface.cpp +++ b/src/qwaylandlayersurface.cpp @@ -8,16 +8,20 @@ #include "interfaces/window.h" #include "layershellqt_logging.h" #include "qwaylandlayersurface_p.h" +#include "qwaylandxdgactivationv1_p.h" #include #include #include +#include + namespace LayerShellQt { -QWaylandLayerSurface::QWaylandLayerSurface(QtWayland::zwlr_layer_shell_v1 *shell, QtWaylandClient::QWaylandWindow *window) +QWaylandLayerSurface::QWaylandLayerSurface(QWaylandLayerShellIntegration *shell, QtWaylandClient::QWaylandWindow *window) : QtWaylandClient::QWaylandShellSurface(window) , QtWayland::zwlr_layer_surface_v1() + , m_shell(shell) , m_interface(Window::get(window->window())) { wl_output *output = nullptr; @@ -155,4 +159,54 @@ void QWaylandLayerSurface::setWindowGeometry(const QRect &geometry) set_size(size.width(), size.height()); } +bool QWaylandLayerSurface::requestActivate() +{ + QWaylandXdgActivationV1 *activation = m_shell->activation(); + if (!activation->isActive()) { + return false; + } + if (!m_activationToken.isEmpty()) { + activation->activate(m_activationToken, window()->wlSurface()); + m_activationToken = {}; + return true; + } else { + const auto focusWindow = QGuiApplication::focusWindow(); + const auto wlWindow = focusWindow ? static_cast(focusWindow->handle()) : window(); + if (const auto seat = wlWindow->display()->lastInputDevice()) { + const auto tokenProvider = activation->requestXdgActivationToken( + wlWindow->display(), wlWindow->wlSurface(), 0, QString()); + connect(tokenProvider, &QWaylandXdgActivationTokenV1::done, this, + [this](const QString &token) { + m_shell->activation()->activate(token, window()->wlSurface()); + }); + connect(tokenProvider, &QWaylandXdgActivationTokenV1::done, tokenProvider, &QObject::deleteLater); + return true; + } + } + return false; } + +void QWaylandLayerSurface::setXdgActivationToken(const QString &token) +{ + m_activationToken = token; +} + +void QWaylandLayerSurface::requestXdgActivationToken(quint32 serial) +{ + QWaylandXdgActivationV1 *activation = m_shell->activation(); + if (!activation->isActive()) { + return; + } + auto tokenProvider = activation->requestXdgActivationToken( + window()->display(), window()->wlSurface(), serial, QString()); + + connect(tokenProvider, &QWaylandXdgActivationTokenV1::done, this, + [this](const QString &token) { + Q_EMIT window()->xdgActivationTokenCreated(token); + }); + connect(tokenProvider, &QWaylandXdgActivationTokenV1::done, tokenProvider, &QObject::deleteLater); + +} + +} + diff --git a/src/qwaylandlayersurface_p.h b/src/qwaylandlayersurface_p.h index dec2da0..4b5940c 100644 --- a/src/qwaylandlayersurface_p.h +++ b/src/qwaylandlayersurface_p.h @@ -10,6 +10,8 @@ #include +#include "qwaylandlayershellintegration_p.h" + #include "layershellqt_export.h" #include #include @@ -23,7 +25,7 @@ class LAYERSHELLQT_EXPORT QWaylandLayerSurface : public QtWaylandClient::QWaylan { Q_OBJECT public: - QWaylandLayerSurface(QtWayland::zwlr_layer_shell_v1 *shell, QtWaylandClient::QWaylandWindow *window); + QWaylandLayerSurface(QWaylandLayerShellIntegration *shell, QtWaylandClient::QWaylandWindow *window); ~QWaylandLayerSurface() override; bool isExposed() const override @@ -43,12 +45,18 @@ public: void applyConfigure() override; void setWindowGeometry(const QRect &geometry) override; + bool requestActivate() override; + void setXdgActivationToken(const QString &token) override; + void requestXdgActivationToken(quint32 serial) override; + private: void zwlr_layer_surface_v1_configure(uint32_t serial, uint32_t width, uint32_t height) override; void zwlr_layer_surface_v1_closed() override; + QWaylandLayerShellIntegration *m_shell; LayerShellQt::Window *m_interface; QSize m_pendingSize; + QString m_activationToken; bool m_configured = false; }; diff --git a/src/qwaylandxdgactivationv1.cpp b/src/qwaylandxdgactivationv1.cpp new file mode 100644 index 0000000..d89ffcb --- /dev/null +++ b/src/qwaylandxdgactivationv1.cpp @@ -0,0 +1,44 @@ +/** Copyright (C) 2020 Aleix Pol Gonzalez + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + +#include "qwaylandxdgactivationv1_p.h" +#include +#include + +QWaylandXdgActivationV1::QWaylandXdgActivationV1() + : QWaylandClientExtensionTemplate(1) +{ + initialize(); +} + +QWaylandXdgActivationV1::~QWaylandXdgActivationV1() +{ + if (isActive()) { + destroy(); + } +} + +QWaylandXdgActivationTokenV1 *QWaylandXdgActivationV1::requestXdgActivationToken(QtWaylandClient::QWaylandDisplay *display, + struct ::wl_surface *surface, + std::optional serial, + const QString &app_id) +{ + auto wl = get_activation_token(); + auto provider = new QWaylandXdgActivationTokenV1; + provider->init(wl); + + if (surface) { + provider->set_surface(surface); + } + if (!app_id.isEmpty()) { + provider->set_app_id(app_id); + } + if (serial && display->lastInputDevice()) { + provider->set_serial(*serial, display->lastInputDevice()->wl_seat()); + } + provider->commit(); + return provider; +} + +#include "moc_qwaylandxdgactivationv1_p.cpp" diff --git a/src/qwaylandxdgactivationv1_p.h b/src/qwaylandxdgactivationv1_p.h new file mode 100644 index 0000000..5012fb1 --- /dev/null +++ b/src/qwaylandxdgactivationv1_p.h @@ -0,0 +1,48 @@ +/** Copyright (C) 2020 Aleix Pol Gonzalez + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + +#ifndef QWAYLANDXDGACTIVATIONV1_P_H +#define QWAYLANDXDGACTIVATIONV1_P_H + +#include "qwayland-xdg-activation-v1.h" +#include + +#include + +namespace QtWaylandClient +{ +class QWaylandDisplay; +class QWaylandSurface; +} + +class QWaylandXdgActivationTokenV1 : public QObject, public QtWayland::xdg_activation_token_v1 +{ + Q_OBJECT +public: + ~QWaylandXdgActivationTokenV1() override + { + destroy(); + } + +protected: + void xdg_activation_token_v1_done(const QString &token) override + { + Q_EMIT done(token); + } + +Q_SIGNALS: + void done(const QString &token); +}; + +class QWaylandXdgActivationV1 : public QWaylandClientExtensionTemplate, public QtWayland::xdg_activation_v1 +{ +public: + QWaylandXdgActivationV1(); + ~QWaylandXdgActivationV1() override; + + QWaylandXdgActivationTokenV1 * + requestXdgActivationToken(QtWaylandClient::QWaylandDisplay *display, struct ::wl_surface *surface, std::optional serial, const QString &app_id); +}; + +#endif // QWAYLANDXDGACTIVATIONV1_P_H