diff --git a/effects.cpp b/effects.cpp index 868364f406..4b1c242af5 100644 --- a/effects.cpp +++ b/effects.cpp @@ -152,6 +152,7 @@ void EffectsHandlerImpl::setupClientConnections(Client* c) connect(c, SIGNAL(clientMinimized(KWin::Client*,bool)), this, SLOT(slotClientMinimized(KWin::Client*,bool))); connect(c, SIGNAL(clientUnminimized(KWin::Client*,bool)), this, SLOT(slotClientUnminimized(KWin::Client*,bool))); connect(c, SIGNAL(geometryShapeChanged(KWin::Toplevel*,QRect)), this, SLOT(slotGeometryShapeChanged(KWin::Toplevel*,QRect))); + connect(c, SIGNAL(paddingChanged(KWin::Toplevel*,QRect)), this, SLOT(slotPaddingChanged(KWin::Toplevel*,QRect))); connect(c, SIGNAL(damaged(KWin::Toplevel*,QRect)), this, SLOT(slotWindowDamaged(KWin::Toplevel*,QRect))); connect(c, SIGNAL(propertyNotify(KWin::Toplevel*,long)), this, SLOT(slotPropertyNotify(KWin::Toplevel*,long))); } @@ -161,6 +162,7 @@ void EffectsHandlerImpl::setupUnmanagedConnections(Unmanaged* u) connect(u, SIGNAL(windowClosed(KWin::Toplevel*,KWin::Deleted*)), this, SLOT(slotWindowClosed(KWin::Toplevel*))); connect(u, SIGNAL(opacityChanged(KWin::Toplevel*,qreal)), this, SLOT(slotOpacityChanged(KWin::Toplevel*,qreal))); connect(u, SIGNAL(geometryShapeChanged(KWin::Toplevel*,QRect)), this, SLOT(slotGeometryShapeChanged(KWin::Toplevel*,QRect))); + connect(u, SIGNAL(paddingChanged(KWin::Toplevel*,QRect)), this, SLOT(slotPaddingChanged(KWin::Toplevel*,QRect))); connect(u, SIGNAL(damaged(KWin::Toplevel*,QRect)), this, SLOT(slotWindowDamaged(KWin::Toplevel*,QRect))); connect(u, SIGNAL(propertyNotify(KWin::Toplevel*,long)), this, SLOT(slotPropertyNotify(KWin::Toplevel*,long))); } @@ -484,6 +486,15 @@ void EffectsHandlerImpl::slotGeometryShapeChanged(Toplevel* t, const QRect& old) emit windowGeometryShapeChanged(t->effectWindow(), old); } +void EffectsHandlerImpl::slotPaddingChanged(Toplevel* t, const QRect& old) +{ + // during late cleanup effectWindow() may be already NULL + // in some functions that may still call this + if (t == NULL || t->effectWindow() == NULL) + return; + emit windowPaddingChanged(t->effectWindow(), old); +} + void EffectsHandlerImpl::setActiveFullScreenEffect(Effect* e) { fullscreen_effect = e; diff --git a/effects.h b/effects.h index a110242584..7278118228 100644 --- a/effects.h +++ b/effects.h @@ -197,6 +197,7 @@ protected Q_SLOTS: void slotClientMinimized(KWin::Client *c, bool animate); void slotClientUnminimized(KWin::Client *c, bool animate); void slotGeometryShapeChanged(KWin::Toplevel *t, const QRect &old); + void slotPaddingChanged(KWin::Toplevel *t, const QRect &old); void slotWindowDamaged(KWin::Toplevel *t, const QRect& r); void slotPropertyNotify(KWin::Toplevel *t, long atom); void slotPropertyNotify(long atom); diff --git a/libkwineffects/kwinanimationeffect.cpp b/libkwineffects/kwinanimationeffect.cpp index ee0772dbe4..481340f643 100644 --- a/libkwineffects/kwinanimationeffect.cpp +++ b/libkwineffects/kwinanimationeffect.cpp @@ -27,10 +27,10 @@ along with this program. If not, see . namespace KWin { struct AnimationEffectPrivate { public: - AnimationEffectPrivate() { m_animated = false; } + AnimationEffectPrivate() { m_animated = m_damageDirty = false; } AnimationEffect::AniMap m_animations; EffectWindowList m_zombies; - bool m_animated; + bool m_animated, m_damageDirty, m_needSceneRepaint; }; } @@ -51,8 +51,12 @@ void AnimationEffect::init() * connect it we can provide auto-referencing of animated and closed windows, since at the time * our slot will be called, the slot of the subclass has been (SIGNAL/SLOT connections are FIFO) * and has pot. started an animation so we have the window in our hash :) */ - connect ( effects, SIGNAL(windowClosed(KWin::EffectWindow*)), SLOT(_windowClosed(KWin::EffectWindow*)) ); - connect ( effects, SIGNAL(windowDeleted(KWin::EffectWindow*)), SLOT(_windowDeleted(KWin::EffectWindow*)) ); + connect ( effects, SIGNAL(windowClosed(KWin::EffectWindow*)), SLOT(_windowClosed(KWin::EffectWindow*)) ); + connect ( effects, SIGNAL(windowDeleted(KWin::EffectWindow*)), SLOT(_windowDeleted(KWin::EffectWindow*)) ); + connect ( effects, SIGNAL(windowGeometryShapeChanged(KWin::EffectWindow*, const QRect&)), + SLOT(_expandedGeometryChanged(KWin::EffectWindow*, const QRect&)) ); + connect ( effects, SIGNAL(windowPaddingChanged(KWin::EffectWindow*, const QRect&)), + SLOT(_expandedGeometryChanged(KWin::EffectWindow*, const QRect&)) ); } bool AnimationEffect::isActive() const @@ -124,17 +128,17 @@ void AnimationEffect::animate( EffectWindow *w, Attribute a, uint meta, int ms, RELATIVE_XY(Source); from.set( relative[0] ? from[0] * area.width() : from[0], relative[1] ? from[1] * area.height() : from[1] ); - } - else + } else { from.set(w->width(), w->height()); + } if (to.isValid()) { RELATIVE_XY(Target); to.set( relative[0] ? to[0] * area.width() : to[0], relative[1] ? to[1] * area.height() : to[1] ); - } - else + } else { from.set(w->width(), w->height()); + } } else if (a == Translation) { @@ -143,27 +147,34 @@ void AnimationEffect::animate( EffectWindow *w, Attribute a, uint meta, int ms, RELATIVE_XY(Source); from.set( relative[0] ? from[0] * area.width() : from[0], relative[1] ? from[1] * area.height() : from[1] ); - } else + } else { from.set(0.0, 0.0); + } if (to.isValid()) { RELATIVE_XY(Target); to.set( relative[0] ? to[0] * area.width() : to[0], relative[1] ? to[1] * area.height() : to[1] ); - } else + } else { to.set(0.0, 0.0); + } } Q_D(AnimationEffect); AniMap::iterator it = d->m_animations.find(w); if (it == d->m_animations.end()) - it = d->m_animations.insert(w, QList()); - it->append(AniData(a, meta, ms, to, curve, delay, from, waitAtSource)); + it = d->m_animations.insert(w, QPair, QRect>(QList(), QRect())); + it->first.append(AniData(a, meta, ms, to, curve, delay, from, waitAtSource)); + it->second = QRect(); - if (delay > 0) + if (delay > 0) { QTimer::singleShot(delay, this, SLOT(triggerRepaint())); - else + if (waitAtSource) + w->addLayerRepaint(0, 0, displayWidth(), displayHeight()); + } + else { triggerRepaint(); + } } void AnimationEffect::prePaintScreen( ScreenPrePaintData& data, int time ) @@ -174,42 +185,55 @@ void AnimationEffect::prePaintScreen( ScreenPrePaintData& data, int time ) return; } - AniMap::iterator entry = d->m_animations.begin(); + AniMap::iterator entry = d->m_animations.begin(), mapEnd = d->m_animations.end(); d->m_animated = false; - while (entry != d->m_animations.end()) { - QList::iterator anim = entry->begin(); - while (anim != entry->end()) { +// short int transformed = 0; + while (entry != mapEnd) { + bool invalidateLayerRect = false; + QList::iterator anim = entry->first.begin(), animEnd = entry->first.end(); + while (anim != animEnd) { if (QTime::currentTime() < anim->startTime) { if (!anim->waitAtSource) { ++anim; continue; } - } else + } else { anim->addTime(time); + } if (anim->time < anim->duration) { +// if (anim->attribute != Brightness && anim->attribute != Saturation && anim->attribute != Opacity) +// transformed = true; d->m_animated = true; ++anim; - } - else { + } else { animationEnded(entry.key(), anim->attribute); - anim = entry->erase(anim); + anim = entry->first.erase(anim); + invalidateLayerRect = d->m_damageDirty = true; + animEnd = entry->first.end(); } } - if (entry->isEmpty()) { + if (entry->first.isEmpty()) { const int i = d->m_zombies.indexOf(entry.key()); if ( i > -1 ) { d->m_zombies.removeAt( i ); entry.key()->unrefWindow(); } + data.paint |= entry->second; +// d->m_damageDirty = true; // TODO likely no longer required entry = d->m_animations.erase(entry); - } - else + mapEnd = d->m_animations.end(); + } else { + if (invalidateLayerRect) + *const_cast(&(entry->second)) = QRect(); // invalidate ++entry; + } } - if ( d->m_animated ) - data.mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS; + // NOTICE PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_WITHOUT_FULL_REPAINTS and thus now no flag should be required + // ... unless we start to get glitches ;-) +// if ( transformed ) +// data.mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_WITHOUT_FULL_REPAINTS; //PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS; // janitorial... if ( !(d->m_animations.count() || d->m_zombies.isEmpty()) ) @@ -229,14 +253,14 @@ void AnimationEffect::prePaintWindow( EffectWindow* w, WindowPrePaintData& data, AniMap::const_iterator entry = d->m_animations.constFind( w ); if ( entry != d->m_animations.constEnd() ) { bool isUsed = false; - for (QList::const_iterator anim = entry->constBegin(); anim != entry->constEnd(); ++anim) { + for (QList::const_iterator anim = entry->first.constBegin(); anim != entry->first.constEnd(); ++anim) { if (QTime::currentTime() < anim->startTime && !anim->waitAtSource) continue; isUsed = true; if (anim->attribute == Opacity) data.setTranslucent(); - else { + else if (!(anim->attribute == Brightness || anim->attribute == Saturation)) { data.setTransformed(); data.mask |= PAINT_WINDOW_TRANSFORMED; } @@ -248,8 +272,8 @@ void AnimationEffect::prePaintWindow( EffectWindow* w, WindowPrePaintData& data, w->enablePainting( EffectWindow::PAINT_DISABLED_BY_DELETE ); else if ( !w->isOnCurrentDesktop() ) w->enablePainting( EffectWindow::PAINT_DISABLED_BY_DESKTOP ); - if( !w->isPaintingEnabled() && !effects->activeFullScreenEffect() ) - w->addRepaintFull(); +// if( !w->isPaintingEnabled() && !effects->activeFullScreenEffect() ) +// effects->addLayerRepaint(w->expandedGeometry()); } } } @@ -290,7 +314,7 @@ void AnimationEffect::paintWindow( EffectWindow* w, int mask, QRegion region, Wi if ( d->m_animated ) { AniMap::const_iterator entry = d->m_animations.constFind( w ); if ( entry != d->m_animations.constEnd() ) { - for ( QList::const_iterator anim = entry->constBegin(); anim != entry->constEnd(); ++anim ) { + for ( QList::const_iterator anim = entry->first.constBegin(); anim != entry->first.constEnd(); ++anim ) { if (QTime::currentTime() < anim->startTime && !anim->waitAtSource) continue; @@ -395,8 +419,18 @@ void AnimationEffect::paintWindow( EffectWindow* w, int mask, QRegion region, Wi void AnimationEffect::postPaintScreen() { Q_D(AnimationEffect); - if ( d->m_animated ) - effects->addRepaintFull(); + if ( d->m_animated ) { + if (d->m_damageDirty) + updateLayerRepaints(); + if (d->m_needSceneRepaint) { + effects->addRepaintFull(); + } else { + AniMap::const_iterator it = d->m_animations.constBegin(), end = d->m_animations.constEnd(); + for (; it != end; ++it) { + it.key()->addLayerRepaint(it->second); + } + } + } effects->postPaintScreen(); } @@ -478,10 +512,153 @@ void AnimationEffect::setMetaData( MetaType type, uint value, uint &meta ) void AnimationEffect::triggerRepaint() { Q_D(AnimationEffect); - if (!d->m_animated) + for (AniMap::const_iterator entry = d->m_animations.constBegin(), mapEnd = d->m_animations.constEnd(); entry != mapEnd; ++entry) + *const_cast(&(entry->second)) = QRect(); + updateLayerRepaints(); + if (d->m_needSceneRepaint) { effects->addRepaintFull(); + } else { + AniMap::const_iterator it = d->m_animations.constBegin(), end = d->m_animations.constEnd(); + for (; it != end; ++it) + it.key()->addLayerRepaint(it->second); + } } +static float fixOvershoot(float f, const AniData &d, short int dir, float s = 1.1) +{ + switch(d.curve.type()) { + case QEasingCurve::InOutElastic: + case QEasingCurve::InOutBack: + return f * s; + case QEasingCurve::InElastic: + case QEasingCurve::OutInElastic: + case QEasingCurve::OutBack: + return (dir&2) ? f * s : f; + case QEasingCurve::OutElastic: + case QEasingCurve::InBack: + return (dir&1) ? f * s : f; + default: + return f; + } +} + +void AnimationEffect::updateLayerRepaints() +{ + Q_D(AnimationEffect); + d->m_needSceneRepaint = false; + for (AniMap::const_iterator entry = d->m_animations.constBegin(), mapEnd = d->m_animations.constEnd(); entry != mapEnd; ++entry) { + if (!entry->second.isNull()) + continue; + float f[2] = {1.0, 1.0}; + float t[2] = {0.0, 0.0}; + bool createRegion = false; + QList rects; + QRect *layerRect = const_cast(&(entry->second)); + for (QList::const_iterator anim = entry->first.constBegin(), animEnd = entry->first.constEnd(); anim != animEnd; ++anim) { + if (QTime::currentTime() < anim->startTime) + continue; + switch (anim->attribute) { + case Opacity: + case Brightness: + case Saturation: + createRegion = true; + break; + case Rotation: + createRegion = false; + *layerRect = QRect(0, 0, displayWidth(), displayHeight()); + goto region_creation; // sic! no need to do anything else + case Generic: + d->m_needSceneRepaint = true; // we don't know whether this will change visual stacking order + return; // sic! no need to do anything else + case Translation: + case Position: { + createRegion = true; + QRect r(entry.key()->geometry()); + int x[2] = {0,0}; + int y[2] = {0,0}; + if (anim->attribute == Translation) { + x[0] = anim->from[0]; + x[1] = anim->to[0]; + y[0] = anim->from[1]; + y[1] = anim->to[1]; + } else { + if ( anim->from[0] >= 0.0 && anim->to[0] >= 0.0 ) { + x[0] = anim->from[0] - xCoord(r, metaData(SourceAnchor, anim->meta)); + x[1] = anim->to[0] - xCoord(r, metaData(TargetAnchor, anim->meta)); + } + if ( anim->from[1] >= 0.0 && anim->to[1] >= 0.0 ) { + y[0] = anim->from[1] - yCoord(r, metaData(SourceAnchor, anim->meta)); + y[1] = anim->to[1] - yCoord(r, metaData(TargetAnchor, anim->meta)); + } + } + r = entry.key()->expandedGeometry(); + rects << r.translated(x[0], y[0]) << r.translated(x[1], y[1]); + break; + } + case Size: + case Scale: { + createRegion = true; + const QSize sz = entry.key()->geometry().size(); + float fx = qMax(fixOvershoot(anim->from[0], *anim, 1), fixOvershoot(anim->to[0], *anim, 2)); +// float fx = qMax(interpolated(*anim,0), anim->to[0]); + if (fx >= 0.0) { + if (anim->attribute == Size) + fx /= sz.width(); + f[0] *= fx; + t[0] += geometryCompensation( anim->meta & AnimationEffect::Horizontal, fx ) * sz.width(); + } +// float fy = qMax(interpolated(*anim,1), anim->to[1]); + float fy = qMax(fixOvershoot(anim->from[1], *anim, 1), fixOvershoot(anim->to[1], *anim, 2)); + if (fy >= 0.0) { + if (anim->attribute == Size) + fy /= sz.height(); + if (!anim->isOneDimensional()) { + f[1] *= fy; + t[1] += geometryCompensation( anim->meta & AnimationEffect::Vertical, fy ) * sz.height(); + } else if ( ((anim->meta & AnimationEffect::Vertical)>>1) != (anim->meta & AnimationEffect::Horizontal) ) { + f[1] *= fx; + t[1] += geometryCompensation( anim->meta & AnimationEffect::Vertical, fx ) * sz.height(); + } + } + break; + } + } + } +region_creation: + if (createRegion) { + const QRect geo = entry.key()->expandedGeometry(); + if (rects.isEmpty()) + rects << geo; + QList::const_iterator r, rEnd = rects.constEnd(); + for ( r = rects.constBegin(); r != rEnd; ++r) { // transform + const_cast(&(*r))->setSize(QSize(qRound(r->width()*f[0]), qRound(r->height()*f[1]))); + const_cast(&(*r))->translate(t[0], t[1]); // "const_cast" - don't do that at home, kids ;-) + } + QRect rect = rects.at(0); + if (rects.count() > 1) { + for ( r = rects.constBegin() + 1; r != rEnd; ++r) // unite + rect |= *r; + const int dx = 110*(rect.width() - geo.width())/100 + 1 - rect.width() + geo.width(); + const int dy = 110*(rect.height() - geo.height())/100 + 1 - rect.height() + geo.height(); + rect.adjust(-dx,-dy,dx,dy); // fix pot. overshoot + } + *layerRect = rect; + } + } + d->m_damageDirty = false; +} + +void AnimationEffect::_expandedGeometryChanged(KWin::EffectWindow *w, const QRect &old) +{ + Q_D(AnimationEffect); + AniMap::const_iterator entry = d->m_animations.constFind(w); + if (entry != d->m_animations.constEnd()) { + *const_cast(&(entry->second)) = QRect(); + updateLayerRepaints(); + if (!entry->second.isNull()) // actually got updated, ie. is in use - ensure it get's a repaint + w->addLayerRepaint(entry->second); + } +} void AnimationEffect::_windowClosed( EffectWindow* w ) { @@ -495,6 +672,7 @@ void AnimationEffect::_windowClosed( EffectWindow* w ) void AnimationEffect::_windowDeleted( EffectWindow* w ) { Q_D(AnimationEffect); + d->m_zombies.removeAll( w ); // TODO this line is a workaround for a bug in KWin 4.8.0 & 4.8.1 d->m_animations.remove( w ); } diff --git a/libkwineffects/kwinanimationeffect.h b/libkwineffects/kwinanimationeffect.h index 61dea357b1..2c356c8700 100644 --- a/libkwineffects/kwinanimationeffect.h +++ b/libkwineffects/kwinanimationeffect.h @@ -167,13 +167,15 @@ protected: private: float interpolated( const AniData&, int i = 0 ) const; float progress( const AniData& ) const; + void updateLayerRepaints(); private Q_SLOTS: void init(); void triggerRepaint(); void _windowClosed( KWin::EffectWindow* w ); void _windowDeleted( KWin::EffectWindow* w ); + void _expandedGeometryChanged(KWin::EffectWindow *w, const QRect &old); private: - typedef QMap< EffectWindow*, QList > AniMap; + typedef QMap< EffectWindow*, QPair, QRect> > AniMap; AnimationEffectPrivate * const d_ptr; Q_DECLARE_PRIVATE(AnimationEffect) }; diff --git a/libkwineffects/kwineffects.cpp b/libkwineffects/kwineffects.cpp index 135c9ae165..b4228a9b77 100644 --- a/libkwineffects/kwineffects.cpp +++ b/libkwineffects/kwineffects.cpp @@ -307,6 +307,7 @@ WINDOW_HELPER(QPoint, pos, "pos") WINDOW_HELPER(QSize, size, "size") WINDOW_HELPER(int, screen, "screen") WINDOW_HELPER(QRect, geometry, "geometry") +WINDOW_HELPER(QRect, expandedGeometry, "visibleRect") WINDOW_HELPER(QRect, rect, "rect") WINDOW_HELPER(int, desktop, "desktop") WINDOW_HELPER(bool, isDesktop, "desktopWindow") diff --git a/libkwineffects/kwineffects.h b/libkwineffects/kwineffects.h index 906aed3630..c8fa295dc9 100644 --- a/libkwineffects/kwineffects.h +++ b/libkwineffects/kwineffects.h @@ -954,6 +954,13 @@ Q_SIGNALS: * @since 4.7 **/ void windowGeometryShapeChanged(KWin::EffectWindow *w, const QRect &old); + /** + * Signal emitted when the padding of a window changed. (eg. shadow size) + * @param w The window whose geometry changed + * @param old The previous expandedGeometry() + * @since 4.9 + **/ + void windowPaddingChanged(KWin::EffectWindow *w, const QRect &old); /** * Signal emitted when the windows opacity is changed. * @param w The window whose opacity level is changed. @@ -1097,6 +1104,7 @@ class KWIN_EXPORT EffectWindow : public QObject Q_OBJECT Q_PROPERTY(bool alpha READ hasAlpha CONSTANT) Q_PROPERTY(QRect geometry READ geometry) + Q_PROPERTY(QRect expandedGeometry READ expandedGeometry) Q_PROPERTY(int height READ height) Q_PROPERTY(qreal opacity READ opacity) Q_PROPERTY(QPoint pos READ pos) @@ -1332,6 +1340,12 @@ public: */ QSize basicUnit() const; QRect geometry() const; + /** + * Geometry of the window including decoration and potentially shadows. + * May be different from geometry() if the window has a shadow. + * @since 4.9 + */ + QRect expandedGeometry() const; virtual QRegion shape() const = 0; int screen() const; /** @internal Do not use */ @@ -1344,6 +1358,7 @@ public: bool isUserMove() const; bool isUserResize() const; QRect iconGeometry() const; + /** * Geometry of the actual window contents inside the whole (including decorations) window. */ diff --git a/toplevel.cpp b/toplevel.cpp index 05810025b0..70f543e00e 100644 --- a/toplevel.cpp +++ b/toplevel.cpp @@ -369,6 +369,7 @@ bool Toplevel::isOnScreen(int screen) const void Toplevel::getShadow() { QRect dirtyRect; // old & new shadow region + const QRect oldVisibleRect = visibleRect(); if (hasShadow()) { dirtyRect = shadow()->shadowRegion().boundingRect(); effectWindow()->sceneWindow()->shadow()->updateShadow(); @@ -377,6 +378,8 @@ void Toplevel::getShadow() } if (hasShadow()) dirtyRect |= shadow()->shadowRegion().boundingRect(); + if (oldVisibleRect != visibleRect()) + emit paddingChanged(this, oldVisibleRect); if (dirtyRect.isValid()) { dirtyRect.translate(pos()); addLayerRepaint(dirtyRect); diff --git a/toplevel.h b/toplevel.h index fabb129e74..5f189ff56b 100644 --- a/toplevel.h +++ b/toplevel.h @@ -49,6 +49,7 @@ class Toplevel Q_PROPERTY(bool alpha READ hasAlpha CONSTANT) Q_PROPERTY(qulonglong frameId READ frameId) Q_PROPERTY(QRect geometry READ geometry NOTIFY geometryChanged) + Q_PROPERTY(QRect visibleRect READ visibleRect) Q_PROPERTY(int height READ height) Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity NOTIFY opacityChanged) Q_PROPERTY(QPoint pos READ pos) @@ -285,6 +286,7 @@ signals: void propertyNotify(KWin::Toplevel* toplevel, long a); void geometryChanged(); void geometryShapeChanged(KWin::Toplevel* toplevel, const QRect& old); + void paddingChanged(KWin::Toplevel* toplevel, const QRect& old); void windowClosed(KWin::Toplevel* toplevel, KWin::Deleted* deleted); void windowShown(KWin::Toplevel* toplevel); /**