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 }