File indexing completed on 2024-05-12 17:07:11

0001 // vim: noexpandtab shiftwidth=4 tabstop=4
0002 /*
0003    SPDX-FileCopyrightText: 2002 Daniel Molkentin <molkentin@kde.org>
0004    SPDX-FileCopyrightText: 2020 Kai Uwe Broulik <kde@broulik.de>
0005 
0006    SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 #include "kcmkded.h"
0010 
0011 #include "debug.h"
0012 
0013 #include <QDBusConnection>
0014 #include <QDBusConnectionInterface>
0015 #include <QDBusPendingCall>
0016 #include <QDBusPendingCallWatcher>
0017 
0018 #include <KAboutData>
0019 #include <KConfig>
0020 #include <KConfigGroup>
0021 #include <KLocalizedString>
0022 #include <KPluginFactory>
0023 
0024 #include <algorithm>
0025 
0026 #include "filterproxymodel.h"
0027 #include "modulesmodel.h"
0028 
0029 #include "kded_interface.h"
0030 #include "kdedconfigdata.h"
0031 
0032 K_PLUGIN_FACTORY_WITH_JSON(KCMStyleFactory, "kcm_kded.json", registerPlugin<KDEDConfig>(); registerPlugin<KDEDConfigData>();)
0033 
0034 static const QString s_kdedServiceName = QStringLiteral("org.kde.kded5");
0035 
0036 KDEDConfig::KDEDConfig(QObject *parent, const QVariantList &args)
0037     : KQuickAddons::ConfigModule(parent, args)
0038     , m_model(new ModulesModel(this))
0039     , m_filteredModel(new FilterProxyModel(this))
0040     , m_kdedInterface(new org::kde::kded5(s_kdedServiceName, QStringLiteral("/kded"), QDBusConnection::sessionBus()))
0041     , m_kdedWatcher(new QDBusServiceWatcher(s_kdedServiceName, QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForOwnerChange, this))
0042 {
0043     qmlRegisterUncreatableType<KDEDConfig>("org.kde.private.kcms.style", 1, 0, "KCM", QStringLiteral("Cannot create instances of KCM"));
0044     qmlRegisterAnonymousType<ModulesModel>("org.kde.plasma.kded.kcm", 0);
0045     qmlRegisterAnonymousType<FilterProxyModel>("org.kde.plasma.kded.kcm", 0);
0046 
0047     KAboutData *about = new KAboutData(QStringLiteral("kcm5_kded"),
0048                                        i18n("Background Services"),
0049                                        QStringLiteral("2.0"),
0050                                        QString(),
0051                                        KAboutLicense::GPL,
0052                                        i18n("(c) 2002 Daniel Molkentin, (c) 2020 Kai Uwe Broulik"));
0053     about->addAuthor(i18n("Daniel Molkentin"), QString(), QStringLiteral("molkentin@kde.org"));
0054     about->addAuthor(i18n("Kai Uwe Broulik"), QString(), QStringLiteral("kde@broulik.de"));
0055     setAboutData(about);
0056     setButtons(Apply | Default | Help);
0057 
0058     m_filteredModel->setSourceModel(m_model);
0059 
0060     connect(m_model, &ModulesModel::autoloadedModulesChanged, this, [this] {
0061         setNeedsSave(m_model->needsSave());
0062         setRepresentsDefaults(m_model->representsDefault());
0063     });
0064 
0065     connect(m_kdedWatcher, &QDBusServiceWatcher::serviceOwnerChanged, this, [this](const QString &service, const QString &oldOwner, const QString &newOwner) {
0066         Q_UNUSED(service)
0067         Q_UNUSED(oldOwner)
0068         setKdedRunning(!newOwner.isEmpty());
0069     });
0070     setKdedRunning(QDBusConnection::sessionBus().interface()->isServiceRegistered(s_kdedServiceName));
0071 }
0072 
0073 ModulesModel *KDEDConfig::model() const
0074 {
0075     return m_model;
0076 }
0077 
0078 FilterProxyModel *KDEDConfig::filteredModel() const
0079 {
0080     return m_filteredModel;
0081 }
0082 
0083 bool KDEDConfig::kdedRunning() const
0084 {
0085     return m_kdedRunning;
0086 }
0087 
0088 void KDEDConfig::setKdedRunning(bool kdedRunning)
0089 {
0090     if (m_kdedRunning == kdedRunning) {
0091         return;
0092     }
0093 
0094     m_kdedRunning = kdedRunning;
0095     Q_EMIT kdedRunningChanged();
0096 
0097     if (kdedRunning) {
0098         getModuleStatus();
0099     } else {
0100         m_model->setRunningModulesKnown(false);
0101     }
0102 }
0103 
0104 void KDEDConfig::startModule(const QString &moduleName)
0105 {
0106     startOrStopModule(moduleName, Running);
0107 }
0108 
0109 void KDEDConfig::stopModule(const QString &moduleName)
0110 {
0111     startOrStopModule(moduleName, NotRunning);
0112 }
0113 
0114 void KDEDConfig::startOrStopModule(const QString &moduleName, ModuleStatus status)
0115 {
0116     auto call = (status == NotRunning ? m_kdedInterface->unloadModule(moduleName) : m_kdedInterface->loadModule(moduleName));
0117 
0118     QDBusPendingCallWatcher *callWatcher = new QDBusPendingCallWatcher(call, this);
0119     connect(callWatcher, &QDBusPendingCallWatcher::finished, this, [this, moduleName, status](QDBusPendingCallWatcher *watcher) {
0120         QDBusPendingReply<bool> reply = *watcher;
0121         watcher->deleteLater();
0122 
0123         if (reply.isError()) {
0124             if (status == NotRunning) {
0125                 Q_EMIT errorMessage(i18n("Failed to stop service: %1", reply.error().message()));
0126             } else {
0127                 Q_EMIT errorMessage(i18n("Failed to start service: %1", reply.error().message()));
0128             }
0129             return;
0130         }
0131 
0132         if (!reply.value()) {
0133             if (status == NotRunning) {
0134                 Q_EMIT errorMessage(i18n("Failed to stop service."));
0135             } else {
0136                 Q_EMIT errorMessage(i18n("Failed to start service."));
0137             }
0138             return;
0139         }
0140 
0141         qCDebug(KCM_KDED) << "Successfully" << (status == Running ? "started" : "stopped") << moduleName;
0142         if (status == Running) {
0143             m_lastStartedModule = moduleName;
0144         } else {
0145             m_lastStartedModule.clear();
0146         }
0147         getModuleStatus();
0148     });
0149 }
0150 
0151 void KDEDConfig::getModuleStatus()
0152 {
0153     auto call = m_kdedInterface->loadedModules();
0154 
0155     QDBusPendingCallWatcher *callWatcher = new QDBusPendingCallWatcher(call, this);
0156     connect(callWatcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) {
0157         QDBusPendingReply<QStringList> reply = *watcher;
0158         watcher->deleteLater();
0159 
0160         if (reply.isError()) {
0161             qCWarning(KCM_KDED) << "Failed to get loaded modules" << reply.error().name() << reply.error().message();
0162             return;
0163         }
0164 
0165         QStringList runningModules = reply.value();
0166         m_model->setRunningModules(runningModules);
0167         m_model->setRunningModulesKnown(true);
0168 
0169         // Check if the user just tried starting a module that then disabled itself again.
0170         // Some kded modules disable themselves on start when they deem themselves unnecessary
0171         // based on some configuration independent of kded or the current environment.
0172         // At least provide some feedback and not leave the user wondering why the service doesn't start.
0173         if (!m_lastStartedModule.isEmpty() && !runningModules.contains(m_lastStartedModule)) {
0174             Q_EMIT showSelfDisablingModulesHint();
0175         }
0176         m_lastStartedModule.clear();
0177 
0178         // Check if any modules got started/stopped as a result of reloading kded
0179         if (!m_runningModulesBeforeReconfigure.isEmpty()) {
0180             std::sort(m_runningModulesBeforeReconfigure.begin(), m_runningModulesBeforeReconfigure.end());
0181             std::sort(runningModules.begin(), runningModules.end());
0182 
0183             if (m_runningModulesBeforeReconfigure != runningModules) {
0184                 Q_EMIT showRunningModulesChangedAfterSaveHint();
0185             }
0186         }
0187         m_runningModulesBeforeReconfigure.clear();
0188     });
0189 }
0190 
0191 void KDEDConfig::load()
0192 {
0193     m_model->load();
0194 
0195     setNeedsSave(false);
0196     setRepresentsDefaults(m_model->representsDefault());
0197 }
0198 
0199 void KDEDConfig::save()
0200 {
0201     KConfig kdedrc(QStringLiteral("kded5rc"), KConfig::NoGlobals);
0202 
0203     for (int i = 0; i < m_model->rowCount(); ++i) {
0204         const QModelIndex idx = m_model->index(i, 0);
0205 
0206         const auto type = static_cast<ModuleType>(idx.data(ModulesModel::TypeRole).toInt());
0207         if (type != AutostartType) {
0208             continue;
0209         }
0210 
0211         const QString moduleName = idx.data(ModulesModel::ModuleNameRole).toString();
0212 
0213         const bool autoloadEnabled = idx.data(ModulesModel::AutoloadEnabledRole).toBool();
0214         KConfigGroup cg(&kdedrc, QStringLiteral("Module-%1").arg(moduleName));
0215         cg.writeEntry("autoload", autoloadEnabled);
0216     }
0217 
0218     kdedrc.sync();
0219     m_model->refreshAutoloadEnabledSavedState();
0220     setNeedsSave(false);
0221 
0222     m_runningModulesBeforeReconfigure = m_model->runningModules();
0223 
0224     // Is all of this really necessary? I would also think it to be fire and forget...
0225     // Only if changing autoload for a module may load/unload it, otherwise there's no point.
0226     // Autoload doesn't affect a running session and reloading the running modules is also useless then.
0227     auto call = m_kdedInterface->reconfigure();
0228     QDBusPendingCallWatcher *callWatcher = new QDBusPendingCallWatcher(call, this);
0229     connect(callWatcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) {
0230         QDBusPendingReply<void> reply = *watcher;
0231         watcher->deleteLater();
0232 
0233         if (reply.isError()) {
0234             Q_EMIT errorMessage(i18n("Failed to notify KDE Service Manager (kded5) of saved changed: %1", reply.error().message()));
0235             return;
0236         }
0237 
0238         qCDebug(KCM_KDED) << "Successfully reconfigured kded";
0239         getModuleStatus();
0240     });
0241 }
0242 
0243 void KDEDConfig::defaults()
0244 {
0245     for (int i = 0; i < m_model->rowCount(); ++i) {
0246         const QModelIndex idx = m_model->index(i, 0);
0247         m_model->setData(idx, true, ModulesModel::AutoloadEnabledRole);
0248     }
0249 }
0250 
0251 #include "kcmkded.moc"
0252 #include "moc_kcmkded.cpp"