Skip to content

Commit 54ab4a3

Browse files
committed
chore: port more behaviors from dde-daemon to dde-tray-loader
为 dde-tray-loader 补齐更多与 xembed 托盘相关的行为差异,包括: 1. 仅当托盘图标实际变化时才发送变化信号 2. 补充 _NET_SYSTEM_TRAY_ORIENTATION 设置(目前写死其值) 3. undock() buchong xcb_damage_destroy 4. 增加 checkValid 机制 5. 补充 Inited 信号(尽管目前未发现消费者) 6. GetName 增加与 dde-daemon 一致的 fallback 行为 Log:
1 parent d0a3ebb commit 54ab4a3

6 files changed

Lines changed: 175 additions & 15 deletions

File tree

plugins/application-tray/fdoselectionmanager.cpp

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,21 @@ void FdoSelectionManager::init()
6868
connect(m_selectionOwner, &KSelectionOwner::failedToClaimOwnership, this, &FdoSelectionManager::onFailedToClaimOwnership);
6969
connect(m_selectionOwner, &KSelectionOwner::lostOwnership, this, &FdoSelectionManager::onLostOwnership);
7070
m_selectionOwner->claim(true);
71+
}
7172

72-
connect(m_trayManager, &TrayManager1::reclainRequested, this, [this](){
73-
m_selectionOwner->claim(true);
74-
});
73+
void FdoSelectionManager::checkValid()
74+
{
75+
if (!m_trayManager) {
76+
return;
77+
}
78+
79+
const TrayList trayIcons = m_trayManager->trayIcons();
80+
for (uint id : trayIcons) {
81+
const xcb_window_t window = static_cast<xcb_window_t>(id);
82+
if (!UTIL->isValidX11Window(window)) {
83+
undock(window);
84+
}
85+
}
7586
}
7687

7788
bool FdoSelectionManager::addDamageWatch(xcb_window_t client)
@@ -82,7 +93,6 @@ bool FdoSelectionManager::addDamageWatch(xcb_window_t client)
8293
const auto attribsCookie = xcb_get_window_attributes_unchecked(c, client);
8394

8495
const auto damageId = xcb_generate_id(c);
85-
m_damageWatches[client] = damageId;
8696
xcb_damage_create(c, damageId, client, XCB_DAMAGE_REPORT_LEVEL_NON_EMPTY);
8797

8898
xcb_generic_error_t *error = nullptr;
@@ -94,6 +104,7 @@ bool FdoSelectionManager::addDamageWatch(xcb_window_t client)
94104
}
95105
// if window is already gone, there is no need to handle it.
96106
if (getAttrError && getAttrError->error_code == XCB_WINDOW) {
107+
xcb_damage_destroy(c, damageId);
97108
return false;
98109
}
99110
// the event mask will not be removed again. We cannot track whether another component also needs STRUCTURE_NOTIFY (e.g. KWindowSystem).
@@ -102,9 +113,12 @@ bool FdoSelectionManager::addDamageWatch(xcb_window_t client)
102113
UniqueCPointer<xcb_generic_error_t> changeAttrError(xcb_request_check(c, changeAttrCookie));
103114
// if window is gone by this point, it will be caught by eventFilter, so no need to check later errors.
104115
if (changeAttrError && changeAttrError->error_code == XCB_WINDOW) {
116+
xcb_damage_destroy(c, damageId);
105117
return false;
106118
}
107119

120+
m_damageWatches[client] = damageId;
121+
108122
return true;
109123
}
110124

@@ -160,6 +174,8 @@ void FdoSelectionManager::dock(xcb_window_t winId)
160174
Q_CHECK_PTR(m_trayManager);
161175
qCDebug(SELECTIONMGR) << "trying to dock window " << winId;
162176

177+
checkValid();
178+
163179
if (m_trayManager->haveIcon(winId)) {
164180
return;
165181
}
@@ -182,6 +198,12 @@ void FdoSelectionManager::undock(xcb_window_t winId)
182198

183199
// Unregister from TrayManager1 if available
184200
m_trayManager->unregisterIcon(winId);
201+
202+
const auto damageId = m_damageWatches.take(winId);
203+
if (damageId) {
204+
xcb_damage_destroy(Util::instance()->getX11Connection(), damageId);
205+
xcb_flush(Util::instance()->getX11Connection());
206+
}
185207

