File indexing completed on 2024-12-01 09:48:49

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 1999 David Faure <faure@kde.org>
0004     SPDX-FileCopyrightText: 2000 Waldo Bastian <bastian@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-only
0007 */
0008 
0009 #include "kded.h"
0010 #include "kded_version.h"
0011 #include "kdedadaptor.h"
0012 
0013 #include <KCrash>
0014 
0015 #include <qplatformdefs.h>
0016 
0017 #include <QApplication>
0018 #include <QCommandLineParser>
0019 #include <QDir>
0020 #include <QLoggingCategory>
0021 #include <QProcess>
0022 
0023 #include <QDBusConnection>
0024 #include <QDBusConnectionInterface>
0025 #include <QDBusServiceWatcher>
0026 
0027 #include <KConfigGroup>
0028 #include <KDBusService>
0029 #include <KDirWatch>
0030 #include <KPluginFactory>
0031 #include <KPluginInfo>
0032 #include <KPluginMetaData>
0033 #include <KServiceTypeTrader>
0034 #include <KSharedConfig>
0035 
0036 #ifdef Q_OS_OSX
0037 #include <CoreFoundation/CoreFoundation.h>
0038 #endif
0039 
0040 #include <memory>
0041 
0042 Q_DECLARE_LOGGING_CATEGORY(KDED)
0043 
0044 Q_LOGGING_CATEGORY(KDED, "kf.kded", QtWarningMsg)
0045 
0046 Kded *Kded::_self = nullptr;
0047 
0048 static bool delayedCheck;
0049 static bool bCheckSycoca;
0050 static bool bCheckUpdates;
0051 
0052 #ifdef Q_DBUS_EXPORT
0053 extern Q_DBUS_EXPORT void qDBusAddSpyHook(void (*)(const QDBusMessage &));
0054 #else
0055 extern QDBUS_EXPORT void qDBusAddSpyHook(void (*)(const QDBusMessage &));
0056 #endif
0057 
0058 static void runKonfUpdate()
0059 {
0060     QProcess kconfUpdate;
0061     kconfUpdate.start(QStringLiteral(KCONF_UPDATE_EXE), QStringList());
0062     kconfUpdate.waitForFinished();
0063 }
0064 
0065 Kded::Kded()
0066     : m_pDirWatch(new KDirWatch(this))
0067     , m_pTimer(new QTimer(this))
0068     , m_needDelayedCheck(false)
0069 {
0070     _self = this;
0071 
0072     m_serviceWatcher = new QDBusServiceWatcher(this);
0073     m_serviceWatcher->setConnection(QDBusConnection::sessionBus());
0074     m_serviceWatcher->setWatchMode(QDBusServiceWatcher::WatchForUnregistration);
0075     QObject::connect(m_serviceWatcher, &QDBusServiceWatcher::serviceUnregistered, this, &Kded::slotApplicationRemoved);
0076 
0077     new KBuildsycocaAdaptor(this);
0078     new KdedAdaptor(this);
0079 
0080     QDBusConnection session = QDBusConnection::sessionBus();
0081     session.registerObject(QStringLiteral("/kbuildsycoca"), this);
0082     session.registerObject(QStringLiteral("/kded"), this);
0083 
0084     qDBusAddSpyHook(messageFilter);
0085 
0086     m_pTimer->setSingleShot(true);
0087     connect(m_pTimer, &QTimer::timeout, this, static_cast<void (Kded::*)()>(&Kded::recreate));
0088 }
0089 
0090 Kded::~Kded()
0091 {
0092     _self = nullptr;
0093     m_pTimer->stop();
0094 
0095     for (auto it = m_modules.cbegin(); it != m_modules.cend(); ++it) {
0096         KDEDModule *module(it.value());
0097 
0098         // first disconnect otherwise slotKDEDModuleRemoved() is called
0099         // and changes m_modules while we're iterating over it
0100         disconnect(module, &KDEDModule::moduleDeleted, this, &Kded::slotKDEDModuleRemoved);
0101 
0102         delete module;
0103     }
0104 }
0105 
0106 // on-demand module loading
0107 // this function is called by the D-Bus message processing function before
0108 // calls are delivered to objects
0109 void Kded::messageFilter(const QDBusMessage &message)
0110 {
0111     // This happens when kded goes down and some modules try to clean up.
0112     if (!self()) {
0113         return;
0114     }
0115 
0116     QString obj = KDEDModule::moduleForMessage(message);
0117     if (obj.isEmpty() || obj == QLatin1String("ksycoca")) {
0118         return;
0119     }
0120 
0121     if (self()->m_dontLoad.value(obj, nullptr)) {
0122         return;
0123     }
0124 
0125     self()->loadModule(obj, true);
0126 }
0127 
0128 static int phaseForModule(const KPluginMetaData &module)
0129 {
0130     return module.value(QStringLiteral("X-KDE-Kded-phase"), 2);
0131 }
0132 
0133 QVector<KPluginMetaData> Kded::availableModules() const
0134 {
0135     QVector<KPluginMetaData> plugins = KPluginMetaData::findPlugins(QStringLiteral("kf" QT_STRINGIFY(QT_VERSION_MAJOR) "/kded"));
0136     QSet<QString> moduleIds;
0137     for (const KPluginMetaData &md : std::as_const(plugins)) {
0138         moduleIds.insert(md.pluginId());
0139     }
0140 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 0)
0141     // also search for old .desktop based kded modules
0142     QT_WARNING_PUSH
0143     QT_WARNING_DISABLE_CLANG("-Wdeprecated-declarations")
0144     QT_WARNING_DISABLE_GCC("-Wdeprecated-declarations")
0145     const KPluginInfo::List oldStylePlugins = KPluginInfo::fromServices(KServiceTypeTrader::self()->query(QStringLiteral("KDEDModule")));
0146     QT_WARNING_POP
0147     for (const KPluginInfo &info : oldStylePlugins) {
0148         if (moduleIds.contains(info.pluginName())) {
0149             qCWarning(KDED).nospace() << "kded module " << info.pluginName()
0150                                       << " has already been found using "
0151                                          "JSON metadata, please don't install the now unneeded .desktop file ("
0152                                       << info.entryPath() << ").";
0153         } else {
0154             qCDebug(KDED).nospace() << "kded module " << info.pluginName() << " still uses .desktop files (" << info.entryPath()
0155                                     << "). Please port it to JSON metadata.";
0156             plugins.append(info.toMetaData());
0157         }
0158     }
0159 #endif
0160     return plugins;
0161 }
0162 
0163 static KPluginMetaData findModule(const QString &id)
0164 {
0165     KPluginMetaData module(QStringLiteral("kf" QT_STRINGIFY(QT_VERSION_MAJOR) "/kded/") + id);
0166     if (module.isValid()) {
0167         return module;
0168     }
0169 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 0)
0170     // TODO KF6: remove the .desktop fallback code
0171     KService::Ptr oldStyleModule = KService::serviceByDesktopPath(QStringLiteral("kded/") + id + QStringLiteral(".desktop"));
0172     if (oldStyleModule) {
0173         qCDebug(KDED).nospace() << "kded module " << oldStyleModule->desktopEntryName() << " still uses .desktop files (" << oldStyleModule->entryPath()
0174                                 << "). Please port it to JSON metadata.";
0175         QT_WARNING_PUSH
0176         QT_WARNING_DISABLE_CLANG("-Wdeprecated-declarations")
0177         QT_WARNING_DISABLE_GCC("-Wdeprecated-declarations")
0178         return KPluginInfo(oldStyleModule).toMetaData();
0179         QT_WARNING_POP
0180     }
0181 #endif
0182     qCWarning(KDED) << "could not find kded module with id" << id;
0183     return KPluginMetaData();
0184 }
0185 
0186 void Kded::initModules()
0187 {
0188     m_dontLoad.clear();
0189 #ifdef Q_OS_OSX
0190     // it seems there is no reason to honour KDE_FULL_SESSION on OS X even if it's set for some reason.
0191     // That way kdeinit5 is always auto-started if required (and the kded is as good a candidate
0192     // for starting this service as any other application).
0193     bool kde_running = false;
0194 #else
0195     bool kde_running = !qEnvironmentVariableIsEmpty("KDE_FULL_SESSION");
0196 #endif
0197     if (kde_running) {
0198 #ifndef Q_OS_WIN
0199         // not the same user like the one running the session (most likely we're run via sudo or something)
0200         const QByteArray sessionUID = qgetenv("KDE_SESSION_UID");
0201         if (!sessionUID.isEmpty() && uid_t(sessionUID.toInt()) != getuid()) {
0202             kde_running = false;
0203         }
0204 #endif
0205         // TODO: Change 5 to KDED_VERSION_MAJOR the moment KF5 are stable
0206         // not the same kde version as the current desktop
0207         const QByteArray kdeSession = qgetenv("KDE_SESSION_VERSION");
0208         if (kdeSession.toInt() != 5) {
0209             kde_running = false;
0210         }
0211     }
0212 
0213     // Preload kded modules.
0214     const QVector<KPluginMetaData> kdedModules = availableModules();
0215     for (const KPluginMetaData &module : kdedModules) {
0216         // Should the service load on startup?
0217         const bool autoload = isModuleAutoloaded(module);
0218         if (!platformSupportsModule(module)) {
0219             continue;
0220         }
0221 
0222         // see ksmserver's README for description of the phases
0223         bool prevent_autoload = false;
0224         switch (phaseForModule(module)) {
0225         case 0: // always autoload
0226             break;
0227         case 1: // autoload only in KDE
0228             if (!kde_running) {
0229                 prevent_autoload = true;
0230             }
0231             break;
0232         case 2: // autoload delayed, only in KDE
0233         default:
0234             if (!kde_running) {
0235                 prevent_autoload = true;
0236             }
0237             break;
0238         }
0239 
0240         // Load the module if necessary and allowed
0241         if (autoload && !prevent_autoload) {
0242             if (!loadModule(module, false)) {
0243                 continue;
0244             }
0245         }
0246 
0247         // Remember if the module is allowed to load on demand
0248         bool loadOnDemand = isModuleLoadedOnDemand(module);
0249         if (!loadOnDemand) {
0250             noDemandLoad(module.pluginId());
0251         }
0252 
0253         // In case of reloading the configuration it is possible for a module
0254         // to run even if it is now allowed to. Stop it then.
0255         if (!loadOnDemand && !autoload) {
0256             unloadModule(module.pluginId());
0257         }
0258     }
0259 }
0260 
0261 void Kded::loadSecondPhase()
0262 {
0263     qCDebug(KDED) << "Second phase autoload is deprecated";
0264 }
0265 
0266 void Kded::noDemandLoad(const QString &obj)
0267 {
0268     m_dontLoad.insert(obj, this);
0269 }
0270 
0271 void Kded::setModuleAutoloading(const QString &obj, bool autoload)
0272 {
0273     KSharedConfig::Ptr config = KSharedConfig::openConfig();
0274     // Ensure the service exists.
0275     KPluginMetaData module = findModule(obj);
0276     if (!module.isValid()) {
0277         return;
0278     }
0279     KConfigGroup cg(config, QStringLiteral("Module-").append(module.pluginId()));
0280     cg.writeEntry("autoload", autoload);
0281     cg.sync();
0282 }
0283 
0284 bool Kded::isModuleAutoloaded(const QString &obj) const
0285 {
0286     return isModuleAutoloaded(findModule(obj));
0287 }
0288 
0289 bool Kded::isModuleAutoloaded(const KPluginMetaData &module) const
0290 {
0291     if (!module.isValid()) {
0292         return false;
0293     }
0294     KSharedConfig::Ptr config = KSharedConfig::openConfig();
0295     bool autoload = module.value(QStringLiteral("X-KDE-Kded-autoload"), false);
0296     KConfigGroup cg(config, QStringLiteral("Module-").append(module.pluginId()));
0297     autoload = cg.readEntry("autoload", autoload);
0298     return autoload;
0299 }
0300 
0301 bool Kded::platformSupportsModule(const KPluginMetaData &module) const
0302 {
0303     const QStringList supportedPlatforms = module.value(QStringLiteral("X-KDE-OnlyShowOnQtPlatforms"), QStringList());
0304 
0305     return supportedPlatforms.isEmpty() || supportedPlatforms.contains(qApp->platformName());
0306 }
0307 
0308 bool Kded::isModuleLoadedOnDemand(const QString &obj) const
0309 {
0310     return isModuleLoadedOnDemand(findModule(obj));
0311 }
0312 
0313 bool Kded::isModuleLoadedOnDemand(const KPluginMetaData &module) const
0314 {
0315     if (!module.isValid()) {
0316         return false;
0317     }
0318     KSharedConfig::Ptr config = KSharedConfig::openConfig();
0319     return module.value(QStringLiteral("X-KDE-Kded-load-on-demand"), true);
0320 }
0321 
0322 KDEDModule *Kded::loadModule(const QString &obj, bool onDemand)
0323 {
0324     // Make sure this method is only called with valid module names.
0325     if (obj.contains(QLatin1Char('/'))) {
0326         qCWarning(KDED) << "attempting to load invalid kded module name:" << obj;
0327         return nullptr;
0328     }
0329     KDEDModule *module = m_modules.value(obj, nullptr);
0330     if (module) {
0331         return module;
0332     }
0333     return loadModule(findModule(obj), onDemand);
0334 }
0335 
0336 KDEDModule *Kded::loadModule(const KPluginMetaData &module, bool onDemand)
0337 {
0338     if (!module.isValid() || module.fileName().isEmpty()) {
0339         qCWarning(KDED) << "attempted to load an invalid module.";
0340         return nullptr;
0341     }
0342     const QString moduleId = module.pluginId();
0343     KDEDModule *oldModule = m_modules.value(moduleId, nullptr);
0344     if (oldModule) {
0345         qCDebug(KDED) << "kded module" << moduleId << "is already loaded.";
0346         return oldModule;
0347     }
0348 
0349     if (onDemand) {
0350         if (!module.value(QStringLiteral("X-KDE-Kded-load-on-demand"), true)) {
0351             noDemandLoad(moduleId);
0352             return nullptr;
0353         }
0354     }
0355 
0356     KDEDModule *kdedModule = nullptr;
0357 
0358     auto factoryResult = KPluginFactory::loadFactory(module);
0359     if (factoryResult) {
0360         kdedModule = factoryResult.plugin->create<KDEDModule>(this);
0361     } else {
0362         // TODO: remove this fallback code, the kded modules should all be fixed instead
0363         factoryResult = KPluginFactory::loadFactory(KPluginMetaData(QStringLiteral("kded_") + module.fileName()));
0364         if (factoryResult) {
0365             qCWarning(KDED).nospace() << "found kded module " << moduleId << " by prepending 'kded_' to the library path, please fix your metadata.";
0366             kdedModule = factoryResult.plugin->create<KDEDModule>(this);
0367         } else {
0368             qCWarning(KDED).nospace() << "Could not load kded module " << moduleId << ":" << factoryResult.errorText
0369                                       << " (library path was:" << module.fileName() << ")";
0370         }
0371     }
0372 
0373     if (kdedModule) {
0374         kdedModule->setModuleName(moduleId);
0375         m_modules.insert(moduleId, kdedModule);
0376         // m_libs.insert(moduleId, lib);
0377         connect(kdedModule, &KDEDModule::moduleDeleted, this, &Kded::slotKDEDModuleRemoved);
0378         qCDebug(KDED) << "Successfully loaded module" << moduleId;
0379         return kdedModule;
0380     }
0381     return nullptr;
0382 }
0383 
0384 bool Kded::unloadModule(const QString &obj)
0385 {
0386     KDEDModule *module = m_modules.value(obj, nullptr);
0387     if (!module) {
0388         return false;
0389     }
0390     qCDebug(KDED) << "Unloading module" << obj;
0391     m_modules.remove(obj);
0392     delete module;
0393     return true;
0394 }
0395 
0396 QStringList Kded::loadedModules()
0397 {
0398     return m_modules.keys();
0399 }
0400 
0401 void Kded::slotKDEDModuleRemoved(KDEDModule *module)
0402 {
0403     m_modules.remove(module->moduleName());
0404 }
0405 
0406 void Kded::slotApplicationRemoved(const QString &name)
0407 {
0408 #if 0 // see kdedmodule.cpp (KDED_OBJECTS)
0409     foreach (KDEDModule *module, m_modules) {
0410         module->removeAll(appId);
0411     }
0412 #endif
0413     m_serviceWatcher->removeWatchedService(name);
0414     const QList<qlonglong> windowIds = m_windowIdList.value(name);
0415     for (const auto id : windowIds) {
0416         m_globalWindowIdList.remove(id);
0417         for (KDEDModule *module : std::as_const(m_modules)) {
0418             Q_EMIT module->windowUnregistered(id);
0419         }
0420     }
0421     m_windowIdList.remove(name);
0422 }
0423 
0424 void Kded::updateDirWatch()
0425 {
0426     if (!bCheckUpdates) {
0427         return;
0428     }
0429 
0430     delete m_pDirWatch;
0431     m_pDirWatch = new KDirWatch(this);
0432 
0433     QObject::connect(m_pDirWatch, &KDirWatch::dirty, this, &Kded::update);
0434     QObject::connect(m_pDirWatch, &KDirWatch::created, this, &Kded::update);
0435     QObject::connect(m_pDirWatch, &KDirWatch::deleted, this, &Kded::dirDeleted);
0436 
0437     // For each resource
0438     for (const QString &dir : std::as_const(m_allResourceDirs)) {
0439         readDirectory(dir);
0440     }
0441 
0442     QStringList dataDirs = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
0443     for (auto &dir : dataDirs) {
0444         dir += QLatin1String("/icons");
0445         if (!m_pDirWatch->contains(dir)) {
0446             m_pDirWatch->addDir(dir, KDirWatch::WatchDirOnly);
0447         }
0448     }
0449 }
0450 
0451 void Kded::updateResourceList()
0452 {
0453     KSycoca::clearCaches();
0454 
0455     if (!bCheckUpdates) {
0456         return;
0457     }
0458 
0459     if (delayedCheck) {
0460         return;
0461     }
0462 
0463     const QStringList dirs = KSycoca::self()->allResourceDirs();
0464     // For each resource
0465     for (const auto &dir : dirs) {
0466         if (!m_allResourceDirs.contains(dir)) {
0467             m_allResourceDirs.append(dir);
0468             readDirectory(dir);
0469         }
0470     }
0471 }
0472 
0473 void Kded::recreate()
0474 {
0475     recreate(false);
0476 }
0477 
0478 void Kded::runDelayedCheck()
0479 {
0480     if (m_needDelayedCheck) {
0481         recreate(false);
0482     }
0483     m_needDelayedCheck = false;
0484 }
0485 
0486 void Kded::recreate(bool initial)
0487 {
0488     // Using KLauncher here is difficult since we might not have a
0489     // database
0490 
0491     if (!initial) {
0492         updateDirWatch(); // Update tree first, to be sure to miss nothing.
0493         KSycoca::self()->ensureCacheValid();
0494         recreateDone();
0495     } else {
0496         if (!delayedCheck) {
0497             updateDirWatch(); // this would search all the directories
0498         }
0499         if (bCheckSycoca) {
0500             KSycoca::self()->ensureCacheValid();
0501         }
0502         recreateDone();
0503         if (delayedCheck) {
0504             // do a proper ksycoca check after a delay
0505             QTimer::singleShot(60000, this, &Kded::runDelayedCheck);
0506             m_needDelayedCheck = true;
0507             delayedCheck = false;
0508         } else {
0509             m_needDelayedCheck = false;
0510         }
0511     }
0512 }
0513 
0514 void Kded::recreateDone()
0515 {
0516     updateResourceList();
0517 
0518     initModules();
0519 }
0520 
0521 void Kded::dirDeleted(const QString &path)
0522 {
0523     update(path);
0524 }
0525 
0526 void Kded::update(const QString &path)
0527 {
0528     if (path.endsWith(QLatin1String("/icons")) && m_pDirWatch->contains(path)) {
0529         // If the dir was created or updated there could be new folders to merge into the active theme(s)
0530         QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/KIconLoader"), QStringLiteral("org.kde.KIconLoader"), QStringLiteral("iconChanged"));
0531         message << 0;
0532         QDBusConnection::sessionBus().send(message);
0533     } else {
0534         m_pTimer->start(1000);
0535     }
0536 }
0537 
0538 void Kded::readDirectory(const QString &_path)
0539 {
0540     QString path(_path);
0541     if (!path.endsWith(QLatin1Char('/'))) {
0542         path += QLatin1Char('/');
0543     }
0544 
0545     if (m_pDirWatch->contains(path)) { // Already seen this one?
0546         return;
0547     }
0548 
0549     Q_ASSERT(path != QDir::homePath());
0550     m_pDirWatch->addDir(path, KDirWatch::WatchFiles | KDirWatch::WatchSubDirs); // add watch on this dir
0551 }
0552 
0553 void Kded::registerWindowId(qlonglong windowId, const QString &sender)
0554 {
0555     if (!m_windowIdList.contains(sender)) {
0556         m_serviceWatcher->addWatchedService(sender);
0557     }
0558 
0559     m_globalWindowIdList.insert(windowId);
0560     QList<qlonglong> windowIds = m_windowIdList.value(sender);
0561     windowIds.append(windowId);
0562     m_windowIdList.insert(sender, windowIds);
0563 
0564     for (KDEDModule *module : std::as_const(m_modules)) {
0565         qCDebug(KDED) << module->moduleName();
0566         Q_EMIT module->windowRegistered(windowId);
0567     }
0568 }
0569 
0570 void Kded::unregisterWindowId(qlonglong windowId, const QString &sender)
0571 {
0572     m_globalWindowIdList.remove(windowId);
0573     QList<qlonglong> windowIds = m_windowIdList.value(sender);
0574     if (!windowIds.isEmpty()) {
0575         windowIds.removeAll(windowId);
0576         if (windowIds.isEmpty()) {
0577             m_serviceWatcher->removeWatchedService(sender);
0578             m_windowIdList.remove(sender);
0579         } else {
0580             m_windowIdList.insert(sender, windowIds);
0581         }
0582     }
0583 
0584     for (KDEDModule *module : std::as_const(m_modules)) {
0585         qCDebug(KDED) << module->moduleName();
0586         Q_EMIT module->windowUnregistered(windowId);
0587     }
0588 }
0589 
0590 static void sighandler(int /*sig*/)
0591 {
0592     if (qApp) {
0593         qApp->quit();
0594     }
0595 }
0596 
0597 KUpdateD::KUpdateD()
0598 {
0599     m_pDirWatch = new KDirWatch(this);
0600     m_pTimer = new QTimer(this);
0601     m_pTimer->setSingleShot(true);
0602     connect(m_pTimer, &QTimer::timeout, this, &KUpdateD::runKonfUpdate);
0603     QObject::connect(m_pDirWatch, &KDirWatch::dirty, this, &KUpdateD::slotNewUpdateFile);
0604 
0605     QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("kconf_update"), QStandardPaths::LocateDirectory);
0606     for (auto &path : dirs) {
0607         Q_ASSERT(path != QDir::homePath());
0608         if (!path.endsWith(QLatin1Char('/'))) {
0609             path += QLatin1Char('/');
0610         }
0611 
0612         if (!m_pDirWatch->contains(path)) {
0613             m_pDirWatch->addDir(path, KDirWatch::WatchFiles);
0614         }
0615     }
0616 }
0617 
0618 KUpdateD::~KUpdateD()
0619 {
0620 }
0621 
0622 void KUpdateD::runKonfUpdate()
0623 {
0624     ::runKonfUpdate();
0625 }
0626 
0627 void KUpdateD::slotNewUpdateFile(const QString &dirty)
0628 {
0629     Q_UNUSED(dirty);
0630     qCDebug(KDED) << dirty;
0631     m_pTimer->start(500);
0632 }
0633 
0634 KBuildsycocaAdaptor::KBuildsycocaAdaptor(QObject *parent)
0635     : QDBusAbstractAdaptor(parent)
0636 {
0637 }
0638 
0639 void KBuildsycocaAdaptor::recreate()
0640 {
0641     Kded::self()->recreate();
0642 }
0643 
0644 // KF6: remove
0645 bool KBuildsycocaAdaptor::isTestModeEnabled()
0646 {
0647     return QStandardPaths::isTestModeEnabled();
0648 }
0649 
0650 // KF6: remove
0651 void KBuildsycocaAdaptor::setTestModeEnabled()
0652 {
0653     QStandardPaths::setTestModeEnabled(true);
0654 }
0655 
0656 static void setupAppInfo(QApplication *app)
0657 {
0658     app->setApplicationName(QStringLiteral("kded5"));
0659     app->setApplicationDisplayName(QStringLiteral("KDE Daemon"));
0660     app->setOrganizationDomain(QStringLiteral("kde.org"));
0661     app->setApplicationVersion(QStringLiteral(KDED_VERSION_STRING));
0662 }
0663 
0664 static bool detectPlatform(int argc, char **argv)
0665 {
0666     if (qEnvironmentVariableIsSet("QT_QPA_PLATFORM")) {
0667         return false;
0668     }
0669     for (int i = 0; i < argc; i++) {
0670         /* clang-format off */
0671         if (qstrcmp(argv[i], "-platform") == 0
0672             || qstrcmp(argv[i], "--platform") == 0
0673             || QByteArray(argv[i]).startsWith("-platform=")
0674             || QByteArray(argv[i]).startsWith("--platform=")) { /* clang-format on */
0675             return false;
0676         }
0677     }
0678     const QByteArray sessionType = qgetenv("XDG_SESSION_TYPE");
0679     if (sessionType.isEmpty()) {
0680         return false;
0681     }
0682     if (qstrcmp(sessionType.data(), "wayland") == 0) {
0683         qputenv("QT_QPA_PLATFORM", "wayland");
0684         return true;
0685     } else if (qstrcmp(sessionType.data(), "x11") == 0) {
0686         qputenv("QT_QPA_PLATFORM", "xcb");
0687         return true;
0688     }
0689     return false;
0690 }
0691 
0692 int main(int argc, char *argv[])
0693 {
0694 #ifdef Q_OS_OSX
0695     CFBundleRef mainBundle = CFBundleGetMainBundle();
0696     if (mainBundle) {
0697         // get the application's Info Dictionary. For app bundles this would live in the bundle's Info.plist,
0698         // for regular executables it is obtained in another way.
0699         CFMutableDictionaryRef infoDict = (CFMutableDictionaryRef)CFBundleGetInfoDictionary(mainBundle);
0700         if (infoDict) {
0701             // Add or set the "LSUIElement" key with/to value "1". This can simply be a CFString.
0702             CFDictionarySetValue(infoDict, CFSTR("LSUIElement"), CFSTR("1"));
0703             // That's it. We're now considered as an "agent" by the window server, and thus will have
0704             // neither menubar nor presence in the Dock or App Switcher.
0705         }
0706     }
0707 #endif
0708     // options.add("check", qi18n("Check Sycoca database only once"));
0709 
0710     // WABA: Make sure not to enable session management.
0711     qunsetenv("SESSION_MANAGER");
0712 
0713     const bool unsetQpa = detectPlatform(argc, argv);
0714 
0715     // In older versions, QApplication creation was postponed until after
0716     // testing for --check, in which case, only a QCoreApplication was created.
0717     // Since that option is no longer used at startup, we removed that speed
0718     // optimization for code clarity and easier support of standard parameters.
0719 
0720     // Fixes blurry icons with Fractional scaling
0721     QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
0722     QApplication app(argc, argv);
0723     if (unsetQpa) {
0724         qunsetenv("QT_QPA_PLATFORM");
0725     }
0726     setupAppInfo(&app);
0727     app.setQuitOnLastWindowClosed(false);
0728 
0729     QCommandLineParser parser;
0730     parser.addHelpOption();
0731     parser.addVersionOption();
0732     parser.addOption(QCommandLineOption(QStringLiteral("check"), QStringLiteral("Check cache validity")));
0733     QCommandLineOption replaceOption({QStringLiteral("replace")}, QStringLiteral("Replace an existing instance"));
0734     parser.addOption(replaceOption);
0735     parser.process(app);
0736 
0737     // Parse command line before checking D-Bus
0738     if (parser.isSet(QStringLiteral("check"))) {
0739         // KDBusService not wanted here.
0740         KSycoca::self()->ensureCacheValid();
0741         runKonfUpdate();
0742         return 0;
0743     }
0744 
0745     KDBusService service(KDBusService::Unique | KDBusService::StartupOption(parser.isSet(replaceOption) ? KDBusService::Replace : 0));
0746 
0747     QDBusConnectionInterface *bus = QDBusConnection::sessionBus().interface();
0748     // Also register as all the names we should respond to (org.kde.kcookiejar, org.kde.khotkeys etc.)
0749     // so that the calling code is independent from the physical "location" of the service.
0750     const QVector<KPluginMetaData> plugins = KPluginMetaData::findPlugins(QStringLiteral("kf" QT_STRINGIFY(QT_VERSION_MAJOR) "/kded"));
0751     for (const KPluginMetaData &metaData : plugins) {
0752         const QString serviceName = metaData.value(QStringLiteral("X-KDE-DBus-ServiceName"));
0753         if (serviceName.isEmpty()) {
0754             continue;
0755         }
0756         if (!bus->registerService(serviceName)) {
0757             qCWarning(KDED) << "Couldn't register name" << serviceName << "with DBUS - another process owns it already!";
0758         }
0759     }
0760 
0761     KSharedConfig::Ptr config = KSharedConfig::openConfig();
0762     KConfigGroup cg(config, "General");
0763 
0764     bCheckSycoca = cg.readEntry("CheckSycoca", true);
0765     bCheckUpdates = cg.readEntry("CheckUpdates", true);
0766     delayedCheck = cg.readEntry("DelayedCheck", false);
0767 
0768 #ifndef Q_OS_WIN
0769     signal(SIGTERM, sighandler);
0770     signal(SIGHUP, sighandler);
0771 #endif
0772 
0773     KCrash::setFlags(KCrash::AutoRestart);
0774 
0775     std::unique_ptr<Kded> kded = std::make_unique<Kded>();
0776 
0777     kded->recreate(true); // initial
0778 
0779     if (bCheckUpdates) {
0780         (void)new KUpdateD; // Watch for updates
0781     }
0782 
0783     runKonfUpdate(); // Run it once.
0784 
0785     return app.exec(); // keep running
0786 }
0787 
0788 #include "moc_kded.cpp"