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
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
CMAKE_MINIMUM_REQUIRED(VERSION 3.16)

cmake_policy(SET CMP0160 OLD)
set(DTL_VERSION "1.99.0" CACHE STRING "Define project version")
set(DTL_VERSION "2.0.99" CACHE STRING "Define project version")
set(DOCK_VERSION "6.0.37" CACHE STRING "Dock compatible version")

project(dde-tray-loader
Expand Down
176 changes: 176 additions & 0 deletions LICENSES/LGPL-2.1-or-later.txt

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions debian/control
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Build-Depends: debhelper-compat ( =12),
libgtest-dev,
libgmock-dev,
libxcursor-dev,
libkf6windowsystem-dev,
dde-api-dev (>>5.6.3)
Standards-Version: 3.9.8
Homepage: http://www.deepin.org/
Expand All @@ -42,6 +43,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends},
libdtk6core,
libdtk6gui,
dde-qt6xcb-plugin,
libkf6windowsystem6,
dde-daemon (>=6.1.26),
lastore-daemon (>=5.2.9),
Conflicts:
Expand Down
26 changes: 24 additions & 2 deletions plugins/application-tray/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,38 @@ project(${PLUGIN_NAME})
find_package(PkgConfig REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} ${REQUIRED_QT_VERSION} REQUIRED Core Gui Widgets DBus)
find_package(Dtk${DTK_VERSION_MAJOR} REQUIRED Core Gui Widget)
find_package(KF6WindowSystem 6.6 REQUIRED) # for x11 tray selection owner

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)
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)

