Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 38 additions & 7 deletions plugins/application-tray/fdoselectionmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,21 @@ void FdoSelectionManager::init()
connect(m_selectionOwner, &KSelectionOwner::failedToClaimOwnership, this, &FdoSelectionManager::onFailedToClaimOwnership);
connect(m_selectionOwner, &KSelectionOwner::lostOwnership, this, &FdoSelectionManager::onLostOwnership);
m_selectionOwner->claim(true);
}

connect(m_trayManager, &TrayManager1::reclainRequested, this, [this](){
m_selectionOwner->claim(true);
});
void FdoSelectionManager::checkValid()
{
if (!m_trayManager) {
return;
}

const TrayList trayIcons = m_trayManager->trayIcons();
for (uint id : trayIcons) {
const xcb_window_t window = static_cast<xcb_window_t>(id);
if (!UTIL->isValidX11Window(window)) {
undock(window);
}
}
}

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

const auto damageId = xcb_generate_id(c);
m_damageWatches[client] = damageId;
xcb_damage_create(c, damageId, client, XCB_DAMAGE_REPORT_LEVEL_NON_EMPTY);

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

m_damageWatches[client] = damageId;

return true;
}

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

checkValid();

if (m_trayManager->haveIcon(winId)) {
return;
}
Expand All @@ -182,6 +198,12 @@ void FdoSelectionManager::undock(xcb_window_t winId)

// Unregister from TrayManager1 if available
m_trayManager->unregisterIcon(winId);

const auto damageId = m_damageWatches.take(winId);
if (damageId) {
xcb_damage_destroy(Util::instance()->getX11Connection(), damageId);
xcb_flush(Util::instance()->getX11Connection());
}

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

initTrayManager();
setSystemTrayVisual();
setSystemTrayProperties();
m_trayManager->notifyInited();
}

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

void FdoSelectionManager::setSystemTrayVisual()
void FdoSelectionManager::setSystemTrayProperties()
{
xcb_connection_t *c = Util::instance()->getX11Connection();
auto screen = xcb_setup_roots_iterator(xcb_get_setup(c)).data;
Expand Down Expand Up @@ -234,6 +257,11 @@ void FdoSelectionManager::setSystemTrayVisual()
}

xcb_change_property(c, XCB_PROP_MODE_REPLACE, m_selectionOwner->ownerWindow(), UTIL->getAtomByName("_NET_SYSTEM_TRAY_VISUAL"), XCB_ATOM_VISUALID, 32, 1, &trayVisual);

const uint32_t orientation = 0;
xcb_change_property(c, XCB_PROP_MODE_REPLACE, m_selectionOwner->ownerWindow(), UTIL->getAtomByName("_NET_SYSTEM_TRAY_ORIENTATION"), XCB_ATOM_CARDINAL, 32, 1, &orientation);
xcb_change_property(c, XCB_PROP_MODE_REPLACE, m_selectionOwner->ownerWindow(), UTIL->getAtomByName("NET_SYSTEM_TRAY_ORIENTAION"), XCB_ATOM_CARDINAL, 32, 1, &orientation);
xcb_flush(c);
}

void FdoSelectionManager::initTrayManager()
Expand All @@ -253,8 +281,11 @@ void FdoSelectionManager::initTrayManager()
QDBusConnection::sessionBus().registerService(
QStringLiteral("org.deepin.dde.TrayManager1")
);

connect(m_trayManager, &TrayManager1::reclainRequested, this, [this](){
m_selectionOwner->claim(true);
});

qCDebug(SELECTIONMGR) << "TrayManager1 DBus interface registered";
}
}

3 changes: 2 additions & 1 deletion plugins/application-tray/fdoselectionmanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,11 @@ private Q_SLOTS:

private:
void init();
void checkValid();
bool addDamageWatch(xcb_window_t client);
void dock(xcb_window_t embed_win);
void undock(xcb_window_t client);
void setSystemTrayVisual();
void setSystemTrayProperties();
void initTrayManager();

TrayManager1 *m_trayManager = nullptr;
Expand Down
37 changes: 32 additions & 5 deletions plugins/application-tray/traymanager1.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Deepin DDE TrayManager1 implementation
//
// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later

Expand All @@ -15,6 +15,8 @@

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

using Util = tray::Util;

