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 f78ad21e..12976ad4 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