restrict animationeffect repaints

REVIEW: 103932
icc-effect-5.14.5
Thomas Lübking 2012-03-24 22:42:29 +01:00
parent cd8e84f5f2
commit 590d9b42d8
8 changed files with 249 additions and 36 deletions

View File

@ -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;

View File

@ -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);

View File

@ -27,10 +27,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
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<AniData>());
it->append(AniData(a, meta, ms, to, curve, delay, from, waitAtSource));
it = d->m_animations.insert(w, QPair<QList<AniData>, QRect>(QList<AniData>(), 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<AniData>::iterator anim = entry->begin();
while (anim != entry->end()) {
// short int transformed = 0;
while (entry != mapEnd) {
bool invalidateLayerRect = false;
QList<AniData>::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<QRect*>(&(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<AniData>::const_iterator anim = entry->constBegin(); anim != entry->constEnd(); ++anim) {
for (QList<AniData>::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<AniData>::const_iterator anim = entry->constBegin(); anim != entry->constEnd(); ++anim ) {
for ( QList<AniData>::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<QRect*>(&(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<QRect> rects;
QRect *layerRect = const_cast<QRect*>(&(entry->second));
for (QList<AniData>::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<QRect>::const_iterator r, rEnd = rects.constEnd();
for ( r = rects.constBegin(); r != rEnd; ++r) { // transform
const_cast<QRect*>(&(*r))->setSize(QSize(qRound(r->width()*f[0]), qRound(r->height()*f[1])));
const_cast<QRect*>(&(*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<QRect*>(&(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 );
}

View File

@ -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<AniData> > AniMap;
typedef QMap< EffectWindow*, QPair<QList<AniData>, QRect> > AniMap;
AnimationEffectPrivate * const d_ptr;
Q_DECLARE_PRIVATE(AnimationEffect)
};

View File

@ -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")

View File

@ -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.
*/

View File

@ -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);

View File

@ -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);
/**