Skip to content

Commit 1895028

Browse files
committed
feat: register tray selectionmanager on wayland
支持在 wayland 环境下为 Xembed 支持注册 selectionowner 并暴露相关 托盘到 dbus 接口上。 Log:
1 parent ccc873c commit 1895028

10 files changed

Lines changed: 551 additions & 3 deletions

File tree

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
CMAKE_MINIMUM_REQUIRED(VERSION 3.16)
66

77
cmake_policy(SET CMP0160 OLD)
8-
set(DTL_VERSION "1.99.0" CACHE STRING "Define project version")
8+
set(DTL_VERSION "2.0.99" CACHE STRING "Define project version")
99
set(DOCK_VERSION "6.0.37" CACHE STRING "Dock compatible version")
1010

1111
project(dde-tray-loader

debian/control

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ Build-Depends: debhelper-compat ( =12),
3030
libgtest-dev,
3131
libgmock-dev,
3232
libxcursor-dev,
33+
libkf6windowsystem-dev,
3334
dde-api-dev (>>5.6.3)
3435
Standards-Version: 3.9.8
3536
Homepage: http://www.deepin.org/
@@ -42,6 +43,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends},
4243
libdtk6core,
4344
libdtk6gui,
4445
dde-qt6xcb-plugin,
46+
libkf6windowsystem6,
4547
dde-daemon (>=6.1.26),
4648
lastore-daemon (>=5.2.9),
4749
Conflicts:

plugins/application-tray/CMakeLists.txt

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,38 @@ project(${PLUGIN_NAME})
99
find_package(PkgConfig REQUIRED)
1010
find_package(Qt${QT_VERSION_MAJOR} ${REQUIRED_QT_VERSION} REQUIRED Core Gui Widgets DBus)
1111
find_package(Dtk${DTK_VERSION_MAJOR} REQUIRED Core Gui Widget)
12+
find_package(KF6WindowSystem 6.6 REQUIRED) # for x11 tray section owner
1213

13-
pkg_check_modules(X11 REQUIRED IMPORTED_TARGET x11 xcb xcb-image xcb-composite xcb-xfixes xcb-util xcb-shape xtst xcb-xtest xcb-res xcb-ewmh)
14+
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)
1415

