From b1221908db472d084dd70053fe5892f6268e7254 Mon Sep 17 00:00:00 2001 From: yeshanshan Date: Wed, 1 Jul 2026 13:07:47 +0800 Subject: [PATCH] feat: replace custom XDG activation with Dde::Shell integration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Remove custom XdgActivationV1 implementation in src/ddeintegration 2. Use ds::XdgActivation from Dde::Shell library instead 3. Refactor launchByDesktopId to use asynchronous token request 4. Update CMakeLists to link against Dde::Shell and raise libdde-shell- dev dependency to >= 2.0.45 5. Remove wayland-protocols build dependency from debian/control 6. Remove obsolete HAVE_WAYLAND_XDG_ACTIVATION compile flag and related conditionals 7. Fix launch order: move LauncherController.visible = false after launch call to ensure proper window lifecycle Log: Replaced customWayland XDG activation implementation with Dde::Shell's standard XdgActivation API for improved maintainability and compatibility with newer DDE versions. Influence: 1. Verify application launch works correctly via desktop ID 2. Test XDG activation token flow on Wayland compositors 3. Ensure backward compatibility with older DDE versions that do not provide Dde::Shell 4. Check build system changes do not break existing builds 5. Verify LauncherController visibility toggle works as expected after launching apps 6. Test on both Wayland and X11 sessions (fallback behavior) 7. Verify token timeout and error handling (compositor not advertising xdg_activation_v1) feat: 使用 Dde::Shell 集成替换自定义 XDG 激活实现 1. 移除 src/ddeintegration 中的自定义 XdgActivationV1 实现 2. 改用 Dde::Shell 库中的 ds::XdgActivation 3. 重构 launchByDesktopId 使用异步令牌请求 4. 更新 CMakeLists 链接 Dde::Shell,并将 libdde-shell-dev 依赖提升至 >= 2.0.45 5. 从 debian/control 中移除 wayland-protocols 构建依赖 6. 移除已废弃的 HAVE_WAYLAND_XDG_ACTIVATION 编译标志及相关条件编译 7. 修复启动顺序:将 LauncherController.visible = false 移到启动调用之后 以确保窗口生命周期正确 Log: 用 Dde::Shell 提供的标准 XdgActivation API 替换了自定义的 Wayland XDG 激活实现,提升了可维护性和与新版 DDE 的兼容性。 Influence: 1. 验证通过桌面 ID 启动应用程序功能是否正常 2. 在 Wayland 合成器上测试 XDG 激活令牌流程 3. 确保与不提供 Dde::Shell 的旧版 DDE 的向后兼容性 4. 检查构建系统变更不会破坏现有构建 5. 验证启动应用后 LauncherController 的可见性切换功能 6. 在 Wayland 和 X11 会话上测试(回退行为) 7. 测试令牌超时和错误处理(合成器未通告 xdg_activation_v1) PMS: BUG-365883 --- CMakeLists.txt | 2 + debian/control | 3 +- desktopintegration.cpp | 35 ++--- .../package/launcheritem.qml | 2 +- src/ddeintegration/CMakeLists.txt | 52 -------- src/ddeintegration/xdgactivation.cpp | 125 ------------------ src/ddeintegration/xdgactivation.h | 53 -------- 7 files changed, 13 insertions(+), 259 deletions(-) delete mode 100644 src/ddeintegration/xdgactivation.cpp delete mode 100644 src/ddeintegration/xdgactivation.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 1c530ac6..5b2387dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,7 @@ find_package(QT NAMES ${QT_NS} REQUIRED COMPONENTS Core) find_package(${QT_NS} REQUIRED COMPONENTS Core Gui Concurrent Qml Svg Quick QuickControls2 LinguistTools) find_package(${DTK_NS} REQUIRED COMPONENTS Core Gui) find_package(${ASQT_NS} 1.0 REQUIRED) +find_package(DDEShell REQUIRED) find_package(ECM NO_MODULE) if(Qt6_VERSION VERSION_GREATER_EQUAL 6.10) find_package(Qt6 COMPONENTS GuiPrivate REQUIRED) @@ -147,6 +148,7 @@ target_link_libraries(launchpadcommon PUBLIC launcher-models dde-integration-dbus treeland-integration + Dde::Shell ) if (HAVE_DDE_API_EVENTLOGGER) diff --git a/debian/control b/debian/control index 0a63cb37..f2fc7210 100644 --- a/debian/control +++ b/debian/control @@ -17,7 +17,6 @@ Build-Depends: qt6-tools-dev-tools, qt6-wayland-dev, qt6-wayland-private-dev, - wayland-protocols, libdtkcommon-dev, libdtk6core-dev (>= 6.0.43), # v-- provides qdbusxml2cpp-fix binary @@ -26,7 +25,7 @@ Build-Depends: libdtk6gui-dev (>= 6.0.19), libdtk6declarative-dev (>> 6.7.33), dde-api-dev (>> 6.0.39), - libdde-shell-dev (>= 0.0.10), + libdde-shell-dev (>= 2.0.45), libappstreamqt-dev (>= 1.0.0) Standards-Version: 4.6.0 Rules-Requires-Root: no diff --git a/desktopintegration.cpp b/desktopintegration.cpp index 78946301..a6ca7130 100644 --- a/desktopintegration.cpp +++ b/desktopintegration.cpp @@ -15,10 +15,7 @@ #include #include -#ifdef HAVE_WAYLAND_XDG_ACTIVATION #include -#include -#endif #include @@ -51,16 +48,15 @@ void DesktopIntegration::openSystemSettings() void DesktopIntegration::launchByDesktopId(const QString &desktopId) { qCInfo(logDesktopIntegration) << "Launching app by desktop ID:" << desktopId; - QString token; -#ifdef HAVE_WAYLAND_XDG_ACTIVATION - auto xdgActivation = DDEIntegration::XdgActivationV1::instance(); - if (xdgActivation->isActive()) - token = xdgActivation->requestToken(QGuiApplication::focusWindow(), desktopId); -#endif - if (!AppMgr::launchApp(desktopId, token)) { - qCDebug(logDesktopIntegration) << "AppMgr launch failed, trying AppInfo launch"; - AppInfo::launchByDesktopId(desktopId); - } + auto *activation = new ds::XdgActivation(&instance()); + connect(activation, &ds::XdgActivation::tokenReady, &instance(), [desktopId, activation](const QString &token) { + if (!AppMgr::launchApp(desktopId, token)) { + qCDebug(logDesktopIntegration) << "AppMgr launch failed, trying AppInfo launch"; + AppInfo::launchByDesktopId(desktopId); + } + activation->deleteLater(); + }); + activation->requestToken(); } QString DesktopIntegration::environmentVariable(const QString &env) @@ -266,19 +262,6 @@ DesktopIntegration::DesktopIntegration(QObject *parent) m_iconScaleFactor = dconfig->value("iconScaleFactor", 1.0).toReal(); qCInfo(logDesktopIntegration) << "Icon scale factor loaded:" << m_iconScaleFactor; -#ifdef HAVE_WAYLAND_XDG_ACTIVATION - if (DTK_GUI_NAMESPACE::DGuiApplicationHelper::testAttribute( - DTK_GUI_NAMESPACE::DGuiApplicationHelper::IsWaylandPlatform)) { - auto *xdgActivation = DDEIntegration::XdgActivationV1::instance(); - connect(xdgActivation, &DDEIntegration::XdgActivationV1::activeChanged, this, []() { - if (DDEIntegration::XdgActivationV1::instance()->isActive()) - qCInfo(logDesktopIntegration) << "XdgActivationV1: ready, XDG activation token support enabled"; - else - qCWarning(logDesktopIntegration) << "XdgActivationV1: compositor did not advertise xdg_activation_v1, token requests will be skipped"; - }); - } -#endif - connect(m_dockIntegration, &DdeDock::directionChanged, this, &DesktopIntegration::dockPositionChanged); connect(m_dockIntegration, &DdeDock::geometryChanged, this, &DesktopIntegration::dockGeometryChanged); connect(m_appearanceIntegration, &Appearance::wallpaperBlurhashChanged, this, &DesktopIntegration::backgroundUrlChanged); diff --git a/shell-launcher-applet/package/launcheritem.qml b/shell-launcher-applet/package/launcheritem.qml index b3953703..17201c99 100644 --- a/shell-launcher-applet/package/launcheritem.qml +++ b/shell-launcher-applet/package/launcheritem.qml @@ -152,8 +152,8 @@ AppletItem { } function launchApp(desktopId) { - LauncherController.visible = false; DesktopIntegration.launchByDesktopId(desktopId); + LauncherController.visible = false; } function assignFullscreenFrameScreen() { diff --git a/src/ddeintegration/CMakeLists.txt b/src/ddeintegration/CMakeLists.txt index 0d2e8635..d49f67c3 100644 --- a/src/ddeintegration/CMakeLists.txt +++ b/src/ddeintegration/CMakeLists.txt @@ -150,55 +150,3 @@ if (HAVE_DDE_API_EVENTLOGGER) target_compile_definitions(dde-integration-dbus PRIVATE HAVE_DDE_API_EVENTLOGGER) target_link_libraries(dde-integration-dbus PRIVATE DDEAPI::EventLogger) endif() - -# Optional Wayland XDG activation support: request a token from the compositor -# before launching applications so they can raise their window on first map. -# Requires Qt6WaylandClient and wayland-protocols (for xdg-activation-v1.xml). -find_package(Qt6 COMPONENTS WaylandClient QUIET) -find_package(PkgConfig QUIET) -if(Qt6WaylandClient_FOUND AND PkgConfig_FOUND) - pkg_check_modules(WaylandProtocols QUIET wayland-protocols) - if(WaylandProtocols_FOUND) - pkg_get_variable(WAYLAND_PROTOCOLS_DATADIR wayland-protocols pkgdatadir) - set(XDG_ACTIVATION_XML - "${WAYLAND_PROTOCOLS_DATADIR}/staging/xdg-activation/xdg-activation-v1.xml") - endif() -endif() - -if(Qt6WaylandClient_FOUND AND DEFINED XDG_ACTIVATION_XML AND EXISTS "${XDG_ACTIVATION_XML}") - if(Qt6_VERSION VERSION_GREATER_EQUAL 6.10) - find_package(Qt6 COMPONENTS WaylandClientPrivate QUIET) - endif() - - qt_generate_wayland_protocol_client_sources(dde-integration-dbus - FILES - ${XDG_ACTIVATION_XML} - ) - - target_sources(dde-integration-dbus PRIVATE - xdgactivation.cpp - ) - - target_sources(dde-integration-dbus PUBLIC - FILE_SET HEADERS FILES xdgactivation.h - ) - - # The Wayland protocol generator emits headers into the binary dir; - # expose it publicly so consumers (e.g. launchpadcommon) can find them. - target_include_directories(dde-integration-dbus PUBLIC - ${CMAKE_CURRENT_BINARY_DIR} - ) - - target_link_libraries(dde-integration-dbus PUBLIC - Qt6::WaylandClient - Qt6::WaylandClientPrivate - ${DTK_NS}::Gui - ) - - target_compile_definitions(dde-integration-dbus PUBLIC - HAVE_WAYLAND_XDG_ACTIVATION - ) - message(STATUS "XDG activation support enabled (${XDG_ACTIVATION_XML})") -else() - message(STATUS "XDG activation support disabled (Qt6WaylandClient or wayland-protocols not found)") -endif() diff --git a/src/ddeintegration/xdgactivation.cpp b/src/ddeintegration/xdgactivation.cpp deleted file mode 100644 index 0641b062..00000000 --- a/src/ddeintegration/xdgactivation.cpp +++ /dev/null @@ -1,125 +0,0 @@ -// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. -// -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "xdgactivation.h" - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -Q_DECLARE_LOGGING_CATEGORY(logDdeIntegration) - -namespace DDEIntegration { - -// --------------------------------------------------------------------------- -// XdgActivationTokenV1 -// --------------------------------------------------------------------------- - -XdgActivationTokenV1::~XdgActivationTokenV1() -{ - destroy(); -} - -void XdgActivationTokenV1::xdg_activation_token_v1_done(const QString &token) -{ - Q_EMIT done(token); -} - -// --------------------------------------------------------------------------- -// XdgActivationV1 -// --------------------------------------------------------------------------- - -XdgActivationV1 *XdgActivationV1::instance() -{ - // Not a function-local static: a static is destroyed at process exit, after - // ~QGuiApplication has dropped the Wayland connection, so destroy() in the - // destructor would marshal onto a freed wl_proxy and crash. Parent to qApp so - // it's torn down with the Qt object tree instead. - static QPointer s_instance; - if (!s_instance) { - s_instance = new XdgActivationV1; - s_instance->setParent(qApp); - } - return s_instance; -} - -XdgActivationV1::XdgActivationV1() - : QWaylandClientExtensionTemplate(1) -{ -} - -XdgActivationV1::~XdgActivationV1() -{ - // Runs only at shutdown, when qApp and the Wayland connection may already be - // gone; guard so we never send destroy() onto a dead wl_display. - if (qApp && isInitialized()) - destroy(); -} - -QString XdgActivationV1::requestToken(QWindow *window, const QString &appId) -{ - if (!isActive()) { - qCWarning(logDdeIntegration) << "xdg_activation_v1 is not active, cannot request token"; - return {}; - } - - auto *provider = new XdgActivationTokenV1; - provider->init(get_activation_token()); - - // Attach the surface and input serial of the requesting window so the - // compositor can verify focus and apply focus-stealing-prevention rules. - if (window) { - if (auto *waylandWindow = - dynamic_cast(window->handle())) { - if (auto *surface = waylandWindow->wlSurface()) { - provider->set_surface(surface); - } - // set_serial tells the compositor which input event triggered this - // launch request; without it the compositor may deny focus for the - // new window (focus-stealing prevention). - if (auto *inputDevice = waylandWindow->display()->lastInputDevice()) { - provider->set_serial(inputDevice->serial(), inputDevice->wl_seat()); - } - } - } - - if (!appId.isEmpty()) - provider->set_app_id(appId); - - provider->commit(); - - // Block until the compositor delivers the token or the timeout fires. - QString token; - QEventLoop loop; - QTimer timeout; - timeout.setSingleShot(true); - timeout.setInterval(2000); - - connect(provider, &XdgActivationTokenV1::done, &loop, - [&token, &loop](const QString &t) { - token = t; - loop.quit(); - }); - connect(&timeout, &QTimer::timeout, &loop, &QEventLoop::quit); - - timeout.start(); - loop.exec(); - - if (token.isEmpty()) - qCWarning(logDdeIntegration) << "XDG activation token request timed out"; - else - qCDebug(logDdeIntegration) << "Received XDG activation token for app:" << appId; - - provider->deleteLater(); - return token; -} - -} // namespace DDEIntegration diff --git a/src/ddeintegration/xdgactivation.h b/src/ddeintegration/xdgactivation.h deleted file mode 100644 index 837c16ec..00000000 --- a/src/ddeintegration/xdgactivation.h +++ /dev/null @@ -1,53 +0,0 @@ -// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. -// -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include -#include - -#include "qwayland-xdg-activation-v1.h" - -class QWindow; - -namespace DDEIntegration { - -// Token provider: wraps the xdg_activation_token_v1 object and emits done() -// when the compositor delivers the token. -class XdgActivationTokenV1 : public QObject, public QtWayland::xdg_activation_token_v1 -{ - Q_OBJECT -public: - ~XdgActivationTokenV1() override; - -Q_SIGNALS: - void done(const QString &token); - -protected: - void xdg_activation_token_v1_done(const QString &token) override; -}; - -// Client extension: binds to the xdg_activation_v1 global and allows -// requesting activation tokens. -class XdgActivationV1 : public QWaylandClientExtensionTemplate, - public QtWayland::xdg_activation_v1 -{ - Q_OBJECT -public: - // Returns the process-wide singleton instance (created on first call). - static XdgActivationV1 *instance(); - - ~XdgActivationV1() override; - - // Synchronously request a token (blocks with a nested event loop until the - // compositor delivers it or the 2-second timeout elapses). - // Returns an empty string when not running on Wayland or when the - // compositor does not expose the extension. - QString requestToken(QWindow *window, const QString &appId); - -private: - explicit XdgActivationV1(); -}; - -} // namespace DDEIntegration