From 017515d5d5fab85f907db1e35533e77b30d6c3c0 Mon Sep 17 00:00:00 2001 From: reionwong Date: Mon, 29 Nov 2021 11:54:15 +0800 Subject: [PATCH] Add StatusNotifierItemHost --- CMakeLists.txt | 1 + src/systemtray/statusnotifieritemhost.cpp | 189 ++++++++++++++++++++++ src/systemtray/statusnotifieritemhost.h | 51 ++++++ src/systemtray/systemtraymodel.cpp | 18 +-- src/systemtray/systemtraymodel.h | 2 + 5 files changed, 252 insertions(+), 9 deletions(-) create mode 100644 src/systemtray/statusnotifieritemhost.cpp create mode 100644 src/systemtray/statusnotifieritemhost.h diff --git a/CMakeLists.txt b/CMakeLists.txt index eb3b62e..41dc895 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,7 @@ set(SRCS src/systemtray/systemtraytypedefs.h src/systemtray/systemtraymodel.cpp src/systemtray/statusnotifierwatcher.cpp + src/systemtray/statusnotifieritemhost.cpp src/libdbusmenuqt/dbusmenuimporter.cpp src/libdbusmenuqt/dbusmenushortcut_p.cpp diff --git a/src/systemtray/statusnotifieritemhost.cpp b/src/systemtray/statusnotifieritemhost.cpp new file mode 100644 index 0000000..fb20dc6 --- /dev/null +++ b/src/systemtray/statusnotifieritemhost.cpp @@ -0,0 +1,189 @@ +/* + + SPDX-FileCopyrightText: 2009 Marco Martin + SPDX-FileCopyrightText: 2009 Matthieu Gallien + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#include "statusnotifieritemhost.h" +#include "statusnotifieritemsource.h" +#include +#include + +#include "dbusproperties.h" + +#include + +class StatusNotifierItemHostSingleton +{ +public: + StatusNotifierItemHost self; +}; + +Q_GLOBAL_STATIC(StatusNotifierItemHostSingleton, privateStatusNotifierItemHostSelf) + +static const QString s_watcherServiceName(QStringLiteral("org.kde.StatusNotifierWatcher")); + +StatusNotifierItemHost::StatusNotifierItemHost() + : QObject() + , m_statusNotifierWatcher(nullptr) +{ + init(); +} + +StatusNotifierItemHost::~StatusNotifierItemHost() +{ + QDBusConnection::sessionBus().unregisterService(m_serviceName); +} + +StatusNotifierItemHost *StatusNotifierItemHost::self() +{ + return &privateStatusNotifierItemHostSelf()->self; +} + +const QList StatusNotifierItemHost::services() const +{ + return m_sniServices.keys(); +} + +StatusNotifierItemSource *StatusNotifierItemHost::itemForService(const QString service) +{ + return m_sniServices.value(service); +} + +void StatusNotifierItemHost::init() +{ + if (QDBusConnection::sessionBus().isConnected()) { + m_serviceName = "org.kde.StatusNotifierHost-" + QString::number(QCoreApplication::applicationPid()); + QDBusConnection::sessionBus().registerService(m_serviceName); + + QDBusServiceWatcher *watcher = + new QDBusServiceWatcher(s_watcherServiceName, QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForOwnerChange, this); + connect(watcher, &QDBusServiceWatcher::serviceOwnerChanged, this, &StatusNotifierItemHost::serviceChange); + + registerWatcher(s_watcherServiceName); + } +} + +void StatusNotifierItemHost::serviceChange(const QString &name, const QString &oldOwner, const QString &newOwner) +{ + qDebug() << "Service" << name << "status change, old owner:" << oldOwner << "new:" << newOwner; + + if (newOwner.isEmpty()) { + // unregistered + unregisterWatcher(name); + } else if (oldOwner.isEmpty()) { + // registered + registerWatcher(name); + } +} + +void StatusNotifierItemHost::registerWatcher(const QString &service) +{ + if (service == s_watcherServiceName) { + delete m_statusNotifierWatcher; + + m_statusNotifierWatcher = + new org::kde::StatusNotifierWatcher(s_watcherServiceName, QStringLiteral("/StatusNotifierWatcher"), QDBusConnection::sessionBus()); + if (m_statusNotifierWatcher->isValid()) { + m_statusNotifierWatcher->call(QDBus::NoBlock, QStringLiteral("RegisterStatusNotifierHost"), m_serviceName); + + OrgFreedesktopDBusPropertiesInterface propetriesIface(m_statusNotifierWatcher->service(), + m_statusNotifierWatcher->path(), + m_statusNotifierWatcher->connection()); + + connect(m_statusNotifierWatcher, + &OrgKdeStatusNotifierWatcherInterface::StatusNotifierItemRegistered, + this, + &StatusNotifierItemHost::serviceRegistered); + connect(m_statusNotifierWatcher, + &OrgKdeStatusNotifierWatcherInterface::StatusNotifierItemUnregistered, + this, + &StatusNotifierItemHost::serviceUnregistered); + + QDBusPendingReply pendingItems = propetriesIface.Get(m_statusNotifierWatcher->interface(), "RegisteredStatusNotifierItems"); + + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingItems, this); + connect(watcher, &QDBusPendingCallWatcher::finished, this, [=]() { + watcher->deleteLater(); + QDBusReply reply = *watcher; + QStringList registeredItems = reply.value().variant().toStringList(); + foreach (const QString &service, registeredItems) { + if (!m_sniServices.contains(service)) { // due to async nature of this call, service may be already there + addSNIService(service); + } + } + }); + } else { + delete m_statusNotifierWatcher; + m_statusNotifierWatcher = nullptr; + qDebug() << "System tray daemon not reachable"; + } + } +} + +void StatusNotifierItemHost::unregisterWatcher(const QString &service) +{ + if (service == s_watcherServiceName) { + qDebug() << s_watcherServiceName << "disappeared"; + + disconnect(m_statusNotifierWatcher, + &OrgKdeStatusNotifierWatcherInterface::StatusNotifierItemRegistered, + this, + &StatusNotifierItemHost::serviceRegistered); + disconnect(m_statusNotifierWatcher, + &OrgKdeStatusNotifierWatcherInterface::StatusNotifierItemUnregistered, + this, + &StatusNotifierItemHost::serviceUnregistered); + + removeAllSNIServices(); + + delete m_statusNotifierWatcher; + m_statusNotifierWatcher = nullptr; + } +} + +void StatusNotifierItemHost::serviceRegistered(const QString &service) +{ + qDebug() << "Registering" << service; + addSNIService(service); +} + +void StatusNotifierItemHost::serviceUnregistered(const QString &service) +{ + removeSNIService(service); +} + +void StatusNotifierItemHost::removeAllSNIServices() +{ + QHashIterator it(m_sniServices); + while (it.hasNext()) { + it.next(); + + StatusNotifierItemSource *item = it.value(); + item->disconnect(); + item->deleteLater(); + Q_EMIT itemRemoved(it.key()); + } + m_sniServices.clear(); +} + +void StatusNotifierItemHost::addSNIService(const QString &service) +{ + StatusNotifierItemSource *item = new StatusNotifierItemSource(service, this); + m_sniServices.insert(service, item); + Q_EMIT itemAdded(service); +} + +void StatusNotifierItemHost::removeSNIService(const QString &service) +{ + if (m_sniServices.contains(service)) { + auto item = m_sniServices.value(service); + item->disconnect(); + item->deleteLater(); + m_sniServices.remove(service); + Q_EMIT itemRemoved(service); + } +} + diff --git a/src/systemtray/statusnotifieritemhost.h b/src/systemtray/statusnotifieritemhost.h new file mode 100644 index 0000000..65d5214 --- /dev/null +++ b/src/systemtray/statusnotifieritemhost.h @@ -0,0 +1,51 @@ +/* + SPDX-FileCopyrightText: 2009 Marco Martin + SPDX-FileCopyrightText: 2009 Matthieu Gallien + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#pragma once + +#include "statusnotifierwatcher_interface.h" +#include + +class StatusNotifierItemSource; + +// Define our plasma Runner +class StatusNotifierItemHost : public QObject +{ + Q_OBJECT + +public: + StatusNotifierItemHost(); + virtual ~StatusNotifierItemHost(); + + static StatusNotifierItemHost *self(); + + const QList services() const; + StatusNotifierItemSource *itemForService(const QString service); + +Q_SIGNALS: + void itemAdded(const QString &service); + void itemRemoved(const QString &service); + +private Q_SLOTS: + void serviceChange(const QString &name, const QString &oldOwner, const QString &newOwner); + void registerWatcher(const QString &service); + void unregisterWatcher(const QString &service); + void serviceRegistered(const QString &service); + void serviceUnregistered(const QString &service); + +private: + void init(); + void removeAllSNIServices(); + void addSNIService(const QString &service); + void removeSNIService(const QString &service); + int indexOfItem(const QString &service) const; + + org::kde::StatusNotifierWatcher *m_statusNotifierWatcher; + QString m_serviceName; + static const int s_protocolVersion = 0; + QHash m_sniServices; +}; diff --git a/src/systemtray/systemtraymodel.cpp b/src/systemtray/systemtraymodel.cpp index 74c86cc..f9c89da 100644 --- a/src/systemtray/systemtraymodel.cpp +++ b/src/systemtray/systemtraymodel.cpp @@ -23,25 +23,25 @@ #include #include +#include + SystemTrayModel::SystemTrayModel(QObject *parent) : QAbstractListModel(parent) { - m_hostName = "org.kde.StatusNotifierHost-" + QString::number(QCoreApplication::applicationPid()); - QDBusConnection::sessionBus().interface()->registerService(m_hostName, QDBusConnectionInterface::DontQueueService); - m_watcher = new StatusNotifierWatcher; - m_watcher->RegisterStatusNotifierHost(m_hostName); - m_watcher->moveToThread(QApplication::instance()->thread()); + m_sniHost = StatusNotifierItemHost::self(); + + connect(m_sniHost, &StatusNotifierItemHost::itemAdded, this, &SystemTrayModel::onItemAdded); + connect(m_sniHost, &StatusNotifierItemHost::itemRemoved, this, &SystemTrayModel::onItemRemoved); - connect(m_watcher, &StatusNotifierWatcher::StatusNotifierItemRegistered, this, &SystemTrayModel::onItemAdded); - connect(m_watcher, &StatusNotifierWatcher::StatusNotifierItemUnregistered, this, &SystemTrayModel::onItemRemoved); + for (auto service : m_sniHost->services()) { + onItemAdded(service); + } } SystemTrayModel::~SystemTrayModel() { QDBusConnection::sessionBus().unregisterService(m_hostName); - - delete m_watcher; } int SystemTrayModel::rowCount(const QModelIndex &parent) const diff --git a/src/systemtray/systemtraymodel.h b/src/systemtray/systemtraymodel.h index f2b3729..52e99cb 100644 --- a/src/systemtray/systemtraymodel.h +++ b/src/systemtray/systemtraymodel.h @@ -27,6 +27,7 @@ #include #include "statusnotifierwatcher.h" +#include "statusnotifieritemhost.h" #include "statusnotifieritemsource.h" class SystemTrayModel : public QAbstractListModel @@ -73,6 +74,7 @@ private slots: private: StatusNotifierWatcher *m_watcher; + StatusNotifierItemHost *m_sniHost; QList m_items; QString m_hostName; };