File indexing completed on 2024-12-01 11:10:05

0001 /*
0002     SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "pluginmanager.h"
0008 #include "dbusinterface.h"
0009 #include "main.h"
0010 #include "plugin.h"
0011 #include "utils/common.h"
0012 
0013 #include <KConfigGroup>
0014 #include <KPluginFactory>
0015 #include <KPluginMetaData>
0016 
0017 namespace KWin
0018 {
0019 
0020 static const QString s_pluginDirectory = QStringLiteral("kwin/plugins");
0021 
0022 static QJsonValue readPluginInfo(const QJsonObject &metadata, const QString &key)
0023 {
0024     return metadata.value(QLatin1String("KPlugin")).toObject().value(key);
0025 }
0026 
0027 PluginManager::PluginManager()
0028 {
0029     const KConfigGroup config(kwinApp()->config(), QStringLiteral("Plugins"));
0030 
0031     auto checkEnabled = [&config](const QString &pluginId, const QJsonObject &metadata) {
0032         const QString configKey = pluginId + QLatin1String("Enabled");
0033         if (config.hasKey(configKey)) {
0034             return config.readEntry(configKey, false);
0035         }
0036         return readPluginInfo(metadata, QStringLiteral("EnabledByDefault")).toBool(false);
0037     };
0038 
0039     const QVector<QStaticPlugin> staticPlugins = QPluginLoader::staticPlugins();
0040     for (const QStaticPlugin &staticPlugin : staticPlugins) {
0041         const QJsonObject rootMetaData = staticPlugin.metaData();
0042         if (rootMetaData.value(QLatin1String("IID")) != QLatin1String(PluginFactory_iid)) {
0043             continue;
0044         }
0045 
0046         const QJsonObject pluginMetaData = rootMetaData.value(QLatin1String("MetaData")).toObject();
0047         const QString pluginId = readPluginInfo(pluginMetaData, QStringLiteral("Id")).toString();
0048         if (pluginId.isEmpty()) {
0049             continue;
0050         }
0051         if (m_staticPlugins.contains(pluginId)) {
0052             qCWarning(KWIN_CORE) << "Conflicting plugin id" << pluginId;
0053             continue;
0054         }
0055 
0056         m_staticPlugins.insert(pluginId, staticPlugin);
0057 
0058         if (checkEnabled(pluginId, pluginMetaData)) {
0059             loadStaticPlugin(pluginId);
0060         }
0061     }
0062 
0063     const QVector<KPluginMetaData> plugins = KPluginMetaData::findPlugins(s_pluginDirectory);
0064     for (const KPluginMetaData &metadata : plugins) {
0065         if (m_plugins.find(metadata.pluginId()) != m_plugins.end()) {
0066             qCWarning(KWIN_CORE) << "Conflicting plugin id" << metadata.pluginId();
0067             continue;
0068         }
0069         if (checkEnabled(metadata.pluginId(), metadata.rawData())) {
0070             loadDynamicPlugin(metadata);
0071         }
0072     }
0073 
0074     new PluginManagerDBusInterface(this);
0075 }
0076 
0077 PluginManager::~PluginManager() = default;
0078 
0079 QStringList PluginManager::loadedPlugins() const
0080 {
0081     QStringList ret;
0082     ret.reserve(m_plugins.size());
0083     for (const auto &[key, _] : m_plugins) {
0084         ret.push_back(key);
0085     }
0086     return ret;
0087 }
0088 
0089 QStringList PluginManager::availablePlugins() const
0090 {
0091     QStringList ret = m_staticPlugins.keys();
0092 
0093     const QVector<KPluginMetaData> plugins = KPluginMetaData::findPlugins(s_pluginDirectory);
0094     for (const KPluginMetaData &metadata : plugins) {
0095         ret.append(metadata.pluginId());
0096     }
0097 
0098     return ret;
0099 }
0100 
0101 bool PluginManager::loadPlugin(const QString &pluginId)
0102 {
0103     if (m_plugins.find(pluginId) != m_plugins.end()) {
0104         qCDebug(KWIN_CORE) << "Plugin with id" << pluginId << "is already loaded";
0105         return false;
0106     }
0107     return loadStaticPlugin(pluginId) || loadDynamicPlugin(pluginId);
0108 }
0109 
0110 bool PluginManager::loadStaticPlugin(const QString &pluginId)
0111 {
0112     auto staticIt = m_staticPlugins.find(pluginId);
0113     if (staticIt == m_staticPlugins.end()) {
0114         return false;
0115     }
0116 
0117     std::unique_ptr<PluginFactory> factory(qobject_cast<PluginFactory *>(staticIt->instance()));
0118     if (!factory) {
0119         qCWarning(KWIN_CORE) << "Failed to get plugin factory for" << pluginId;
0120         return false;
0121     }
0122 
0123     return instantiatePlugin(pluginId, factory.get());
0124 }
0125 
0126 bool PluginManager::loadDynamicPlugin(const QString &pluginId)
0127 {
0128     const KPluginMetaData metadata = KPluginMetaData::findPluginById(s_pluginDirectory, pluginId);
0129     if (metadata.isValid()) {
0130         if (loadDynamicPlugin(metadata)) {
0131             return true;
0132         }
0133     }
0134     return false;
0135 }
0136 
0137 bool PluginManager::loadDynamicPlugin(const KPluginMetaData &metadata)
0138 {
0139     if (!metadata.isValid()) {
0140         qCDebug(KWIN_CORE) << "PluginManager::loadPlugin needs a valid plugin metadata";
0141         return false;
0142     }
0143 
0144     const QString pluginId = metadata.pluginId();
0145     QPluginLoader pluginLoader(metadata.fileName());
0146     if (pluginLoader.metaData().value("IID").toString() != PluginFactory_iid) {
0147         qCWarning(KWIN_CORE) << pluginId << "has mismatching plugin version";
0148         return false;
0149     }
0150 
0151     std::unique_ptr<PluginFactory> factory(qobject_cast<PluginFactory *>(pluginLoader.instance()));
0152     if (!factory) {
0153         qCWarning(KWIN_CORE) << "Failed to get plugin factory for" << pluginId;
0154         return false;
0155     }
0156 
0157     return instantiatePlugin(pluginId, factory.get());
0158 }
0159 
0160 bool PluginManager::instantiatePlugin(const QString &pluginId, PluginFactory *factory)
0161 {
0162     if (std::unique_ptr<Plugin> plugin = factory->create()) {
0163         m_plugins[pluginId] = std::move(plugin);
0164         return true;
0165     } else {
0166         return false;
0167     }
0168 }
0169 
0170 void PluginManager::unloadPlugin(const QString &pluginId)
0171 {
0172     auto it = m_plugins.find(pluginId);
0173     if (it != m_plugins.end()) {
0174         m_plugins.erase(it);
0175     } else {
0176         qCWarning(KWIN_CORE) << "No plugin with the specified id:" << pluginId;
0177     }
0178 }
0179 
0180 } // namespace KWin