186208
// m_proxies[winId]->deleteLater();
187209
// m_proxies.remove(winId);
@@ -192,7 +214,8 @@ void FdoSelectionManager::onClaimedOwnership()
192214
qCDebug(SELECTIONMGR) << "Manager selection claimed";
193215

194216
initTrayManager();
195-
setSystemTrayVisual();
217+
setSystemTrayProperties();
218+
m_trayManager->notifyInited();
196219
}
197220

198221
void FdoSelectionManager::onFailedToClaimOwnership()
@@ -205,7 +228,7 @@ void FdoSelectionManager::onLostOwnership()
205228
qCWarning(SELECTIONMGR) << "lost ownership of Systray Manager";
206229
}
207230

208-
void FdoSelectionManager::setSystemTrayVisual()
231+
void FdoSelectionManager::setSystemTrayProperties()
209232
{
210233
xcb_connection_t *c = Util::instance()->getX11Connection();
211234
auto screen = xcb_setup_roots_iterator(xcb_get_setup(c)).data;
@@ -234,6 +257,11 @@ void FdoSelectionManager::setSystemTrayVisual()
234257
}
235258

236259
xcb_change_property(c, XCB_PROP_MODE_REPLACE, m_selectionOwner->ownerWindow(), UTIL->getAtomByName("_NET_SYSTEM_TRAY_VISUAL"), XCB_ATOM_VISUALID, 32, 1, &trayVisual);
260+
261+
const uint32_t orientation = 0;
262+
xcb_change_property(c, XCB_PROP_MODE_REPLACE, m_selectionOwner->ownerWindow(), UTIL->getAtomByName("_NET_SYSTEM_TRAY_ORIENTATION"), XCB_ATOM_CARDINAL, 32, 1, &orientation);
263+
xcb_change_property(c, XCB_PROP_MODE_REPLACE, m_selectionOwner->ownerWindow(), UTIL->getAtomByName("NET_SYSTEM_TRAY_ORIENTAION"), XCB_ATOM_CARDINAL, 32, 1, &orientation);
264+
xcb_flush(c);
237265
}
238266

239267
void FdoSelectionManager::initTrayManager()
@@ -253,8 +281,11 @@ void FdoSelectionManager::initTrayManager()
253281
QDBusConnection::sessionBus().registerService(
254282
QStringLiteral("org.deepin.dde.TrayManager1")
255283
);
284+
285+
connect(m_trayManager, &TrayManager1::reclainRequested, this, [this](){
286+
m_selectionOwner->claim(true);
287+
});
256288

257289
qCDebug(SELECTIONMGR) << "TrayManager1 DBus interface registered";
258290
}
259291
}
260-

plugins/application-tray/fdoselectionmanager.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,11 @@ private Q_SLOTS:
3636

3737
private:
3838
void init();
39+
void checkValid();
3940
bool addDamageWatch(xcb_window_t client);
4041
void dock(xcb_window_t embed_win);
4142
void undock(xcb_window_t client);
42-
void setSystemTrayVisual();
43+
void setSystemTrayProperties();
4344
void initTrayManager();
4445

4546
TrayManager1 *m_trayManager = nullptr;

plugins/application-tray/traymanager1.cpp

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Deepin DDE TrayManager1 implementation
22
//
3-
// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
3+
// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd.
44
//
55
// SPDX-License-Identifier: GPL-3.0-or-later
66

@@ -15,6 +15,8 @@
1515

1616
Q_LOGGING_CATEGORY(TRAYMGR, "org.deepin.dde.trayloader.traymgr")
1717

18+
using Util = tray::Util;
19+
1820
TrayManager1::TrayManager1(QObject *parent)
1921
: QObject(parent)
2022
, m_adaptor(new TrayManager1Adaptor(this))
@@ -34,7 +36,9 @@ void TrayManager1::registerIcon(xcb_window_t win)
3436
return;
3537
}
3638

37-
m_icons[win] = true;
39+
IconState state;
40+
UTIL->getX11WindowPixmapData(win, &state.pixmapData);
41+
m_icons[win] = state;
3842
qCDebug(TRAYMGR) << "Icon registered:" << win ;//<< "name:" << proxy->name();
3943

4044
Q_EMIT Added(static_cast<uint32_t>(win));
@@ -55,15 +59,28 @@ void TrayManager1::unregisterIcon(xcb_window_t win)
5559

