Skip to content

Commit a799a2e

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

File tree

11 files changed

+555
-3
lines changed

11 files changed

+555
-3
lines changed

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 selection 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: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
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.";
62+
}
63+
64+
qApp->installNativeEventFilter(this);
65+
66+
connect(m_selectionOwner, &KSelectionOwner::claimedOwnership, this, &FdoSelectionManager::onClaimedOwnership);
67+
connect(m_selectionOwner, &KSelectionOwner::failedToClaimOwnership, this, &FdoSelectionManager::onFailedToClaimOwnership);
68+
connect(m_selectionOwner, &KSelectionOwner::lostOwnership, this, &FdoSelectionManager::onLostOwnership);
69+
m_selectionOwner->claim(true);
70+
71+
connect(m_trayManager, &TrayManager1::reclainRequested, this, [this](){
72+
m_selectionOwner->claim(true);
73+
});
74+
}
75+
76+
bool FdoSelectionManager::addDamageWatch(xcb_window_t client)
77+
{
78+
qCDebug(SELECTIONMGR) << "adding damage watch for " << client;
79+
80+
xcb_connection_t *c = Util::instance()->getX11Connection();
81+
const auto attribsCookie = xcb_get_window_attributes_unchecked(c, client);
82+
83+
const auto damageId = xcb_generate_id(c);
84+
m_damageWatches[client] = damageId;
85+
xcb_damage_create(c, damageId, client, XCB_DAMAGE_REPORT_LEVEL_NON_EMPTY);
86+
87+
xcb_generic_error_t *error = nullptr;
88+
UniqueCPointer<xcb_get_window_attributes_reply_t> attr(xcb_get_window_attributes_reply(c, attribsCookie, &error));
89+
UniqueCPointer<xcb_generic_error_t> getAttrError(error);
90+
uint32_t events = XCB_EVENT_MASK_STRUCTURE_NOTIFY;
91+
if (attr) {
92+
events = events | attr->your_event_mask;
93+
}
94+
// if window is already gone, there is no need to handle it.
95+
if (getAttrError && getAttrError->error_code == XCB_WINDOW) {
96+
return false;
97+
}
98+
// the event mask will not be removed again. We cannot track whether another component also needs STRUCTURE_NOTIFY (e.g. KWindowSystem).
99+
// if we would remove the event mask again, other areas will break.
100+
const auto changeAttrCookie = xcb_change_window_attributes_checked(c, client, XCB_CW_EVENT_MASK, &events);
101+
UniqueCPointer<xcb_generic_error_t> changeAttrError(xcb_request_check(c, changeAttrCookie));
102+
// if window is gone by this point, it will be caught by eventFilter, so no need to check later errors.
103+
if (changeAttrError && changeAttrError->error_code == XCB_WINDOW) {
104+
return false;
105+
}
106+
107+
return true;
108+
}
109+
110+
bool FdoSelectionManager::nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result)
111+
{
112+
Q_UNUSED(result)
113+
114+
if (eventType != "xcb_generic_event_t") {
115+
return false;
116+
}
117+
118+
auto *ev = static_cast<xcb_generic_event_t *>(message);
119+
120+
const auto responseType = XCB_EVENT_RESPONSE_TYPE(ev);
121+
if (responseType == XCB_CLIENT_MESSAGE) {
122+
const auto ce = reinterpret_cast<xcb_client_message_event_t *>(ev);
123+
if (ce->type == UTIL->getAtomByName("_NET_SYSTEM_TRAY_OPCODE")) {
124+
switch (ce->data.data32[1]) {
125+
case SYSTEM_TRAY_REQUEST_DOCK:
126+
dock(ce->data.data32[2]);
127+
return true;
128+
}
129+
}
130+
} else if (responseType == XCB_UNMAP_NOTIFY) {
131+
// const auto unmappedWId = reinterpret_cast<xcb_unmap_notify_event_t *>(ev)->window;
132+
// if (m_proxies.contains(unmappedWId)) {
133+
// undock(unmappedWId);
134+
// }
135+
} else if (responseType == XCB_DESTROY_NOTIFY) {
136+
const auto destroyedWId = reinterpret_cast<xcb_destroy_notify_event_t *>(ev)->window;
137+
if (m_trayManager->haveIcon(destroyedWId)) {
138+
undock(destroyedWId);
139+
}
140+
} else if (responseType == m_damageEventBase + XCB_DAMAGE_NOTIFY) {
141+
const auto damagedWId = reinterpret_cast<xcb_damage_notify_event_t *>(ev)->drawable;
142+
m_trayManager->notifyIconChanged(damagedWId);
143+
} else if (responseType == XCB_CONFIGURE_REQUEST) {
144+
// const auto event = reinterpret_cast<xcb_configure_request_event_t *>(ev);
145+
// const auto tmProxy = m_proxies.value(event->window);
146+
// if (tmProxy) {
147+
// // The embedded window tries to move or resize. Ignore move, handle resize only.
148+
// if ((event->value_mask & XCB_CONFIG_WINDOW_WIDTH) || (event->value_mask & XCB_CONFIG_WINDOW_HEIGHT)) {
149+
// tmProxy->resizeWindow(event->width, event->height);
150+
// }
151+
// }
152+
}
153+
154+
return false;
155+
}
156+
157+
void FdoSelectionManager::dock(xcb_window_t winId)
158+
{
159+
Q_CHECK_PTR(m_trayManager);
160+
qCDebug(SELECTIONMGR) << "trying to dock window " << winId;
161+
162+
if (m_trayManager->haveIcon(winId)) {
163+
return;
164+
}
165+
166+
if (addDamageWatch(winId)) {
167+
// Register with TrayManager1 if available
168+
m_trayManager->registerIcon(winId);
169+
}
170+
}
171+
172+
void FdoSelectionManager::undock(xcb_window_t winId)
173+
{
174+
Q_CHECK_PTR(m_trayManager);
175+
qCDebug(SELECTIONMGR) << "trying to undock window " << winId;
176+
177+
if (m_trayManager->haveIcon(winId)) {
178+
return;
179+
}
180+
181+
// Unregister from TrayManager1 if available
182+
m_trayManager->unregisterIcon(winId);
183+
184+
// m_proxies[winId]->deleteLater();
185+
// m_proxies.remove(winId);
186+
}
187+
188+
void FdoSelectionManager::onClaimedOwnership()
189+
{
190+
qCDebug(SELECTIONMGR) << "Manager selection claimed";
191+
192+
initTrayManager();
193+
setSystemTrayVisual();
194+
}
195+
196+
void FdoSelectionManager::onFailedToClaimOwnership()
197+
{
198+
qCWarning(SELECTIONMGR) << "failed to claim ownership of Systray Manager";
199+
}
200+
201+
void FdoSelectionManager::onLostOwnership()
202+
{
203+
qCWarning(SELECTIONMGR) << "lost ownership of Systray Manager";
204+
}
205+
206+
void FdoSelectionManager::setSystemTrayVisual()
207+
{
208+
xcb_connection_t *c = Util::instance()->getX11Connection();
209+
auto screen = xcb_setup_roots_iterator(xcb_get_setup(c)).data;
210+
auto trayVisual = screen->root_visual;
211+
xcb_depth_iterator_t depth_iterator = xcb_screen_allowed_depths_iterator(screen);
212+
xcb_depth_t *depth = nullptr;
213+
214+
while (depth_iterator.rem) {
215+
if (depth_iterator.data->depth == 32) {
216+
depth = depth_iterator.data;
217+
break;
218+
}
219+
xcb_depth_next(&depth_iterator);
220+
}
221+
222+
if (depth) {
223+
xcb_visualtype_iterator_t visualtype_iterator = xcb_depth_visuals_iterator(depth);
224+
while (visualtype_iterator.rem) {
225+
xcb_visualtype_t *visualtype = visualtype_iterator.data;
226+
if (visualtype->_class == XCB_VISUAL_CLASS_TRUE_COLOR) {
227+
trayVisual = visualtype->visual_id;
228+
break;
229+
}
230+
xcb_visualtype_next(&visualtype_iterator);
231+
}
232+
}
233+
234+
xcb_change_property(c, XCB_PROP_MODE_REPLACE, m_selectionOwner->ownerWindow(), UTIL->getAtomByName("_NET_SYSTEM_TRAY_VISUAL"), XCB_ATOM_VISUALID, 32, 1, &trayVisual);
235+
}
236+
237+
void FdoSelectionManager::initTrayManager()
238+
{
239+
// Create and register the TrayManager1 DBus interface
240+
if (!m_trayManager) {
241+
m_trayManager = new TrayManager1(this);
242+
243+
// Export the object on DBus
244+
QDBusConnection::sessionBus().registerObject(
245+
QStringLiteral("/org/deepin/dde/TrayManager1"),
246+
m_trayManager,
247+
QDBusConnection::ExportAdaptors
248+
);
249+
250+
// Request the service name
251+
QDBusConnection::sessionBus().registerService(
252+
QStringLiteral("org.deepin.dde.TrayManager1")
253+
);
254+
255+
qCDebug(SELECTIONMGR) << "TrayManager1 DBus interface registered";
256+
}
257+
}
258+

0 commit comments

Comments
 (0)