TrayManager1::TrayManager1(QObject *parent)
: QObject(parent)
, m_adaptor(new TrayManager1Adaptor(this))
Expand All @@ -34,7 +36,9 @@ void TrayManager1::registerIcon(xcb_window_t win)
return;
}

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

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

void TrayManager1::notifyIconChanged(xcb_window_t win)
{
if (!m_icons.contains(win)) {
auto it = m_icons.find(win);
if (it == m_icons.end()) {
return;
}

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

QByteArray newPixmapData;
if (!UTIL->getX11WindowPixmapData(win, &newPixmapData)) {
return;
}

if (it->pixmapData == newPixmapData) {
qCDebug(TRAYMGR) << "Icon changed ignored, pixmap unchanged:" << win;
return;
}

it->pixmapData = std::move(newPixmapData);

qCDebug(TRAYMGR) << "Icon changed:" << win;
Q_EMIT Changed(static_cast<uint32_t>(win));
}
Expand All @@ -83,6 +100,16 @@ bool TrayManager1::haveIcon(xcb_window_t win) const
return m_icons.contains(win);
}

void TrayManager1::notifyInited()
{
if (m_inited) {
return;
}

m_inited = true;
Q_EMIT Inited();
}

// DBus method implementations
bool TrayManager1::Manage()
{
Expand All @@ -103,7 +130,7 @@ void TrayManager1::EnableNotification(uint32_t win, bool enabled)
return;
}

m_icons[win] = enabled;
m_icons[win].enableNotify = enabled;

qCDebug(TRAYMGR) << "EnableNotification for" << win << "=" << enabled;
}
12 changes: 10 additions & 2 deletions plugins/application-tray/traymanager1.h
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
// Deepin DDE TrayManager1 implementation
//
// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later

#pragma once

#include <QObject>

Check warning on line 9 in plugins/application-tray/traymanager1.h

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QObject> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QByteArray>

Check warning on line 10 in plugins/application-tray/traymanager1.h

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QByteArray> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QHash>

Check warning on line 11 in plugins/application-tray/traymanager1.h

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QHash> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QList>

Check warning on line 12 in plugins/application-tray/traymanager1.h

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QList> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QString>

Check warning on line 13 in plugins/application-tray/traymanager1.h

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QString> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QDBusContext>

#include <xcb/xcb.h>
Expand All @@ -36,6 +37,11 @@
explicit TrayManager1(QObject *parent = nullptr);
~TrayManager1() override;

struct IconState {
bool enableNotify = true;
QByteArray pixmapData;
};

