diff --git a/effects/glide/glide.h b/effects/glide/glide.h --- a/effects/glide/glide.h +++ b/effects/glide/glide.h @@ -5,6 +5,7 @@ Copyright (C) 2007 Philip Falkner Copyright (C) 2009 Martin Gräßlin Copyright (C) 2010 Alexandre Pereira +Copyright (C) 2018 Vlad Zagorodniy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -23,81 +24,133 @@ #ifndef KWIN_GLIDE_H #define KWIN_GLIDE_H +// kwineffects #include -class QTimeLine; - namespace KWin { -class GlideEffect - : public Effect +class GlideEffect : public Effect { Q_OBJECT - Q_PROPERTY(int duration READ configuredDuration) - Q_PROPERTY(int effect READ configuredEffect) - Q_PROPERTY(int angle READ configuredAngle) + Q_PROPERTY(int duration READ duration) + Q_PROPERTY(RotationEdge inRotationEdge READ inRotationEdge) + Q_PROPERTY(qreal inRotationAngle READ inRotationAngle) + Q_PROPERTY(qreal inDistance READ inDistance) + Q_PROPERTY(qreal inOpacity READ inOpacity) + Q_PROPERTY(RotationEdge outRotationEdge READ outRotationEdge) + Q_PROPERTY(qreal outRotationAngle READ outRotationAngle) + Q_PROPERTY(qreal outDistance READ outDistance) + Q_PROPERTY(qreal outOpacity READ outOpacity) + public: GlideEffect(); - ~GlideEffect(); - virtual void reconfigure(ReconfigureFlags); - virtual void prePaintScreen(ScreenPrePaintData& data, int time); - virtual void prePaintWindow(EffectWindow* w, WindowPrePaintData& data, int time); - virtual void paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data); - virtual void postPaintWindow(EffectWindow* w); - virtual bool isActive() const; - - int requestedEffectChainPosition() const override { - return 50; - } + ~GlideEffect() override; + + void reconfigure(ReconfigureFlags flags) override; + + void prePaintScreen(ScreenPrePaintData &data, int time) override; + void prePaintWindow(EffectWindow *w, WindowPrePaintData &data, int time) override; + void paintWindow(EffectWindow *w, int mask, QRegion region, WindowPaintData &data) override; + void postPaintScreen() override; + + bool isActive() const override; + int requestedEffectChainPosition() const override; static bool supported(); - // for properties - int configuredDuration() const { - return duration; - } - int configuredEffect() const { - return effect; - } - int configuredAngle() const { - return angle; - } -public Q_SLOTS: - void slotWindowAdded(KWin::EffectWindow* c); - void slotWindowClosed(KWin::EffectWindow *c); - void slotWindowDeleted(KWin::EffectWindow *w); + enum RotationEdge { + Top = 0, + Right = 1, + Bottom = 2, + Left = 3 + }; + Q_ENUM(RotationEdge) + + int duration() const; + RotationEdge inRotationEdge() const; + qreal inRotationAngle() const; + qreal inDistance() const; + qreal inOpacity() const; + RotationEdge outRotationEdge() const; + qreal outRotationAngle() const; + qreal outDistance() const; + qreal outOpacity() const; + +private Q_SLOTS: + void windowAdded(EffectWindow *w); + void windowClosed(EffectWindow *w); + void windowDeleted(EffectWindow *w); + void windowDataChanged(EffectWindow *w, int role); private: - class WindowInfo; - typedef QMap< const EffectWindow*, WindowInfo > InfoHash; - void glideIn(EffectWindow* w, WindowPaintData& data, const InfoHash::const_iterator &info); - void glideOut(EffectWindow* w, WindowPaintData& data, const InfoHash::const_iterator &info); - bool isGlideWindow(EffectWindow* w); - void cancelWindowGrab(EffectWindow *w, int grabRole); - InfoHash windows; - float duration; - int angle; - enum EffectStyle { - GlideIn = 0, - GlideInOut = 1, - GlideOutIn = 2, - GlideOut = 3 + bool isGlideWindow(EffectWindow *w) const; + + std::chrono::milliseconds m_duration; + QHash m_animations; + + struct GlideParams { + RotationEdge edge; + struct { + qreal from; + qreal to; + } angle, distance, opacity; }; - EffectStyle effect; + + GlideParams m_inParams; + GlideParams m_outParams; }; -class GlideEffect::WindowInfo +inline int GlideEffect::requestedEffectChainPosition() const { -public: - WindowInfo(); - ~WindowInfo(); - bool deleted; - bool added; - bool closed; - QTimeLine *timeLine; -}; + return 50; +} + +inline int GlideEffect::duration() const +{ + return m_duration.count(); +} + +inline GlideEffect::RotationEdge GlideEffect::inRotationEdge() const +{ + return m_inParams.edge; +} + +inline qreal GlideEffect::inRotationAngle() const +{ + return m_inParams.angle.from; +} + +inline qreal GlideEffect::inDistance() const +{ + return m_inParams.distance.from; +} + +inline qreal GlideEffect::inOpacity() const +{ + return m_inParams.opacity.from; +} + +inline GlideEffect::RotationEdge GlideEffect::outRotationEdge() const +{ + return m_outParams.edge; +} + +inline qreal GlideEffect::outRotationAngle() const +{ + return m_outParams.angle.to; +} + +inline qreal GlideEffect::outDistance() const +{ + return m_outParams.distance.to; +} + +inline qreal GlideEffect::outOpacity() const +{ + return m_outParams.opacity.to; +} -} // namespace +} // namespace KWin #endif diff --git a/effects/glide/glide.cpp b/effects/glide/glide.cpp --- a/effects/glide/glide.cpp +++ b/effects/glide/glide.cpp @@ -5,6 +5,7 @@ Copyright (C) 2007 Philip Falkner Copyright (C) 2009 Martin Gräßlin Copyright (C) 2010 Alexandre Pereira +Copyright (C) 2018 Vlad Zagorodniy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,246 +21,293 @@ along with this program. If not, see . *********************************************************************/ +// own #include "glide.h" + // KConfigSkeleton #include "glideconfig.h" +// Qt +#include #include -#include -#include - -// Effect is based on fade effect by Philip Falkner namespace KWin { -static const int IsGlideWindow = 0x22A982D4; - static const QSet s_blacklist { - "ksmserver ksmserver", - "ksplashx ksplashx", - "ksplashsimple ksplashsimple", - "ksplashqml ksplashqml" + QStringLiteral("ksmserver ksmserver"), + QStringLiteral("ksplashqml ksplashqml"), + QStringLiteral("ksplashsimple ksplashsimple"), + QStringLiteral("ksplashx ksplashx") }; GlideEffect::GlideEffect() - : Effect() { initConfig(); reconfigure(ReconfigureAll); - connect(effects, SIGNAL(windowAdded(KWin::EffectWindow*)), this, SLOT(slotWindowAdded(KWin::EffectWindow*))); - connect(effects, SIGNAL(windowClosed(KWin::EffectWindow*)), this, SLOT(slotWindowClosed(KWin::EffectWindow*))); - connect(effects, SIGNAL(windowDeleted(KWin::EffectWindow*)), this, SLOT(slotWindowDeleted(KWin::EffectWindow*))); - - connect(effects, &EffectsHandler::windowDataChanged, this, &GlideEffect::cancelWindowGrab); + connect(effects, &EffectsHandler::windowAdded, this, &GlideEffect::windowAdded); + connect(effects, &EffectsHandler::windowClosed, this, &GlideEffect::windowClosed); + connect(effects, &EffectsHandler::windowDeleted, this, &GlideEffect::windowDeleted); + connect(effects, &EffectsHandler::windowDataChanged, this, &GlideEffect::windowDataChanged); } GlideEffect::~GlideEffect() = default; -bool GlideEffect::supported() +void GlideEffect::reconfigure(ReconfigureFlags flags) { - return effects->isOpenGLCompositing() && effects->animationsSupported(); -} + Q_UNUSED(flags) -void GlideEffect::reconfigure(ReconfigureFlags) -{ - // Fetch config with KConfigXT GlideConfig::self()->read(); - duration = animationTime(350); - effect = (EffectStyle) GlideConfig::glideEffect(); - angle = GlideConfig::glideAngle(); + m_duration = std::chrono::milliseconds(animationTime(160)); + + m_inParams.edge = static_cast(GlideConfig::inRotationEdge()); + m_inParams.angle.from = GlideConfig::inRotationAngle(); + m_inParams.angle.to = 0.0; + m_inParams.distance.from = GlideConfig::inDistance(); + m_inParams.distance.to = 0.0; + m_inParams.opacity.from = GlideConfig::inOpacity(); + m_inParams.opacity.to = 1.0; + + m_outParams.edge = static_cast(GlideConfig::outRotationEdge()); + m_outParams.angle.from = 0.0; + m_outParams.angle.to = GlideConfig::outRotationAngle(); + m_outParams.distance.from = 0.0; + m_outParams.distance.to = GlideConfig::outDistance(); + m_outParams.opacity.from = 1.0; + m_outParams.opacity.to = GlideConfig::outOpacity(); } -void GlideEffect::prePaintScreen(ScreenPrePaintData& data, int time) +void GlideEffect::prePaintScreen(ScreenPrePaintData &data, int time) { - if (!windows.isEmpty()) - data.mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS; + const std::chrono::milliseconds delta(time); + + auto animationIt = m_animations.begin(); + while (animationIt != m_animations.end()) { + (*animationIt).update(delta); + ++animationIt; + } + + data.mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS; + effects->prePaintScreen(data, time); } -void GlideEffect::prePaintWindow(EffectWindow* w, WindowPrePaintData& data, int time) +void GlideEffect::prePaintWindow(EffectWindow *w, WindowPrePaintData &data, int time) { - InfoHash::iterator info = windows.find(w); - if (info != windows.end()) { + if (m_animations.contains(w)) { data.setTransformed(); - if (info->added) - info->timeLine->setCurrentTime(info->timeLine->currentTime() + time); - else if (info->closed) { - info->timeLine->setCurrentTime(info->timeLine->currentTime() - time); - if (info->deleted) - w->enablePainting(EffectWindow::PAINT_DISABLED_BY_DELETE); - } + w->enablePainting(EffectWindow::PAINT_DISABLED_BY_DELETE); } effects->prePaintWindow(w, data, time); - - // if the window isn't to be painted, then let's make sure - // to track its progress - if (info != windows.end() && !w->isPaintingEnabled() && !effects->activeFullScreenEffect()) - w->addRepaintFull(); } -void GlideEffect::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data) +void GlideEffect::paintWindow(EffectWindow *w, int mask, QRegion region, WindowPaintData &data) { - InfoHash::const_iterator info = windows.constFind(w); - if (info != windows.constEnd()) { - const double progress = info->timeLine->currentValue(); + auto animationIt = m_animations.constFind(w); + if (animationIt == m_animations.constEnd()) { + effects->paintWindow(w, mask, region, data); + return; + } + + // Perspective projection distorts objects near edges + // of the viewport. This is critical because distortions + // near edges of the viewport are not desired with this effect. + // To fix this, the center of the window will be moved to the origin, + // after applying perspective projection, the center is moved back + // to its "original" projected position. Overall, this is how the window + // will be transformed: + // [move to the origin] -> [rotate] -> [translate] -> + // -> [perspective projection] -> [reverse "move to the origin"] + const QMatrix4x4 oldProjMatrix = data.screenProjectionMatrix(); + const QRectF windowGeo = w->geometry(); + const QVector3D invOffset = oldProjMatrix.map(QVector3D(windowGeo.center())); + QMatrix4x4 invOffsetMatrix; + invOffsetMatrix.translate(invOffset.x(), invOffset.y()); + data.setProjectionMatrix(invOffsetMatrix * oldProjMatrix); + + // Move the center of the window to the origin. + const QRectF screenGeo = effects->virtualScreenGeometry(); + const QPointF offset = screenGeo.center() - windowGeo.center(); + data.translate(offset.x(), offset.y()); + + const GlideParams params = w->isDeleted() ? m_outParams : m_inParams; + const qreal t = (*animationIt).value(); + + switch (params.edge) { + case RotationEdge::Top: data.setRotationAxis(Qt::XAxis); - data.setRotationAngle(angle * (1 - progress)); - data.multiplyOpacity(progress); - switch(effect) { - default: - case GlideInOut: - if (info->added) - glideIn(w, data, info); - else if (info->closed) - glideOut(w, data, info); - break; - case GlideOutIn: - if (info->added) - glideOut(w, data, info); - if (info->closed) - glideIn(w, data, info); - break; - case GlideIn: glideIn(w, data, info); break; - case GlideOut: glideOut(w, data, info); break; - } + data.setRotationOrigin(QVector3D(0, 0, 0)); + data.setRotationAngle(-interpolate(params.angle.from, params.angle.to, t)); + break; + + case RotationEdge::Right: + data.setRotationAxis(Qt::YAxis); + data.setRotationOrigin(QVector3D(w->width(), 0, 0)); + data.setRotationAngle(-interpolate(params.angle.from, params.angle.to, t)); + break; + + case RotationEdge::Bottom: + data.setRotationAxis(Qt::XAxis); + data.setRotationOrigin(QVector3D(0, w->height(), 0)); + data.setRotationAngle(interpolate(params.angle.from, params.angle.to, t)); + break; + + case RotationEdge::Left: + data.setRotationAxis(Qt::YAxis); + data.setRotationOrigin(QVector3D(0, 0, 0)); + data.setRotationAngle(interpolate(params.angle.from, params.angle.to, t)); + break; + + default: + // Fallback to Top. + data.setRotationAxis(Qt::XAxis); + data.setRotationOrigin(QVector3D(0, 0, 0)); + data.setRotationAngle(-interpolate(params.angle.from, params.angle.to, t)); + break; } + + data.setZTranslation(-interpolate(params.distance.from, params.distance.to, t)); + data.multiplyOpacity(interpolate(params.opacity.from, params.opacity.to, t)); + effects->paintWindow(w, mask, region, data); } -void GlideEffect::glideIn(EffectWindow* w, WindowPaintData& data, const InfoHash::const_iterator &info) +void GlideEffect::postPaintScreen() { - const double progress = info->timeLine->currentValue(); - data *= progress; - data.translate(int(w->width() / 2 * (1 - progress)), int(w->height() / 2 * (1 - progress))); + auto animationIt = m_animations.begin(); + while (animationIt != m_animations.end()) { + if ((*animationIt).done()) { + EffectWindow *w = animationIt.key(); + if (w->isDeleted()) { + w->unrefWindow(); + } + animationIt = m_animations.erase(animationIt); + } else { + ++animationIt; + } + } + + effects->addRepaintFull(); + effects->postPaintScreen(); } -void GlideEffect::glideOut(EffectWindow* w, WindowPaintData& data, const InfoHash::const_iterator &info) +bool GlideEffect::isActive() const { - const double progress = info->timeLine->currentValue(); - data *= (2 - progress); - data.translate(- int(w->width() / 2 * (1 - progress)), - int(w->height() / 2 * (1 - progress))); + return !m_animations.isEmpty(); } -void GlideEffect::postPaintWindow(EffectWindow* w) +bool GlideEffect::supported() { - InfoHash::iterator info = windows.find(w); - if (info != windows.end()) { - if (info->added && info->timeLine->currentValue() == 1.0) { - windows.remove(w); - effects->addRepaintFull(); - } else if (info->closed && info->timeLine->currentValue() == 0.0) { - info->closed = false; - if (info->deleted) { - windows.remove(w); - w->unrefWindow(); - } - effects->addRepaintFull(); - } - if (info->added || info->closed) - w->addRepaintFull(); - } - effects->postPaintWindow(w); + return effects->isOpenGLCompositing() + && effects->animationsSupported(); } -void GlideEffect::slotWindowAdded(EffectWindow* w) +void GlideEffect::windowAdded(EffectWindow *w) { - if (!isGlideWindow(w)) + if (effects->activeFullScreenEffect()) { + return; + } + + if (!isGlideWindow(w)) { return; - w->setData(IsGlideWindow, true); + } + + if (!w->isVisible()) { + return; + } + const void *addGrab = w->data(WindowAddedGrabRole).value(); - if (addGrab && addGrab != this) + if (addGrab && addGrab != this) { return; + } + w->setData(WindowAddedGrabRole, QVariant::fromValue(static_cast(this))); - InfoHash::iterator it = windows.find(w); - WindowInfo *info = (it == windows.end()) ? &windows[w] : &it.value(); - info->added = true; - info->closed = false; - info->deleted = false; - delete info->timeLine; - info->timeLine = new QTimeLine(duration); - info->timeLine->setCurveShape(QTimeLine::EaseOutCurve); - w->addRepaintFull(); + TimeLine &timeLine = m_animations[w]; + timeLine.reset(); + timeLine.setDirection(TimeLine::Forward); + timeLine.setDuration(m_duration); + timeLine.setEasingCurve(QEasingCurve::InCurve); + + effects->addRepaintFull(); } -void GlideEffect::slotWindowClosed(EffectWindow* w) +void GlideEffect::windowClosed(EffectWindow *w) { - if (!isGlideWindow(w)) + if (effects->activeFullScreenEffect()) { + return; + } + + if (!isGlideWindow(w)) { + return; + } + + if (!w->isVisible()) { return; + } + const void *closeGrab = w->data(WindowClosedGrabRole).value(); - if (closeGrab && closeGrab != this) + if (closeGrab && closeGrab != this) { return; + } + w->refWindow(); w->setData(WindowClosedGrabRole, QVariant::fromValue(static_cast(this))); - InfoHash::iterator it = windows.find(w); - WindowInfo *info = (it == windows.end()) ? &windows[w] : &it.value(); - info->added = false; - info->closed = true; - info->deleted = true; - delete info->timeLine; - info->timeLine = new QTimeLine(duration); - info->timeLine->setCurveShape(QTimeLine::EaseInCurve); - info->timeLine->setCurrentTime(info->timeLine->duration()); - w->addRepaintFull(); -} + TimeLine &timeLine = m_animations[w]; + timeLine.reset(); + timeLine.setDirection(TimeLine::Forward); + timeLine.setDuration(m_duration); + timeLine.setEasingCurve(QEasingCurve::OutCurve); -void GlideEffect::slotWindowDeleted(EffectWindow* w) -{ - windows.remove(w); + effects->addRepaintFull(); } -bool GlideEffect::isGlideWindow(EffectWindow* w) +void GlideEffect::windowDeleted(EffectWindow *w) { - if (effects->activeFullScreenEffect()) - return false; - if (!w->isVisible()) - return false; - if (s_blacklist.contains(w->windowClass())) - return false; - if (w->data(IsGlideWindow).toBool()) - return true; - if (w->hasDecoration()) - return true; - if (!w->isManaged() || w->isMenu() || w->isNotification() || w->isDesktop() || - w->isDock() || w->isSplash() || w->isToolbar()) - return false; - return true; + m_animations.remove(w); } -bool GlideEffect::isActive() const +void GlideEffect::windowDataChanged(EffectWindow *w, int role) { - return !windows.isEmpty(); -} + if (role != WindowAddedGrabRole && role != WindowClosedGrabRole) { + return; + } -void GlideEffect::cancelWindowGrab(EffectWindow *w, int grabRole) -{ - if (grabRole != WindowAddedGrabRole && grabRole != WindowClosedGrabRole) { + if (w->data(role).value() == this) { return; } - if (!w->data(IsGlideWindow).toBool()) { + + auto animationIt = m_animations.find(w); + if (animationIt == m_animations.end()) { return; } - if (w->data(grabRole).value() != this) { - windows.remove(w); - w->setData(IsGlideWindow, false); + + if (w->isDeleted() && role == WindowClosedGrabRole) { + w->unrefWindow(); } -} -GlideEffect::WindowInfo::WindowInfo() - : deleted(false) - , added(false) - , closed(false) - , timeLine(0) -{ + m_animations.erase(animationIt); } -GlideEffect::WindowInfo::~WindowInfo() +bool GlideEffect::isGlideWindow(EffectWindow *w) const { - delete timeLine; + if (s_blacklist.contains(w->windowClass())) { + return false; + } + + if (w->hasDecoration()) { + return true; + } + + if (!w->isManaged()) { + return false; + } + + return w->isNormalWindow() + || w->isDialog(); } -} // namespace +} // namespace KWin diff --git a/effects/glide/glide.kcfg b/effects/glide/glide.kcfg --- a/effects/glide/glide.kcfg +++ b/effects/glide/glide.kcfg @@ -8,11 +8,33 @@ 0 - + 0 - - -90 + + 3.0 + + + 30.0 + + + 0.4 + 0.0 + 1.0 + + + 2 + + + 3.0 + + + 30.0 + + + 0.0 + 0.0 + 1.0 diff --git a/effects/glide/glide_config.h b/effects/glide/glide_config.h --- a/effects/glide/glide_config.h +++ b/effects/glide/glide_config.h @@ -32,10 +32,10 @@ Q_OBJECT public: - explicit GlideEffectConfig(QWidget *parent = 0, const QVariantList& args = QVariantList()); - ~GlideEffectConfig(); + explicit GlideEffectConfig(QWidget *parent = nullptr, const QVariantList &args = QVariantList()); + ~GlideEffectConfig() override; - void save(); + void save() override; private: ::Ui::GlideEffectConfig ui; diff --git a/effects/glide/glide_config.cpp b/effects/glide/glide_config.cpp --- a/effects/glide/glide_config.cpp +++ b/effects/glide/glide_config.cpp @@ -55,5 +55,7 @@ QDBusConnection::sessionBus()); interface.reconfigureEffect(QStringLiteral("glide")); } + } // namespace KWin + #include "glide_config.moc" diff --git a/effects/glide/glide_config.ui b/effects/glide/glide_config.ui --- a/effects/glide/glide_config.ui +++ b/effects/glide/glide_config.ui @@ -6,159 +6,249 @@ 0 0 - 306 - 130 + 440 + 375 - - - Glide Effect: - - - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 20 - 20 - - - - - - + + + - In + Duration: - - - - 0 - - - 3 + + + + + 0 + 0 + - - 1 - - - 1 + + Default - - Qt::Horizontal + + milliseconds - - QSlider::TicksBelow + + 9999 - - - - - - Out + + 5 - - - Qt::Vertical + + + Window Open Animation - - - 0 - 0 - - - + + + + + Rotation edge: + + + + + + + + 0 + 0 + + + + + Top + + + + + Right + + + + + Bottom + + + + + Left + + + + + + + + Rotation angle: + + + + + + + + 0 + 0 + + + + + + + -360 + + + 360 + + + + + + + Distance: + + + + + + + + 0 + 0 + + + + -5000 + + + 5000 + + + 5 + + + + + - - - Glide Angle: + + + Window Close Animation + + + + + Rotation edge: + + + + + + + + 0 + 0 + + + + + Top + + + + + Right + + + + + Bottom + + + + + Left + + + + + + + + Rotation angle: + + + + + + + Distance: + + + + + + + + 0 + 0 + + + + + + + -360 + + + 360 + + + + + + + + 0 + 0 + + + + -5000 + + + 5000 + + + 5 + + + + - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 20 - 20 - - - - - - - - -90 - - - - - - - -90 - - - 90 - - - 10 - - - 45 - - - Qt::Horizontal - - - QSlider::TicksBelow - - - - - - - 90 - - - - - - - + Qt::Vertical - 0 - 0 + 20 + 40