5660
void TrayManager1::notifyIconChanged(xcb_window_t win)
5761
{
58-
if (!m_icons.contains(win)) {
62+
auto it = m_icons.find(win);
63+
if (it == m_icons.end()) {
5964
return;
6065
}
6166

62-
if (!m_icons[win]) {
67+
if (!it->enableNotify) {
6368
qCDebug(TRAYMGR) << "EnableNotification is false, not sending changed signal for:" << win;
6469
return;
6570
}
6671

72+
QByteArray newPixmapData;
73+
if (!UTIL->getX11WindowPixmapData(win, &newPixmapData)) {
74+
return;
75+
}
76+
77+
if (it->pixmapData == newPixmapData) {
78+
qCDebug(TRAYMGR) << "Icon changed ignored, pixmap unchanged:" << win;
79+
return;
80+
}
81+
82+
it->pixmapData = std::move(newPixmapData);
83+
6784
qCDebug(TRAYMGR) << "Icon changed:" << win;
6885
Q_EMIT Changed(static_cast<uint32_t>(win));
6986
}
@@ -83,6 +100,16 @@ bool TrayManager1::haveIcon(xcb_window_t win) const
83100
return m_icons.contains(win);
84101
}
85102

103+
void TrayManager1::notifyInited()
104+
{
105+
if (m_inited) {
106+
return;
107+
}
108+
109+
m_inited = true;
110+
Q_EMIT Inited();
111+
}
112+
86113
// DBus method implementations
87114
bool TrayManager1::Manage()
88115
{
@@ -103,7 +130,7 @@ void TrayManager1::EnableNotification(uint32_t win, bool enabled)
103130
return;
104131
}
105132

106-
m_icons[win] = enabled;
133+
m_icons[win].enableNotify = enabled;
107134

108135
qCDebug(TRAYMGR) << "EnableNotification for" << win << "=" << enabled;
109136
}

plugins/application-tray/traymanager1.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
// Deepin DDE TrayManager1 implementation
22
//
3-
// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
3+
// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd.
44
//
55
// SPDX-License-Identifier: GPL-3.0-or-later
66

77
#pragma once
88

99
#include <QObject>
10+
#include <QByteArray>
1011
#include <QHash>
1112
#include <QList>
1213
#include <QString>
@@ -36,6 +37,11 @@ class TrayManager1 : public QObject, protected QDBusContext
3637
explicit TrayManager1(QObject *parent = nullptr);
3738
~TrayManager1() override;
3839

