From 02f62c801305917b2d2e221827797c1f1dafe0dc Mon Sep 17 00:00:00 2001 From: yeshanshan Date: Wed, 10 Jun 2026 13:53:18 +0800 Subject: [PATCH] feat: add XdgActivation support for notifications MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Implement ActivationToken signal in notification DBus adapters 2. Add XdgActivation token request for non-extended actions 3. Integrate activation token into dde-am execution for extended actions 4. Add isExtendedAction helper to check for deepin-specific actions Log: Notifications now properly set XDG_ACTIVATION_TOKEN when launching actions Influence: 1. Verify notification action buttons trigger ActivationToken signal 2. Test extended actions (x-deepin-action-*) set XDG_ACTIVATION_TOKEN in environment 3. Confirm non-extended actions emit ActivationToken before ActionInvoked 4. Check token is passed to dde-am process for app launching 5. Verify backward compatibility with existing notification flows 6. Test multiple rapid notifications to ensure proper token handling feat: 通知支持 XdgActivation 协议 1. 在通知DBus适配器中实现ActivationToken信号 2. 为非扩展操作添加XdgActivation令牌请求 3. 将激活令牌集成到扩展操作的dde-am执行中 4. 添加isExtendedAction辅助方法检查深度特定操作 Log: 通知现在在启动操作时正确设置 XDG_ACTIVATION_TOKEN Influence: 1. 验证通知操作按钮触发ActivationToken信号 2. 测试扩展操作(x-deepin-action-*)在环境中设置XDG_ACTIVATION_TOKEN 3. 确认非扩展操作在ActionInvoked之前发出ActivationToken 4. 检查令牌是否正确传递给dde-am进程用于应用启动 5. 验证与现有通知流程的向后兼容性 6. 测试多个快速通知以确保令牌处理正确 --- debian/control | 2 +- frame/wayland/xdgactivation.cpp | 8 ++- panels/notification/server/dbusadaptor.h | 6 +- .../server/notificationmanager.cpp | 65 ++++++++++++++++--- .../notification/server/notificationmanager.h | 4 ++ 5 files changed, 71 insertions(+), 14 deletions(-) diff --git a/debian/control b/debian/control index b30978d23..5370e5c2c 100644 --- a/debian/control +++ b/debian/control @@ -82,7 +82,7 @@ Depends: qml6-module-qtquick-layouts, qml6-module-qtquick-window, qt6-wayland (>= 6.8), - dde-application-manager (>> 1.2.54), + dde-application-manager (>> 1.2.55), ${misc:Depends}, ${shlibs:Depends}, Breaks: diff --git a/frame/wayland/xdgactivation.cpp b/frame/wayland/xdgactivation.cpp index ca5675cf2..118fdf1cc 100644 --- a/frame/wayland/xdgactivation.cpp +++ b/frame/wayland/xdgactivation.cpp @@ -4,8 +4,9 @@ #include "xdgactivation_p.h" -#include #include +#include +#include #include #include #include @@ -150,6 +151,11 @@ void XdgActivation::requestToken(QWindow *window, const QString &appId) qCWarning(dsXdgActivation) << "XDG activation request has empty app id"; auto effectiveWindow = window ? window : QGuiApplication::focusWindow(); + if (!effectiveWindow) { + // fallback to top-level window at cursor position if the focused window is not a Wayland window + qCDebug(dsXdgActivation) << "No focused window, falling back to top-level window at cursor position"; + effectiveWindow = QGuiApplication::topLevelAt(QCursor::pos()); + } if (!effectiveWindow) { qCWarning(dsXdgActivation) << "XDG activation request has no target window"; Q_EMIT tokenReady({}); diff --git a/panels/notification/server/dbusadaptor.h b/panels/notification/server/dbusadaptor.h index 243921756..cc1de93d4 100644 --- a/panels/notification/server/dbusadaptor.h +++ b/panels/notification/server/dbusadaptor.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -32,7 +32,7 @@ public Q_SLOTS: // methods Q_SIGNALS: void ActionInvoked(uint id, const QString &actionKey); void NotificationClosed(uint id, uint reason); - // todo void ActivationToken(uint id, const QString &activationToken) + void ActivationToken(uint id, const QString &token); }; class DDENotificationDbusAdaptor : public QDBusAbstractAdaptor @@ -54,7 +54,7 @@ public Q_SLOTS: // methods Q_SIGNALS: void ActionInvoked(uint id, const QString &actionKey); void NotificationClosed(uint id, uint reason); - // todo void ActivationToken(uint id, const QString &activationToken) + void ActivationToken(uint id, const QString &token); public Q_SLOTS: // methods uint recordCount() const; diff --git a/panels/notification/server/notificationmanager.cpp b/panels/notification/server/notificationmanager.cpp index b8f565c90..74c564197 100644 --- a/panels/notification/server/notificationmanager.cpp +++ b/panels/notification/server/notificationmanager.cpp @@ -26,6 +26,7 @@ #include #include +#include DCORE_USE_NAMESPACE DS_USE_NAMESPACE @@ -134,8 +135,30 @@ void NotificationManager::actionInvoked(qint64 id, uint bubbleId, const QString qInfo(notifyLog) << "Action invoked, bubbleId:" << bubbleId << ", id:" << id << ", actionKey" << actionKey; actionInvoked(id, actionKey); - Q_EMIT ActionInvoked(bubbleId, actionKey); - Q_EMIT NotificationClosed(bubbleId, NotifyEntity::Closed); + if (isExtendedAction(id, actionKey)) { + // Extended action (x-deepin-action-*) handles token request internally in doActionInvoked, + // so we skip the outer token request to avoid requesting twice. + Q_EMIT ActionInvoked(bubbleId, actionKey); + Q_EMIT NotificationClosed(bubbleId, NotifyEntity::Closed); + } else { + // For non-extended actions, emit ActivationToken first if available + auto *activation = new DS_NAMESPACE::XdgActivation(this); + connect( + activation, + &DS_NAMESPACE::XdgActivation::tokenReady, + this, + [this, bubbleId, actionKey, activation](const QString &token) { + if (!token.isEmpty()) { + Q_EMIT ActivationToken(bubbleId, token); + qDebug(notifyLog) << "Emitted ActivationToken for non-extended action:" << token; + } + Q_EMIT ActionInvoked(bubbleId, actionKey); + Q_EMIT NotificationClosed(bubbleId, NotifyEntity::Closed); + activation->deleteLater(); + }, + Qt::SingleShotConnection); + activation->requestToken(); + } } void NotificationManager::notificationClosed(qint64 id, uint bubbleId, uint reason) @@ -542,6 +565,16 @@ QString NotificationManager::appIdByAppName(const QString &appName) const return QString(); } +bool NotificationManager::isExtendedAction(qint64 id, const QString &actionId) const +{ + auto entity = m_persistence->fetchEntity(id); + if (!entity.isValid()) { + return false; + } + QMap hints = entity.hints(); + return hints.contains("x-deepin-action-" + actionId); +} + void NotificationManager::doActionInvoked(const NotifyEntity &entity, const QString &actionId) { qDebug(notifyLog) << "Invoke the notification:" << entity.id() << entity.appName() << actionId; @@ -566,13 +599,27 @@ void NotificationManager::doActionInvoked(const NotifyEntity &entity, const QStr amArgs << "--" << args; } - QProcess pro; - pro.setProgram("dde-am"); - pro.setArguments(amArgs); - QProcessEnvironment proEnv = QProcessEnvironment::systemEnvironment(); - proEnv.remove("DSG_APP_ID"); - pro.setProcessEnvironment(proEnv); - pro.startDetached(); + // Get activation token, then start process + auto *activation = new DS_NAMESPACE::XdgActivation(this); + auto amArgsCopy = amArgs; + connect(activation, &DS_NAMESPACE::XdgActivation::tokenReady, this, + [amArgsCopy, activation](const QString &token) { + QProcess pro; + pro.setProgram("dde-am"); + pro.setArguments(amArgsCopy); + QProcessEnvironment proEnv = QProcessEnvironment::systemEnvironment(); + proEnv.remove("DSG_APP_ID"); + + if (!token.isEmpty()) { + proEnv.insert("XDG_ACTIVATION_TOKEN", token); + qDebug(notifyLog) << "Set XDG_ACTIVATION_TOKEN for extended action:" << token; + } + + pro.setProcessEnvironment(proEnv); + pro.startDetached(); + activation->deleteLater(); + }, Qt::SingleShotConnection); + activation->requestToken(); } } else if (i.key() == "deepin-dde-shell-action-" + actionId) { const QString data(i.value().toString()); diff --git a/panels/notification/server/notificationmanager.h b/panels/notification/server/notificationmanager.h index 790ab7f59..f2756669d 100644 --- a/panels/notification/server/notificationmanager.h +++ b/panels/notification/server/notificationmanager.h @@ -50,6 +50,9 @@ class NotificationManager : public QObject, public QDBusContext void NotificationStateChanged(qint64 id, int processedType); + // Activation token signal for Wayland + void ActivationToken(uint id, const QString &token); + public Q_SLOTS: // Standard Notifications dbus implementation QStringList GetCapabilities(); @@ -78,6 +81,7 @@ public Q_SLOTS: QString appIdByAppName(const QString &appName) const; void doActionInvoked(const NotifyEntity &entity, const QString &actionId); + bool isExtendedAction(qint64 id, const QString &actionId) const; bool invokeShellAction(const QString &data); void initScreenLockedState();