From 06879c464c7451538dcc1afa1c11d56e48072311 Mon Sep 17 00:00:00 2001 From: yeshanshan Date: Mon, 8 Jun 2026 11:23:07 +0800 Subject: [PATCH] feat: add xdg-activation-v1 support to dock compositor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement the xdg-activation-v1 protocol for the dock compositor, enabling applications to activate their windows through the dock's taskbar integration. This allows proper window focus management when clicking dock icons. The patch adds: 1. XdgActivationManager compositor extension implementing xdg_activation_v1 global 2. XdgActivationTokenV1 implementation for token lifecycle management 3. Integration with outer compositor via ds::XdgActivation for token delegation 4. QML registration for easy inclusion in DockCompositor When a plugin client commits an activation token, the manager requests a real token from the outer compositor and forwards it back, ensuring proper multi-layer Wayland compositor activation handling. Log: Added xdg-activation-v1 protocol support for dock activation functionality Influence: 1. Test clicking dock icons focuses applications correctly 2. Verify activation tokens are properly forwarded between compositor layers 3. Test multiple rapid activation requests 4. Verify proper cleanup when clients disconnect 5. Test activation tokens with different app IDs feat: 为任务栏合成器添加 xdg-activation-v1 支持 在任务栏合成器中实现 xdg-activation-v1 协议,使应用程序能通过任务栏集成 激活其窗口。这允许在点击任务栏图标时进行正确的窗口焦点管理。 修改内容: 1. 添加 XdgActivationManager 合成器扩展,实现 xdg_activation_v1 全局对象 2. 实现 XdgActivationTokenV1 用于令牌生命周期管理 3. 通过 ds::XdgActivation 与外部合成器集成进行令牌代理 4. 在 DockCompositor 中注册 QML 组件 当插件客户端提交激活令牌时,管理器从外部合成器请求真实令牌并转发回去,确 保多层 Wayland 合成器激活机制的正确处理。 Log: 新增 xdg-activation-v1 协议支持任务栏激活功能 Influence: 1. 测试点击任务栏图标能否正确聚焦应用程序 2. 验证激活令牌在合成器层之间正确转发 3. 测试多次快速激活请求 4. 验证客户端断开连接时的清理逻辑 5. 测试不同应用 ID 的激活令牌 --- panels/dock/CMakeLists.txt | 3 + panels/dock/DockCompositor.qml | 2 + panels/dock/xdgactivationmanager.cpp | 132 +++++++++++++++++++++++++++ panels/dock/xdgactivationmanager_p.h | 70 ++++++++++++++ 4 files changed, 207 insertions(+) create mode 100644 panels/dock/xdgactivationmanager.cpp create mode 100644 panels/dock/xdgactivationmanager_p.h diff --git a/panels/dock/CMakeLists.txt b/panels/dock/CMakeLists.txt index 34825ad34..16d6aa5e0 100644 --- a/panels/dock/CMakeLists.txt +++ b/panels/dock/CMakeLists.txt @@ -117,6 +117,8 @@ file( pluginmanagerintegration.cpp dockpositioner.h dockpositioner.cpp + xdgactivationmanager_p.h + xdgactivationmanager.cpp ) set_source_files_properties(DockCompositor.qml PROPERTIES @@ -148,6 +150,7 @@ qt_generate_wayland_protocol_server_sources(dock-plugin FILES ${DDE_TRAY_LOADER_PROTOCOL} ${WaylandProtocols_DATADIR}/staging/fractional-scale/fractional-scale-v1.xml + ${WaylandProtocols_DATADIR}/staging/xdg-activation/xdg-activation-v1.xml ) target_link_libraries(dock-plugin PUBLIC diff --git a/panels/dock/DockCompositor.qml b/panels/dock/DockCompositor.qml index 730cdfe40..dd1ed95e9 100644 --- a/panels/dock/DockCompositor.qml +++ b/panels/dock/DockCompositor.qml @@ -139,5 +139,7 @@ Item { id: pluginScaleManager pluginScale: dockCompositor.panelScale * 120 } + + XdgActivationManager {} } } diff --git a/panels/dock/xdgactivationmanager.cpp b/panels/dock/xdgactivationmanager.cpp new file mode 100644 index 000000000..7c029f291 --- /dev/null +++ b/panels/dock/xdgactivationmanager.cpp @@ -0,0 +1,132 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "xdgactivationmanager_p.h" + +#include + +#include +#include +#include + +#include + +Q_LOGGING_CATEGORY(xdgActivationMgr, "dde.shell.xdgactivation.manager") + +// --------------------------------------------------------------------------- +// XdgActivationManager +// --------------------------------------------------------------------------- + +XdgActivationManager::XdgActivationManager(QWaylandCompositor *compositor) + : QWaylandCompositorExtensionTemplate(compositor) + , m_compositor(compositor) +{ +} + +void XdgActivationManager::initialize() +{ + QWaylandCompositorExtensionTemplate::initialize(); + QWaylandCompositor *compositor = static_cast(extensionContainer()); + Q_ASSERT(compositor); + m_compositor = compositor; + init(compositor->display(), 1); + + // Create the client-side xdg_activation_v1 connection to the outer compositor + m_outerActivation = new ds::XdgActivation(this); + connect(m_outerActivation, &ds::XdgActivation::tokenReady, this, &XdgActivationManager::onTokenReady); +} + +void XdgActivationManager::xdg_activation_v1_destroy(Resource *resource) +{ + wl_resource_destroy(resource->handle); +} + +void XdgActivationManager::xdg_activation_v1_get_activation_token(Resource *resource, uint32_t id) +{ + QWaylandResource tokenResource(wl_resource_create(resource->client(), &xdg_activation_token_v1_interface, wl_resource_get_version(resource->handle), id)); + new XdgActivationTokenV1(this, tokenResource); +} + +void XdgActivationManager::setPendingToken(XdgActivationTokenV1 *token) +{ + m_pendingToken = token; +} + +void XdgActivationManager::requestOuterToken(const QString &appId) +{ + QWindow *window = QGuiApplication::focusWindow(); + m_outerActivation->requestToken(window, appId); +} + +void XdgActivationManager::clearPendingTokenIf(XdgActivationTokenV1 *token) +{ + if (m_pendingToken == token) { + m_pendingToken = nullptr; + } +} + +void XdgActivationManager::onTokenReady(const QString &token) +{ + if (m_pendingToken) { + qCDebug(xdgActivationMgr) << "Forwarding activation token to plugin client"; + m_pendingToken->sendToken(token); + m_pendingToken = nullptr; + } +} + +// --------------------------------------------------------------------------- +// XdgActivationTokenV1 +// --------------------------------------------------------------------------- +// XdgActivationTokenV1 +// --------------------------------------------------------------------------- + +XdgActivationTokenV1::XdgActivationTokenV1(XdgActivationManager *manager, const QWaylandResource &resource) + : QObject(manager) + , m_manager(manager) +{ + init(resource.resource()); +} + +XdgActivationTokenV1::~XdgActivationTokenV1() = default; + +void XdgActivationTokenV1::sendToken(const QString &token) +{ + send_done(token); +} + +void XdgActivationTokenV1::xdg_activation_token_v1_set_serial(Resource *resource, uint32_t serial, struct ::wl_resource *seat) +{ + Q_UNUSED(resource) + Q_UNUSED(seat) + m_serial = serial; +} + +void XdgActivationTokenV1::xdg_activation_token_v1_set_app_id(Resource *resource, const QString &app_id) +{ + Q_UNUSED(resource) + m_appId = app_id; +} + +void XdgActivationTokenV1::xdg_activation_token_v1_set_surface(Resource *resource, struct ::wl_resource *surface) +{ + Q_UNUSED(resource) + m_surface = QWaylandSurface::fromResource(surface); +} + +void XdgActivationTokenV1::xdg_activation_token_v1_commit(Resource *resource) +{ + Q_UNUSED(resource) + qCDebug(xdgActivationMgr) << "Token committed by plugin client, appId:" << m_appId; + + // Store this token as pending and request from the outer compositor + m_manager->setPendingToken(this); + m_manager->requestOuterToken(m_appId); +} + +void XdgActivationTokenV1::xdg_activation_token_v1_destroy(Resource *resource) +{ + Q_UNUSED(resource) + m_manager->clearPendingTokenIf(this); + deleteLater(); +} diff --git a/panels/dock/xdgactivationmanager_p.h b/panels/dock/xdgactivationmanager_p.h new file mode 100644 index 000000000..36803ef79 --- /dev/null +++ b/panels/dock/xdgactivationmanager_p.h @@ -0,0 +1,70 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "qwayland-server-xdg-activation-v1.h" + +namespace ds +{ +class XdgActivation; +} + +class XdgActivationTokenV1; +class XdgActivationManager : public QWaylandCompositorExtensionTemplate, public QtWaylandServer::xdg_activation_v1 +{ + Q_OBJECT + QML_ELEMENT +public: + XdgActivationManager(QWaylandCompositor *compositor = nullptr); + void initialize() override; + + void setPendingToken(XdgActivationTokenV1 *token); + void requestOuterToken(const QString &appId); + void clearPendingTokenIf(XdgActivationTokenV1 *token); + +protected: + void xdg_activation_v1_destroy(Resource *resource) override; + void xdg_activation_v1_get_activation_token(Resource *resource, uint32_t id) override; + +private: + void onTokenReady(const QString &token); + + QWaylandCompositor *m_compositor = nullptr; + ds::XdgActivation *m_outerActivation = nullptr; + QPointer m_pendingToken; +}; + +class XdgActivationTokenV1 : public QObject, public QtWaylandServer::xdg_activation_token_v1 +{ + Q_OBJECT +public: + XdgActivationTokenV1(XdgActivationManager *manager, const QWaylandResource &resource); + ~XdgActivationTokenV1() override; + + void sendToken(const QString &token); + +protected: + void xdg_activation_token_v1_set_serial(Resource *resource, uint32_t serial, struct ::wl_resource *seat) override; + void xdg_activation_token_v1_set_app_id(Resource *resource, const QString &app_id) override; + void xdg_activation_token_v1_set_surface(Resource *resource, struct ::wl_resource *surface) override; + void xdg_activation_token_v1_commit(Resource *resource) override; + void xdg_activation_token_v1_destroy(Resource *resource) override; + +private: + XdgActivationManager *m_manager; + QPointer m_surface; + uint32_t m_serial = 0; + QString m_appId; +}; + +Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_CLASS(XdgActivationManager)