File indexing completed on 2024-05-12 16:59:20

0001 /*
0002  *   SPDX-FileCopyrightText: 2010-2016 Ivan Cukic <ivan.cukic@kde.org>
0003  *
0004  *   SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005  */
0006 
0007 // Self
0008 #include "Application.h"
0009 #include <kactivities-features.h>
0010 
0011 // Qt
0012 #include <QDBusConnection>
0013 #include <QDBusConnectionInterface>
0014 #include <QDBusServiceWatcher>
0015 #include <QThread>
0016 
0017 // KDE
0018 #include <KCrash>
0019 #include <KPluginMetaData>
0020 #include <kdbusservice.h>
0021 #include <ksharedconfig.h>
0022 
0023 // Boost and utils
0024 #include <boost/range/adaptor/filtered.hpp>
0025 #include <boost/range/adaptor/transformed.hpp>
0026 #include <utils/d_ptr_implementation.h>
0027 
0028 // System
0029 #include <functional>
0030 #include <memory>
0031 #include <signal.h>
0032 #include <stdlib.h>
0033 
0034 // Local
0035 #include "Activities.h"
0036 #include "Config.h"
0037 #include "DebugApplication.h"
0038 #include "Features.h"
0039 #include "Plugin.h"
0040 #include "Resources.h"
0041 #include "common/dbus/common.h"
0042 
0043 namespace
0044 {
0045 QList<QThread *> s_moduleThreads;
0046 }
0047 
0048 // Runs a QObject inside a QThread
0049 
0050 template<typename T>
0051 T *runInQThread()
0052 {
0053     T *object = new T();
0054 
0055     class Thread : public QThread
0056     {
0057     public:
0058         Thread(T *ptr = nullptr)
0059             : QThread()
0060             , object(ptr)
0061         {
0062         }
0063 
0064         void run() override
0065         {
0066             std::unique_ptr<T> o(object);
0067             exec();
0068         }
0069 
0070     private:
0071         T *object;
0072 
0073     } *thread = new Thread(object);
0074 
0075     s_moduleThreads << thread;
0076 
0077     object->moveToThread(thread);
0078     thread->start();
0079 
0080     return object;
0081 }
0082 
0083 class Application::Private
0084 {
0085 public:
0086     Private()
0087     {
0088     }
0089 
0090     static inline bool isPluginEnabled(const KConfigGroup &config, const KPluginMetaData &plugin)
0091     {
0092         const auto pluginName = plugin.pluginId();
0093         qCDebug(KAMD_LOG_APPLICATION) << "Plugin Name is " << pluginName << plugin.fileName();
0094 
0095         if (pluginName == QLatin1String("org.kde.ActivityManager.ResourceScoring")) {
0096             // SQLite plugin is necessary for the proper workspace behaviour
0097             return true;
0098         } else {
0099             return config.readEntry(pluginName + QStringLiteral("Enabled"), plugin.isEnabledByDefault());
0100         }
0101     }
0102 
0103     bool loadPlugin(const KPluginMetaData &plugin);
0104 
0105     Resources *resources;
0106     Activities *activities;
0107     Features *features;
0108 
0109     QStringList pluginIds;
0110     QList<Plugin *> plugins;
0111 
0112     static Application *s_instance;
0113 };
0114 
0115 Application *Application::Private::s_instance = nullptr;
0116 
0117 Application::Application(int &argc, char **argv)
0118     : QApplication(argc, argv)
0119 {
0120 }
0121 
0122 void Application::init()
0123 {
0124 
0125     // KAMD is a daemon, if it crashes it is not a problem as
0126     // long as it restarts properly
0127     // TODO: Restart on crash
0128     //       KCrash::setFlags(KCrash::AutoRestart);
0129     d->resources = runInQThread<Resources>();
0130     d->activities = runInQThread<Activities>();
0131     d->features = runInQThread<Features>();
0132     /* d->config */ new Config(this); // this does not need a separate thread
0133 
0134     QMetaObject::invokeMethod(this, "loadPlugins", Qt::QueuedConnection);
0135 
0136     QDBusConnection::sessionBus().registerObject(QStringLiteral("/ActivityManager"), this, QDBusConnection::ExportAllSlots);
0137 
0138     if (!QDBusConnection::sessionBus().registerService(KAMD_DBUS_SERVICE)) {
0139         QCoreApplication::exit(EXIT_SUCCESS);
0140     }
0141 }
0142 
0143 bool Application::Private::loadPlugin(const KPluginMetaData &plugin)
0144 {
0145     if (!plugin.isValid()) {
0146         qCWarning(KAMD_LOG_APPLICATION) << "[ FAILED ] plugin offer not valid";
0147         return false;
0148     }
0149 
0150     if (pluginIds.contains(plugin.pluginId())) {
0151         qCDebug(KAMD_LOG_APPLICATION) << "[   OK   ] already loaded:  " << plugin.pluginId();
0152         return true;
0153     }
0154 
0155     const auto result = KPluginFactory::instantiatePlugin<Plugin>(plugin);
0156 
0157     if (!result) {
0158         qCWarning(KAMD_LOG_APPLICATION) << "[ FAILED ] Could not load plugin:" << plugin.pluginId() << result.errorText;
0159         return false;
0160     }
0161 
0162     auto pluginInstance = result.plugin;
0163 
0164     auto &modules = Module::get();
0165 
0166     bool success = pluginInstance->init(modules);
0167 
0168     if (success) {
0169         pluginIds << plugin.pluginId();
0170         plugins << pluginInstance;
0171 
0172         qCDebug(KAMD_LOG_APPLICATION) << "[   OK   ] loaded:  " << plugin.pluginId();
0173         return true;
0174     } else {
0175         qCWarning(KAMD_LOG_APPLICATION) << "[ FAILED ] init: " << plugin.pluginId();
0176         // TODO: Show a notification for a plugin that failed to load
0177         delete pluginInstance;
0178         return false;
0179     }
0180 }
0181 
0182 void Application::loadPlugins()
0183 {
0184     using namespace std::placeholders;
0185 
0186     const auto config = KSharedConfig::openConfig(QStringLiteral("kactivitymanagerdrc"))->group("Plugins");
0187     const auto offers = KPluginMetaData::findPlugins(QStringLiteral(KAMD_PLUGIN_DIR), std::bind(Private::isPluginEnabled, config, _1));
0188     qCDebug(KAMD_LOG_APPLICATION) << "Found" << offers.size() << "enabled plugins:";
0189 
0190     for (const auto &offer : offers) {
0191         d->loadPlugin(offer);
0192     }
0193 }
0194 
0195 bool Application::loadPlugin(const QString &pluginId)
0196 {
0197     auto offer = KPluginMetaData::findPluginById(QStringLiteral(KAMD_PLUGIN_DIR), pluginId);
0198 
0199     if (!offer.isValid()) {
0200         qCWarning(KAMD_LOG_APPLICATION) << "[ FAILED ] not found: " << pluginId;
0201         return false;
0202     }
0203 
0204     return d->loadPlugin(offer);
0205 }
0206 
0207 Application::~Application()
0208 {
0209     qCDebug(KAMD_LOG_APPLICATION) << "Cleaning up...";
0210 
0211     // Waiting for the threads to finish
0212     for (const auto thread : s_moduleThreads) {
0213         thread->quit();
0214         thread->wait();
0215 
0216         delete thread;
0217     }
0218 
0219     // Deleting plugin objects
0220     for (const auto plugin : d->plugins) {
0221         delete plugin;
0222     }
0223 
0224     Private::s_instance = nullptr;
0225 }
0226 
0227 int Application::newInstance()
0228 {
0229     // We don't want to show the mainWindow()
0230     return 0;
0231 }
0232 
0233 Activities &Application::activities() const
0234 {
0235     return *d->activities;
0236 }
0237 
0238 Resources &Application::resources() const
0239 {
0240     return *d->resources;
0241 }
0242 
0243 // void Application::quit()
0244 // {
0245 //     if (Private::s_instance) {
0246 //         Private::s_instance->exit();
0247 //         delete Private::s_instance;
0248 //     }
0249 // }
0250 
0251 void Application::quit()
0252 {
0253     QApplication::quit();
0254 }
0255 
0256 #include "Version.h"
0257 QString Application::serviceVersion() const
0258 {
0259     return KACTIVITIES_VERSION_STRING;
0260 }
0261 
0262 int main(int argc, char **argv)
0263 {
0264     // Disable session management for this process
0265     qunsetenv("SESSION_MANAGER");
0266 
0267     QGuiApplication::setDesktopSettingsAware(false);
0268 
0269     Application application(argc, argv);
0270     application.setApplicationName(QStringLiteral("ActivityManager"));
0271     application.setOrganizationDomain(QStringLiteral("kde.org"));
0272 
0273     KCrash::initialize();
0274 
0275     application.init();
0276 
0277     return application.exec();
0278 }
0279 
0280 QStringList Application::loadedPlugins() const
0281 {
0282     return d->pluginIds;
0283 }