File indexing completed on 2024-04-28 05:27:06
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 = std::make_shared<KDecoration2::DecorationSettings>(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 (button == nullptr) { 0098 return; 0099 } 0100 0101 button->setGeometry(DecorationPainter::ButtonGeometry); 0102 0103 if (buttonType == QStringLiteral("maximized")) { 0104 // Different decorations use different ways to know if the window is maximized 0105 // For example Breeze uses 'checked' property, but Oxygen uses client's 'isMaximized' method 0106 button->setChecked(true); 0107 if (m_client) { 0108 dynamic_cast<DummyDecoratedClient *>(m_client)->setMaximized(true); 0109 } 0110 } 0111 0112 if (buttonState.contains(QStringLiteral("active"))) { 0113 passMousePressEventToButton(button.get()); 0114 } else if (buttonState.contains(QStringLiteral("hover"))) { 0115 passMouseHoverEventToButton(button.get()); 0116 } 0117 0118 if (buttonState.contains(QStringLiteral("backdrop"))) { 0119 if (m_client) { 0120 dynamic_cast<DummyDecoratedClient *>(m_client)->setActive(false); 0121 } 0122 } else { 0123 if (m_client) { 0124 dynamic_cast<DummyDecoratedClient *>(m_client)->setActive(true); 0125 } 0126 } 0127 0128 button->paint(&painter, DecorationPainter::ButtonGeometry); 0129 enableAnimations(); 0130 } 0131 0132 void DummyDecorationBridge::disableAnimations() 0133 { 0134 KSharedConfig::Ptr decorationConfig = KSharedConfig::openConfig(m_decorationsConfigFileName, KConfig::NoGlobals); 0135 if (decorationConfig) { 0136 KConfigGroup group = decorationConfig->group(QStringLiteral("Windeco")); 0137 group.writeEntry(QStringLiteral("AnimationsEnabled"), false, KConfig::WriteConfigFlags()); 0138 } 0139 0140 // In case decoration is using global animation settings 0141 KSharedConfig::Ptr globalConfig = KSharedConfig::openConfig(); 0142 if (globalConfig) { 0143 auto group = globalConfig->group(QStringLiteral("KDE")); 0144 globalAnimationEntryValue = group.readEntry(QStringLiteral("AnimationDurationFactor"), 1.0); 0145 group.writeEntry(QStringLiteral("AnimationDurationFactor"), 0, KConfig::WriteConfigFlags()); 0146 } 0147 } 0148 0149 void DummyDecorationBridge::enableAnimations() 0150 { 0151 KSharedConfig::Ptr globalConfig = KSharedConfig::openConfig(); 0152 if (globalConfig) { 0153 auto group = globalConfig->group(QStringLiteral("KDE")); 0154 group.writeEntry(QStringLiteral("AnimationDurationFactor"), globalAnimationEntryValue, KConfig::WriteConfigFlags()); 0155 } 0156 } 0157 0158 QString DummyDecorationBridge::windowDecorationPluginPath(const QString &decorationTheme) const 0159 { 0160 const auto decorationPlugins = KPluginMetaData::findPlugins(QStringLiteral("org.kde.kdecoration2")); 0161 0162 QString defaultPluginPath; 0163 0164 for (const auto &pluginMetaData : decorationPlugins) { 0165 if (pluginMetaData.pluginId() == QLatin1String("org.kde.breeze")) { 0166 defaultPluginPath = pluginMetaData.fileName(); 0167 } 0168 0169 if (pluginMetaData.name() == decorationTheme) { 0170 return pluginMetaData.fileName(); 0171 } 0172 } 0173 return defaultPluginPath; 0174 } 0175 0176 void DummyDecorationBridge::passMouseHoverEventToButton(KDecoration2::DecorationButton *button) const 0177 { 0178 QHoverEvent event{QEvent::HoverEnter, 0179 { 0180 DecorationPainter::ButtonGeometry.width() / 2.0, 0181 DecorationPainter::ButtonGeometry.height() / 2.0, 0182 }, 0183 { 0184 (DecorationPainter::ButtonGeometry.width() / 2.0) - 1, 0185 (DecorationPainter::ButtonGeometry.height() / 2.0) - 1, 0186 }, 0187 Qt::NoModifier}; 0188 QCoreApplication::instance()->sendEvent(button, &event); 0189 } 0190 0191 void DummyDecorationBridge::passMousePressEventToButton(KDecoration2::DecorationButton *button) const 0192 { 0193 QMouseEvent event{QEvent::MouseButtonPress, 0194 { 0195 DecorationPainter::ButtonGeometry.width() / 2.0, 0196 DecorationPainter::ButtonGeometry.height() / 2.0, 0197 }, 0198 Qt::LeftButton, 0199 Qt::LeftButton, 0200 Qt::NoModifier}; 0201 QCoreApplication::instance()->sendEvent(button, &event); 0202 } 0203 0204 KDecoration2::DecorationButtonType DummyDecorationBridge::strToButtonType(const QString &type) const 0205 { 0206 if (type == QStringLiteral("minimize")) { 0207 return KDecoration2::DecorationButtonType::Minimize; 0208 } else if (type == QStringLiteral("close")) { 0209 return KDecoration2::DecorationButtonType::Close; 0210 } else { 0211 return KDecoration2::DecorationButtonType::Maximize; 0212 } 0213 } 0214 0215 } 0216 0217 #include "moc_dummydecorationbridge.cpp"