/**
* @brief Register a new tray icon with the manager
* @param win Window ID of the embedded tray icon
Expand All @@ -61,8 +67,9 @@
TrayList trayIcons() const;

bool haveIcon(xcb_window_t win) const;
void notifyInited();

public Q_SLOTS:

Check warning on line 72 in plugins/application-tray/traymanager1.h

View workflow job for this annotation

GitHub Actions / cppcheck

There is an unknown macro here somewhere. Configuration is required. If Q_SLOTS is a macro then please configure it.
// DBus methods
bool Manage();
QString GetName(uint32_t win);
Expand All @@ -79,5 +86,6 @@

private:
TrayManager1Adaptor * m_adaptor;
QHash<xcb_window_t, bool> m_icons; // <winid, enableNotify>
QHash<xcb_window_t, IconState> m_icons;
bool m_inited = false;
};
90 changes: 90 additions & 0 deletions plugins/application-tray/util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@
#include <QtGlobal>
#include <QSocketNotifier>
#include <QCoreApplication>
#include <QAbstractEventDispatcher>

Check warning on line 16 in plugins/application-tray/util.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QAbstractEventDispatcher> not found. Please note: Cppcheck does not need standard library headers to get proper results.

#include <X11/Xlib.h>

Check warning on line 18 in plugins/application-tray/util.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <X11/Xlib.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <X11/Xutil.h>

Check warning on line 19 in plugins/application-tray/util.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <X11/Xutil.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.

#include <mutex>

Check warning on line 21 in plugins/application-tray/util.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <mutex> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <xcb/res.h>
#include <xcb/xcb.h>
#include <xcb/xcb_atom.h>
Expand Down Expand Up @@ -218,9 +219,54 @@
ret.assign(reply.strings, reply.strings_len);
xcb_ewmh_get_utf8_strings_reply_wipe(&reply);
}

if (!ret.empty()) {
return QString::fromUtf8(ret.c_str(), static_cast<int>(ret.size()));
}

if (m_display) {
char *windowName = nullptr;
if (XFetchName(m_display, window, &windowName) > 0 && windowName) {
ret.assign(windowName);
XFree(windowName);
}
}

if (!ret.empty()) {
return QString::fromLocal8Bit(ret.c_str(), static_cast<int>(ret.size()));
}

if (m_display) {
XClassHint classHint;
if (XGetClassHint(m_display, window, &classHint)) {
const QString wmClass = QString::fromLocal8Bit(classHint.res_class ? classHint.res_class : "");
const QString wmInstance = QString::fromLocal8Bit(classHint.res_name ? classHint.res_name : "");
if (classHint.res_name) {
XFree(classHint.res_name);
}
if (classHint.res_class) {
XFree(classHint.res_class);
}
if (!wmClass.isEmpty() || !wmInstance.isEmpty()) {
return QStringLiteral("[%1|%2]").arg(wmClass, wmInstance);
}
}
}

return ret.c_str();
}

bool Util::isValidX11Window(const xcb_window_t& window) const
{
xcb_generic_error_t *error = nullptr;
QSharedPointer<xcb_get_window_attributes_reply_t> reply(
xcb_get_window_attributes_reply(m_x11connection, xcb_get_window_attributes(m_x11connection, window), &error),
[](xcb_get_window_attributes_reply_t *ptr) { free(ptr); });
QSharedPointer<xcb_generic_error_t> replyError(error, [](xcb_generic_error_t *ptr) { free(ptr); });

return reply && !replyError;
}

void Util::setX11WindowInputShape(const xcb_window_t& window, const QSize& size)
{
xcb_rectangle_t rectangle;
Expand Down Expand Up @@ -269,6 +315,50 @@
}
}

bool Util::getX11WindowPixmapData(const xcb_window_t& window, QByteArray *data)
{
if (!data) {
return false;
}

data->clear();

const QRect geometry = getX11WindowGeometry(window);
if (geometry.isEmpty()) {
return false;
}

xcb_connection_t *connection = m_x11connection;
const xcb_pixmap_t pixmap = xcb_generate_id(connection);
const auto namePixmapCookie = xcb_composite_name_window_pixmap_checked(connection, window, pixmap);
QSharedPointer<xcb_generic_error_t> namePixmapError(xcb_request_check(connection, namePixmapCookie), [](xcb_generic_error_t *ptr) { free(ptr); });
if (namePixmapError) {
return false;
}

QSharedPointer<xcb_get_image_reply_t> imageReply(
xcb_get_image_reply(connection,
xcb_get_image(connection,
XCB_IMAGE_FORMAT_Z_PIXMAP,
pixmap,
0,
0,
geometry.width(),
geometry.height(),
0xFFFFFFFF),
nullptr),
[](xcb_get_image_reply_t *ptr) { free(ptr); });
xcb_free_pixmap(connection, pixmap);

if (!imageReply) {
return false;
}

*data = QByteArray(reinterpret_cast<const char *>(xcb_get_image_data(imageReply.get())),
xcb_get_image_data_length(imageReply.get()));
return true;
}

void Util::setX11WindowOpacity(const xcb_window_t& window, const double& opacity)
{
xcb_atom_t opacityAtom = getAtomByName("_NET_WM_WINDOW_OPACITY");
Expand Down
3 changes: 3 additions & 0 deletions plugins/application-tray/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#pragma once

#include <QByteArray>
#include <QHash>
#include <QImage>
#include <QSharedPointer>
Expand Down Expand Up @@ -41,8 +42,10 @@ class Util : public QObject
void setX11WindowSize(const xcb_window_t& window, const QSize& size);
[[nodiscard]] QRect getX11WindowGeometry(const xcb_window_t& window) const;
QString getX11WindowName(const xcb_window_t& window);
bool isValidX11Window(const xcb_window_t& window) const;
void setX11WindowInputShape(const xcb_window_t& widnow, const QSize& size);
QImage getX11WindowImageNonComposite(const xcb_window_t& window);
bool getX11WindowPixmapData(const xcb_window_t& window, QByteArray *data);
void setX11WindowOpacity(const xcb_window_t& window, const double& opacity);
pid_t getWindowPid(const xcb_window_t& window);
QString getProcExe(const pid_t& pid);
Expand Down
Loading