File indexing completed on 2024-05-12 05:29:22

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     bool loadPlugin(const KPluginMetaData &plugin);
0091 
0092     Resources *resources;
0093     Activities *activities;
0094     Features *features;
0095 
0096     QStringList pluginIds;
0097     QList<Plugin *> plugins;
0098 
0099     static Application *s_instance;
0100 };
0101 
0102 Application *Application::Private::s_instance = nullptr;
0103 
0104 Application::Application(int &argc, char **argv)
0105     : QApplication(argc, argv)
0106 {
0107 }
0108 
0109 void Application::init()
0110 {
0111     // KAMD is a daemon, if it crashes it is not a problem as
0112     // long as it restarts properly
0113     // TODO: Restart on crash
0114     //       KCrash::setFlags(KCrash::AutoRestart);
0115     d->resources = runInQThread<Resources>();
0116     d->activities = runInQThread<Activities>();
0117     d->features = runInQThread<Features>();
0118     /* d->config */ new Config(this); // this does not need a separate thread
0119 
0120     QMetaObject::invokeMethod(this, "loadPlugins", Qt::QueuedConnection);
0121 
0122     QDBusConnection::sessionBus().registerObject(QStringLiteral("/ActivityManager"), this, QDBusConnection::ExportAllSlots);
0123 
0124     if (!QDBusConnection::sessionBus().registerService(KAMD_DBUS_SERVICE)) {
0125         QCoreApplication::exit(EXIT_SUCCESS);
0126     }
0127 }
0128 
0129 bool Application::Private::loadPlugin(const KPluginMetaData &plugin)
0130 {
0131     if (!plugin.isValid()) {
0132         qCWarning(KAMD_LOG_APPLICATION) << "[ FAILED ] plugin offer not valid";
0133         return false;
0134     }
0135 
0136     if (pluginIds.contains(plugin.pluginId())) {
0137         qCDebug(KAMD_LOG_APPLICATION) << "[   OK   ] already loaded:  " << plugin.pluginId();
0138         return true;
0139     }
0140 
0141     const auto result = KPluginFactory::instantiatePlugin<Plugin>(plugin);
0142 
0143     if (!result) {
0144         qCWarning(KAMD_LOG_APPLICATION) << "[ FAILED ] Could not load plugin:" << plugin.pluginId() << result.errorText;
0145         return false;
0146     }
0147 
0148     auto pluginInstance = result.plugin;
0149 
0150     auto &modules = Module::get();
0151 
0152     bool success = pluginInstance->init(modules);
0153 
0154     if (success) {
0155         pluginIds << plugin.pluginId();
0156         plugins << pluginInstance;
0157 
0158         qCDebug(KAMD_LOG_APPLICATION) << "[   OK   ] loaded:  " << plugin.pluginId();
0159         return true;
0160     } else {
0161         qCWarning(KAMD_LOG_APPLICATION) << "[ FAILED ] init: " << plugin.pluginId();
0162         // TODO: Show a notification for a plugin that failed to load
0163         delete pluginInstance;
0164         return false;
0165     }
0166 }
0167 
0168 void Application::loadPlugins()
0169 {
0170     using namespace std::placeholders;
0171 
0172     const auto offers = KPluginMetaData::findPlugins(QStringLiteral(KAMD_PLUGIN_DIR), {}, KPluginMetaData::AllowEmptyMetaData);
0173     qCDebug(KAMD_LOG_APPLICATION) << "Found" << offers.size() << "enabled plugins:";
0174 
0175     for (const auto &offer : offers) {
0176         d->loadPlugin(offer);
0177     }
0178 }
0179 
0180 bool Application::loadPlugin(const QString &pluginId)
0181 {
0182     auto offer = KPluginMetaData::findPluginById(QStringLiteral(KAMD_PLUGIN_DIR), pluginId, KPluginMetaData::AllowEmptyMetaData);
0183     if (!offer.isValid()) {
0184         qCWarning(KAMD_LOG_APPLICATION) << "[ FAILED ] not found: " << pluginId;
0185         return false;
0186     }
0187 
0188     return d->loadPlugin(offer);
0189 }
0190 
0191 Application::~Application()
0192 {
0193     qCDebug(KAMD_LOG_APPLICATION) << "Cleaning up...";
0194 
0195     // Waiting for the threads to finish
0196     for (const auto thread : s_moduleThreads) {
0197         thread->quit();
0198         thread->wait();
0199 
0200         delete thread;
0201     }
0202 
0203     // Deleting plugin objects
0204     for (const auto plugin : d->plugins) {
0205         delete plugin;
0206     }
0207 
0208     Private::s_instance = nullptr;
0209 }
0210 
0211 int Application::newInstance()
0212 {
0213     // We don't want to show the mainWindow()
0214     return 0;
0215 }
0216 
0217 Activities &Application::activities() const
0218 {
0219     return *d->activities;
0220 }
0221 
0222 Resources &Application::resources() const
0223 {
0224     return *d->resources;
0225 }
0226 
0227 // void Application::quit()
0228 // {
0229 //     if (Private::s_instance) {
0230 //         Private::s_instance->exit();
0231 //         delete Private::s_instance;
0232 //     }
0233 // }
0234 
0235 void Application::quit()
0236 {
0237     QApplication::quit();
0238 }
0239 
0240 #include "Version.h"
0241 QString Application::serviceVersion() const
0242 {
0243     return KACTIVITIES_VERSION_STRING;
0244 }
0245 
0246 int main(int argc, char **argv)
0247 {
0248     // Disable session management for this process
0249     qunsetenv("SESSION_MANAGER");
0250 
0251     QGuiApplication::setDesktopSettingsAware(false);
0252 
0253     Application application(argc, argv);
0254     application.setApplicationName(QStringLiteral("ActivityManager"));
0255     application.setOrganizationDomain(QStringLiteral("kde.org"));
0256 
0257     KCrash::initialize();
0258 
0259     application.init();
0260 
0261     return application.exec();
0262 }
0263 
0264 QStringList Application::loadedPlugins() const
0265 {
0266     return d->pluginIds;
0267 }
0268 
0269 #include "moc_Application.cpp"