File indexing completed on 2024-05-12 05:35:40

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