File indexing completed on 2024-05-12 13:31:12
0001 /* 0002 * SPDX-FileCopyrightText: 2020 Mikhail Zolotukhin <zomial@protonmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 0007 #include <QCoreApplication> 0008 #include <QMouseEvent> 0009 0010 #include <KConfigGroup> 0011 #include <KDecoration2/DecoratedClient> 0012 #include <KDecoration2/DecorationSettings> 0013 #include <KDecoration2/Private/DecoratedClientPrivate> 0014 #include <KDecoration2/Private/DecorationSettingsPrivate> 0015 #include <KPluginFactory> 0016 #include <KPluginMetaData> 0017 #include <KSharedConfig> 0018 0019 #include "decorationpainter.h" 0020 #include "dummydecoratedclient.h" 0021 #include "dummydecorationbridge.h" 0022 #include "dummydecorationsettings.h" 0023 0024 namespace KDecoration2 0025 { 0026 DummyDecorationBridge::DummyDecorationBridge(const QString &decorationTheme, QObject *parent) 0027 : DecorationBridge(parent) 0028 , m_decorationsConfigFileName() 0029 , m_factory() 0030 , m_decoration() 0031 , m_client() 0032 { 0033 // HACK: 0034 // Some window decorations use button fade-in and fade-out animations. 0035 // These animations are very slow, and they are preventing this bridge 0036 // to correctly draw hover states of the buttons, when we pass a "hover" 0037 // event to them. To avoid this harmful side effect we use a hack: 0038 // We disable the animations via user configuration file temporary if 0039 // they were enabled, draw a buttons and then enable them again. 0040 if (decorationTheme == QStringLiteral("Oxygen")) { 0041 m_decorationsConfigFileName = QStringLiteral("oxygenrc"); 0042 } else { // for Breeze window decorations and its forks 0043 m_decorationsConfigFileName = QStringLiteral("breezerc"); 0044 } 0045 0046 disableAnimations(); 0047 0048 const QString pluginPath = windowDecorationPluginPath(decorationTheme); 0049 m_pluginLoader.setFileName(pluginPath); 0050 m_factory = qobject_cast<KPluginFactory *>(m_pluginLoader.instance()); 0051 if (m_factory) { 0052 const QVariantMap args({{QStringLiteral("bridge"), QVariant::fromValue(this)}}); 0053 m_decoration = m_factory->create<KDecoration2::Decoration>(m_factory, QVariantList({args})); 0054 } 0055 0056 auto decorationSettings = QSharedPointer<KDecoration2::DecorationSettings>::create(this); 0057 m_decoration->setSettings(decorationSettings); 0058 m_decoration->init(); 0059 0060 // Update decoration settings, e.g. Breeze's "Draw a circle around close button" 0061 if (m_settings) { 0062 Q_EMIT m_settings->decorationSettings()->reconfigured(); 0063 } 0064 enableAnimations(); 0065 } 0066 0067 DummyDecorationBridge::~DummyDecorationBridge() 0068 { 0069 m_pluginLoader.unload(); 0070 } 0071 0072 std::unique_ptr<KDecoration2::DecorationSettingsPrivate> DummyDecorationBridge::settings(KDecoration2::DecorationSettings *parent) 0073 { 0074 auto newSettings = std::unique_ptr<DummyDecorationSettings>(new DummyDecorationSettings(parent)); 0075 m_settings = newSettings.get(); 0076 return newSettings; 0077 } 0078 0079 std::unique_ptr<KDecoration2::DecoratedClientPrivate> DummyDecorationBridge::createClient(KDecoration2::DecoratedClient *client, 0080 KDecoration2::Decoration *decoration) 0081 { 0082 auto ptr = std::unique_ptr<DummyDecoratedClient>(new DummyDecoratedClient(client, decoration)); 0083 m_client = ptr.get(); 0084 return ptr; 0085 } 0086 0087 void DummyDecorationBridge::paintButton(QPainter &painter, const QString &buttonType, const QString &buttonState) 0088 { 0089 disableAnimations(); 0090 std::unique_ptr<KDecoration2::DecorationButton> button{ 0091 m_factory->create<KDecoration2::DecorationButton>(m_decoration, 0092 QVariantList({ 0093 QVariant::fromValue(strToButtonType(buttonType)), 0094 QVariant::fromValue(m_decoration), 0095 }))}; 0096 0097 #if KCOREADDONS_BUILD_DEPRECATED_SINCE(5, 87) 0098 if (button == nullptr) { 0099 button.reset(m_factory->create<KDecoration2::DecorationButton>(QStringLiteral("button"), 0100 m_decoration, 0101 QVariantList({ 0102 QVariant::fromValue(strToButtonType(buttonType)), 0103 QVariant::fromValue(m_decoration), 0104 }))); 0105 } 0106 #endif 0107 0108 if (button == nullptr) { 0109 return; 0110 } 0111 0112 button->setGeometry(DecorationPainter::ButtonGeometry); 0113 0114 if (buttonType == QStringLiteral("maximized")) { 0115 // Different decorations use different ways to know if the window is maximized 0116 // For example Breeze uses 'checked' property, but Oxygen uses client's 'isMaximized' method 0117 button->setChecked(true); 0118 if (m_client) { 0119 dynamic_cast<DummyDecoratedClient *>(m_client)->setMaximized(true); 0120 } 0121 } 0122 0123 if (buttonState.contains(QStringLiteral("active"))) { 0124 passMousePressEventToButton(button.get()); 0125 } else if (buttonState.contains(QStringLiteral("hover"))) { 0126 passMouseHoverEventToButton(button.get()); 0127 } 0128 0129 if (buttonState.contains(QStringLiteral("backdrop"))) { 0130 if (m_client) { 0131 dynamic_cast<DummyDecoratedClient *>(m_client)->setActive(false); 0132 } 0133 } else { 0134 if (m_client) { 0135 dynamic_cast<DummyDecoratedClient *>(m_client)->setActive(true); 0136 } 0137 } 0138 0139 button->paint(&painter, DecorationPainter::ButtonGeometry); 0140 enableAnimations(); 0141 } 0142 0143 void DummyDecorationBridge::disableAnimations() 0144 { 0145 KSharedConfig::Ptr decorationConfig = KSharedConfig::openConfig(m_decorationsConfigFileName, KConfig::NoGlobals); 0146 if (decorationConfig) { 0147 KConfigGroup group = decorationConfig->group(QStringLiteral("Windeco")); 0148 group.writeEntry(QStringLiteral("AnimationsEnabled"), false, KConfig::WriteConfigFlags()); 0149 } 0150 0151 // In case decoration is using global animation settings 0152 KSharedConfig::Ptr globalConfig = KSharedConfig::openConfig(); 0153 if (globalConfig) { 0154 auto group = globalConfig->group(QStringLiteral("KDE")); 0155 globalAnimationEntryValue = group.readEntry(QStringLiteral("AnimationDurationFactor"), 1.0); 0156 group.writeEntry(QStringLiteral("AnimationDurationFactor"), 0, KConfig::WriteConfigFlags()); 0157 } 0158 } 0159 0160 void DummyDecorationBridge::enableAnimations() 0161 { 0162 KSharedConfig::Ptr globalConfig = KSharedConfig::openConfig(); 0163 if (globalConfig) { 0164 auto group = globalConfig->group(QStringLiteral("KDE")); 0165 group.writeEntry(QStringLiteral("AnimationDurationFactor"), globalAnimationEntryValue, KConfig::WriteConfigFlags()); 0166 } 0167 } 0168 0169 QString DummyDecorationBridge::windowDecorationPluginPath(const QString &decorationTheme) const 0170 { 0171 const auto decorationPlugins = KPluginMetaData::findPlugins(QStringLiteral("org.kde.kdecoration2")); 0172 0173 QString defaultPluginPath; 0174 0175 for (const auto &pluginMetaData : decorationPlugins) { 0176 if (pluginMetaData.pluginId() == QLatin1String("org.kde.breeze")) { 0177 defaultPluginPath = pluginMetaData.fileName(); 0178 } 0179 0180 if (pluginMetaData.name() == decorationTheme) { 0181 return pluginMetaData.fileName(); 0182 } 0183 } 0184 return defaultPluginPath; 0185 } 0186 0187 void DummyDecorationBridge::passMouseHoverEventToButton(KDecoration2::DecorationButton *button) const 0188 { 0189 QHoverEvent event{QEvent::HoverEnter, 0190 { 0191 DecorationPainter::ButtonGeometry.width() / 2.0, 0192 DecorationPainter::ButtonGeometry.height() / 2.0, 0193 }, 0194 { 0195 (DecorationPainter::ButtonGeometry.width() / 2.0) - 1, 0196 (DecorationPainter::ButtonGeometry.height() / 2.0) - 1, 0197 }, 0198 Qt::NoModifier}; 0199 QCoreApplication::instance()->sendEvent(button, &event); 0200 } 0201 0202 void DummyDecorationBridge::passMousePressEventToButton(KDecoration2::DecorationButton *button) const 0203 { 0204 QMouseEvent event{QEvent::MouseButtonPress, 0205 { 0206 DecorationPainter::ButtonGeometry.width() / 2.0, 0207 DecorationPainter::ButtonGeometry.height() / 2.0, 0208 }, 0209 Qt::LeftButton, 0210 Qt::LeftButton, 0211 Qt::NoModifier}; 0212 QCoreApplication::instance()->sendEvent(button, &event); 0213 } 0214 0215 KDecoration2::DecorationButtonType DummyDecorationBridge::strToButtonType(const QString &type) const 0216 { 0217 if (type == QStringLiteral("minimize")) { 0218 return KDecoration2::DecorationButtonType::Minimize; 0219 } else if (type == QStringLiteral("close")) { 0220 return KDecoration2::DecorationButtonType::Close; 0221 } else { 0222 return KDecoration2::DecorationButtonType::Maximize; 0223 } 0224 } 0225 0226 }