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"