Skip to content

Commit 1a23057

Browse files
committed
feat: add xdg-activation-v1 client for SNI tray activation token
Add XdgActivationClient that uses QWaylandClientExtension to bind dde-shell's xdg_activation_v1 global and request activation tokens via standard Wayland protocol before calling SNI Activate. Modified sniprotocolhandler to request token asynchronously before calling Activate, setting XDG_ACTIVATION_TOKEN env var. Log: SNI托盘图标Wayland下点击时申请xdgactivation token激活DBus窗口 Issue: #6
1 parent 54ab4a3 commit 1a23057

6 files changed

Lines changed: 218 additions & 4 deletions

File tree

CMakeLists.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd.
1+
# SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd.
22
#
33
# SPDX-License-Identifier: CC0-1.0
44

@@ -40,6 +40,9 @@ endif()
4040
include(GNUInstallDirs)
4141
include(CMakePackageConfigHelpers)
4242

43+
find_package(PkgConfig REQUIRED)
44+
pkg_get_variable(WAYLAND_PROTOCOLS_DATADIR wayland-protocols pkgdatadir)
45+
4346
add_subdirectory(plugins)
4447
add_subdirectory(src)
4548

plugins/application-tray/CMakeLists.txt

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,15 @@ set(PLUGIN_NAME "application-tray")
77
project(${PLUGIN_NAME})
88

99
find_package(PkgConfig REQUIRED)
10-
find_package(Qt${QT_VERSION_MAJOR} ${REQUIRED_QT_VERSION} REQUIRED Core Gui Widgets DBus)
10+
find_package(Qt${QT_VERSION_MAJOR} ${REQUIRED_QT_VERSION} REQUIRED Core Gui Widgets DBus WaylandClient)
11+
if(Qt${QT_VERSION_MAJOR}_VERSION VERSION_GREATER_EQUAL 6.10)
12+
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS GuiPrivate WaylandClientPrivate REQUIRED)
13+
endif()
1114
find_package(Dtk${DTK_VERSION_MAJOR} REQUIRED Core Gui Widget Tools)
1215
find_package(KF6WindowSystem 6.6 REQUIRED) # for x11 tray selection owner
1316

1417
pkg_check_modules(X11 REQUIRED IMPORTED_TARGET x11 xcb xcb-image xcb-damage xcb-composite xcb-xfixes xcb-util xcb-shape xtst xcb-xtest xcb-res xcb-ewmh)
18+
pkg_check_modules(WaylandClient REQUIRED IMPORTED_TARGET wayland-client)
1519

