File indexing completed on 2024-05-12 04:00:25
0001 /* 0002 SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org> 0003 SPDX-FileCopyrightText: 2015 Marco Martin <mart@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0006 */ 0007 0008 #include "windoweffects.h" 0009 0010 #include <QDebug> 0011 #include <QExposeEvent> 0012 #include <QGuiApplication> 0013 0014 #include <private/qwaylandwindow_p.h> 0015 0016 #include <QWaylandClientExtensionTemplate> 0017 #include <qwaylandclientextension.h> 0018 0019 #include "qwayland-blur.h" 0020 #include "qwayland-contrast.h" 0021 #include "qwayland-slide.h" 0022 0023 #include "surfacehelper.h" 0024 0025 static wl_region *createRegion(const QRegion ®ion) 0026 { 0027 QPlatformNativeInterface *native = qGuiApp->platformNativeInterface(); 0028 if (!native) { 0029 return nullptr; 0030 } 0031 auto compositor = reinterpret_cast<wl_compositor *>(native->nativeResourceForIntegration(QByteArrayLiteral("compositor"))); 0032 if (!compositor) { 0033 return nullptr; 0034 } 0035 auto wl_region = wl_compositor_create_region(compositor); 0036 for (const auto &rect : region) { 0037 wl_region_add(wl_region, rect.x(), rect.y(), rect.width(), rect.height()); 0038 } 0039 return wl_region; 0040 } 0041 0042 class BlurManager : public QWaylandClientExtensionTemplate<BlurManager>, public QtWayland::org_kde_kwin_blur_manager 0043 { 0044 public: 0045 BlurManager() 0046 : QWaylandClientExtensionTemplate<BlurManager>(1) 0047 { 0048 } 0049 }; 0050 0051 class Blur : public QObject, public QtWayland::org_kde_kwin_blur 0052 { 0053 public: 0054 Blur(struct ::org_kde_kwin_blur *object, QObject *parent) 0055 : QObject(parent) 0056 , QtWayland::org_kde_kwin_blur(object) 0057 { 0058 } 0059 0060 ~Blur() override 0061 { 0062 release(); 0063 } 0064 }; 0065 0066 class ContrastManager : public QWaylandClientExtensionTemplate<ContrastManager>, public QtWayland::org_kde_kwin_contrast_manager 0067 { 0068 public: 0069 ContrastManager() 0070 : QWaylandClientExtensionTemplate<ContrastManager>(2) 0071 { 0072 } 0073 }; 0074 0075 class Contrast : public QObject, public QtWayland::org_kde_kwin_contrast 0076 { 0077 public: 0078 Contrast(struct ::org_kde_kwin_contrast *object, QObject *parent) 0079 : QObject(parent) 0080 , QtWayland::org_kde_kwin_contrast(object) 0081 { 0082 } 0083 0084 ~Contrast() override 0085 { 0086 release(); 0087 } 0088 }; 0089 0090 class SlideManager : public QWaylandClientExtensionTemplate<SlideManager>, public QtWayland::org_kde_kwin_slide_manager 0091 { 0092 public: 0093 SlideManager() 0094 : QWaylandClientExtensionTemplate<SlideManager>(1) 0095 { 0096 } 0097 }; 0098 0099 class Slide : public QObject, public QtWayland::org_kde_kwin_slide 0100 { 0101 public: 0102 Slide(struct ::org_kde_kwin_slide *object, QObject *parent) 0103 : QObject(parent) 0104 , QtWayland::org_kde_kwin_slide(object) 0105 { 0106 } 0107 0108 ~Slide() override 0109 { 0110 release(); 0111 } 0112 }; 0113 0114 WindowEffects::WindowEffects() 0115 : QObject() 0116 , KWindowEffectsPrivate() 0117 { 0118 m_blurManager = new BlurManager(); 0119 m_contrastManager = new ContrastManager(); 0120 m_slideManager = new SlideManager(); 0121 0122 // The KWindowEffects API doesn't provide any signals to notify that the particular 0123 // effect has become unavailable. So we re-install effects when the corresponding globals 0124 // are added. 0125 0126 connect(m_blurManager, &BlurManager::activeChanged, this, [this] { 0127 for (auto it = m_blurRegions.constBegin(); it != m_blurRegions.constEnd(); ++it) { 0128 installBlur(it.key(), m_blurManager->isActive(), *it); 0129 } 0130 }); 0131 0132 connect(m_contrastManager, &ContrastManager::activeChanged, this, [this] { 0133 for (auto it = m_backgroundConstrastRegions.constBegin(); it != m_backgroundConstrastRegions.constEnd(); ++it) { 0134 if (m_contrastManager->isActive()) { 0135 installContrast(it.key(), true, it->contrast, it->intensity, it->saturation, it->region); 0136 } else { 0137 installContrast(it.key(), false); 0138 } 0139 } 0140 }); 0141 0142 connect(m_slideManager, &SlideManager::activeChanged, this, [this] { 0143 for (auto it = m_slideMap.constBegin(); it != m_slideMap.constEnd(); ++it) { 0144 if (m_slideManager->isActive()) { 0145 installSlide(it.key(), it->location, it->offset); 0146 } else { 0147 installSlide(it.key(), KWindowEffects::SlideFromLocation::NoEdge, 0); 0148 } 0149 } 0150 }); 0151 } 0152 0153 WindowEffects::~WindowEffects() 0154 { 0155 delete m_blurManager; 0156 delete m_contrastManager; 0157 delete m_slideManager; 0158 } 0159 0160 void WindowEffects::trackWindow(QWindow *window) 0161 { 0162 if (!m_windowWatchers.contains(window)) { 0163 window->installEventFilter(this); 0164 auto conn = connect(window, &QObject::destroyed, this, [this, window]() { 0165 resetBlur(window); 0166 m_blurRegions.remove(window); 0167 resetContrast(window); 0168 m_backgroundConstrastRegions.remove(window); 0169 m_slideMap.remove(window); 0170 m_windowWatchers.remove(window); 0171 }); 0172 m_windowWatchers[window] << conn; 0173 auto waylandWindow = dynamic_cast<QtWaylandClient::QWaylandWindow *>(window->handle()); 0174 if (waylandWindow) { 0175 auto conn = connect(waylandWindow, &QtWaylandClient::QWaylandWindow::wlSurfaceDestroyed, this, [this, window]() { 0176 resetBlur(window); 0177 resetContrast(window); 0178 }); 0179 m_windowWatchers[window] << conn; 0180 } 0181 } 0182 } 0183 0184 void WindowEffects::releaseWindow(QWindow *window) 0185 { 0186 if (!m_blurRegions.contains(window) && !m_backgroundConstrastRegions.contains(window) && !m_slideMap.contains(window)) { 0187 for (const auto &conn : m_windowWatchers[window]) { 0188 disconnect(conn); 0189 } 0190 window->removeEventFilter(this); 0191 m_windowWatchers.remove(window); 0192 } 0193 } 0194 0195 // Helper function to replace a QObject value in the map and delete the old one. 0196 template<typename MapType> 0197 void replaceValue(MapType &map, typename MapType::key_type key, typename MapType::mapped_type value) 0198 { 0199 if (auto oldValue = map.take(key)) { 0200 oldValue->deleteLater(); 0201 } 0202 if (value) { 0203 map[key] = value; 0204 } 0205 } 0206 0207 void WindowEffects::resetBlur(QWindow *window, Blur *blur) 0208 { 0209 replaceValue(m_blurs, window, blur); 0210 } 0211 0212 void WindowEffects::resetContrast(QWindow *window, Contrast *contrast) 0213 { 0214 replaceValue(m_contrasts, window, contrast); 0215 } 0216 0217 bool WindowEffects::eventFilter(QObject *watched, QEvent *event) 0218 { 0219 if (event->type() == QEvent::Expose) { 0220 auto window = qobject_cast<QWindow *>(watched); 0221 if (!window || !window->isExposed()) { 0222 return false; 0223 } 0224 0225 { 0226 auto it = m_blurRegions.constFind(window); 0227 if (it != m_blurRegions.constEnd()) { 0228 installBlur(window, true, *it); 0229 } 0230 } 0231 { 0232 auto it = m_backgroundConstrastRegions.constFind(window); 0233 if (it != m_backgroundConstrastRegions.constEnd()) { 0234 installContrast(window, true, it->contrast, it->intensity, it->saturation, it->region); 0235 } 0236 } 0237 { 0238 auto it = m_slideMap.constFind(window); 0239 if (it != m_slideMap.constEnd()) { 0240 installSlide(window, it->location, it->offset); 0241 } 0242 } 0243 } 0244 return false; 0245 } 0246 0247 bool WindowEffects::isEffectAvailable(KWindowEffects::Effect effect) 0248 { 0249 switch (effect) { 0250 case KWindowEffects::BackgroundContrast: 0251 return m_contrastManager->isActive(); 0252 case KWindowEffects::BlurBehind: 0253 return m_blurManager->isActive(); 0254 case KWindowEffects::Slide: 0255 return m_slideManager->isActive(); 0256 default: 0257 return false; 0258 } 0259 } 0260 0261 void WindowEffects::slideWindow(QWindow *window, KWindowEffects::SlideFromLocation location, int offset) 0262 { 0263 if (location != KWindowEffects::SlideFromLocation::NoEdge) { 0264 m_slideMap[window] = SlideData{ 0265 .location = location, 0266 .offset = offset, 0267 }; 0268 trackWindow(window); 0269 } else { 0270 m_slideMap.remove(window); 0271 releaseWindow(window); 0272 } 0273 0274 installSlide(window, location, offset); 0275 } 0276 0277 void WindowEffects::installSlide(QWindow *window, KWindowEffects::SlideFromLocation location, int offset) 0278 { 0279 if (!m_slideManager->isActive()) { 0280 return; 0281 } 0282 wl_surface *surface = surfaceForWindow(window); 0283 if (surface) { 0284 if (location != KWindowEffects::SlideFromLocation::NoEdge) { 0285 auto slide = new Slide(m_slideManager->create(surface), window); 0286 0287 Slide::location convertedLoc; 0288 switch (location) { 0289 case KWindowEffects::SlideFromLocation::TopEdge: 0290 convertedLoc = Slide::location::location_top; 0291 break; 0292 case KWindowEffects::SlideFromLocation::LeftEdge: 0293 convertedLoc = Slide::location::location_left; 0294 break; 0295 case KWindowEffects::SlideFromLocation::RightEdge: 0296 convertedLoc = Slide::location::location_right; 0297 break; 0298 case KWindowEffects::SlideFromLocation::BottomEdge: 0299 default: 0300 convertedLoc = Slide::location::location_bottom; 0301 break; 0302 } 0303 0304 slide->set_location(convertedLoc); 0305 slide->set_offset(offset); 0306 slide->commit(); 0307 } else { 0308 m_slideManager->unset(surface); 0309 } 0310 } 0311 } 0312 0313 void WindowEffects::enableBlurBehind(QWindow *window, bool enable, const QRegion ®ion) 0314 { 0315 if (enable) { 0316 trackWindow(window); 0317 m_blurRegions[window] = region; 0318 } else { 0319 resetBlur(window); 0320 m_blurRegions.remove(window); 0321 releaseWindow(window); 0322 } 0323 0324 installBlur(window, enable, region); 0325 } 0326 0327 void WindowEffects::installBlur(QWindow *window, bool enable, const QRegion ®ion) 0328 { 0329 if (!m_blurManager->isActive()) { 0330 return; 0331 } 0332 0333 wl_surface *surface = surfaceForWindow(window); 0334 0335 if (surface) { 0336 if (enable) { 0337 auto wl_region = createRegion(region); 0338 if (!wl_region) { 0339 return; 0340 } 0341 auto blur = new Blur(m_blurManager->create(surface), window); 0342 blur->set_region(wl_region); 0343 blur->commit(); 0344 wl_region_destroy(wl_region); 0345 resetBlur(window, blur); 0346 } else { 0347 resetBlur(window); 0348 m_blurManager->unset(surface); 0349 } 0350 } 0351 } 0352 0353 void WindowEffects::enableBackgroundContrast(QWindow *window, bool enable, qreal contrast, qreal intensity, qreal saturation, const QRegion ®ion) 0354 { 0355 if (enable) { 0356 trackWindow(window); 0357 m_backgroundConstrastRegions[window].contrast = contrast; 0358 m_backgroundConstrastRegions[window].intensity = intensity; 0359 m_backgroundConstrastRegions[window].saturation = saturation; 0360 m_backgroundConstrastRegions[window].region = region; 0361 } else { 0362 resetContrast(window); 0363 m_backgroundConstrastRegions.remove(window); 0364 releaseWindow(window); 0365 } 0366 0367 installContrast(window, enable, contrast, intensity, saturation, region); 0368 } 0369 0370 void WindowEffects::installContrast(QWindow *window, bool enable, qreal contrast, qreal intensity, qreal saturation, const QRegion ®ion) 0371 { 0372 if (!m_contrastManager->isActive()) { 0373 return; 0374 } 0375 0376 wl_surface *surface = surfaceForWindow(window); 0377 if (surface) { 0378 if (enable) { 0379 auto wl_region = createRegion(region); 0380 if (!wl_region) { 0381 return; 0382 } 0383 auto backgroundContrast = new Contrast(m_contrastManager->create(surface), window); 0384 backgroundContrast->set_region(wl_region); 0385 backgroundContrast->set_contrast(wl_fixed_from_double(contrast)); 0386 backgroundContrast->set_intensity(wl_fixed_from_double(intensity)); 0387 backgroundContrast->set_saturation(wl_fixed_from_double(saturation)); 0388 backgroundContrast->commit(); 0389 wl_region_destroy(wl_region); 0390 resetContrast(window, backgroundContrast); 0391 } else { 0392 resetContrast(window); 0393 m_contrastManager->unset(surface); 0394 } 0395 } 0396 }