40+
struct IconState {
41+
bool enableNotify = true;
42+
QByteArray pixmapData;
43+
};
44+
3945
/**
4046
* @brief Register a new tray icon with the manager
4147
* @param win Window ID of the embedded tray icon
@@ -61,6 +67,7 @@ class TrayManager1 : public QObject, protected QDBusContext
6167
TrayList trayIcons() const;
6268

6369
bool haveIcon(xcb_window_t win) const;
70+
void notifyInited();
6471

6572
public Q_SLOTS:
6673
// DBus methods
@@ -79,5 +86,6 @@ public Q_SLOTS:
7986

8087
private:
8188
TrayManager1Adaptor * m_adaptor;
82-
QHash<xcb_window_t, bool> m_icons; // <winid, enableNotify>
89+
QHash<xcb_window_t, IconState> m_icons;
90+
bool m_inited = false;
8391
};

plugins/application-tray/util.cpp

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <QAbstractEventDispatcher>
1717

1818
#include <X11/Xlib.h>
19+
#include <X11/Xutil.h>
1920

2021
#include <mutex>
2122
#include <xcb/res.h>
@@ -218,9 +219,54 @@ QString Util::getX11WindowName(const xcb_window_t& window)
218219
ret.assign(reply.strings, reply.strings_len);
219220
xcb_ewmh_get_utf8_strings_reply_wipe(&reply);
220221
}
222+
223+
if (!ret.empty()) {
224+
return QString::fromUtf8(ret.c_str(), static_cast<int>(ret.size()));
225+
}
226+
227+
if (m_display) {
228+
char *windowName = nullptr;
229+
if (XFetchName(m_display, window, &windowName) > 0 && windowName) {
230+
ret.assign(windowName);
231+
XFree(windowName);
232+
}
233+
}
234+
235+
if (!ret.empty()) {
236+
return QString::fromLocal8Bit(ret.c_str(), static_cast<int>(ret.size()));
237+
}
238+
239+
if (m_display) {
240+
XClassHint classHint;
241+
if (XGetClassHint(m_display, window, &classHint)) {
242+
const QString wmClass = QString::fromLocal8Bit(classHint.res_class ? classHint.res_class : "");
243+
const QString wmInstance = QString::fromLocal8Bit(classHint.res_name ? classHint.res_name : "");
244+
if (classHint.res_name) {
245+
XFree(classHint.res_name);
246+
}
247+
if (classHint.res_class) {
248+
XFree(classHint.res_class);
249+
}
250+
if (!wmClass.isEmpty() || !wmInstance.isEmpty()) {
251+
return QStringLiteral("[%1|%2]").arg(wmClass, wmInstance);
252+
}
253+
}
254+
}
255+
221256
return ret.c_str();
222257
}
223258

259+
bool Util::isValidX11Window(const xcb_window_t& window) const
260+
{
261+
xcb_generic_error_t *error = nullptr;
262+
QSharedPointer<xcb_get_window_attributes_reply_t> reply(
263+
xcb_get_window_attributes_reply(m_x11connection, xcb_get_window_attributes(m_x11connection, window), &error),
264+
[](xcb_get_window_attributes_reply_t *ptr) { free(ptr); });
265+
QSharedPointer<xcb_generic_error_t> replyError(error, [](xcb_generic_error_t *ptr) { free(ptr); });
266+
267+
return reply && !replyError;
268+
}
269+
224270
void Util::setX11WindowInputShape(const xcb_window_t& window, const QSize& size)
225271
{
226272
xcb_rectangle_t rectangle;
@@ -269,6 +315,50 @@ QImage Util::getX11WindowImageNonComposite(const xcb_window_t& window)
269315
}
270316
}
271317

318+
bool Util::getX11WindowPixmapData(const xcb_window_t& window, QByteArray *data)
319+
{
320+
if (!data) {
321+
return false;
322+
}
323+
324+
data->clear();
325+
326+
const QRect geometry = getX11WindowGeometry(window);
327+
if (geometry.isEmpty()) {
328+
return false;
329+
}
330+
331+
xcb_connection_t *connection = m_x11connection;
332+
const xcb_pixmap_t pixmap = xcb_generate_id(connection);
333+
const auto namePixmapCookie = xcb_composite_name_window_pixmap_checked(connection, window, pixmap);
334+
QSharedPointer<xcb_generic_error_t> namePixmapError(xcb_request_check(connection, namePixmapCookie), [](xcb_generic_error_t *ptr) { free(ptr); });
335+
if (namePixmapError) {
336+
return false;
337+
}
338+
339+
QSharedPointer<xcb_get_image_reply_t> imageReply(
340+
xcb_get_image_reply(connection,
341+
xcb_get_image(connection,
342+
XCB_IMAGE_FORMAT_Z_PIXMAP,
343+
pixmap,
344+
0,
345+
0,
346+
geometry.width(),
347+
geometry.height(),
348+
0xFFFFFFFF),
349+
nullptr),
350+
[](xcb_get_image_reply_t *ptr) { free(ptr); });
351+
xcb_free_pixmap(connection, pixmap);
352+
353+
if (!imageReply) {
354+
return false;
355+
}
356+
357+
*data = QByteArray(reinterpret_cast<const char *>(xcb_get_image_data(imageReply.get())),
358+
xcb_get_image_data_length(imageReply.get()));
359+
return true;
360+
}
361+
272362
void Util::setX11WindowOpacity(const xcb_window_t& window, const double& opacity)
273363
{
274364
xcb_atom_t opacityAtom = getAtomByName("_NET_WM_WINDOW_OPACITY");

plugins/application-tray/util.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#pragma once
66

7+
#include <QByteArray>
78
#include <QHash>
89
#include <QImage>
910
#include <QSharedPointer>
@@ -41,8 +42,10 @@ class Util : public QObject
4142
void setX11WindowSize(const xcb_window_t& window, const QSize& size);
4243
[[nodiscard]] QRect getX11WindowGeometry(const xcb_window_t& window) const;
4344
QString getX11WindowName(const xcb_window_t& window);
45+
bool isValidX11Window(const xcb_window_t& window) const;
4446
void setX11WindowInputShape(const xcb_window_t& widnow, const QSize& size);
4547
QImage getX11WindowImageNonComposite(const xcb_window_t& window);
48+
bool getX11WindowPixmapData(const xcb_window_t& window, QByteArray *data);
4649
void setX11WindowOpacity(const xcb_window_t& window, const double& opacity);
4750
pid_t getWindowPid(const xcb_window_t& window);
4851
QString getProcExe(const pid_t& pid);

0 commit comments

Comments
 (0)