File indexing completed on 2024-05-12 17:08:49
0001 /* 0002 SPDX-FileCopyrightText: 2015 Marco Martin <mart@kde.org> 0003 SPDX-FileCopyrightText: 2020 Konrad Materka <materka@gmail.com> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "dbusserviceobserver.h" 0009 #include "debug.h" 0010 0011 #include "systemtraysettings.h" 0012 0013 #include <KPluginMetaData> 0014 0015 #include <QDBusConnection> 0016 #include <QDBusConnectionInterface> 0017 #include <QDBusServiceWatcher> 0018 0019 DBusServiceObserver::DBusServiceObserver(const QPointer<SystemTraySettings> &settings, QObject *parent) 0020 : QObject(parent) 0021 , m_settings(settings) 0022 , m_sessionServiceWatcher(new QDBusServiceWatcher(this)) 0023 , m_systemServiceWatcher(new QDBusServiceWatcher(this)) 0024 { 0025 m_sessionServiceWatcher->setConnection(QDBusConnection::sessionBus()); 0026 m_systemServiceWatcher->setConnection(QDBusConnection::systemBus()); 0027 0028 connect(m_settings, &SystemTraySettings::enabledPluginsChanged, this, &DBusServiceObserver::initDBusActivatables); 0029 0030 // Watch for new services 0031 connect(m_sessionServiceWatcher, &QDBusServiceWatcher::serviceRegistered, this, [this](const QString &serviceName) { 0032 if (!m_dbusSessionServiceNamesFetched) { 0033 return; 0034 } 0035 serviceRegistered(serviceName); 0036 }); 0037 connect(m_sessionServiceWatcher, &QDBusServiceWatcher::serviceUnregistered, this, [this](const QString &serviceName) { 0038 if (!m_dbusSessionServiceNamesFetched) { 0039 return; 0040 } 0041 serviceUnregistered(serviceName); 0042 }); 0043 connect(m_systemServiceWatcher, &QDBusServiceWatcher::serviceRegistered, this, [this](const QString &serviceName) { 0044 if (!m_dbusSystemServiceNamesFetched) { 0045 return; 0046 } 0047 serviceRegistered(serviceName); 0048 }); 0049 connect(m_systemServiceWatcher, &QDBusServiceWatcher::serviceUnregistered, this, [this](const QString &serviceName) { 0050 if (!m_dbusSystemServiceNamesFetched) { 0051 return; 0052 } 0053 serviceUnregistered(serviceName); 0054 }); 0055 } 0056 0057 void DBusServiceObserver::registerPlugin(const KPluginMetaData &pluginMetaData) 0058 { 0059 const QString dbusactivation = pluginMetaData.value(QStringLiteral("X-Plasma-DBusActivationService")); 0060 if (!dbusactivation.isEmpty()) { 0061 qCDebug(SYSTEM_TRAY) << "Found DBus-able Applet: " << pluginMetaData.pluginId() << dbusactivation; 0062 QRegExp rx(dbusactivation); 0063 rx.setPatternSyntax(QRegExp::Wildcard); 0064 m_dbusActivatableTasks[pluginMetaData.pluginId()] = rx; 0065 0066 const QString watchedService = QString(dbusactivation).replace(QLatin1String(".*"), QLatin1String("*")); 0067 m_sessionServiceWatcher->addWatchedService(watchedService); 0068 m_systemServiceWatcher->addWatchedService(watchedService); 0069 } 0070 } 0071 0072 void DBusServiceObserver::unregisterPlugin(const QString &pluginId) 0073 { 0074 if (m_dbusActivatableTasks.contains(pluginId)) { 0075 QRegExp rx = m_dbusActivatableTasks.take(pluginId); 0076 const QString watchedService = rx.pattern().replace(QLatin1String(".*"), QLatin1String("*")); 0077 m_sessionServiceWatcher->removeWatchedService(watchedService); 0078 m_systemServiceWatcher->removeWatchedService(watchedService); 0079 } 0080 } 0081 0082 bool DBusServiceObserver::isDBusActivable(const QString &pluginId) 0083 { 0084 return m_dbusActivatableTasks.contains(pluginId); 0085 } 0086 0087 /* Loading and unloading Plasmoids when dbus services come and go 0088 * 0089 * This works as follows: 0090 * - we collect a list of plugins and related services in m_dbusActivatableTasks 0091 * - we query DBus for the list of services, async (initDBusActivatables()) 0092 * - we go over that list, adding tasks when a service and plugin match ({session,system}BusNameFetchFinished()) 0093 * - we start watching for new services, and do the same (serviceRegistered()) 0094 * - whenever a service is gone, we check whether to unload a Plasmoid (serviceUnregistered()) 0095 * 0096 * Order of events has to be: 0097 * - create a match rule for new service on DBus daemon 0098 * - start fetching a list of names 0099 * - ignore all changes that happen in the meantime 0100 * - handle the list of all names 0101 */ 0102 void DBusServiceObserver::initDBusActivatables() 0103 { 0104 // fetch list of existing services 0105 QDBusConnection::sessionBus().interface()->callWithCallback(QStringLiteral("ListNames"), 0106 QList<QVariant>(), 0107 this, 0108 SLOT(sessionBusNameFetchFinished(QStringList)), 0109 SLOT(sessionBusNameFetchError(QDBusError))); 0110 0111 QDBusConnection::systemBus().interface()->callWithCallback(QStringLiteral("ListNames"), 0112 QList<QVariant>(), 0113 this, 0114 SLOT(systemBusNameFetchFinished(QStringList)), 0115 SLOT(systemBusNameFetchError(QDBusError))); 0116 } 0117 0118 void DBusServiceObserver::sessionBusNameFetchFinished(const QStringList &list) 0119 { 0120 for (const QString &serviceName : list) { 0121 serviceRegistered(serviceName); 0122 } 0123 0124 m_dbusSessionServiceNamesFetched = true; 0125 } 0126 0127 void DBusServiceObserver::sessionBusNameFetchError(const QDBusError &error) 0128 { 0129 qCWarning(SYSTEM_TRAY) << "Could not get list of available D-Bus services on the session bus:" << error.name() << ":" << error.message(); 0130 } 0131 0132 void DBusServiceObserver::systemBusNameFetchFinished(const QStringList &list) 0133 { 0134 for (const QString &serviceName : list) { 0135 serviceRegistered(serviceName); 0136 } 0137 0138 m_dbusSystemServiceNamesFetched = true; 0139 } 0140 0141 void DBusServiceObserver::systemBusNameFetchError(const QDBusError &error) 0142 { 0143 qCWarning(SYSTEM_TRAY) << "Could not get list of available D-Bus services on the system bus:" << error.name() << ":" << error.message(); 0144 } 0145 0146 void DBusServiceObserver::serviceRegistered(const QString &service) 0147 { 0148 if (service.startsWith(QLatin1Char(':'))) { 0149 return; 0150 } 0151 0152 for (auto it = m_dbusActivatableTasks.constBegin(), end = m_dbusActivatableTasks.constEnd(); it != end; ++it) { 0153 const QString &plugin = it.key(); 0154 if (!m_settings->isEnabledPlugin(plugin)) { 0155 continue; 0156 } 0157 0158 const auto &rx = it.value(); 0159 if (rx.exactMatch(service)) { 0160 qCDebug(SYSTEM_TRAY) << "DBus service" << service << "matching" << m_dbusActivatableTasks[plugin] << "appeared. Loading" << plugin; 0161 Q_EMIT serviceStarted(plugin); 0162 m_dbusServiceCounts[plugin]++; 0163 } 0164 } 0165 } 0166 0167 void DBusServiceObserver::serviceUnregistered(const QString &service) 0168 { 0169 for (auto it = m_dbusActivatableTasks.constBegin(), end = m_dbusActivatableTasks.constEnd(); it != end; ++it) { 0170 const QString &plugin = it.key(); 0171 if (!m_settings->isEnabledPlugin(plugin)) { 0172 continue; 0173 } 0174 0175 const auto &rx = it.value(); 0176 if (rx.exactMatch(service)) { 0177 m_dbusServiceCounts[plugin]--; 0178 Q_ASSERT(m_dbusServiceCounts[plugin] >= 0); 0179 if (m_dbusServiceCounts[plugin] == 0) { 0180 qCDebug(SYSTEM_TRAY) << "DBus service" << service << "matching" << m_dbusActivatableTasks[plugin] << "disappeared. Unloading" << plugin; 0181 Q_EMIT serviceStopped(plugin); 0182 } 0183 } 0184 } 0185 }