15-
file (GLOB TRAY_SOURCES *.cpp *.h api/types/*.cpp)
16+
set(TRAY_SOURCES
17+
api/types/dbusimagelist.cpp api/types/dbusimagelist.h
18+
api/types/dbustooltip.cpp api/types/dbustooltip.h
19+
api/types/traylist.h
20+
21+
abstracttrayprotocol.h
22+
ddeindicatortrayprotocol.cpp ddeindicatortrayprotocol.h
23+
sniprotocolhandler.cpp sniprotocolhandler.h
24+
trayplugin.cpp trayplugin.h
25+
traywidget.cpp traywidget.h
26+
util.cpp util.h
27+
xembedprotocolhandler.cpp xembedprotocolhandler.h
28+
29+
c_ptr.h
30+
traymanager1.cpp traymanager1.h
31+
fdoselectionmanager.cpp fdoselectionmanager.h
32+
)
1633

1734
set_source_files_properties(
1835
${CMAKE_CURRENT_SOURCE_DIR}/api/dbus/org.deepin.dde.TrayManager1.xml
1936
PROPERTIES INCLUDE api/types/traylist.h
2037
CLASSNAME TrayManager
2138
)
2239

40+
qt_add_dbus_adaptor(TRAY_SOURCES
41+
api/dbus/org.deepin.dde.TrayManager1.xml traymanager1.h TrayManager1
42+
)
43+
2344
set_source_files_properties(
2445
${CMAKE_CURRENT_SOURCE_DIR}/api/dbus/org.kde.StatusNotifierItem.xml
2546
PROPERTIES INCLUDE api/types/dbusimagelist.h
@@ -50,6 +71,7 @@ target_link_libraries(${PLUGIN_NAME}
5071
Dtk${DTK_VERSION_MAJOR}::Core
5172
Dtk${DTK_VERSION_MAJOR}::Gui
5273
Dtk${DTK_VERSION_MAJOR}::Widget
74+
KF6::WindowSystem
5375
PkgConfig::X11
5476
dbusmenuqt
5577
dockpluginmanager-interface

plugins/application-tray/c_ptr.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
SPDX-FileCopyrightText: 2022 Xaver Hugl <xaver.hugl@gmail.com>
3+
4+
SPDX-License-Identifier: LGPL-2.1-or-later
5+
*/
6+
#pragma once
7+
#include <memory>
8+
9+
struct CDeleter {
10+
template<typename T>
11+
void operator()(T *ptr)
12+
{
13+
if (ptr) {
14+
free(ptr);
15+
}
16+
}
17+
};
18+
19+
template<typename T>
20+
using UniqueCPointer = std::unique_ptr<T, CDeleter>;
Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
/*
2+
Registers as a embed container
3+
SPDX-FileCopyrightText: 2015 David Edmundson <davidedmundson@kde.org>
4+
SPDX-FileCopyrightText: 2019 Konrad Materka <materka@gmail.com>
5+
SPDX-FileCopyrightText: 2025 Wang Zichong <wangzichong@deepin.org>
6+
7+
SPDX-License-Identifier: LGPL-2.1-or-later
8+
*/
9+
#include "fdoselectionmanager.h"
10+
11+
#include <QTimer>
12+
#include <QDBusConnection>
13+
#include <QLoggingCategory>
14+
15+
#include <KSelectionOwner>
16+
17+
#include <xcb/composite.h>
18+
#include <xcb/damage.h>
19+
#include <xcb/xcb_atom.h>
20+
#include <xcb/xcb_event.h>
21+
22+
#include "traymanager1.h"
23+
#include "c_ptr.h"
24+
#include "util.h"
25+
26+
using Util = tray::Util;
27+
28+
#define SYSTEM_TRAY_REQUEST_DOCK 0
29+
#define SYSTEM_TRAY_BEGIN_MESSAGE 1
30+
#define SYSTEM_TRAY_CANCEL_MESSAGE 2
31+
32+
Q_LOGGING_CATEGORY(SELECTIONMGR, "org.deepin.dde.trayloader.selectionmgr")
33+
34+
FdoSelectionManager::FdoSelectionManager(QObject *parent)
35+
: QObject(parent)
36+
, m_selectionOwner(new KSelectionOwner(UTIL->getAtomFromDisplay("_NET_SYSTEM_TRAY"), UTIL->getX11Connection(), UTIL->getRootWindow(), this))
37+
{
38+
qDebug(SELECTIONMGR) << "starting";
39+
40+
// we may end up calling QCoreApplication::quit() in this method, at which point we need the event loop running
41+
QTimer::singleShot(0, this, &FdoSelectionManager::init);
42+
}
43+
44+
FdoSelectionManager::~FdoSelectionManager()
45+
{
46+
qCDebug(SELECTIONMGR) << "closing";
47+
m_selectionOwner->release();
48+
}
49+
50+
void FdoSelectionManager::init()
51+
{
52+
// load damage extension
53+
xcb_connection_t *c = Util::instance()->getX11Connection();
54+
xcb_prefetch_extension_data(c, &xcb_damage_id);
55+
const auto *reply = xcb_get_extension_data(c, &xcb_damage_id);
56+
if (reply && reply->present) {
57+
m_damageEventBase = reply->first_event;
58+
xcb_damage_query_version_unchecked(c, XCB_DAMAGE_MAJOR_VERSION, XCB_DAMAGE_MINOR_VERSION);
59+
} else {
60+
// no XDamage means
61+
qCCritical(SELECTIONMGR) << "could not load damage extension. Quitting";
62+
qApp->exit(-1);
63+
}
64+
65+
qApp->installNativeEventFilter(this);
66+
67+
connect(m_selectionOwner, &KSelectionOwner::claimedOwnership, this, &FdoSelectionManager::onClaimedOwnership);
68+
connect(m_selectionOwner, &KSelectionOwner::failedToClaimOwnership, this, &FdoSelectionManager::onFailedToClaimOwnership);
69+
connect(m_selectionOwner, &KSelectionOwner::lostOwnership, this, &FdoSelectionManager::onLostOwnership);
70+
m_selectionOwner->claim(false);
71+
}
72+
73+
bool FdoSelectionManager::addDamageWatch(xcb_window_t client)
74+
{
75+
qCDebug(SELECTIONMGR) << "adding damage watch for " << client;
76+
77+
xcb_connection_t *c = Util::instance()->getX11Connection();
78+
const auto attribsCookie = xcb_get_window_attributes_unchecked(c, client);
79+
80+
const auto damageId = xcb_generate_id(c);
81+
m_damageWatches[client] = damageId;
82+
xcb_damage_create(c, damageId, client, XCB_DAMAGE_REPORT_LEVEL_NON_EMPTY);
83+
84+
xcb_generic_error_t *error = nullptr;
85+
UniqueCPointer<xcb_get_window_attributes_reply_t> attr(xcb_get_window_attributes_reply(c, attribsCookie, &error));
86+
UniqueCPointer<xcb_generic_error_t> getAttrError(error);
87+
uint32_t events = XCB_EVENT_MASK_STRUCTURE_NOTIFY;
88+
if (attr) {
89+
events = events | attr->your_event_mask;
90+
}
91+
// if window is already gone, there is no need to handle it.
92+
if (getAttrError && getAttrError->error_code == XCB_WINDOW) {
93+
return false;
94+
}
95+
// the event mask will not be removed again. We cannot track whether another component also needs STRUCTURE_NOTIFY (e.g. KWindowSystem).
96+
// if we would remove the event mask again, other areas will break.
97+
const auto changeAttrCookie = xcb_change_window_attributes_checked(c, client, XCB_CW_EVENT_MASK, &events);
98+
UniqueCPointer<xcb_generic_error_t> changeAttrError(xcb_request_check(c, changeAttrCookie));
99+
// if window is gone by this point, it will be caught by eventFilter, so no need to check later errors.
100+
if (changeAttrError && changeAttrError->error_code == XCB_WINDOW) {
101+
return false;
102+
}
103+
104+
return true;
105+
}
106+
107+
bool FdoSelectionManager::nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result)
108+
{
109+
Q_UNUSED(result)
110+
111+
if (eventType != "xcb_generic_event_t") {
112+
return false;
113+
}
114+
115+
auto *ev = static_cast<xcb_generic_event_t *>(message);
116+
117+
const auto responseType = XCB_EVENT_RESPONSE_TYPE(ev);
118+
if (responseType == XCB_CLIENT_MESSAGE) {
119+
const auto ce = reinterpret_cast<xcb_client_message_event_t *>(ev);
120+
if (ce->type == UTIL->getAtomByName("_NET_SYSTEM_TRAY_OPCODE")) {
121+
switch (ce->data.data32[1]) {
122+
case SYSTEM_TRAY_REQUEST_DOCK:
123+
dock(ce->data.data32[2]);
124+
return true;
125+
}
126+
}
127+
} else if (responseType == XCB_UNMAP_NOTIFY) {
128+
// const auto unmappedWId = reinterpret_cast<xcb_unmap_notify_event_t *>(ev)->window;
129+
// if (m_proxies.contains(unmappedWId)) {
130+
// undock(unmappedWId);
131+
// }
132+
} else if (responseType == XCB_DESTROY_NOTIFY) {
133+
const auto destroyedWId = reinterpret_cast<xcb_destroy_notify_event_t *>(ev)->window;
134+
if (m_trayManager->haveIcon(destroyedWId)) {
135+
undock(destroyedWId);
136+
}
137+
} else if (responseType == m_damageEventBase + XCB_DAMAGE_NOTIFY) {
138+
const auto damagedWId = reinterpret_cast<xcb_damage_notify_event_t *>(ev)->drawable;
139+
m_trayManager->notifyIconChanged(damagedWId);
140+
} else if (responseType == XCB_CONFIGURE_REQUEST) {
141+
// const auto event = reinterpret_cast<xcb_configure_request_event_t *>(ev);
142+
// const auto tmProxy = m_proxies.value(event->window);
143+
// if (tmProxy) {
144+
// // The embedded window tries to move or resize. Ignore move, handle resize only.
145+
// if ((event->value_mask & XCB_CONFIG_WINDOW_WIDTH) || (event->value_mask & XCB_CONFIG_WINDOW_HEIGHT)) {
146+
// tmProxy->resizeWindow(event->width, event->height);
147+
// }
148+
// }
149+
}
150+
151+
return false;
152+
}
153+
154+
void FdoSelectionManager::dock(xcb_window_t winId)
155+
{
156+
Q_CHECK_PTR(m_trayManager);
157+
qCDebug(SELECTIONMGR) << "trying to dock window " << winId;
158+
159+
if (m_trayManager->haveIcon(winId)) {
160+
return;
161+
}
162+
163+
if (addDamageWatch(winId)) {
164+
// Register with TrayManager1 if available
165+
m_trayManager->registerIcon(winId);
166+
}
167+
}
168+
169+
void FdoSelectionManager::undock(xcb_window_t winId)
170+
{
171+
Q_CHECK_PTR(m_trayManager);
172+
qCDebug(SELECTIONMGR) << "trying to undock window " << winId;
173+
174+
if (m_trayManager->haveIcon(winId)) {
175+
return;
176+
}
177+
178+
// Unregister from TrayManager1 if available
179+
m_trayManager->unregisterIcon(winId);
180+
181+
// m_proxies[winId]->deleteLater();
182+
// m_proxies.remove(winId);
183+
}
184+
185+
void FdoSelectionManager::onClaimedOwnership()
186+
{
187+
qCDebug(SELECTIONMGR) << "Manager selection claimed";
188+
189+
initTrayManager();
190+
setSystemTrayVisual();
191+
}
192+
193+
void FdoSelectionManager::onFailedToClaimOwnership()
194+
{
195+
qCWarning(SELECTIONMGR) << "failed to claim ownership of Systray Manager";
196+
qApp->exit(-1);
197+
}
198+
199+
void FdoSelectionManager::onLostOwnership()
200+
{
201+
qCWarning(SELECTIONMGR) << "lost ownership of Systray Manager";
202+
qApp->exit(-1);
203+
}
204+
205+
void FdoSelectionManager::setSystemTrayVisual()
206+
{
207+
xcb_connection_t *c = Util::instance()->getX11Connection();
208+
auto screen = xcb_setup_roots_iterator(xcb_get_setup(c)).data;
209+
auto trayVisual = screen->root_visual;
210+
xcb_depth_iterator_t depth_iterator = xcb_screen_allowed_depths_iterator(screen);
211+
xcb_depth_t *depth = nullptr;
212+
213+
while (depth_iterator.rem) {
214+
if (depth_iterator.data->depth == 32) {
215+
depth = depth_iterator.data;
216+
break;
217+
}
218+
xcb_depth_next(&depth_iterator);
219+
}
220+
221+
if (depth) {
222+
xcb_visualtype_iterator_t visualtype_iterator = xcb_depth_visuals_iterator(depth);
223+
while (visualtype_iterator.rem) {
224+
xcb_visualtype_t *visualtype = visualtype_iterator.data;
225+
if (visualtype->_class == XCB_VISUAL_CLASS_TRUE_COLOR) {
226+
trayVisual = visualtype->visual_id;
227+
break;
228+
}
229+
xcb_visualtype_next(&visualtype_iterator);
230+
}
231+
}
232+
233+
xcb_change_property(c, XCB_PROP_MODE_REPLACE, m_selectionOwner->ownerWindow(), UTIL->getAtomByName("_NET_SYSTEM_TRAY_VISUAL"), XCB_ATOM_VISUALID, 32, 1, &trayVisual);
234+
}
235+
236+
void FdoSelectionManager::initTrayManager()
237+
{
238+
// Create and register the TrayManager1 DBus interface
239+
if (!m_trayManager) {
240+
m_trayManager = new TrayManager1(this);
241+
242+
// Export the object on DBus
243+
QDBusConnection::sessionBus().registerObject(
244+
QStringLiteral("/org/deepin/dde/TrayManager1"),
245+
m_trayManager,
246+
QDBusConnection::ExportAdaptors
247+
);
248+
249+
// Request the service name
250+
QDBusConnection::sessionBus().registerService(
251+
QStringLiteral("org.deepin.dde.TrayManager1")
252+
);
253+
254+
qCDebug(SELECTIONMGR) << "TrayManager1 DBus interface registered";
255+
}
256+
}
257+

0 commit comments

Comments
 (0)