1 /*
2 SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
3 SPDX-FileCopyrightText: 2015 Marco Martin <mart@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
6 */
7
8 #include "windoweffects.h"
9 #include "waylandintegration.h"
10
11 #include <QDebug>
12 #include <QExposeEvent>
13 #include <QGuiApplication>
14 #include <QWidget>
15
16 #include <KWayland/Client/blur.h>
17 #include <KWayland/Client/compositor.h>
18 #include <KWayland/Client/connection_thread.h>
19 #include <KWayland/Client/contrast.h>
20 #include <KWayland/Client/plasmashell.h>
21 #include <KWayland/Client/plasmawindowmanagement.h>
22 #include <KWayland/Client/region.h>
23 #include <KWayland/Client/registry.h>
24 #include <KWayland/Client/slide.h>
25 #include <KWayland/Client/surface.h>
26
WindowEffects()27 WindowEffects::WindowEffects()
28 : QObject()
29 , KWindowEffectsPrivateV2()
30 {
31 }
32
~WindowEffects()33 WindowEffects::~WindowEffects()
34 {
35 }
36
windowForId(WId wid)37 QWindow *WindowEffects::windowForId(WId wid)
38 {
39 QWindow *window = nullptr;
40
41 for (auto win : qApp->allWindows()) {
42 if (win->winId() == wid) {
43 window = win;
44 break;
45 }
46 }
47 return window;
48 }
49
trackWindow(QWindow * window)50 void WindowEffects::trackWindow(QWindow *window)
51 {
52 if (!m_windowWatchers.contains(window)) {
53 window->installEventFilter(this);
54 auto conn = connect(window, &QObject::destroyed, this, [this, window]() {
55 m_blurRegions.remove(window);
56 m_backgroundConstrastRegions.remove(window);
57 m_windowWatchers.remove(window);
58 });
59 m_windowWatchers[window] = conn;
60 }
61 }
62
releaseWindow(QWindow * window)63 void WindowEffects::releaseWindow(QWindow *window)
64 {
65 if (!m_blurRegions.contains(window) && !m_backgroundConstrastRegions.contains(window)) {
66 disconnect(m_windowWatchers[window]);
67 window->removeEventFilter(this);
68 m_windowWatchers.remove(window);
69 }
70 }
71
eventFilter(QObject * watched,QEvent * event)72 bool WindowEffects::eventFilter(QObject *watched, QEvent *event)
73 {
74 if (event->type() == QEvent::Expose) {
75 auto ee = static_cast<QExposeEvent *>(event);
76
77 if ((ee->region().isNull())) {
78 return false;
79 }
80
81 auto window = qobject_cast<QWindow *>(watched);
82 if (!window) {
83 return false;
84 }
85
86 {
87 auto it = m_blurRegions.constFind(window);
88 if (it != m_blurRegions.constEnd()) {
89 enableBlurBehind(window, true, *it);
90 }
91 }
92 {
93 auto it = m_backgroundConstrastRegions.constFind(window);
94 if (it != m_backgroundConstrastRegions.constEnd()) {
95 enableBackgroundContrast(window, true, it->contrast, it->intensity, it->saturation, it->region);
96 }
97 }
98 }
99 return false;
100 }
101
isEffectAvailable(KWindowEffects::Effect effect)102 bool WindowEffects::isEffectAvailable(KWindowEffects::Effect effect)
103 {
104 switch (effect) {
105 case KWindowEffects::BackgroundContrast:
106 return WaylandIntegration::self()->waylandContrastManager() != nullptr;
107 case KWindowEffects::BlurBehind:
108 return WaylandIntegration::self()->waylandBlurManager() != nullptr;
109 case KWindowEffects::Slide:
110 return WaylandIntegration::self()->waylandSlideManager() != nullptr;
111 default:
112 return false;
113 }
114 }
115
slideWindow(WId id,KWindowEffects::SlideFromLocation location,int offset)116 void WindowEffects::slideWindow(WId id, KWindowEffects::SlideFromLocation location, int offset)
117 {
118 if (!WaylandIntegration::self()->waylandSlideManager()) {
119 return;
120 }
121 KWayland::Client::Surface *surface = KWayland::Client::Surface::fromQtWinId(id);
122 if (surface) {
123 if (location != KWindowEffects::SlideFromLocation::NoEdge) {
124 auto slide = WaylandIntegration::self()->waylandSlideManager()->createSlide(surface, surface);
125
126 KWayland::Client::Slide::Location convertedLoc;
127 switch (location) {
128 case KWindowEffects::SlideFromLocation::TopEdge:
129 convertedLoc = KWayland::Client::Slide::Location::Top;
130 break;
131 case KWindowEffects::SlideFromLocation::LeftEdge:
132 convertedLoc = KWayland::Client::Slide::Location::Left;
133 break;
134 case KWindowEffects::SlideFromLocation::RightEdge:
135 convertedLoc = KWayland::Client::Slide::Location::Right;
136 break;
137 case KWindowEffects::SlideFromLocation::BottomEdge:
138 default:
139 convertedLoc = KWayland::Client::Slide::Location::Bottom;
140 break;
141 }
142
143 slide->setLocation(convertedLoc);
144 slide->setOffset(offset);
145 slide->commit();
146 } else {
147 WaylandIntegration::self()->waylandSlideManager()->removeSlide(surface);
148 }
149
150 WaylandIntegration::self()->waylandConnection()->flush();
151 }
152 }
153
154 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 81)
windowSizes(const QList<WId> & ids)155 QList<QSize> WindowEffects::windowSizes(const QList<WId> &ids)
156 {
157 Q_UNUSED(ids)
158 QList<QSize> sizes;
159 return sizes;
160 }
161 #endif
162
163 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 82)
presentWindows(WId controller,const QList<WId> & ids)164 void WindowEffects::presentWindows(WId controller, const QList<WId> &ids)
165 {
166 Q_UNUSED(controller)
167 Q_UNUSED(ids)
168 }
169 #endif
170
171 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 82)
presentWindows(WId controller,int desktop)172 void WindowEffects::presentWindows(WId controller, int desktop)
173 {
174 Q_UNUSED(controller)
175 Q_UNUSED(desktop)
176 }
177 #endif
178
179 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 82)
highlightWindows(WId controller,const QList<WId> & ids)180 void WindowEffects::highlightWindows(WId controller, const QList<WId> &ids)
181 {
182 Q_UNUSED(controller)
183 Q_UNUSED(ids)
184 }
185 #endif
186
enableBlurBehind(WId winId,bool enable,const QRegion & region)187 void WindowEffects::enableBlurBehind(WId winId, bool enable, const QRegion ®ion)
188 {
189 auto window = windowForId(winId);
190 if (!window) {
191 return;
192 }
193 if (enable) {
194 trackWindow(window);
195 m_blurRegions[window] = region;
196 } else {
197 m_blurRegions.remove(window);
198 releaseWindow(window);
199 }
200
201 enableBlurBehind(window, enable, region);
202 }
203
enableBlurBehind(QWindow * window,bool enable,const QRegion & region)204 void WindowEffects::enableBlurBehind(QWindow *window, bool enable, const QRegion ®ion)
205 {
206 if (!WaylandIntegration::self()->waylandBlurManager()) {
207 return;
208 }
209 KWayland::Client::Surface *surface = KWayland::Client::Surface::fromWindow(window);
210
211 if (surface) {
212 if (enable) {
213 auto blur = WaylandIntegration::self()->waylandBlurManager()->createBlur(surface, surface);
214 blur->setRegion(WaylandIntegration::self()->waylandCompositor()->createRegion(region, nullptr));
215 blur->commit();
216 } else {
217 WaylandIntegration::self()->waylandBlurManager()->removeBlur(surface);
218 }
219
220 WaylandIntegration::self()->waylandConnection()->flush();
221 }
222 }
223
enableBackgroundContrast(WId winId,bool enable,qreal contrast,qreal intensity,qreal saturation,const QRegion & region)224 void WindowEffects::enableBackgroundContrast(WId winId, bool enable, qreal contrast, qreal intensity, qreal saturation, const QRegion ®ion)
225 {
226 auto window = windowForId(winId);
227 if (!window) {
228 return;
229 }
230 if (enable) {
231 trackWindow(window);
232 m_backgroundConstrastRegions[window].contrast = contrast;
233 m_backgroundConstrastRegions[window].intensity = intensity;
234 m_backgroundConstrastRegions[window].saturation = saturation;
235 m_backgroundConstrastRegions[window].region = region;
236 } else {
237 m_backgroundConstrastRegions.remove(window);
238 releaseWindow(window);
239 }
240
241 enableBackgroundContrast(window, enable, contrast, intensity, saturation, region);
242 }
243
enableBackgroundContrast(QWindow * window,bool enable,qreal contrast,qreal intensity,qreal saturation,const QRegion & region)244 void WindowEffects::enableBackgroundContrast(QWindow *window, bool enable, qreal contrast, qreal intensity, qreal saturation, const QRegion ®ion)
245 {
246 if (!WaylandIntegration::self()->waylandContrastManager()) {
247 return;
248 }
249 KWayland::Client::Surface *surface = KWayland::Client::Surface::fromWindow(window);
250 if (surface) {
251 if (enable) {
252 auto backgroundContrast = WaylandIntegration::self()->waylandContrastManager()->createContrast(surface, surface);
253 backgroundContrast->setRegion(WaylandIntegration::self()->waylandCompositor()->createRegion(region, nullptr));
254 backgroundContrast->setContrast(contrast);
255 backgroundContrast->setIntensity(intensity);
256 backgroundContrast->setSaturation(saturation);
257 backgroundContrast->commit();
258 } else {
259 WaylandIntegration::self()->waylandContrastManager()->removeContrast(surface);
260 }
261
262 WaylandIntegration::self()->waylandConnection()->flush();
263 }
264 }
265
setBackgroundFrost(QWindow * window,QColor color,const QRegion & region)266 void WindowEffects::setBackgroundFrost(QWindow *window, QColor color, const QRegion ®ion)
267 {
268 if (!WaylandIntegration::self()->waylandContrastManager()) {
269 return;
270 }
271
272 KWayland::Client::Surface *surface = KWayland::Client::Surface::fromWindow(window);
273 if (!surface) {
274 return;
275 }
276 if (!color.isValid()) {
277 WaylandIntegration::self()->waylandContrastManager()->removeContrast(surface);
278 return;
279 }
280
281 auto backgroundContrast = WaylandIntegration::self()->waylandContrastManager()->createContrast(surface, surface);
282 backgroundContrast->setRegion(WaylandIntegration::self()->waylandCompositor()->createRegion(region, nullptr));
283 backgroundContrast->setFrost(color);
284 backgroundContrast->commit();
285
286 WaylandIntegration::self()->waylandConnection()->flush();
287 }
288
289 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 67)
markAsDashboard(WId window)290 void WindowEffects::markAsDashboard(WId window)
291 {
292 Q_UNUSED(window)
293 }
294 #endif
295