KWin
Loading...
Searching...
No Matches
glide.cpp
Go to the documentation of this file.
1/*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 2007 Philip Falkner <philip.falkner@gmail.com>
6 SPDX-FileCopyrightText: 2009 Martin Gräßlin <mgraesslin@kde.org>
7 SPDX-FileCopyrightText: 2010 Alexandre Pereira <pereira.alex@gmail.com>
8 SPDX-FileCopyrightText: 2018 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
9
10 SPDX-License-Identifier: GPL-2.0-or-later
11*/
12
13// own
14#include "glide.h"
15
16// KConfigSkeleton
17#include "glideconfig.h"
18
19#include "core/rendertarget.h"
20#include "core/renderviewport.h"
22
23// Qt
24#include <QMatrix4x4>
25#include <QSet>
26#include <qmath.h>
27
28#include <cmath>
29
30namespace KWin
31{
32
33static const QSet<QString> s_blacklist{
34 QStringLiteral("ksmserver ksmserver"),
35 QStringLiteral("ksmserver-logout-greeter ksmserver-logout-greeter"),
36 QStringLiteral("ksplashqml ksplashqml"),
37 // Spectacle needs to be blacklisted in order to stay out of its own screenshots.
38 QStringLiteral("spectacle spectacle"), // x11
39 QStringLiteral("spectacle org.kde.spectacle"), // wayland
40};
41
42static QMatrix4x4 createPerspectiveMatrix(const QRectF &rect, const qreal scale, const QMatrix4x4 &renderTargetTransformation)
43{
44 QMatrix4x4 ret = renderTargetTransformation;
45
46 const float fovY = std::tan(qDegreesToRadians(60.0f) / 2);
47 const float aspect = 1.0f;
48 const float zNear = 0.1f;
49 const float zFar = 100.0f;
50
51 const float yMax = zNear * fovY;
52 const float yMin = -yMax;
53 const float xMin = yMin * aspect;
54 const float xMax = yMax * aspect;
55
56 ret.frustum(xMin, xMax, yMin, yMax, zNear, zFar);
57
58 const auto deviceRect = scaledRect(rect, scale);
59
60 const float scaleFactor = 1.1 * fovY / yMax;
61 ret.translate(xMin * scaleFactor, yMax * scaleFactor, -1.1);
62 ret.scale((xMax - xMin) * scaleFactor / deviceRect.width(),
63 -(yMax - yMin) * scaleFactor / deviceRect.height(),
64 0.001);
65 ret.translate(-deviceRect.x(), -deviceRect.y());
66
67 return ret;
68}
69
71{
72 GlideConfig::instance(effects->config());
74
75 connect(effects, &EffectsHandler::windowAdded, this, &GlideEffect::windowAdded);
76 connect(effects, &EffectsHandler::windowClosed, this, &GlideEffect::windowClosed);
77 connect(effects, &EffectsHandler::windowDataChanged, this, &GlideEffect::windowDataChanged);
78}
79
81
82void GlideEffect::reconfigure(ReconfigureFlags flags)
83{
84 GlideConfig::self()->read();
85 m_duration = std::chrono::milliseconds(animationTime<GlideConfig>(160));
86
87 m_inParams.edge = static_cast<RotationEdge>(GlideConfig::inRotationEdge());
88 m_inParams.angle.from = GlideConfig::inRotationAngle();
89 m_inParams.angle.to = 0.0;
90 m_inParams.distance.from = GlideConfig::inDistance();
91 m_inParams.distance.to = 0.0;
92 m_inParams.opacity.from = GlideConfig::inOpacity();
93 m_inParams.opacity.to = 1.0;
94
95 m_outParams.edge = static_cast<RotationEdge>(GlideConfig::outRotationEdge());
96 m_outParams.angle.from = 0.0;
97 m_outParams.angle.to = GlideConfig::outRotationAngle();
98 m_outParams.distance.from = 0.0;
99 m_outParams.distance.to = GlideConfig::outDistance();
100 m_outParams.opacity.from = 1.0;
101 m_outParams.opacity.to = GlideConfig::outOpacity();
102}
103
104void GlideEffect::prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime)
105{
107
108 effects->prePaintScreen(data, presentTime);
109}
110
111void GlideEffect::prePaintWindow(EffectWindow *w, WindowPrePaintData &data, std::chrono::milliseconds presentTime)
112{
113 auto animationIt = m_animations.find(w);
114 if (animationIt != m_animations.end()) {
115 (*animationIt).timeLine.advance(presentTime);
116 data.setTransformed();
117 }
118
119 effects->prePaintWindow(w, data, presentTime);
120}
121
122void GlideEffect::paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, QRegion region, WindowPaintData &data)
123{
124 auto animationIt = m_animations.constFind(w);
125 if (animationIt == m_animations.constEnd()) {
126 effects->paintWindow(renderTarget, viewport, w, mask, region, data);
127 return;
128 }
129
130 // Perspective projection distorts objects near edges
131 // of the viewport. This is critical because distortions
132 // near edges of the viewport are not desired with this effect.
133 // To fix this, the center of the window will be moved to the origin,
134 // after applying perspective projection, the center is moved back
135 // to its "original" projected position. Overall, this is how the window
136 // will be transformed:
137 // [move to the origin] -> [rotate] -> [translate] ->
138 // -> [perspective projection] -> [reverse "move to the origin"]
139
140 const QMatrix4x4 oldProjMatrix = createPerspectiveMatrix(viewport.renderRect(), viewport.scale(), renderTarget.transform().toMatrix());
141 const auto frame = w->frameGeometry();
142 const QRectF windowGeo = scaledRect(frame, viewport.scale());
143 const QVector3D invOffset = oldProjMatrix.map(QVector3D(windowGeo.center()));
144 QMatrix4x4 invOffsetMatrix;
145 invOffsetMatrix.translate(invOffset.x(), invOffset.y());
146
147 data.setProjectionMatrix(invOffsetMatrix * oldProjMatrix);
148
149 // Move the center of the window to the origin.
150 const QPointF offset = viewport.renderRect().center() - w->frameGeometry().center();
151 data.translate(offset.x(), offset.y());
152
153 const GlideParams params = w->isDeleted() ? m_outParams : m_inParams;
154 const qreal t = (*animationIt).timeLine.value();
155
156 switch (params.edge) {
158 data.setRotationAxis(Qt::XAxis);
159 data.setRotationOrigin(QVector3D(0, 0, 0));
160 data.setRotationAngle(-interpolate(params.angle.from, params.angle.to, t));
161 break;
162
164 data.setRotationAxis(Qt::YAxis);
165 data.setRotationOrigin(QVector3D(w->width(), 0, 0));
166 data.setRotationAngle(-interpolate(params.angle.from, params.angle.to, t));
167 break;
168
170 data.setRotationAxis(Qt::XAxis);
171 data.setRotationOrigin(QVector3D(0, w->height(), 0));
172 data.setRotationAngle(interpolate(params.angle.from, params.angle.to, t));
173 break;
174
176 data.setRotationAxis(Qt::YAxis);
177 data.setRotationOrigin(QVector3D(0, 0, 0));
178 data.setRotationAngle(interpolate(params.angle.from, params.angle.to, t));
179 break;
180
181 default:
182 // Fallback to Top.
183 data.setRotationAxis(Qt::XAxis);
184 data.setRotationOrigin(QVector3D(0, 0, 0));
185 data.setRotationAngle(-interpolate(params.angle.from, params.angle.to, t));
186 break;
187 }
188
189 data.setZTranslation(-interpolate(params.distance.from, params.distance.to, t));
190 data.multiplyOpacity(interpolate(params.opacity.from, params.opacity.to, t));
191
192 effects->paintWindow(renderTarget, viewport, w, mask, region, data);
193}
194
196{
197 auto animationIt = m_animations.begin();
198 while (animationIt != m_animations.end()) {
199 if ((*animationIt).timeLine.done()) {
200 animationIt = m_animations.erase(animationIt);
201 } else {
202 ++animationIt;
203 }
204 }
205
208}
209
211{
212 return !m_animations.isEmpty();
213}
214
220
221void GlideEffect::windowAdded(EffectWindow *w)
222{
224 return;
225 }
226
227 if (!isGlideWindow(w)) {
228 return;
229 }
230
231 if (!w->isVisible()) {
232 return;
233 }
234
235 const void *addGrab = w->data(WindowAddedGrabRole).value<void *>();
236 if (addGrab && addGrab != this) {
237 return;
238 }
239
240 w->setData(WindowAddedGrabRole, QVariant::fromValue(static_cast<void *>(this)));
241
242 GlideAnimation &animation = m_animations[w];
243 animation.timeLine.reset();
244 animation.timeLine.setDirection(TimeLine::Forward);
245 animation.timeLine.setDuration(m_duration);
246 animation.timeLine.setEasingCurve(QEasingCurve::InCurve);
247
249}
250
251void GlideEffect::windowClosed(EffectWindow *w)
252{
254 return;
255 }
256
257 if (!isGlideWindow(w)) {
258 return;
259 }
260
261 if (!w->isVisible() || w->skipsCloseAnimation()) {
262 return;
263 }
264
265 const void *closeGrab = w->data(WindowClosedGrabRole).value<void *>();
266 if (closeGrab && closeGrab != this) {
267 return;
268 }
269
270 w->setData(WindowClosedGrabRole, QVariant::fromValue(static_cast<void *>(this)));
271
272 GlideAnimation &animation = m_animations[w];
273 animation.deletedRef = EffectWindowDeletedRef(w);
274 animation.timeLine.reset();
275 animation.timeLine.setDirection(TimeLine::Forward);
276 animation.timeLine.setDuration(m_duration);
277 animation.timeLine.setEasingCurve(QEasingCurve::OutCurve);
278
280}
281
282void GlideEffect::windowDataChanged(EffectWindow *w, int role)
283{
284 if (role != WindowAddedGrabRole && role != WindowClosedGrabRole) {
285 return;
286 }
287
288 if (w->data(role).value<void *>() == this) {
289 return;
290 }
291
292 auto animationIt = m_animations.find(w);
293 if (animationIt != m_animations.end()) {
294 m_animations.erase(animationIt);
295 }
296}
297
298bool GlideEffect::isGlideWindow(EffectWindow *w) const
299{
300 // We don't want to animate most of plasmashell's windows, yet, some
301 // of them we want to, for example, Task Manager Settings window.
302 // The problem is that all those window share single window class.
303 // So, the only way to decide whether a window should be animated is
304 // to use a heuristic: if a window has decoration, then it's most
305 // likely a dialog or a settings window so we have to animate it.
306 if (w->windowClass() == QLatin1String("plasmashell plasmashell")
307 || w->windowClass() == QLatin1String("plasmashell org.kde.plasmashell")) {
308 return w->hasDecoration();
309 }
310
311 if (s_blacklist.contains(w->windowClass())) {
312 return false;
313 }
314
315 if (w->hasDecoration()) {
316 return true;
317 }
318
319 // Don't animate combobox popups, tooltips, popup menus, etc.
320 if (w->isPopupWindow()) {
321 return false;
322 }
323
324 // Don't animate the outline and the screenlocker as it looks bad.
325 if (w->isLockScreen() || w->isOutline()) {
326 return false;
327 }
328
329 // Override-redirect windows are usually used for user interface
330 // concepts that are not expected to be animated by this effect.
331 if (w->isX11Client() && !w->isManaged()) {
332 return false;
333 }
334
335 return w->isNormalWindow()
336 || w->isDialog();
337}
338
339} // namespace KWin
340
341#include "moc_glide.cpp"
Representation of a window used by/for Effect classes.
bool isVisible() const
Q_SCRIPTABLE void setData(int role, const QVariant &data)
Q_SCRIPTABLE QVariant data(int role) const
QRectF frameGeometry() const
bool isDeleted() const
bool animationsSupported() const
void windowClosed(KWin::EffectWindow *w)
void paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const QRegion &region, WindowPaintData &data)
void prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime)
bool isOpenGLCompositing() const
Whether the Compositor is OpenGL based (either GL 1 or 2).
void prePaintWindow(EffectWindow *w, WindowPrePaintData &data, std::chrono::milliseconds presentTime)
KSharedConfigPtr config() const
void windowAdded(KWin::EffectWindow *w)
Q_SCRIPTABLE void addRepaintFull()
Effect * activeFullScreenEffect() const
void windowDataChanged(KWin::EffectWindow *w, int role)
void postPaintScreen() override
Definition glide.cpp:195
void reconfigure(ReconfigureFlags flags) override
Definition glide.cpp:82
~GlideEffect() override
bool isActive() const override
Definition glide.cpp:210
void prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime) override
Definition glide.cpp:104
void paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, QRegion region, WindowPaintData &data) override
Definition glide.cpp:122
void prePaintWindow(EffectWindow *w, WindowPrePaintData &data, std::chrono::milliseconds presentTime) override
Definition glide.cpp:111
static bool supported()
Definition glide.cpp:215
QMatrix4x4 toMatrix() const
Definition output.cpp:300
OutputTransform transform() const
QRectF renderRect() const
void setRotationAngle(qreal angle)
Definition effect.cpp:161
void setProjectionMatrix(const QMatrix4x4 &matrix)
Definition effect.cpp:327
qreal multiplyOpacity(qreal factor)
Definition effect.cpp:309
void setRotationAxis(const QVector3D &axis)
Definition effect.cpp:181
static double interpolate(double x, double y, double a)
Definition effect.h:910
void setRotationOrigin(const QVector3D &origin)
Definition effect.cpp:186
void translate(qreal x, qreal y=0.0, qreal z=0.0)
Definition effect.cpp:116
void setZTranslation(qreal translate)
Definition effect.cpp:111
@ PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS
Definition effect.h:567
@ ReconfigureAll
Definition effect.h:601
@ WindowAddedGrabRole
@ WindowClosedGrabRole
KWIN_EXPORT QRectF scaledRect(const QRectF &rect, qreal scale)
Definition globals.h:243
EffectsHandler * effects