File indexing completed on 2024-05-19 16:34:03

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 #include "decorationbridge.h"
0010 
0011 #include <config-kwin.h>
0012 
0013 #include "decoratedclient.h"
0014 #include "decorations_logging.h"
0015 #include "settings.h"
0016 // KWin core
0017 #include "wayland/server_decoration_interface.h"
0018 #include "wayland_server.h"
0019 #include "window.h"
0020 #include "workspace.h"
0021 
0022 // KDecoration
0023 #include <KDecoration2/DecoratedClient>
0024 #include <KDecoration2/Decoration>
0025 #include <KDecoration2/DecorationSettings>
0026 
0027 // Frameworks
0028 #include <KPluginFactory>
0029 #include <KPluginMetaData>
0030 
0031 // Qt
0032 #include <QMetaProperty>
0033 #include <QPainter>
0034 
0035 namespace KWin
0036 {
0037 namespace Decoration
0038 {
0039 
0040 static const QString s_aurorae = QStringLiteral("org.kde.kwin.aurorae");
0041 static const QString s_pluginName = QStringLiteral("org.kde.kdecoration2");
0042 #if HAVE_BREEZE_DECO
0043 static const QString s_defaultPlugin = QStringLiteral(BREEZE_KDECORATION_PLUGIN_ID);
0044 #else
0045 static const QString s_defaultPlugin = s_aurorae;
0046 #endif
0047 
0048 DecorationBridge::DecorationBridge()
0049     : m_factory(nullptr)
0050     , m_showToolTips(false)
0051     , m_settings()
0052     , m_noPlugin(false)
0053 {
0054     readDecorationOptions();
0055 }
0056 
0057 QString DecorationBridge::readPlugin()
0058 {
0059     return kwinApp()->config()->group(s_pluginName).readEntry("library", s_defaultPlugin);
0060 }
0061 
0062 static bool readNoPlugin()
0063 {
0064     return kwinApp()->config()->group(s_pluginName).readEntry("NoPlugin", false);
0065 }
0066 
0067 QString DecorationBridge::readTheme() const
0068 {
0069     return kwinApp()->config()->group(s_pluginName).readEntry("theme", m_defaultTheme);
0070 }
0071 
0072 void DecorationBridge::readDecorationOptions()
0073 {
0074     m_showToolTips = kwinApp()->config()->group(s_pluginName).readEntry("ShowToolTips", true);
0075 }
0076 
0077 bool DecorationBridge::hasPlugin()
0078 {
0079     const DecorationBridge *bridge = workspace()->decorationBridge();
0080     if (!bridge) {
0081         return false;
0082     }
0083     return !bridge->m_noPlugin && bridge->m_factory;
0084 }
0085 
0086 void DecorationBridge::init()
0087 {
0088     using namespace KWaylandServer;
0089     m_noPlugin = readNoPlugin();
0090     if (m_noPlugin) {
0091         if (waylandServer()) {
0092             waylandServer()->decorationManager()->setDefaultMode(ServerSideDecorationManagerInterface::Mode::None);
0093         }
0094         return;
0095     }
0096     m_plugin = readPlugin();
0097     m_settings = QSharedPointer<KDecoration2::DecorationSettings>::create(this);
0098     if (!initPlugin()) {
0099         if (m_plugin != s_defaultPlugin) {
0100             // try loading default plugin
0101             m_plugin = s_defaultPlugin;
0102             initPlugin();
0103         }
0104         // default plugin failed to load, try fallback
0105         if (!m_factory) {
0106             m_plugin = s_aurorae;
0107             initPlugin();
0108         }
0109     }
0110     if (waylandServer()) {
0111         waylandServer()->decorationManager()->setDefaultMode(m_factory ? ServerSideDecorationManagerInterface::Mode::Server : ServerSideDecorationManagerInterface::Mode::None);
0112     }
0113 }
0114 
0115 bool DecorationBridge::initPlugin()
0116 {
0117     const KPluginMetaData metaData = KPluginMetaData::findPluginById(s_pluginName, m_plugin);
0118     if (!metaData.isValid()) {
0119         qCWarning(KWIN_DECORATIONS) << "Could not locate decoration plugin" << m_plugin;
0120         return false;
0121     }
0122     qCDebug(KWIN_DECORATIONS) << "Trying to load decoration plugin: " << metaData.fileName();
0123     auto factoryResult = KPluginFactory::loadFactory(metaData);
0124     if (factoryResult) {
0125         m_factory.reset(factoryResult.plugin);
0126         loadMetaData(metaData.rawData());
0127         return true;
0128     } else {
0129         qCWarning(KWIN_DECORATIONS) << "Error loading plugin:" << factoryResult.errorText;
0130         return false;
0131     }
0132 }
0133 
0134 static void recreateDecorations()
0135 {
0136     Workspace::self()->forEachAbstractClient([](Window *window) {
0137         window->invalidateDecoration();
0138     });
0139 }
0140 
0141 void DecorationBridge::reconfigure()
0142 {
0143     readDecorationOptions();
0144 
0145     if (m_noPlugin != readNoPlugin()) {
0146         m_noPlugin = !m_noPlugin;
0147         // no plugin setting changed
0148         if (m_noPlugin) {
0149             // decorations disabled now
0150             m_plugin = QString();
0151             m_factory.reset();
0152             m_settings.reset();
0153         } else {
0154             // decorations enabled now
0155             init();
0156         }
0157         recreateDecorations();
0158         return;
0159     }
0160 
0161     const QString newPlugin = readPlugin();
0162     if (newPlugin != m_plugin) {
0163         // plugin changed, recreate everything
0164         auto oldFactory = std::move(m_factory);
0165         const auto oldPluginName = m_plugin;
0166         m_plugin = newPlugin;
0167         if (!initPlugin()) {
0168             // loading new plugin failed
0169             m_factory = std::move(oldFactory);
0170             m_plugin = oldPluginName;
0171         } else {
0172             recreateDecorations();
0173             // TODO: unload and destroy old plugin
0174         }
0175     } else {
0176         // same plugin, but theme might have changed
0177         const QString oldTheme = m_theme;
0178         m_theme = readTheme();
0179         if (m_theme != oldTheme) {
0180             recreateDecorations();
0181         }
0182     }
0183 }
0184 
0185 void DecorationBridge::loadMetaData(const QJsonObject &object)
0186 {
0187     // reset all settings
0188     m_recommendedBorderSize = QString();
0189     m_theme = QString();
0190     m_defaultTheme = QString();
0191 
0192     // load the settings
0193     const QJsonValue decoSettings = object.value(s_pluginName);
0194     if (decoSettings.isUndefined()) {
0195         // no settings
0196         return;
0197     }
0198     const QVariantMap decoSettingsMap = decoSettings.toObject().toVariantMap();
0199     auto recBorderSizeIt = decoSettingsMap.find(QStringLiteral("recommendedBorderSize"));
0200     if (recBorderSizeIt != decoSettingsMap.end()) {
0201         m_recommendedBorderSize = recBorderSizeIt.value().toString();
0202     }
0203     findTheme(decoSettingsMap);
0204 
0205     Q_EMIT metaDataLoaded();
0206 }
0207 
0208 void DecorationBridge::findTheme(const QVariantMap &map)
0209 {
0210     auto it = map.find(QStringLiteral("themes"));
0211     if (it == map.end()) {
0212         return;
0213     }
0214     if (!it.value().toBool()) {
0215         return;
0216     }
0217     it = map.find(QStringLiteral("defaultTheme"));
0218     m_defaultTheme = it != map.end() ? it.value().toString() : QString();
0219     m_theme = readTheme();
0220 }
0221 
0222 std::unique_ptr<KDecoration2::DecoratedClientPrivate> DecorationBridge::createClient(KDecoration2::DecoratedClient *client, KDecoration2::Decoration *decoration)
0223 {
0224     return std::unique_ptr<DecoratedClientImpl>(new DecoratedClientImpl(static_cast<Window *>(decoration->parent()), client, decoration));
0225 }
0226 
0227 std::unique_ptr<KDecoration2::DecorationSettingsPrivate> DecorationBridge::settings(KDecoration2::DecorationSettings *parent)
0228 {
0229     return std::unique_ptr<SettingsImpl>(new SettingsImpl(parent));
0230 }
0231 
0232 KDecoration2::Decoration *DecorationBridge::createDecoration(Window *window)
0233 {
0234     if (m_noPlugin) {
0235         return nullptr;
0236     }
0237     if (!m_factory) {
0238         return nullptr;
0239     }
0240     QVariantMap args({{QStringLiteral("bridge"), QVariant::fromValue(this)}});
0241 
0242     if (!m_theme.isEmpty()) {
0243         args.insert(QStringLiteral("theme"), m_theme);
0244     }
0245     auto deco = m_factory->create<KDecoration2::Decoration>(window, QVariantList({args}));
0246     deco->setSettings(m_settings);
0247     deco->init();
0248     return deco;
0249 }
0250 
0251 static QString settingsProperty(const QVariant &variant)
0252 {
0253     if (QLatin1String(variant.typeName()) == QLatin1String("KDecoration2::BorderSize")) {
0254         return QString::number(variant.toInt());
0255     } else if (QLatin1String(variant.typeName()) == QLatin1String("QVector<KDecoration2::DecorationButtonType>")) {
0256         const auto &b = variant.value<QVector<KDecoration2::DecorationButtonType>>();
0257         QString buffer;
0258         for (auto it = b.begin(); it != b.end(); ++it) {
0259             if (it != b.begin()) {
0260                 buffer.append(QStringLiteral(", "));
0261             }
0262             buffer.append(QString::number(int(*it)));
0263         }
0264         return buffer;
0265     }
0266     return variant.toString();
0267 }
0268 
0269 QString DecorationBridge::supportInformation() const
0270 {
0271     QString b;
0272     if (m_noPlugin) {
0273         b.append(QStringLiteral("Decorations are disabled"));
0274     } else {
0275         b.append(QStringLiteral("Plugin: %1\n").arg(m_plugin));
0276         b.append(QStringLiteral("Theme: %1\n").arg(m_theme));
0277         b.append(QStringLiteral("Plugin recommends border size: %1\n").arg(m_recommendedBorderSize.isNull() ? "No" : m_recommendedBorderSize));
0278         const QMetaObject *metaOptions = m_settings->metaObject();
0279         for (int i = 0; i < metaOptions->propertyCount(); ++i) {
0280             const QMetaProperty property = metaOptions->property(i);
0281             if (QLatin1String(property.name()) == QLatin1String("objectName")) {
0282                 continue;
0283             }
0284             b.append(QStringLiteral("%1: %2\n").arg(property.name(), settingsProperty(m_settings->property(property.name()))));
0285         }
0286     }
0287     return b;
0288 }
0289 
0290 } // Decoration
0291 } // KWin