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