File indexing completed on 2024-05-12 17:08:50

0001 /*
0002 
0003     SPDX-FileCopyrightText: 2009 Marco Martin <notmart@gmail.com>
0004     SPDX-FileCopyrightText: 2009 Matthieu Gallien <matthieu_gallien@yahoo.fr>
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 #include "statusnotifieritemhost.h"
0010 #include "statusnotifieritemsource.h"
0011 #include <QStringList>
0012 
0013 #include "dbusproperties.h"
0014 
0015 #include "debug.h"
0016 #include <iostream>
0017 
0018 class StatusNotifierItemHostSingleton
0019 {
0020 public:
0021     StatusNotifierItemHost self;
0022 };
0023 
0024 Q_GLOBAL_STATIC(StatusNotifierItemHostSingleton, privateStatusNotifierItemHostSelf)
0025 
0026 static const QString s_watcherServiceName(QStringLiteral("org.kde.StatusNotifierWatcher"));
0027 
0028 StatusNotifierItemHost::StatusNotifierItemHost()
0029     : QObject()
0030     , m_statusNotifierWatcher(nullptr)
0031 {
0032     init();
0033 }
0034 
0035 StatusNotifierItemHost::~StatusNotifierItemHost()
0036 {
0037 }
0038 
0039 StatusNotifierItemHost *StatusNotifierItemHost::self()
0040 {
0041     return &privateStatusNotifierItemHostSelf()->self;
0042 }
0043 
0044 const QList<QString> StatusNotifierItemHost::services() const
0045 {
0046     return m_sniServices.keys();
0047 }
0048 
0049 StatusNotifierItemSource *StatusNotifierItemHost::itemForService(const QString service)
0050 {
0051     return m_sniServices.value(service);
0052 }
0053 
0054 void StatusNotifierItemHost::init()
0055 {
0056     if (QDBusConnection::sessionBus().isConnected()) {
0057         m_serviceName = "org.kde.StatusNotifierHost-" + QString::number(QCoreApplication::applicationPid());
0058         QDBusConnection::sessionBus().registerService(m_serviceName);
0059 
0060         QDBusServiceWatcher *watcher =
0061             new QDBusServiceWatcher(s_watcherServiceName, QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForOwnerChange, this);
0062         connect(watcher, &QDBusServiceWatcher::serviceOwnerChanged, this, &StatusNotifierItemHost::serviceChange);
0063 
0064         registerWatcher(s_watcherServiceName);
0065     }
0066 }
0067 
0068 void StatusNotifierItemHost::serviceChange(const QString &name, const QString &oldOwner, const QString &newOwner)
0069 {
0070     qCDebug(SYSTEM_TRAY) << "Service" << name << "status change, old owner:" << oldOwner << "new:" << newOwner;
0071 
0072     if (newOwner.isEmpty()) {
0073         // unregistered
0074         unregisterWatcher(name);
0075     } else if (oldOwner.isEmpty()) {
0076         // registered
0077         registerWatcher(name);
0078     }
0079 }
0080 
0081 void StatusNotifierItemHost::registerWatcher(const QString &service)
0082 {
0083     if (service == s_watcherServiceName) {
0084         delete m_statusNotifierWatcher;
0085 
0086         m_statusNotifierWatcher =
0087             new org::kde::StatusNotifierWatcher(s_watcherServiceName, QStringLiteral("/StatusNotifierWatcher"), QDBusConnection::sessionBus());
0088         if (m_statusNotifierWatcher->isValid()) {
0089             m_statusNotifierWatcher->call(QDBus::NoBlock, QStringLiteral("RegisterStatusNotifierHost"), m_serviceName);
0090 
0091             OrgFreedesktopDBusPropertiesInterface propetriesIface(m_statusNotifierWatcher->service(),
0092                                                                   m_statusNotifierWatcher->path(),
0093                                                                   m_statusNotifierWatcher->connection());
0094 
0095             connect(m_statusNotifierWatcher,
0096                     &OrgKdeStatusNotifierWatcherInterface::StatusNotifierItemRegistered,
0097                     this,
0098                     &StatusNotifierItemHost::serviceRegistered);
0099             connect(m_statusNotifierWatcher,
0100                     &OrgKdeStatusNotifierWatcherInterface::StatusNotifierItemUnregistered,
0101                     this,
0102                     &StatusNotifierItemHost::serviceUnregistered);
0103 
0104             QDBusPendingReply<QDBusVariant> pendingItems = propetriesIface.Get(m_statusNotifierWatcher->interface(), "RegisteredStatusNotifierItems");
0105 
0106             QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingItems, this);
0107             connect(watcher, &QDBusPendingCallWatcher::finished, this, [=]() {
0108                 watcher->deleteLater();
0109                 QDBusReply<QDBusVariant> reply = *watcher;
0110                 QStringList registeredItems = reply.value().variant().toStringList();
0111                 foreach (const QString &service, registeredItems) {
0112                     if (!m_sniServices.contains(service)) { // due to async nature of this call, service may be already there
0113                         addSNIService(service);
0114                     }
0115                 }
0116             });
0117         } else {
0118             delete m_statusNotifierWatcher;
0119             m_statusNotifierWatcher = nullptr;
0120             qCDebug(SYSTEM_TRAY) << "System tray daemon not reachable";
0121         }
0122     }
0123 }
0124 
0125 void StatusNotifierItemHost::unregisterWatcher(const QString &service)
0126 {
0127     if (service == s_watcherServiceName) {
0128         qCDebug(SYSTEM_TRAY) << s_watcherServiceName << "disappeared";
0129 
0130         disconnect(m_statusNotifierWatcher,
0131                    &OrgKdeStatusNotifierWatcherInterface::StatusNotifierItemRegistered,
0132                    this,
0133                    &StatusNotifierItemHost::serviceRegistered);
0134         disconnect(m_statusNotifierWatcher,
0135                    &OrgKdeStatusNotifierWatcherInterface::StatusNotifierItemUnregistered,
0136                    this,
0137                    &StatusNotifierItemHost::serviceUnregistered);
0138 
0139         removeAllSNIServices();
0140 
0141         delete m_statusNotifierWatcher;
0142         m_statusNotifierWatcher = nullptr;
0143     }
0144 }
0145 
0146 void StatusNotifierItemHost::serviceRegistered(const QString &service)
0147 {
0148     qCDebug(SYSTEM_TRAY) << "Registering" << service;
0149     addSNIService(service);
0150 }
0151 
0152 void StatusNotifierItemHost::serviceUnregistered(const QString &service)
0153 {
0154     removeSNIService(service);
0155 }
0156 
0157 void StatusNotifierItemHost::removeAllSNIServices()
0158 {
0159     QHashIterator<QString, StatusNotifierItemSource *> it(m_sniServices);
0160     while (it.hasNext()) {
0161         it.next();
0162 
0163         StatusNotifierItemSource *item = it.value();
0164         item->disconnect();
0165         item->deleteLater();
0166         Q_EMIT itemRemoved(it.key());
0167     }
0168     m_sniServices.clear();
0169 }
0170 
0171 void StatusNotifierItemHost::addSNIService(const QString &service)
0172 {
0173     StatusNotifierItemSource *item = new StatusNotifierItemSource(service, this);
0174     m_sniServices.insert(service, item);
0175     Q_EMIT itemAdded(service);
0176 }
0177 
0178 void StatusNotifierItemHost::removeSNIService(const QString &service)
0179 {
0180     if (m_sniServices.contains(service)) {
0181         auto item = m_sniServices.value(service);
0182         item->disconnect();
0183         item->deleteLater();
0184         m_sniServices.remove(service);
0185         Q_EMIT itemRemoved(service);
0186     }
0187 }