1620
set(TRAY_SOURCES
1721
api/types/dbusimagelist.cpp api/types/dbusimagelist.h
@@ -29,6 +33,9 @@ set(TRAY_SOURCES
2933
c_ptr.h
3034
traymanager1.cpp traymanager1.h
3135
fdoselectionmanager.cpp fdoselectionmanager.h
36+
37+
${CMAKE_SOURCE_DIR}/src/tray-wayland-integration/xdgactivationclient.h
38+
${CMAKE_SOURCE_DIR}/src/tray-wayland-integration/xdgactivationclient.cpp
3239
)
3340

3441
set_source_files_properties(
@@ -65,6 +72,12 @@ add_library(${PLUGIN_NAME} SHARED
6572
application-tray.qrc
6673
)
6774

75+
qt_generate_wayland_protocol_client_sources(${PLUGIN_NAME}
76+
NO_INCLUDE_CORE_ONLY
77+
FILES
78+
${WAYLAND_PROTOCOLS_DATADIR}/staging/xdg-activation/xdg-activation-v1.xml
79+
)
80+
6881
target_compile_definitions(${PLUGIN_NAME} PRIVATE "QT_PLUGIN")
6982

7083
target_link_libraries(${PLUGIN_NAME}
@@ -74,11 +87,16 @@ target_link_libraries(${PLUGIN_NAME}
7487
Qt${QT_VERSION_MAJOR}::Widgets
7588
Qt${QT_VERSION_MAJOR}::CorePrivate
7689
Qt${QT_VERSION_MAJOR}::WidgetsPrivate
90+
Qt${QT_VERSION_MAJOR}::GuiPrivate
91+
Qt${QT_VERSION_MAJOR}::WaylandClient
92+
Qt${QT_VERSION_MAJOR}::WaylandClientPrivate
93+
Wayland::Client
7794
Dtk${DTK_VERSION_MAJOR}::Core
7895
Dtk${DTK_VERSION_MAJOR}::Gui
7996
Dtk${DTK_VERSION_MAJOR}::Widget
8097
KF6::WindowSystem
8198
PkgConfig::X11
99+
PkgConfig::WaylandClient
82100
dbusmenuqt
83101
dockpluginmanager-interface
84102
)

plugins/application-tray/sniprotocolhandler.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "util.h"
1010
#include "plugin.h"
11+
#include "xdgactivationclient.h"
1112

1213
#include "dbusmenuimporter.h"
1314

@@ -317,7 +318,21 @@ bool SniTrayProtocolHandler::eventFilter(QObject *watched, QEvent *event)
317318
if (event->type() == QEvent::MouseButtonRelease) {
318319
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
319320
if (mouseEvent->button() == Qt::LeftButton) {
320-
m_sniInter->Activate(0, 0);
321+
auto *activationClient = XdgActivationClient::instance();
322+
if (activationClient->isActive()) {
323+
auto widget = static_cast<QWidget*>(parent());
324+
auto *win = widget->window()->windowHandle();
325+
auto sniInter = m_sniInter;
326+
activationClient->requestToken(win, QString(), [sniInter](const QString &token) {
327+
if (!token.isEmpty()) {
328+
qputenv("XDG_ACTIVATION_TOKEN", token.toUtf8());
329+
}
330+
sniInter->Activate(0, 0);
331+
qunsetenv("XDG_ACTIVATION_TOKEN");
332+
});
333+
} else {
334+
m_sniInter->Activate(0, 0);
335+
}
321336
} else if (mouseEvent->button() == Qt::RightButton) {
322337
if (!menuImporter()) {
323338
m_sniInter->ContextMenu(0, 0);

src/tray-wayland-integration/CMakeLists.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
1+
# SPDX-FileCopyrightText: 2023 - 2026 UnionTech Software Technology Co., Ltd.
22
#
33
# SPDX-License-Identifier: CC0-1.0
44

@@ -35,13 +35,16 @@ add_library(dockpluginmanager SHARED
3535
pluginmanagerintegration.cpp
3636
pluginsurface_p.h
3737
pluginsurface.cpp
38+
xdgactivationclient.h
39+
xdgactivationclient.cpp
3840
main.cpp
3941
)
4042

4143
qt_generate_wayland_protocol_client_sources(dockpluginmanager
4244
NO_INCLUDE_CORE_ONLY
4345
FILES
4446
${CMAKE_CURRENT_SOURCE_DIR}/../protocol/plugin-manager-v1.xml
47+
${WAYLAND_PROTOCOLS_DATADIR}/staging/xdg-activation/xdg-activation-v1.xml
4548
)
4649

4750
target_link_libraries(dockpluginmanager
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd.
2+
//
3+
// SPDX-License-Identifier: GPL-3.0-or-later
4+
5+
#include "xdgactivationclient.h"
6+
7+
#include <QGuiApplication>
8+
#include <QLoggingCategory>
9+
#include <QPointer>
10+
#include <QWindow>
11+
#include <QtWaylandClient/QWaylandClientExtension>
12+
13+
#include "qwayland-xdg-activation-v1.h"
14+
#include <private/qwaylanddisplay_p.h>
15+
#include <private/qwaylandinputdevice_p.h>
16+
#include <private/qwaylandwindow_p.h>
17+
18+
Q_LOGGING_CATEGORY(trayXdgActivation, "dde.tray.xdgactivation")
19+
20+
namespace tray {
21+
22+
class XdgActivationTokenV1 : public QObject, public QtWayland::xdg_activation_token_v1
23+
{
24+
Q_OBJECT
25+
public:
26+
~XdgActivationTokenV1() override { destroy(); }
27+
28+
Q_SIGNALS:
29+
void done(const QString &token);
30+
31+
protected:
32+
void xdg_activation_token_v1_done(const QString &token) override
33+
{
34+
Q_EMIT done(token);
35+
}
36+
};
37+
38+
class XdgActivationV1 : public QWaylandClientExtensionTemplate<XdgActivationV1>,
39+
public QtWayland::xdg_activation_v1
40+
{
41+
public:
42+
XdgActivationV1()
43+
: QWaylandClientExtensionTemplate<XdgActivationV1>(1)
44+
{
45+
initialize();
46+
}
47+
48+
~XdgActivationV1() override
49+
{
50+
if (isInitialized())
51+
destroy();
52+
}
53+
54+
XdgActivationTokenV1 *createTokenProvider(QWindow *window, const QString &appId)
55+
{
56+
auto *provider = new XdgActivationTokenV1;
57+
provider->init(get_activation_token());
58+
59+
if (window) {
60+
if (auto *waylandWindow = dynamic_cast<QtWaylandClient::QWaylandWindow *>(window->handle())) {
61+
if (auto *surface = waylandWindow->wlSurface())
62+
provider->set_surface(surface);
63+
if (auto *inputDevice = waylandWindow->display()->lastInputDevice())
64+
provider->set_serial(inputDevice->serial(), inputDevice->wl_seat());
65+
}
66+
}
67+
68+
if (!appId.isEmpty())
69+
provider->set_app_id(appId);
70+
71+
provider->commit();
72+
return provider;
73+
}
74+
};
75+
76+
XdgActivationClient *XdgActivationClient::instance()
77+
{
78+
static XdgActivationClient *s_instance = nullptr;
79+
if (!s_instance) {
80+
s_instance = new XdgActivationClient(qApp);
81+
}
82+
return s_instance;
83+
}
84+
85+
XdgActivationClient::XdgActivationClient(QObject *parent)
86+
: QObject(parent)
87+
, m_activation(new XdgActivationV1)
88+
{
89+
m_activation->setParent(this);
90+
}
91+
92+
XdgActivationClient::~XdgActivationClient() = default;
93+
94+
bool XdgActivationClient::isActive() const
95+
{
96+
return m_activation && m_activation->isActive();
97+
}
98+
99+
void XdgActivationClient::requestToken(QWindow *window, const QString &appId, TokenCallback callback)
100+
{
101+
if (m_pendingProvider) {
102+
qCWarning(trayXdgActivation) << "Token request already in progress";
103+
if (callback) callback(QString());
104+
return;
105+
}
106+
107+
if (!isActive()) {
108+
qCDebug(trayXdgActivation) << "xdg_activation_v1 not active";
109+
if (callback) callback(QString());
110+
return;
111+
}
112+
113+
auto effectiveWindow = window ? window : QGuiApplication::focusWindow();
114+
if (!effectiveWindow) {
115+
qCWarning(trayXdgActivation) << "No target window for activation token";
116+
if (callback) callback(QString());
117+
return;
118+
}
119+
120+
auto *provider = m_activation->createTokenProvider(effectiveWindow, appId);
121+
m_pendingProvider = provider;
122+
123+
connect(provider, &XdgActivationTokenV1::done, this, [this, callback](const QString &token) {
124+
m_pendingProvider = nullptr;
125+
126+
if (token.isEmpty())
127+
qCWarning(trayXdgActivation) << "Empty activation token received";
128+
else
129+
qCDebug(trayXdgActivation) << "Activation token received";
130+
131+
if (callback) callback(token);
132+
sender()->deleteLater();
133+
}, Qt::SingleShotConnection);
134+
}
135+
136+
} // namespace tray
137+
138+
#include "xdgactivationclient.moc"
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd.
2+
//
3+
// SPDX-License-Identifier: GPL-3.0-or-later
4+
5+
#pragma once
6+
7+
#include <QObject>
8+
#include <QPointer>
9+
#include <QString>
10+
#include <functional>
11+
12+
class QWindow;
13+
14+
namespace tray {
15+
16+
class XdgActivationTokenV1;
17+
18+
class XdgActivationClient : public QObject
19+
{
20+
Q_OBJECT
21+
public:
22+
static XdgActivationClient *instance();
23+
24+
bool isActive() const;
25+
26+
using TokenCallback = std::function<void(const QString &token)>;
27+
void requestToken(QWindow *window, const QString &appId, TokenCallback callback);
28+
29+
private:
30+
explicit XdgActivationClient(QObject *parent = nullptr);
31+
~XdgActivationClient() override;
32+
33+
class XdgActivationV1 *m_activation = nullptr;
34+
QPointer<XdgActivationTokenV1> m_pendingProvider;
35+
};
36+
37+
} // namespace tray

0 commit comments

Comments
 (0)