File indexing completed on 2024-05-12 05:31:25

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.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 = 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     m_noPlugin = readNoPlugin();
0089     if (m_noPlugin) {
0090         if (waylandServer()) {
0091             waylandServer()->decorationManager()->setDefaultMode(ServerSideDecorationManagerInterface::Mode::None);
0092         }
0093         return;
0094     }
0095     m_plugin = readPlugin();
0096     m_settings = std::make_shared<KDecoration2::DecorationSettings>(this);
0097     if (!initPlugin()) {
0098         if (m_plugin != s_defaultPlugin) {
0099             // try loading default plugin
0100             m_plugin = s_defaultPlugin;
0101             initPlugin();
0102         }
0103         // default plugin failed to load, try fallback
0104         if (!m_factory) {
0105             m_plugin = s_aurorae;
0106             initPlugin();
0107         }
0108     }
0109     if (waylandServer()) {
0110         waylandServer()->decorationManager()->setDefaultMode(m_factory ? ServerSideDecorationManagerInterface::Mode::Server : ServerSideDecorationManagerInterface::Mode::None);
0111     }
0112 }
0113 
0114 bool DecorationBridge::initPlugin()
0115 {
0116     const KPluginMetaData metaData = KPluginMetaData::findPluginById(s_pluginName, m_plugin);
0117     if (!metaData.isValid()) {
0118         qCWarning(KWIN_DECORATIONS) << "Could not locate decoration plugin" << m_plugin;
0119         return false;
0120     }
0121     qCDebug(KWIN_DECORATIONS) << "Trying to load decoration plugin: " << metaData.fileName();
0122     if (auto factoryResult = KPluginFactory::loadFactory(metaData)) {
0123         m_factory.reset(factoryResult.plugin);
0124         loadMetaData(metaData.rawData());
0125         return true;
0126     } else {
0127         qCWarning(KWIN_DECORATIONS) << "Error loading plugin:" << factoryResult.errorText;
0128         return false;
0129     }
0130 }
0131 
0132 static void recreateDecorations()
0133 {
0134     Workspace::self()->forEachWindow([](Window *window) {
0135         window->invalidateDecoration();
0136     });
0137 }
0138 
0139 void DecorationBridge::reconfigure()
0140 {
0141     readDecorationOptions();
0142 
0143     if (m_noPlugin != readNoPlugin()) {
0144         m_noPlugin = !m_noPlugin;
0145         // no plugin setting changed
0146         if (m_noPlugin) {
0147             // decorations disabled now
0148             m_plugin = QString();
0149             m_factory.reset();
0150             m_settings.reset();
0151         } else {
0152             // decorations enabled now
0153             init();
0154         }
0155         recreateDecorations();
0156         return;
0157     }
0158 
0159     const QString newPlugin = readPlugin();
0160     if (newPlugin != m_plugin) {
0161         // plugin changed, recreate everything
0162         auto oldFactory = std::move(m_factory);
0163         const auto oldPluginName = m_plugin;
0164         m_plugin = newPlugin;
0165         if (!initPlugin()) {
0166             // loading new plugin failed
0167             m_factory = std::move(oldFactory);
0168             m_plugin = oldPluginName;
0169         } else {
0170             recreateDecorations();
0171             // TODO: unload and destroy old plugin
0172         }
0173     } else {
0174         // same plugin, but theme might have changed
0175         const QString oldTheme = m_theme;
0176         m_theme = readTheme();
0177         if (m_theme != oldTheme) {
0178             recreateDecorations();
0179         }
0180     }
0181 }
0182 
0183 void DecorationBridge::loadMetaData(const QJsonObject &object)
0184 {
0185     // reset all settings
0186     m_recommendedBorderSize = QString();
0187     m_theme = QString();
0188     m_defaultTheme = QString();
0189 
0190     // load the settings
0191     const QJsonValue decoSettings = object.value(s_pluginName);
0192     if (decoSettings.isUndefined()) {
0193         // no settings
0194         return;
0195     }
0196     const QVariantMap decoSettingsMap = decoSettings.toObject().toVariantMap();
0197     auto recBorderSizeIt = decoSettingsMap.find(QStringLiteral("recommendedBorderSize"));
0198     if (recBorderSizeIt != decoSettingsMap.end()) {
0199         m_recommendedBorderSize = recBorderSizeIt.value().toString();
0200     }
0201     findTheme(decoSettingsMap);
0202 
0203     Q_EMIT metaDataLoaded();
0204 }
0205 
0206 void DecorationBridge::findTheme(const QVariantMap &map)
0207 {
0208     auto it = map.find(QStringLiteral("themes"));
0209     if (it == map.end()) {
0210         return;
0211     }
0212     if (!it.value().toBool()) {
0213         return;
0214     }
0215     it = map.find(QStringLiteral("defaultTheme"));
0216     m_defaultTheme = it != map.end() ? it.value().toString() : QString();
0217     m_theme = readTheme();
0218 }
0219 
0220 std::unique_ptr<KDecoration2::DecoratedClientPrivate> DecorationBridge::createClient(KDecoration2::DecoratedClient *client, KDecoration2::Decoration *decoration)
0221 {
0222     return std::unique_ptr<DecoratedClientImpl>(new DecoratedClientImpl(static_cast<Window *>(decoration->parent()), client, decoration));
0223 }
0224 
0225 std::unique_ptr<KDecoration2::DecorationSettingsPrivate> DecorationBridge::settings(KDecoration2::DecorationSettings *parent)
0226 {
0227     return std::unique_ptr<SettingsImpl>(new SettingsImpl(parent));
0228 }
0229 
0230 KDecoration2::Decoration *DecorationBridge::createDecoration(Window *window)
0231 {
0232     if (m_noPlugin) {
0233         return nullptr;
0234     }
0235     if (!m_factory) {
0236         return nullptr;
0237     }
0238     QVariantMap args({{QStringLiteral("bridge"), QVariant::fromValue(this)}});
0239 
0240     if (!m_theme.isEmpty()) {
0241         args.insert(QStringLiteral("theme"), m_theme);
0242     }
0243     auto deco = m_factory->create<KDecoration2::Decoration>(window, QVariantList{args});
0244     deco->setSettings(m_settings);
0245     deco->init();
0246     return deco;
0247 }
0248 
0249 static QString settingsProperty(const QVariant &variant)
0250 {
0251     if (QLatin1String(variant.typeName()) == QLatin1String("KDecoration2::BorderSize")) {
0252         return QString::number(variant.toInt());
0253     } else if (QLatin1String(variant.typeName()) == QLatin1String("QList<KDecoration2::DecorationButtonType>")) {
0254         const auto &b = variant.value<QList<KDecoration2::DecorationButtonType>>();
0255         QString buffer;
0256         for (auto it = b.begin(); it != b.end(); ++it) {
0257             if (it != b.begin()) {
0258                 buffer.append(QStringLiteral(", "));
0259             }
0260             buffer.append(QString::number(int(*it)));
0261         }
0262         return buffer;
0263     }
0264     return variant.toString();
0265 }
0266 
0267 QString DecorationBridge::supportInformation() const
0268 {
0269     QString b;
0270     if (m_noPlugin) {
0271         b.append(QStringLiteral("Decorations are disabled"));
0272     } else {
0273         b.append(QStringLiteral("Plugin: %1\n").arg(m_plugin));
0274         b.append(QStringLiteral("Theme: %1\n").arg(m_theme));
0275         b.append(QStringLiteral("Plugin recommends border size: %1\n").arg(m_recommendedBorderSize.isNull() ? "No" : m_recommendedBorderSize));
0276         const QMetaObject *metaOptions = m_settings->metaObject();
0277         for (int i = 0; i < metaOptions->propertyCount(); ++i) {
0278             const QMetaProperty property = metaOptions->property(i);
0279             if (QLatin1String(property.name()) == QLatin1String("objectName")) {
0280                 continue;
0281             }
0282             b.append(QStringLiteral("%1: %2\n").arg(property.name(), settingsProperty(m_settings->property(property.name()))));
0283         }
0284     }
0285     return b;
0286 }
0287 
0288 } // Decoration
0289 } // KWin
0290 
0291 #include "moc_decorationbridge.cpp"