file (GLOB TRAY_SOURCES *.cpp *.h api/types/*.cpp)
set(TRAY_SOURCES
api/types/dbusimagelist.cpp api/types/dbusimagelist.h
api/types/dbustooltip.cpp api/types/dbustooltip.h
api/types/traylist.h

abstracttrayprotocol.h
ddeindicatortrayprotocol.cpp ddeindicatortrayprotocol.h
sniprotocolhandler.cpp sniprotocolhandler.h
trayplugin.cpp trayplugin.h
traywidget.cpp traywidget.h
util.cpp util.h
xembedprotocolhandler.cpp xembedprotocolhandler.h

c_ptr.h
traymanager1.cpp traymanager1.h
fdoselectionmanager.cpp fdoselectionmanager.h
)

set_source_files_properties(
${CMAKE_CURRENT_SOURCE_DIR}/api/dbus/org.deepin.dde.TrayManager1.xml
PROPERTIES INCLUDE api/types/traylist.h
CLASSNAME TrayManager
)

qt_add_dbus_adaptor(TRAY_SOURCES
api/dbus/org.deepin.dde.TrayManager1.xml traymanager1.h TrayManager1
)

set_source_files_properties(
${CMAKE_CURRENT_SOURCE_DIR}/api/dbus/org.kde.StatusNotifierItem.xml
PROPERTIES INCLUDE api/types/dbusimagelist.h
Expand Down Expand Up @@ -50,6 +71,7 @@ target_link_libraries(${PLUGIN_NAME}
Dtk${DTK_VERSION_MAJOR}::Core
Dtk${DTK_VERSION_MAJOR}::Gui
Dtk${DTK_VERSION_MAJOR}::Widget
KF6::WindowSystem
PkgConfig::X11
dbusmenuqt
dockpluginmanager-interface
Expand Down
20 changes: 20 additions & 0 deletions plugins/application-tray/c_ptr.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
SPDX-FileCopyrightText: 2022 Xaver Hugl <[email protected]>

SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once
#include <memory>

Check warning on line 7 in plugins/application-tray/c_ptr.h

View workflow job for this annotation

GitHub Actions / cppcheck

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

struct CDeleter {
template<typename T>
void operator()(T *ptr)
{
if (ptr) {
free(ptr);
}
}
};

template<typename T>
using UniqueCPointer = std::unique_ptr<T, CDeleter>;
259 changes: 259 additions & 0 deletions plugins/application-tray/fdoselectionmanager.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
/*
Registers as a embed container
SPDX-FileCopyrightText: 2015 David Edmundson <[email protected]>
SPDX-FileCopyrightText: 2019 Konrad Materka <[email protected]>
SPDX-FileCopyrightText: 2025 Wang Zichong <[email protected]>

SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "fdoselectionmanager.h"

#include <QTimer>

Check warning on line 11 in plugins/application-tray/fdoselectionmanager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

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

Check warning on line 12 in plugins/application-tray/fdoselectionmanager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

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

Check warning on line 13 in plugins/application-tray/fdoselectionmanager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

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

#include <KSelectionOwner>

Check warning on line 15 in plugins/application-tray/fdoselectionmanager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

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

#include <xcb/composite.h>

Check warning on line 17 in plugins/application-tray/fdoselectionmanager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

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

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

View workflow job for this annotation

GitHub Actions / cppcheck

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

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

View workflow job for this annotation

GitHub Actions / cppcheck

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

Check warning on line 20 in plugins/application-tray/fdoselectionmanager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

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

#include "traymanager1.h"
#include "c_ptr.h"
#include "util.h"

using Util = tray::Util;

#define SYSTEM_TRAY_REQUEST_DOCK 0
#define SYSTEM_TRAY_BEGIN_MESSAGE 1
#define SYSTEM_TRAY_CANCEL_MESSAGE 2

Q_LOGGING_CATEGORY(SELECTIONMGR, "org.deepin.dde.trayloader.selectionmgr")

FdoSelectionManager::FdoSelectionManager(QObject *parent)
: QObject(parent)
, m_selectionOwner(new KSelectionOwner(UTIL->getAtomFromDisplay("_NET_SYSTEM_TRAY"), UTIL->getX11Connection(), UTIL->getRootWindow(), this))
{
qDebug(SELECTIONMGR) << "starting";

// we may end up calling QCoreApplication::quit() in this method, at which point we need the event loop running
QTimer::singleShot(0, this, &FdoSelectionManager::init);
}

FdoSelectionManager::~FdoSelectionManager()
{
qCDebug(SELECTIONMGR) << "closing";
m_selectionOwner->release();
Comment on lines +44 to +47
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Native event filter is never removed in the destructor.

Since the constructor installs this as a native event filter via qApp->installNativeEventFilter(this), the destructor should also call qApp->removeNativeEventFilter(this) before releasing state to avoid dangling event filter callbacks into a destroyed object.

}

void FdoSelectionManager::init()
{
// load damage extension
xcb_connection_t *c = Util::instance()->getX11Connection();
xcb_prefetch_extension_data(c, &xcb_damage_id);
const auto *reply = xcb_get_extension_data(c, &xcb_damage_id);
if (reply && reply->present) {
m_damageEventBase = reply->first_event;
xcb_damage_query_version_unchecked(c, XCB_DAMAGE_MAJOR_VERSION, XCB_DAMAGE_MINOR_VERSION);
} else {
// no XDamage means
qCCritical(SELECTIONMGR) << "could not load damage extension. Quitting";
qApp->exit(-1);
}

qApp->installNativeEventFilter(this);

connect(m_selectionOwner, &KSelectionOwner::claimedOwnership, this, &FdoSelectionManager::onClaimedOwnership);
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);
});
}

bool FdoSelectionManager::addDamageWatch(xcb_window_t client)
{
qCDebug(SELECTIONMGR) << "adding damage watch for " << client;

xcb_connection_t *c = Util::instance()->getX11Connection();
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;
UniqueCPointer<xcb_get_window_attributes_reply_t> attr(xcb_get_window_attributes_reply(c, attribsCookie, &error));
UniqueCPointer<xcb_generic_error_t> getAttrError(error);
uint32_t events = XCB_EVENT_MASK_STRUCTURE_NOTIFY;
if (attr) {
events = events | attr->your_event_mask;
}
// if window is already gone, there is no need to handle it.
if (getAttrError && getAttrError->error_code == XCB_WINDOW) {
return false;
}
// the event mask will not be removed again. We cannot track whether another component also needs STRUCTURE_NOTIFY (e.g. KWindowSystem).
// if we would remove the event mask again, other areas will break.
const auto changeAttrCookie = xcb_change_window_attributes_checked(c, client, XCB_CW_EVENT_MASK, &events);
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) {
return false;
}

return true;
}

bool FdoSelectionManager::nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result)
{
Q_UNUSED(result)

if (eventType != "xcb_generic_event_t") {
return false;
}

auto *ev = static_cast<xcb_generic_event_t *>(message);

const auto responseType = XCB_EVENT_RESPONSE_TYPE(ev);
if (responseType == XCB_CLIENT_MESSAGE) {
const auto ce = reinterpret_cast<xcb_client_message_event_t *>(ev);
if (ce->type == UTIL->getAtomByName("_NET_SYSTEM_TRAY_OPCODE")) {
switch (ce->data.data32[1]) {
case SYSTEM_TRAY_REQUEST_DOCK:
dock(ce->data.data32[2]);
return true;
}
}
} else if (responseType == XCB_UNMAP_NOTIFY) {
// const auto unmappedWId = reinterpret_cast<xcb_unmap_notify_event_t *>(ev)->window;
// if (m_proxies.contains(unmappedWId)) {
// undock(unmappedWId);
// }
} else if (responseType == XCB_DESTROY_NOTIFY) {
const auto destroyedWId = reinterpret_cast<xcb_destroy_notify_event_t *>(ev)->window;
if (m_trayManager->haveIcon(destroyedWId)) {
undock(destroyedWId);
}
} else if (responseType == m_damageEventBase + XCB_DAMAGE_NOTIFY) {
const auto damagedWId = reinterpret_cast<xcb_damage_notify_event_t *>(ev)->drawable;
m_trayManager->notifyIconChanged(damagedWId);
} else if (responseType == XCB_CONFIGURE_REQUEST) {
// const auto event = reinterpret_cast<xcb_configure_request_event_t *>(ev);
// const auto tmProxy = m_proxies.value(event->window);
// if (tmProxy) {
// // The embedded window tries to move or resize. Ignore move, handle resize only.
// if ((event->value_mask & XCB_CONFIG_WINDOW_WIDTH) || (event->value_mask & XCB_CONFIG_WINDOW_HEIGHT)) {
// tmProxy->resizeWindow(event->width, event->height);
// }
// }
}

return false;
}

void FdoSelectionManager::dock(xcb_window_t winId)
{
Q_CHECK_PTR(m_trayManager);
qCDebug(SELECTIONMGR) << "trying to dock window " << winId;

if (m_trayManager->haveIcon(winId)) {
return;
}

if (addDamageWatch(winId)) {
Comment on lines +158 to +167
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Potential race/use-before-init of m_trayManager in dock/undock paths.

dock/undock dereference m_trayManager unconditionally, but it’s only created in initTrayManager() (from onClaimedOwnership()). A tray client message can arrive before claimedOwnership, so dock() may run with m_trayManager == nullptr, tripping Q_CHECK_PTR in debug or causing UB in release. Please either ensure m_trayManager is constructed earlier (e.g. in init()) or skip/defer handling until it is initialized.

// Register with TrayManager1 if available
m_trayManager->registerIcon(winId);
}
}

void FdoSelectionManager::undock(xcb_window_t winId)
{
Q_CHECK_PTR(m_trayManager);
qCDebug(SELECTIONMGR) << "trying to undock window " << winId;

if (m_trayManager->haveIcon(winId)) {
return;
}

// Unregister from TrayManager1 if available
Comment on lines +173 to +182
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Undock logic returns early when an icon exists, preventing proper unregister.

if (m_trayManager->haveIcon(winId)) {
    return;
}
// ...
m_trayManager->unregisterIcon(winId);

This condition looks reversed: when the icon exists we return and never call unregisterIcon, so the icon is never removed. I think this should be:

if (!m_trayManager->haveIcon(winId)) {
    return;
}

m_trayManager->unregisterIcon(winId);

m_trayManager->unregisterIcon(winId);

// m_proxies[winId]->deleteLater();
// m_proxies.remove(winId);
}

void FdoSelectionManager::onClaimedOwnership()
{
qCDebug(SELECTIONMGR) << "Manager selection claimed";

initTrayManager();
setSystemTrayVisual();
}

void FdoSelectionManager::onFailedToClaimOwnership()
{
qCWarning(SELECTIONMGR) << "failed to claim ownership of Systray Manager";
}

void FdoSelectionManager::onLostOwnership()
{
qCWarning(SELECTIONMGR) << "lost ownership of Systray Manager";
}

void FdoSelectionManager::setSystemTrayVisual()
{
xcb_connection_t *c = Util::instance()->getX11Connection();
auto screen = xcb_setup_roots_iterator(xcb_get_setup(c)).data;
auto trayVisual = screen->root_visual;
xcb_depth_iterator_t depth_iterator = xcb_screen_allowed_depths_iterator(screen);
xcb_depth_t *depth = nullptr;

while (depth_iterator.rem) {
if (depth_iterator.data->depth == 32) {
depth = depth_iterator.data;
break;
}
xcb_depth_next(&depth_iterator);
}

if (depth) {
xcb_visualtype_iterator_t visualtype_iterator = xcb_depth_visuals_iterator(depth);
while (visualtype_iterator.rem) {
xcb_visualtype_t *visualtype = visualtype_iterator.data;
if (visualtype->_class == XCB_VISUAL_CLASS_TRUE_COLOR) {
trayVisual = visualtype->visual_id;
break;
}
xcb_visualtype_next(&visualtype_iterator);
}
}

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

void FdoSelectionManager::initTrayManager()
{
// Create and register the TrayManager1 DBus interface
if (!m_trayManager) {
m_trayManager = new TrayManager1(this);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Manage() 调用对接到 claim(true)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


// Export the object on DBus
QDBusConnection::sessionBus().registerObject(
QStringLiteral("/org/deepin/dde/TrayManager1"),
m_trayManager,
QDBusConnection::ExportAdaptors
);

// Request the service name
QDBusConnection::sessionBus().registerService(
QStringLiteral("org.deepin.dde.TrayManager1")
);

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

Loading