27 dbg.nospace() << fpx2[0] <<
"," << fpx2[1] << QString(fpx2.
isValid() ? QStringLiteral(
" (valid)") : QStringLiteral(
" (invalid)"));
31QElapsedTimer AnimationEffect::s_clock;
54 if (!s_clock.isValid()) {
59 QMetaObject::invokeMethod(
this, &AnimationEffect::init, Qt::QueuedConnection);
65 if (d->m_isInitialized) {
68 d->m_animations.clear();
71void AnimationEffect::init()
74 if (d->m_isInitialized) {
77 d->m_isInitialized =
true;
92#define RELATIVE_XY(_FIELD_) const bool relative[2] = {static_cast<bool>(metaData(Relative##_FIELD_##X, meta)), \
93 static_cast<bool>(metaData(Relative##_FIELD_##Y, meta))}
95void AnimationEffect::validate(Attribute a, uint &meta, FPx2 *from, FPx2 *to,
const EffectWindow *w)
const
100 if (from && from->isValid()) {
102 from->set(relative[0] ? (*from)[0] * area.width() / w->width() : (*from)[0],
103 relative[1] ? (*from)[1] * area.height() / w->height() : (*from)[1]);
105 if (to && to->isValid()) {
107 to->set(relative[0] ? (*to)[0] * area.width() / w->width() : (*to)[0],
108 relative[1] ? (*to)[1] * area.height() / w->height() : (*to)[1]);
111 if (from && !from->isValid()) {
115 if (to && !to->isValid()) {
120 if (from && !from->isValid()) {
123 if (to && !to->isValid()) {
129 QPointF pt = w->frameGeometry().bottomRight();
131 if (from->isValid()) {
133 from->set(relative[0] ? area.x() + (*from)[0] * area.width() : (*from)[0],
134 relative[1] ? area.y() + (*from)[1] * area.height() : (*from)[1]);
136 from->set(pt.x(), pt.y());
144 to->set(relative[0] ? area.x() + (*to)[0] * area.width() : (*to)[0],
145 relative[1] ? area.y() + (*to)[1] * area.height() : (*to)[1]);
147 to->set(pt.x(), pt.y());
152 }
else if (a ==
Size) {
155 if (from->isValid()) {
157 from->set(relative[0] ? (*from)[0] * area.width() : (*from)[0],
158 relative[1] ? (*from)[1] * area.height() : (*from)[1]);
160 from->set(w->width(), w->height());
167 to->set(relative[0] ? (*to)[0] * area.width() : (*to)[0],
168 relative[1] ? (*to)[1] * area.height() : (*to)[1]);
170 to->set(w->width(), w->height());
175 QRect area = w->rect().toRect();
177 if (from->isValid()) {
179 from->set(relative[0] ? (*from)[0] * area.width() : (*from)[0],
180 relative[1] ? (*from)[1] * area.height() : (*from)[1]);
189 to->set(relative[0] ? (*to)[0] * area.width() : (*to)[0],
190 relative[1] ? (*to)[1] * area.height() : (*to)[1]);
196 }
else if (a ==
Clip) {
197 if (from && !from->isValid()) {
201 if (to && !to->isValid()) {
207 if (from && !from->isValid()) {
210 if (to && !to->isValid()) {
216quint64 AnimationEffect::p_animate(EffectWindow *w, Attribute a, uint meta,
int ms, FPx2 to,
const QEasingCurve &curve,
int delay, FPx2 from,
bool keepAtTarget,
bool fullScreenEffect,
bool keepAlive, GLShader *shader)
218 const bool waitAtSource = from.isValid();
219 validate(a, meta, &from, &to, w);
222 if (!d->m_isInitialized) {
225 AniMap::iterator it = d->m_animations.find(w);
226 if (it == d->m_animations.end()) {
228 this, &AnimationEffect::_windowExpandedGeometryChanged);
229 it = d->m_animations.insert(w, QPair<QList<AniData>, QRect>(QList<AniData>(), QRect()));
232 std::shared_ptr<FullScreenEffectLock> fullscreen;
233 if (fullScreenEffect) {
234 fullscreen = d->m_fullScreenEffectLock.lock();
236 fullscreen = std::make_shared<FullScreenEffectLock>(
this);
237 d->m_fullScreenEffectLock = fullscreen;
245 it->first.append(AniData(
256 const quint64 ret_id = ++d->m_animCounter;
257 AniData &animation = it->first.last();
258 animation.id = ret_id;
262 animation.timeLine.setDuration(std::chrono::milliseconds(ms));
263 animation.timeLine.setEasingCurve(curve);
272 it->second = QRect();
274 d->m_animationsTouched =
true;
277 QTimer::singleShot(delay,
this, &AnimationEffect::triggerRepaint);
280 w->addLayerRepaint(0, 0, s.width(), s.height());
294 if (animationId == d->m_justEndedAnimation) {
297 for (AniMap::iterator entry = d->m_animations.begin(),
298 mapEnd = d->m_animations.end();
299 entry != mapEnd; ++entry) {
300 for (QList<AniData>::iterator anim = entry->first.begin(),
301 animEnd = entry->first.end();
302 anim != animEnd; ++anim) {
303 if (anim->id == animationId) {
304 anim->from.set(interpolated(*anim, 0), interpolated(*anim, 1));
305 validate(anim->attribute, anim->meta,
nullptr, &newTarget, entry.key());
306 anim->to.set(newTarget[0], newTarget[1]);
309 anim->timeLine.setDuration(std::chrono::milliseconds(newRemainingTime));
310 anim->timeLine.reset();
326 if (animationId == d->m_justEndedAnimation) {
329 for (AniMap::iterator entry = d->m_animations.begin(),
330 mapEnd = d->m_animations.end();
331 entry != mapEnd; ++entry) {
332 for (QList<AniData>::iterator anim = entry->first.begin(),
333 animEnd = entry->first.end();
334 anim != animEnd; ++anim) {
335 if (anim->id == animationId) {
336 if (frozenTime >= 0) {
337 anim->timeLine.setElapsed(std::chrono::milliseconds(frozenTime));
339 anim->frozenTime = frozenTime;
350 if (animationId == d->m_justEndedAnimation) {
354 for (
auto entryIt = d->m_animations.begin(); entryIt != d->m_animations.end(); ++entryIt) {
355 auto animIt = std::find_if(entryIt->first.begin(), entryIt->first.end(),
357 return anim.id == animationId;
359 if (animIt == entryIt->first.end()) {
373 animIt->terminationFlags = terminationFlags & ~TerminateAtTarget;
385 if (animationId == d->m_justEndedAnimation) {
389 for (
auto entryIt = d->m_animations.begin(); entryIt != d->m_animations.end(); ++entryIt) {
390 auto animIt = std::find_if(entryIt->first.begin(), entryIt->first.end(),
392 return anim.id == animationId;
394 if (animIt == entryIt->first.end()) {
398 animIt->timeLine.setElapsed(animIt->timeLine.duration());
410 if (animationId == d->m_justEndedAnimation) {
413 for (AniMap::iterator entry = d->m_animations.begin(), mapEnd = d->m_animations.end(); entry != mapEnd; ++entry) {
414 for (QList<AniData>::iterator anim = entry->first.begin(), animEnd = entry->first.end(); anim != animEnd; ++anim) {
415 if (anim->id == animationId) {
417 if (anim->shader && std::none_of(entry->first.begin(), entry->first.end(), [animationId](
const auto &anim) {
418 return anim.id != animationId && anim.shader;
422 entry->first.erase(anim);
423 if (entry->first.isEmpty()) {
425 this, &AnimationEffect::_windowExpandedGeometryChanged);
426 d->m_animations.erase(entry);
428 d->m_animationsTouched =
true;
444static qreal xCoord(
const QRectF &r,
int flag)
451 return r.x() + r.width() / 2;
455static qreal yCoord(
const QRectF &r,
int flag)
462 return r.y() + r.height() / 2;
466QRect AnimationEffect::clipRect(
const QRect &geo,
const AniData &anim)
const
469 FPx2 ratio = anim.from + progress(anim) * (anim.to - anim.from);
470 if (anim.from[0] < 1.0 || anim.to[0] < 1.0) {
471 clip.setWidth(clip.width() * ratio[0]);
473 if (anim.from[1] < 1.0 || anim.to[1] < 1.0) {
474 clip.setHeight(clip.height() * ratio[1]);
476 const QRect center = geo.adjusted(clip.width() / 2, clip.height() / 2,
477 -(clip.width() + 1) / 2, -(clip.height() + 1) / 2);
482 const QPoint d(x[0] + ratio[0] * (x[1] - x[0]), y[0] + ratio[1] * (y[1] - y[0]));
483 clip.moveTopLeft(QPoint(d.x() - clip.width() / 2, d.y() - clip.height() / 2));
490 auto entry = d->m_animations.find(w);
491 if (entry != d->m_animations.end()) {
492 for (
auto anim = entry->first.begin(); anim != entry->first.end(); ++anim) {
493 if (anim->startTime >
clock() && !anim->waitAtSource) {
497 if (anim->frozenTime < 0) {
498 anim->timeLine.advance(presentTime);
511static inline float geometryCompensation(
int flags,
float v)
519 return 0.5 * (1.0 - v);
525 AniMap::const_iterator entry = d->m_animations.constFind(w);
526 auto finalRegion = region;
528 if (entry != d->m_animations.constEnd()) {
529 for (QList<AniData>::const_iterator anim = entry->first.constBegin(); anim != entry->first.constEnd(); ++anim) {
531 if (anim->startTime >
clock() && !anim->waitAtSource) {
535 switch (anim->attribute) {
547 float f1(1.0), f2(0.0);
548 if (anim->from[0] >= 0.0 && anim->to[0] >= 0.0) {
549 f1 = interpolated(*anim, 0);
554 if (anim->from[1] >= 0.0 && anim->to[1] >= 0.0) {
555 if (!anim->isOneDimensional()) {
556 f1 = interpolated(*anim, 1);
570 data += QPointF(interpolated(*anim, 0), interpolated(*anim, 1));
573 FPx2 dest = anim->from + progress(*anim) * (anim->to - anim->from);
576 if (anim->from[0] >= 0.0 && anim->to[0] >= 0.0) {
577 f = dest[0] / sz.width();
581 if (anim->from[1] >= 0.0 && anim->to[1] >= 0.0) {
582 f = dest[1] / sz.height();
590 const float prgrs = progress(*anim);
591 if (anim->from[0] >= 0.0 && anim->to[0] >= 0.0) {
592 float dest = interpolated(*anim, 0);
595 data.
translate(dest - (x[0] + prgrs * (x[1] - x[0])));
597 if (anim->from[1] >= 0.0 && anim->to[1] >= 0.0) {
598 float dest = interpolated(*anim, 1);
601 data.
translate(0.0, dest - (y[0] + prgrs * (y[1] - y[0])));
607 const float prgrs = progress(*anim);
608 data.
setRotationAngle(anim->from[0] + prgrs * (anim->to[0] - anim->from[0]));
610 const QRect geo = w->
rect().toRect();
613 QPointF pt(xCoord(geo, sAnchor), yCoord(geo, sAnchor));
615 if (tAnchor != sAnchor) {
616 QPointF pt2(xCoord(geo, tAnchor), yCoord(geo, tAnchor));
617 pt +=
static_cast<qreal
>(prgrs) * (pt2 - pt);
629 if (anim->shader && anim->shader->isValid()) {
631 anim->shader->
setUniform(
"animationProgress", progress(*anim));
636 if (anim->shader && anim->shader->isValid()) {
638 anim->shader->
setUniform(
"animationProgress", progress(*anim));
639 anim->shader->setUniform(anim->meta, interpolated(*anim));
655 d->m_animationsTouched =
false;
656 bool damageDirty =
false;
657 std::vector<EffectWindowDeletedRef> zombies;
659 for (
auto entry = d->m_animations.begin(); entry != d->m_animations.end();) {
660 bool invalidateLayerRect =
false;
662 for (
auto anim = entry->first.begin(); anim != entry->first.end();) {
663 if (anim->isActive() || (anim->startTime >
clock() && !anim->waitAtSource)) {
669 d->m_justEndedAnimation = anim->id;
670 if (anim->shader && std::none_of(entry->first.begin(), entry->first.end(), [anim](
const auto &other) {
671 return anim->id != other.id && other.shader;
677 d->m_justEndedAnimation = 0;
681 if (d->m_animationsTouched) {
682 d->m_animationsTouched =
false;
683 entry = d->m_animations.begin();
684 while (entry.key() != window && entry != d->m_animations.end()) {
687 Q_ASSERT(entry != d->m_animations.end());
688 anim = entry->first.begin();
689 Q_ASSERT(animCounter < entry->first.count());
690 for (
int i = 0; i < animCounter; ++i) {
697 if (!anim->deletedRef.isNull()) {
698 zombies.emplace_back(std::move(anim->deletedRef));
700 anim = entry->first.erase(anim);
701 invalidateLayerRect = damageDirty =
true;
703 if (entry->first.isEmpty()) {
705 this, &AnimationEffect::_windowExpandedGeometryChanged);
707 entry = d->m_animations.erase(entry);
709 if (invalidateLayerRect) {
710 *
const_cast<QRect *
>(&(entry->second)) = QRect();
717 updateLayerRepaints();
719 if (d->m_needSceneRepaint) {
722 for (
auto entry = d->m_animations.constBegin(); entry != d->m_animations.constEnd(); ++entry) {
723 for (
auto anim = entry->first.constBegin(); anim != entry->first.constEnd(); ++anim) {
724 if (anim->startTime >
clock()) {
727 if (!anim->timeLine.done()) {
728 entry.key()->addLayerRepaint(entry->second);
738float AnimationEffect::interpolated(
const AniData &a,
int i)
const
743float AnimationEffect::progress(
const AniData &a)
const
745 return a.startTime <
clock() ? a.timeLine.value() : 0.0;
760 return ((meta >> 5) & 0x1f);
762 return (meta & 0x1f);
768 return ((meta >> shift) & 1);
771 return ((meta >> 10) & 3);
781 meta &= ~(0x1f << 5);
782 meta |= ((value & 0x1f) << 5);
786 meta |= (value & 0x1f);
794 meta |= (1 << shift);
796 meta &= ~(1 << shift);
802 meta |= ((value & 3) << 10);
809void AnimationEffect::triggerRepaint()
812 for (AniMap::const_iterator entry = d->m_animations.constBegin(), mapEnd = d->m_animations.constEnd(); entry != mapEnd; ++entry) {
813 *
const_cast<QRect *
>(&(entry->second)) = QRect();
815 updateLayerRepaints();
816 if (d->m_needSceneRepaint) {
819 AniMap::const_iterator it = d->m_animations.constBegin(), end = d->m_animations.constEnd();
820 for (; it != end; ++it) {
821 it.key()->addLayerRepaint(it->second);
826static float fixOvershoot(
float f,
const AniData &d,
short int dir,
float s = 1.1)
828 switch (d.timeLine.easingCurve().type()) {
829 case QEasingCurve::InOutElastic:
830 case QEasingCurve::InOutBack:
832 case QEasingCurve::InElastic:
833 case QEasingCurve::OutInElastic:
834 case QEasingCurve::OutBack:
835 return (dir & 2) ? f * s : f;
836 case QEasingCurve::OutElastic:
837 case QEasingCurve::InBack:
838 return (dir & 1) ? f * s : f;
844void AnimationEffect::updateLayerRepaints()
847 d->m_needSceneRepaint =
false;
848 for (AniMap::const_iterator entry = d->m_animations.constBegin(), mapEnd = d->m_animations.constEnd(); entry != mapEnd; ++entry) {
849 if (!entry->second.isNull()) {
852 float f[2] = {1.0, 1.0};
853 float t[2] = {0.0, 0.0};
854 bool createRegion =
false;
856 QRect *layerRect =
const_cast<QRect *
>(&(entry->second));
857 for (QList<AniData>::const_iterator anim = entry->first.constBegin(), animEnd = entry->first.constEnd(); anim != animEnd; ++anim) {
858 if (anim->startTime >
clock()) {
861 switch (anim->attribute) {
871 createRegion =
false;
873 goto region_creation;
875 d->m_needSceneRepaint =
true;
880 QRect r(entry.key()->frameGeometry().toRect());
884 x[0] = anim->from[0];
886 y[0] = anim->from[1];
889 if (anim->from[0] >= 0.0 && anim->to[0] >= 0.0) {
893 if (anim->from[1] >= 0.0 && anim->to[1] >= 0.0) {
898 r = entry.key()->expandedGeometry().toRect();
899 rects << r.translated(x[0], y[0]) << r.translated(x[1], y[1]);
908 const QSize sz = entry.key()->frameGeometry().size().toSize();
909 float fx = std::max(fixOvershoot(anim->from[0], *anim, 1), fixOvershoot(anim->to[0], *anim, 2));
912 if (anim->attribute ==
Size) {
919 float fy = std::max(fixOvershoot(anim->from[1], *anim, 1), fixOvershoot(anim->to[1], *anim, 2));
921 if (anim->attribute ==
Size) {
924 if (!anim->isOneDimensional()) {
938 const QRect geo = entry.key()->expandedGeometry().toRect();
939 if (rects.isEmpty()) {
942 QList<QRect>::const_iterator r, rEnd = rects.constEnd();
943 for (r = rects.constBegin(); r != rEnd; ++r) {
944 const_cast<QRect *
>(&(*r))->setSize(QSize(qRound(r->width() * f[0]), qRound(r->height() * f[1])));
945 const_cast<QRect *
>(&(*r))->translate(t[0], t[1]);
947 QRect rect = rects.at(0);
948 if (rects.count() > 1) {
949 for (r = rects.constBegin() + 1; r != rEnd; ++r) {
952 const int dx = 110 * (rect.width() - geo.width()) / 100 + 1 - rect.width() + geo.width();
953 const int dy = 110 * (rect.height() - geo.height()) / 100 + 1 - rect.height() + geo.height();
954 rect.adjust(-dx, -dy, dx, dy);
964 AniMap::const_iterator entry = d->m_animations.constFind(w);
965 if (entry != d->m_animations.constEnd()) {
966 *
const_cast<QRect *
>(&(entry->second)) = QRect();
967 updateLayerRepaints();
968 if (!entry->second.isNull()) {
974void AnimationEffect::_windowClosed(EffectWindow *w)
978 auto it = d->m_animations.find(w);
979 if (it == d->m_animations.end()) {
983 QList<AniData> &animations = (*it).first;
984 for (
auto animationIt = animations.begin(); animationIt != animations.end(); ++animationIt) {
985 if (animationIt->keepAlive) {
986 animationIt->deletedRef = EffectWindowDeletedRef(w);
991void AnimationEffect::_windowDeleted(EffectWindow *w)
994 d->m_animations.remove(w);
1001 if (d->m_animations.isEmpty()) {
1002 dbg = QStringLiteral(
"No window is animated");
1004 AniMap::const_iterator entry = d->m_animations.constBegin(), mapEnd = d->m_animations.constEnd();
1005 for (; entry != mapEnd; ++entry) {
1006 QString caption = entry.key()->isDeleted() ? QStringLiteral(
"[Deleted]") : entry.key()->caption();
1007 if (caption.isEmpty()) {
1008 caption = QStringLiteral(
"[Untitled]");
1010 dbg += QLatin1String(
"Animating window: ") + caption + QLatin1Char(
'\n');
1011 QList<AniData>::const_iterator anim = entry->first.constBegin(), animEnd = entry->first.constEnd();
1012 for (; anim != animEnd; ++anim) {
1013 dbg += anim->debugInfo();
1023 return d->m_animations;
1028#include "moc_animationeffect.cpp"
#define RELATIVE_XY(_FIELD_)
@ Forward
The animation goes from source to target.
@ Backward
The animation goes from target to source.
bool redirect(quint64 animationId, Direction direction, TerminationFlags terminationFlags=TerminateAtSource)
QMap< EffectWindow *, QPair< QList< AniData >, QRect > > AniMap
void postPaintScreen() override
bool complete(quint64 animationId)
virtual void animationEnded(EffectWindow *w, Attribute a, uint meta)
virtual void genericAnimation(EffectWindow *w, WindowPaintData &data, float progress, uint meta)
void prePaintWindow(EffectWindow *w, WindowPrePaintData &data, std::chrono::milliseconds presentTime) override
static void setMetaData(MetaType type, uint value, uint &meta)
bool retarget(quint64 animationId, FPx2 newTarget, int newRemainingTime=-1)
bool cancel(quint64 animationId)
static int metaData(MetaType type, uint meta)
bool freezeInTime(quint64 animationId, qint64 frozenTime)
void paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, QRegion region, WindowPaintData &data) override
QString debug(const QString ¶meter) const override
~AnimationEffect() override
bool isActive() const override
quint64 m_justEndedAnimation
static quint64 m_animCounter
AnimationEffect::AniMap m_animations
std::weak_ptr< FullScreenEffectLock > m_fullScreenEffectLock
void unredirect(EffectWindow *window)
void setShader(EffectWindow *window, GLShader *shader)
void redirect(EffectWindow *window)
Representation of a window used by/for Effect classes.
Q_SCRIPTABLE void addLayerRepaint(const QRect &r)
void windowExpandedGeometryChanged(KWin::EffectWindow *window)
@ PAINT_DISABLED_BY_DESKTOP
@ PAINT_DISABLED_BY_MINIMIZE
QRectF frameGeometry() const
void windowDeleted(KWin::EffectWindow *w)
void windowClosed(KWin::EffectWindow *w)
Q_SCRIPTABLE void addRepaint(const QRectF &r)
bool isScreenLocked() const
void paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const QRegion ®ion, WindowPaintData &data)
void prePaintWindow(EffectWindow *w, WindowPrePaintData &data, std::chrono::milliseconds presentTime)
QRectF clientArea(clientAreaOption, const Output *screen, const VirtualDesktop *desktop) const
Q_SCRIPTABLE void addRepaintFull()
bool setUniform(const char *name, float value)
void setRotationAngle(qreal angle)
qreal multiplySaturation(qreal factor)
qreal multiplyOpacity(qreal factor)
void setXScale(qreal scale)
void setRotationAxis(const QVector3D &axis)
qreal multiplyBrightness(qreal factor)
void setRotationOrigin(const QVector3D &origin)
void setYScale(qreal scale)
void translate(qreal x, qreal y=0.0, qreal z=0.0)
void setCrossFadeProgress(qreal factor)
Sets the cross fading factor to fade over with previously sized window. If 1.0 only the current windo...
QDebug & operator<<(QDebug &s, const KWin::DrmConnector *obj)