File indexing completed on 2024-05-12 05:37:09
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 QRegularExpression rx = QRegularExpression::fromWildcard(dbusactivation); 0063 m_dbusActivatableTasks[pluginMetaData.pluginId()] = rx; 0064 0065 const QString watchedService = QString(dbusactivation).replace(QLatin1String(".*"), QLatin1String("*")); 0066 m_sessionServiceWatcher->addWatchedService(watchedService); 0067 m_systemServiceWatcher->addWatchedService(watchedService); 0068 } 0069 } 0070 0071 void DBusServiceObserver::unregisterPlugin(const QString &pluginId) 0072 { 0073 if (m_dbusActivatableTasks.contains(pluginId)) { 0074 auto rx = m_dbusActivatableTasks.take(pluginId); 0075 const QString watchedService = rx.pattern().replace(QLatin1String(".*"), QLatin1String("*")); 0076 m_sessionServiceWatcher->removeWatchedService(watchedService); 0077 m_systemServiceWatcher->removeWatchedService(watchedService); 0078 } 0079 } 0080 0081 bool DBusServiceObserver::isDBusActivable(const QString &pluginId) 0082 { 0083 return m_dbusActivatableTasks.contains(pluginId); 0084 } 0085 0086 /* Loading and unloading Plasmoids when dbus services come and go 0087 * 0088 * This works as follows: 0089 * - we collect a list of plugins and related services in m_dbusActivatableTasks 0090 * - we query DBus for the list of services, async (initDBusActivatables()) 0091 * - we go over that list, adding tasks when a service and plugin match ({session,system}BusNameFetchFinished()) 0092 * - we start watching for new services, and do the same (serviceRegistered()) 0093 * - whenever a service is gone, we check whether to unload a Plasmoid (serviceUnregistered()) 0094 * 0095 * Order of events has to be: 0096 * - create a match rule for new service on DBus daemon 0097 * - start fetching a list of names 0098 * - ignore all changes that happen in the meantime 0099 * - handle the list of all names 0100 */ 0101 void DBusServiceObserver::initDBusActivatables() 0102 { 0103 // fetch list of existing services 0104 QDBusConnection::sessionBus().interface()->callWithCallback(QStringLiteral("ListNames"), 0105 QList<QVariant>(), 0106 this, 0107 SLOT(sessionBusNameFetchFinished(QStringList)), 0108 SLOT(sessionBusNameFetchError(QDBusError))); 0109 0110 QDBusConnection::systemBus().interface()->callWithCallback(QStringLiteral("ListNames"), 0111 QList<QVariant>(), 0112 this, 0113 SLOT(systemBusNameFetchFinished(QStringList)), 0114 SLOT(systemBusNameFetchError(QDBusError))); 0115 } 0116 0117 void DBusServiceObserver::sessionBusNameFetchFinished(const QStringList &list) 0118 { 0119 for (const QString &serviceName : list) { 0120 serviceRegistered(serviceName); 0121 } 0122 0123 m_dbusSessionServiceNamesFetched = true; 0124 } 0125 0126 void DBusServiceObserver::sessionBusNameFetchError(const QDBusError &error) 0127 { 0128 qCWarning(SYSTEM_TRAY) << "Could not get list of available D-Bus services on the session bus:" << error.name() << ":" << error.message(); 0129 } 0130 0131 void DBusServiceObserver::systemBusNameFetchFinished(const QStringList &list) 0132 { 0133 for (const QString &serviceName : list) { 0134 serviceRegistered(serviceName); 0135 } 0136 0137 m_dbusSystemServiceNamesFetched = true; 0138 } 0139 0140 void DBusServiceObserver::systemBusNameFetchError(const QDBusError &error) 0141 { 0142 qCWarning(SYSTEM_TRAY) << "Could not get list of available D-Bus services on the system bus:" << error.name() << ":" << error.message(); 0143 } 0144 0145 void DBusServiceObserver::serviceRegistered(const QString &service) 0146 { 0147 if (service.startsWith(QLatin1Char(':'))) { 0148 return; 0149 } 0150 0151 for (auto it = m_dbusActivatableTasks.constBegin(), end = m_dbusActivatableTasks.constEnd(); it != end; ++it) { 0152 const QString &plugin = it.key(); 0153 if (!m_settings->isEnabledPlugin(plugin)) { 0154 continue; 0155 } 0156 0157 const auto &rx = it.value(); 0158 if (rx.match(service).hasMatch()) { 0159 qCDebug(SYSTEM_TRAY) << "DBus service" << service << "matching" << m_dbusActivatableTasks[plugin] << "appeared. Loading" << plugin; 0160 Q_EMIT serviceStarted(plugin); 0161 m_dbusServiceCounts[plugin]++; 0162 } 0163 } 0164 } 0165 0166 void DBusServiceObserver::serviceUnregistered(const QString &service) 0167 { 0168 for (auto it = m_dbusActivatableTasks.constBegin(), end = m_dbusActivatableTasks.constEnd(); it != end; ++it) { 0169 const QString &plugin = it.key(); 0170 if (!m_settings->isEnabledPlugin(plugin)) { 0171 continue; 0172 } 0173 0174 const auto &rx = it.value(); 0175 if (rx.match(service).hasMatch()) { 0176 m_dbusServiceCounts[plugin]--; 0177 Q_ASSERT(m_dbusServiceCounts[plugin] >= 0); 0178 if (m_dbusServiceCounts[plugin] == 0) { 0179 qCDebug(SYSTEM_TRAY) << "DBus service" << service << "matching" << m_dbusActivatableTasks[plugin] << "disappeared. Unloading" << plugin; 0180 Q_EMIT serviceStopped(plugin); 0181 } 0182 } 0183 } 0184 }