File indexing completed on 2024-11-10 04:56:41
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"