// vim: noexpandtab shiftwidth=4 tabstop=4 /* SPDX-FileCopyrightText: 2002 Daniel Molkentin SPDX-FileCopyrightText: 2020 Kai Uwe Broulik SPDX-License-Identifier: GPL-2.0-or-later */ #include "kcmkded.h" #include "debug.h" #include #include #include #include #include #include #include #include #include #include "filterproxymodel.h" #include "modulesmodel.h" #include "kded_interface.h" #include "kdedconfigdata.h" K_PLUGIN_FACTORY_WITH_JSON(KCMStyleFactory, "kcm_kded.json", registerPlugin(); registerPlugin();) static const QString s_kdedServiceName = QStringLiteral("org.kde.kded6"); KDEDConfig::KDEDConfig(QObject *parent, const KPluginMetaData &metaData) : KQuickConfigModule(parent, metaData) , m_model(new ModulesModel(this)) , m_filteredModel(new FilterProxyModel(this)) , m_kdedInterface(new org::kde::kded6(s_kdedServiceName, QStringLiteral("/kded"), QDBusConnection::sessionBus())) , m_kdedWatcher(new QDBusServiceWatcher(s_kdedServiceName, QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForOwnerChange, this)) { qmlRegisterUncreatableType("org.kde.private.kcms.style", 1, 0, "KCM", QStringLiteral("Cannot create instances of KCM")); qmlRegisterAnonymousType("org.kde.plasma.kded.kcm", 0); qmlRegisterAnonymousType("org.kde.plasma.kded.kcm", 0); setButtons(Apply | Default | Help); m_filteredModel->setSourceModel(m_model); connect(m_model, &ModulesModel::autoloadedModulesChanged, this, [this] { setNeedsSave(m_model->needsSave()); setRepresentsDefaults(m_model->representsDefault()); }); connect(m_kdedWatcher, &QDBusServiceWatcher::serviceOwnerChanged, this, [this](const QString &service, const QString &oldOwner, const QString &newOwner) { Q_UNUSED(service) Q_UNUSED(oldOwner) setKdedRunning(!newOwner.isEmpty()); }); setKdedRunning(QDBusConnection::sessionBus().interface()->isServiceRegistered(s_kdedServiceName)); } ModulesModel *KDEDConfig::model() const { return m_model; } FilterProxyModel *KDEDConfig::filteredModel() const { return m_filteredModel; } bool KDEDConfig::kdedRunning() const { return m_kdedRunning; } void KDEDConfig::setKdedRunning(bool kdedRunning) { if (m_kdedRunning == kdedRunning) { return; } m_kdedRunning = kdedRunning; Q_EMIT kdedRunningChanged(); if (kdedRunning) { getModuleStatus(); } else { m_model->setRunningModulesKnown(false); } } void KDEDConfig::startModule(const QString &moduleName) { startOrStopModule(moduleName, Running); } void KDEDConfig::stopModule(const QString &moduleName) { startOrStopModule(moduleName, NotRunning); } void KDEDConfig::startOrStopModule(const QString &moduleName, ModuleStatus status) { auto call = (status == NotRunning ? m_kdedInterface->unloadModule(moduleName) : m_kdedInterface->loadModule(moduleName)); QDBusPendingCallWatcher *callWatcher = new QDBusPendingCallWatcher(call, this); connect(callWatcher, &QDBusPendingCallWatcher::finished, this, [this, moduleName, status](QDBusPendingCallWatcher *watcher) { QDBusPendingReply reply = *watcher; watcher->deleteLater(); if (reply.isError()) { if (status == NotRunning) { Q_EMIT errorMessage(i18n("Failed to stop service: %1", reply.error().message())); } else { Q_EMIT errorMessage(i18n("Failed to start service: %1", reply.error().message())); } return; } if (!reply.value()) { if (status == NotRunning) { Q_EMIT errorMessage(i18n("Failed to stop service.")); } else { Q_EMIT errorMessage(i18n("Failed to start service.")); } return; } qCDebug(KCM_KDED) << "Successfully" << (status == Running ? "started" : "stopped") << moduleName; if (status == Running) { m_lastStartedModule = moduleName; } else { m_lastStartedModule.clear(); } getModuleStatus(); }); } void KDEDConfig::getModuleStatus() { auto call = m_kdedInterface->loadedModules(); QDBusPendingCallWatcher *callWatcher = new QDBusPendingCallWatcher(call, this); connect(callWatcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) { QDBusPendingReply reply = *watcher; watcher->deleteLater(); if (reply.isError()) { qCWarning(KCM_KDED) << "Failed to get loaded modules" << reply.error().name() << reply.error().message(); return; } QStringList runningModules = reply.value(); m_model->setRunningModules(runningModules); m_model->setRunningModulesKnown(true); // Check if the user just tried starting a module that then disabled itself again. // Some kded modules disable themselves on start when they deem themselves unnecessary // based on some configuration independent of kded or the current environment. // At least provide some feedback and not leave the user wondering why the service doesn't start. if (!m_lastStartedModule.isEmpty() && !runningModules.contains(m_lastStartedModule)) { Q_EMIT showSelfDisablingModulesHint(); } m_lastStartedModule.clear(); // Check if any modules got started/stopped as a result of reloading kded if (!m_runningModulesBeforeReconfigure.isEmpty()) { std::sort(m_runningModulesBeforeReconfigure.begin(), m_runningModulesBeforeReconfigure.end()); std::sort(runningModules.begin(), runningModules.end()); if (m_runningModulesBeforeReconfigure != runningModules) { Q_EMIT showRunningModulesChangedAfterSaveHint(); } } m_runningModulesBeforeReconfigure.clear(); }); } void KDEDConfig::load() { m_model->load(); setNeedsSave(false); setRepresentsDefaults(m_model->representsDefault()); } void KDEDConfig::save() { KConfig kdedrc(QStringLiteral("kded5rc"), KConfig::NoGlobals); for (int i = 0; i < m_model->rowCount(); ++i) { const QModelIndex idx = m_model->index(i, 0); const auto type = static_cast(idx.data(ModulesModel::TypeRole).toInt()); if (type != AutostartType) { continue; } const QString moduleName = idx.data(ModulesModel::ModuleNameRole).toString(); const bool autoloadEnabled = idx.data(ModulesModel::AutoloadEnabledRole).toBool(); KConfigGroup cg(&kdedrc, QStringLiteral("Module-%1").arg(moduleName)); cg.writeEntry("autoload", autoloadEnabled); } kdedrc.sync(); m_model->refreshAutoloadEnabledSavedState(); setNeedsSave(false); m_runningModulesBeforeReconfigure = m_model->runningModules(); // Is all of this really necessary? I would also think it to be fire and forget... // Only if changing autoload for a module may load/unload it, otherwise there's no point. // Autoload doesn't affect a running session and reloading the running modules is also useless then. auto call = m_kdedInterface->reconfigure(); QDBusPendingCallWatcher *callWatcher = new QDBusPendingCallWatcher(call, this); connect(callWatcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) { QDBusPendingReply reply = *watcher; watcher->deleteLater(); if (reply.isError()) { Q_EMIT errorMessage(i18n("Failed to notify KDE Service Manager (kded6) of saved changed: %1", reply.error().message())); return; } qCDebug(KCM_KDED) << "Successfully reconfigured kded"; getModuleStatus(); }); } void KDEDConfig::defaults() { for (int i = 0; i < m_model->rowCount(); ++i) { const QModelIndex idx = m_model->index(i, 0); m_model->setData(idx, true, ModulesModel::AutoloadEnabledRole); } } #include "kcmkded.moc" #include "moc_kcmkded.cpp"