/* KWin - the KDE window manager This file is part of the KDE project. SPDX-FileCopyrightText: 2014 Martin Gräßlin SPDX-License-Identifier: GPL-2.0-or-later */ #include "decorationbridge.h" #include "decoratedclient.h" #include "decorations_logging.h" #include "settings.h" // KWin core #include "abstract_client.h" #include "wayland_server.h" #include "workspace.h" #include // KDecoration #include #include #include // KWayland #include // Frameworks #include #include // Qt #include #include namespace KWin { namespace Decoration { static const QString s_aurorae = QStringLiteral("org.kde.kwin.aurorae"); static const QString s_pluginName = QStringLiteral("org.kde.kdecoration2"); #if HAVE_BREEZE_DECO static const QString s_defaultPlugin = QStringLiteral(BREEZE_KDECORATION_PLUGIN_ID); #else static const QString s_defaultPlugin = s_aurorae; #endif KWIN_SINGLETON_FACTORY(DecorationBridge) DecorationBridge::DecorationBridge(QObject *parent) : KDecoration2::DecorationBridge(parent) , m_factory(nullptr) , m_showToolTips(false) , m_settings() , m_noPlugin(false) { readDecorationOptions(); } DecorationBridge::~DecorationBridge() { s_self = nullptr; } QString DecorationBridge::readPlugin() { return kwinApp()->config()->group(s_pluginName).readEntry("library", s_defaultPlugin); } static bool readNoPlugin() { return kwinApp()->config()->group(s_pluginName).readEntry("NoPlugin", false); } QString DecorationBridge::readTheme() const { return kwinApp()->config()->group(s_pluginName).readEntry("theme", m_defaultTheme); } void DecorationBridge::readDecorationOptions() { m_showToolTips = kwinApp()->config()->group(s_pluginName).readEntry("ShowToolTips", true); } bool DecorationBridge::hasPlugin() { const DecorationBridge *bridge = DecorationBridge::self(); if (!bridge) { return false; } return !bridge->m_noPlugin && bridge->m_factory; } void DecorationBridge::init() { using namespace KWaylandServer; m_noPlugin = readNoPlugin(); if (m_noPlugin) { if (waylandServer()) { waylandServer()->decorationManager()->setDefaultMode(ServerSideDecorationManagerInterface::Mode::None); } return; } m_plugin = readPlugin(); m_settings = QSharedPointer::create(this); initPlugin(); if (!m_factory) { if (m_plugin != s_defaultPlugin) { // try loading default plugin m_plugin = s_defaultPlugin; initPlugin(); } // default plugin failed to load, try fallback if (!m_factory) { m_plugin = s_aurorae; initPlugin(); } } if (waylandServer()) { waylandServer()->decorationManager()->setDefaultMode(m_factory ? ServerSideDecorationManagerInterface::Mode::Server : ServerSideDecorationManagerInterface::Mode::None); } } void DecorationBridge::initPlugin() { const KPluginMetaData metaData = KPluginMetaData::findPluginById(s_pluginName, m_plugin); if (!metaData.isValid()) { qCWarning(KWIN_DECORATIONS) << "Could not locate decoration plugin" << m_plugin; return; } qCDebug(KWIN_DECORATIONS) << "Trying to load decoration plugin: " << metaData.fileName(); auto factoryResult = KPluginFactory::loadFactory(metaData); if (!factoryResult) { qCWarning(KWIN_DECORATIONS) << "Error loading plugin:" << factoryResult.errorText; } else { m_factory = factoryResult.plugin; loadMetaData(metaData.rawData()); } } static void recreateDecorations() { Workspace::self()->forEachAbstractClient([](AbstractClient *c) { c->invalidateDecoration(); }); } void DecorationBridge::reconfigure() { readDecorationOptions(); if (m_noPlugin != readNoPlugin()) { m_noPlugin = !m_noPlugin; // no plugin setting changed if (m_noPlugin) { // decorations disabled now m_plugin = QString(); delete m_factory; m_factory = nullptr; m_settings.clear(); } else { // decorations enabled now init(); } recreateDecorations(); return; } const QString newPlugin = readPlugin(); if (newPlugin != m_plugin) { // plugin changed, recreate everything auto oldFactory = m_factory; const auto oldPluginName = m_plugin; m_plugin = newPlugin; initPlugin(); if (m_factory == oldFactory) { // loading new plugin failed m_factory = oldFactory; m_plugin = oldPluginName; } else { recreateDecorations(); // TODO: unload and destroy old plugin } } else { // same plugin, but theme might have changed const QString oldTheme = m_theme; m_theme = readTheme(); if (m_theme != oldTheme) { recreateDecorations(); } } } void DecorationBridge::loadMetaData(const QJsonObject &object) { // reset all settings m_recommendedBorderSize = QString(); m_theme = QString(); m_defaultTheme = QString(); // load the settings const QJsonValue decoSettings = object.value(s_pluginName); if (decoSettings.isUndefined()) { // no settings return; } const QVariantMap decoSettingsMap = decoSettings.toObject().toVariantMap(); auto recBorderSizeIt = decoSettingsMap.find(QStringLiteral("recommendedBorderSize")); if (recBorderSizeIt != decoSettingsMap.end()) { m_recommendedBorderSize = recBorderSizeIt.value().toString(); } findTheme(decoSettingsMap); Q_EMIT metaDataLoaded(); } void DecorationBridge::findTheme(const QVariantMap &map) { auto it = map.find(QStringLiteral("themes")); if (it == map.end()) { return; } if (!it.value().toBool()) { return; } it = map.find(QStringLiteral("defaultTheme")); m_defaultTheme = it != map.end() ? it.value().toString() : QString(); m_theme = readTheme(); } std::unique_ptr DecorationBridge::createClient(KDecoration2::DecoratedClient *client, KDecoration2::Decoration *decoration) { return std::unique_ptr(new DecoratedClientImpl(static_cast(decoration->parent()), client, decoration)); } std::unique_ptr DecorationBridge::settings(KDecoration2::DecorationSettings *parent) { return std::unique_ptr(new SettingsImpl(parent)); } KDecoration2::Decoration *DecorationBridge::createDecoration(AbstractClient *client) { if (m_noPlugin) { return nullptr; } if (!m_factory) { return nullptr; } QVariantMap args({ {QStringLiteral("bridge"), QVariant::fromValue(this)} }); if (!m_theme.isEmpty()) { args.insert(QStringLiteral("theme"), m_theme); } auto deco = m_factory->create(client, QVariantList({args})); deco->setSettings(m_settings); deco->init(); return deco; } static QString settingsProperty(const QVariant &variant) { if (QLatin1String(variant.typeName()) == QLatin1String("KDecoration2::BorderSize")) { return QString::number(variant.toInt()); } else if (QLatin1String(variant.typeName()) == QLatin1String("QVector")) { const auto &b = variant.value>(); QString buffer; for (auto it = b.begin(); it != b.end(); ++it) { if (it != b.begin()) { buffer.append(QStringLiteral(", ")); } buffer.append(QString::number(int(*it))); } return buffer; } return variant.toString(); } QString DecorationBridge::supportInformation() const { QString b; if (m_noPlugin) { b.append(QStringLiteral("Decorations are disabled")); } else { b.append(QStringLiteral("Plugin: %1\n").arg(m_plugin)); b.append(QStringLiteral("Theme: %1\n").arg(m_theme)); b.append(QStringLiteral("Plugin recommends border size: %1\n").arg(m_recommendedBorderSize.isNull() ? "No" : m_recommendedBorderSize)); const QMetaObject *metaOptions = m_settings->metaObject(); for (int i=0; ipropertyCount(); ++i) { const QMetaProperty property = metaOptions->property(i); if (QLatin1String(property.name()) == QLatin1String("objectName")) { continue; } b.append(QStringLiteral("%1: %2\n").arg(property.name(), settingsProperty(m_settings->property(property.name())))); } } return b; } } // Decoration } // KWin