Support for animations, still some todo's pending.

svn path=/branches/work/kwin_composite/; revision=598831
This commit is contained in:
Luboš Luňák 2006-10-24 19:17:48 +00:00
parent 23b30bb2fa
commit 10b31e7222
7 changed files with 238 additions and 46 deletions

View file

@ -101,3 +101,22 @@ TODO:
- XRenderSetPictureTransform() should be capable of doing this
- note that the matrix used seems to be weird (it doesn't act like the normal transformation
matrix as far as I can tell)
* solve somehow disappearing windows
- i.e. when a window is e.g. closed, the Client/Unmanaged object is destroyed, but animations
should be going on
? maybe animations could be done actually before the state change, it makes sense to destroy
the window only after it's finished exploding or really minimizing the window only after
the animation of minimizing to the taskbar is done, however this looks very hairy
and error-prone
? maybe the animation effects should keep the necessary info themselves, so that the object
can be destroyed
- the problem here may be what to do when the window again reappears, a new object will
be created, but the old animation should be stopped - compare window id's?
? maybe just keep the object around in a special list
* add a postpaint pass
- needed for animations to trigger next repaint, currently window damage is reset after
the painting pass, meaning the damage cannot be added from there
- current workaround is to damage the whole screen, as there's a for resetting that damage
in advance

View file

@ -120,8 +120,6 @@ void Workspace::addDamage( const QRect& r )
void Workspace::compositeTimeout()
{
if( damage_region.isEmpty()) // no damage
return;
// The event loop apparently tries to fire a QTimer as often as possible, even
// at the expense of not processing many X events. This means that the composite
// repaints can seriously impact performance of everything else, therefore throttle
@ -129,6 +127,11 @@ void Workspace::compositeTimeout()
// is started.
if( lastCompositePaint.elapsed() < 5 )
return;
if( damage_region.isEmpty()) // no damage
{
scene->idle();
return;
}
ToplevelList windows;
Window* children;
unsigned int children_count;
@ -146,10 +149,12 @@ void Workspace::compositeTimeout()
else if( Unmanaged* c = findUnmanaged( HandleMatchPredicate( children[ i ] )))
windows.append( c );
}
scene->paint( damage_region, windows );
// TODO when effects cause damage, it should be only enqueued for next repaint
QRegion r = damage_region;
damage_region = QRegion();
scene->paint( r, windows );
foreach( Toplevel* c, windows )
c->resetDamage();
damage_region = QRegion();
lastCompositePaint.start();
}

View file

@ -37,9 +37,9 @@ void Effect::windowDeleted( Toplevel* )
{
}
void Effect::prePaintScreen( int* mask, QRegion* region )
void Effect::prePaintScreen( int* mask, QRegion* region, int time )
{
effects->nextPrePaintScreen( mask, region );
effects->nextPrePaintScreen( mask, region, time );
}
void Effect::paintScreen( int mask, QRegion region, ScreenPaintData& data )
@ -47,9 +47,9 @@ void Effect::paintScreen( int mask, QRegion region, ScreenPaintData& data )
effects->nextPaintScreen( mask, region, data );
}
void Effect::prePaintWindow( Scene::Window* w, int* mask, QRegion* region )
void Effect::prePaintWindow( Scene::Window* w, int* mask, QRegion* region, int time )
{
effects->nextPrePaintWindow( w, mask, region );
effects->nextPrePaintWindow( w, mask, region, time );
}
void Effect::paintWindow( Scene::Window* w, int mask, QRegion region, WindowPaintData& data )
@ -57,7 +57,7 @@ void Effect::paintWindow( Scene::Window* w, int mask, QRegion region, WindowPain
effects->nextPaintWindow( w, mask, region, data );
}
void MakeHalfTransparent::prePaintWindow( Scene::Window* w, int* mask, QRegion* region )
void MakeHalfTransparent::prePaintWindow( Scene::Window* w, int* mask, QRegion* region, int time )
{
const Client* c = dynamic_cast< const Client* >( w->window());
if(( c != NULL && ( c->isMove() || c->isResize())) || w->window()->isDialog())
@ -65,7 +65,7 @@ void MakeHalfTransparent::prePaintWindow( Scene::Window* w, int* mask, QRegion*
*mask |= Scene::PAINT_WINDOW_TRANSLUCENT;
*mask &= ~Scene::PAINT_WINDOW_OPAQUE;
}
effects->nextPrePaintWindow( w, mask, region );
effects->nextPrePaintWindow( w, mask, region, time );
}
void MakeHalfTransparent::paintWindow( Scene::Window* w, int mask, QRegion region, WindowPaintData& data )
@ -92,18 +92,18 @@ ShakyMove::ShakyMove()
static const int shaky_diff[] = { 0, 1, 2, 3, 2, 1, 0, -1, -2, -3, -2, -1 };
static const int SHAKY_MAX = sizeof( shaky_diff ) / sizeof( shaky_diff[ 0 ] );
void ShakyMove::prePaintScreen( int* mask, QRegion* region )
void ShakyMove::prePaintScreen( int* mask, QRegion* region, int time )
{
if( !windows.isEmpty())
*mask |= Scene::PAINT_WINDOW_TRANSFORMED;
effects->nextPrePaintScreen( mask, region );
effects->nextPrePaintScreen( mask, region, time );
}
void ShakyMove::prePaintWindow( Scene::Window* w, int* mask, QRegion* region )
void ShakyMove::prePaintWindow( Scene::Window* w, int* mask, QRegion* region, int time )
{
if( windows.contains( w->window()))
*mask |= Scene::PAINT_WINDOW_TRANSFORMED;
effects->nextPrePaintWindow( w, mask, region );
effects->nextPrePaintWindow( w, mask, region, time );
}
void ShakyMove::paintWindow( Scene::Window* w, int mask, QRegion region, WindowPaintData& data )
@ -178,23 +178,36 @@ void GrowMove::windowUserMovedResized( Toplevel* c, bool first, bool last )
ShiftWorkspaceUp::ShiftWorkspaceUp( Workspace* ws )
: up( false )
, diff( 0 )
, wspace( ws )
{
connect( &timer, SIGNAL( timeout()), SLOT( tick()));
timer.start( 2000 );
}
void ShiftWorkspaceUp::prePaintScreen( int* mask, QRegion* region )
void ShiftWorkspaceUp::prePaintScreen( int* mask, QRegion* region, int time )
{
if( up )
if( up && diff < 1000 )
{
diff = qBound( 0, diff + time, 1000 ); // KDE3: note this differs from KCLAMP
if( diff < 1000 )
wspace->addDamage( 0, 0, displayWidth(), displayHeight()); // affects next redraw
}
if( !up && diff > 0 )
{
diff = qBound( 0, diff - time, 1000 );
if( diff > 0 )
wspace->addDamage( 0, 0, displayWidth(), displayHeight()); // affects next redraw
}
if( diff != 0 )
*mask |= Scene::PAINT_SCREEN_TRANSFORMED;
effects->nextPrePaintScreen( mask, region );
effects->nextPrePaintScreen( mask, region, time );
}
void ShiftWorkspaceUp::paintScreen( int mask, QRegion region, ScreenPaintData& data )
{
if( up )
data.yTranslate -= 10;
if( diff != 0 )
data.yTranslate -= diff / 100;
effects->nextPaintScreen( mask, region, data );
}
@ -204,6 +217,96 @@ void ShiftWorkspaceUp::tick()
wspace->addDamage( 0, 0, displayWidth(), displayHeight());
}
void FadeIn::prePaintWindow( Scene::Window* w, int* mask, QRegion* region, int time )
{
if( windows.contains( w->window()))
{
windows[ w->window() ] += time / 1000.; // complete change in 1000ms
if( windows[ w->window() ] < 1 )
{
*mask |= Scene::PAINT_WINDOW_TRANSLUCENT;
*mask &= ~Scene::PAINT_WINDOW_OPAQUE;
// TODO this should just damage the window, but right now window
// damage is cleared after the painting pass - a postpaint pass is needed
w->window()->workspace()->addDamage( 0, 0, displayWidth(), displayHeight());
}
else
windows.remove( w->window());
}
effects->nextPrePaintWindow( w, mask, region, time );
}
void FadeIn::paintWindow( Scene::Window* w, int mask, QRegion region, WindowPaintData& data )
{
if( windows.contains( w->window()))
{
data.opacity *= windows[ w->window()];
}
effects->nextPaintWindow( w, mask, region, data );
}
void FadeIn::windowAdded( Toplevel* c )
{
Client* cc = dynamic_cast< Client* >( c );
if( cc == NULL || cc->isOnCurrentDesktop())
{
windows[ c ] = 0;
c->addDamage( c->rect());
}
}
void FadeIn::windowDeleted( Toplevel* c )
{
windows.remove( c );
}
void ScaleIn::prePaintWindow( Scene::Window* w, int* mask, QRegion* region, int time )
{
if( windows.contains( w->window()))
{
windows[ w->window() ] += time / 500.; // complete change in 500ms
if( windows[ w->window() ] < 1 )
{
*mask |= Scene::PAINT_WINDOW_TRANSFORMED;
// TODO this should just damage the window, but right now window
// damage is cleared after the painting pass - a postpaint pass is needed
w->window()->workspace()->addDamage( 0, 0, displayWidth(), displayHeight());
}
else
windows.remove( w->window());
}
effects->nextPrePaintWindow( w, mask, region, time );
}
void ScaleIn::paintWindow( Scene::Window* w, int mask, QRegion region, WindowPaintData& data )
{
if( windows.contains( w->window()))
{
data.xScale *= windows[ w->window()];
data.yScale *= windows[ w->window()];
data.xTranslate += w->window()->width() / 2 * ( 1 - windows[ w->window()] );
data.yTranslate += w->window()->height() / 2 * ( 1 - windows[ w->window()] );
}
effects->nextPaintWindow( w, mask, region, data );
}
void ScaleIn::windowAdded( Toplevel* c )
{
Client* cc = dynamic_cast< Client* >( c );
if( cc == NULL || cc->isOnCurrentDesktop())
{
windows[ c ] = 0;
c->addDamage( c->rect());
}
}
void ScaleIn::windowDeleted( Toplevel* c )
{
windows.remove( c );
}
//****************************************
// EffectsHandler
//****************************************
@ -218,6 +321,8 @@ EffectsHandler::EffectsHandler( Workspace* ws )
// effects.append( new ShakyMove );
// effects.append( new GrowMove );
// effects.append( new ShiftWorkspaceUp( ws ));
effects.append( new FadeIn );
effects.append( new ScaleIn );
}
EffectsHandler::~EffectsHandler()
@ -244,11 +349,11 @@ void EffectsHandler::windowDeleted( Toplevel* c )
e->windowDeleted( c );
}
void EffectsHandler::prePaintScreen( int* mask, QRegion* region, Effect* final )
void EffectsHandler::prePaintScreen( int* mask, QRegion* region, int time, Effect* final )
{
assert( current_paint_screen == 0 );
effects.append( final );
nextPrePaintScreen( mask, region );
nextPrePaintScreen( mask, region, time );
effects.pop_back();
}
@ -260,11 +365,11 @@ void EffectsHandler::paintScreen( int mask, QRegion region, ScreenPaintData& dat
effects.pop_back();
}
void EffectsHandler::prePaintWindow( Scene::Window* w, int* mask, QRegion* region, Effect* final )
void EffectsHandler::prePaintWindow( Scene::Window* w, int* mask, QRegion* region, int time, Effect* final )
{
assert( current_paint_window == 0 );
effects.append( final );
nextPrePaintWindow( w, mask, region );
nextPrePaintWindow( w, mask, region, time );
effects.pop_back();
}
@ -277,9 +382,9 @@ void EffectsHandler::paintWindow( Scene::Window* w, int mask, QRegion region, Wi
}
// the idea is that effects call this function again which calls the next one
void EffectsHandler::nextPrePaintScreen( int* mask, QRegion* region )
void EffectsHandler::nextPrePaintScreen( int* mask, QRegion* region, int time )
{
effects[ current_paint_screen++ ]->prePaintScreen( mask, region );
effects[ current_paint_screen++ ]->prePaintScreen( mask, region, time );
--current_paint_screen;
}
@ -289,9 +394,9 @@ void EffectsHandler::nextPaintScreen( int mask, QRegion region, ScreenPaintData&
--current_paint_screen;
}
void EffectsHandler::nextPrePaintWindow( Scene::Window* w, int* mask, QRegion* region )
void EffectsHandler::nextPrePaintWindow( Scene::Window* w, int* mask, QRegion* region, int time )
{
effects[ current_paint_window++ ]->prePaintWindow( w, mask, region );
effects[ current_paint_window++ ]->prePaintWindow( w, mask, region, time );
--current_paint_window;
}

View file

@ -48,9 +48,9 @@ class Effect
{
public:
virtual ~Effect();
virtual void prePaintScreen( int* mask, QRegion* region );
virtual void prePaintScreen( int* mask, QRegion* region, int time );
virtual void paintScreen( int mask, QRegion region, ScreenPaintData& data );
virtual void prePaintWindow( Scene::Window* w, int* mask, QRegion* region );
virtual void prePaintWindow( Scene::Window* w, int* mask, QRegion* region, int time );
virtual void paintWindow( Scene::Window* w, int mask, QRegion region, WindowPaintData& data );
// called when moved/resized or once after it's finished
virtual void windowUserMovedResized( Toplevel* c, bool first, bool last );
@ -64,14 +64,14 @@ class EffectsHandler
EffectsHandler( Workspace* ws );
~EffectsHandler();
// for use by effects
void nextPrePaintScreen( int* mask, QRegion* region );
void nextPrePaintScreen( int* mask, QRegion* region, int time );
void nextPaintScreen( int mask, QRegion region, ScreenPaintData& data );
void nextPrePaintWindow( Scene::Window* w, int* mask, QRegion* region );
void nextPrePaintWindow( Scene::Window* w, int* mask, QRegion* region, int time );
void nextPaintWindow( Scene::Window* w, int mask, QRegion region, WindowPaintData& data );
// internal (used by kwin core or compositing code)
void prePaintScreen( int* mask, QRegion* region, Effect* final );
void prePaintScreen( int* mask, QRegion* region, int time, Effect* final );
void paintScreen( int mask, QRegion region, ScreenPaintData& data, Effect* final );
void prePaintWindow( Scene::Window* w, int* mask, QRegion* region, Effect* final );
void prePaintWindow( Scene::Window* w, int* mask, QRegion* region, int time, Effect* final );
void paintWindow( Scene::Window* w, int mask, QRegion region, WindowPaintData& data, Effect* final );
void windowUserMovedResized( Toplevel* c, bool first, bool last );
void windowAdded( Toplevel* c );
@ -89,7 +89,7 @@ class MakeHalfTransparent
{
public:
virtual void windowUserMovedResized( Toplevel* c, bool first, bool last );
virtual void prePaintWindow( Scene::Window* w, int* mask, QRegion* region );
virtual void prePaintWindow( Scene::Window* w, int* mask, QRegion* region, int time );
virtual void paintWindow( Scene::Window* w, int mask, QRegion region, WindowPaintData& data );
};
@ -100,8 +100,8 @@ class ShakyMove
public:
ShakyMove();
virtual void windowUserMovedResized( Toplevel* c, bool first, bool last );
virtual void prePaintScreen( int* mask, QRegion* region );
virtual void prePaintWindow( Scene::Window* w, int* mask, QRegion* region );
virtual void prePaintScreen( int* mask, QRegion* region, int time );
virtual void prePaintWindow( Scene::Window* w, int* mask, QRegion* region, int time );
virtual void paintWindow( Scene::Window* w, int mask, QRegion region, WindowPaintData& data );
virtual void windowDeleted( Toplevel* c );
private slots:
@ -127,16 +127,43 @@ class ShiftWorkspaceUp
Q_OBJECT
public:
ShiftWorkspaceUp( Workspace* ws );
virtual void prePaintScreen( int* mask, QRegion* region );
virtual void prePaintScreen( int* mask, QRegion* region, int time );
virtual void paintScreen( int mask, QRegion region, ScreenPaintData& data );
private slots:
void tick();
private:
QTimer timer;
bool up;
int diff;
Workspace* wspace;
};
class FadeIn
: public Effect
{
public:
virtual void prePaintWindow( Scene::Window* w, int* mask, QRegion* region, int time );
virtual void paintWindow( Scene::Window* w, int mask, QRegion region, WindowPaintData& data );
// TODO react also on virtual desktop changes
virtual void windowAdded( Toplevel* c );
virtual void windowDeleted( Toplevel* c );
private:
QMap< const Toplevel*, double > windows;
};
class ScaleIn
: public Effect
{
public:
virtual void prePaintWindow( Scene::Window* w, int* mask, QRegion* region, int time );
virtual void paintWindow( Scene::Window* w, int mask, QRegion region, WindowPaintData& data );
// TODO react also on virtual desktop changes
virtual void windowAdded( Toplevel* c );
virtual void windowDeleted( Toplevel* c );
private:
QMap< const Toplevel*, double > windows;
};
// a special effect that is last in the order that'll actually call the painting functions
// TODO this should actually be in scene.h
class Scene::WrapperEffect
@ -144,9 +171,9 @@ class Scene::WrapperEffect
{
public:
virtual ~WrapperEffect();
virtual void prePaintScreen( int* mask, QRegion* region );
virtual void prePaintScreen( int* mask, QRegion* region, int time );
virtual void paintScreen( int mask, QRegion region, ScreenPaintData& data );
virtual void prePaintWindow( Scene::Window* w, int* mask, QRegion* region );
virtual void prePaintWindow( Scene::Window* w, int* mask, QRegion* region, int time );
virtual void paintWindow( Scene::Window* w, int mask, QRegion region, WindowPaintData& data );
};

View file

@ -8,11 +8,11 @@ You can Freely distribute this program under the GNU General Public
License. See the file "COPYING" for the exact licensing terms.
******************************************************************/
#include "scene_basic.h"
#include "client.h"
#include "scene.h"
#include <X11/extensions/shape.h>
#include "client.h"
#include "effects.h"
namespace KWinInternal
@ -47,8 +47,9 @@ void Scene::paintScreen( int* mask, QRegion* region )
*mask = ( *region == QRegion( 0, 0, displayWidth(), displayHeight()))
? 0 : PAINT_SCREEN_REGION;
WrapperEffect wrapper;
updateTimeDiff();
// preparation step
effects->prePaintScreen( mask, region, &wrapper );
effects->prePaintScreen( mask, region, time_diff, &wrapper );
if( *mask & ( PAINT_SCREEN_TRANSFORMED | PAINT_WINDOW_TRANSFORMED ))
*mask &= ~PAINT_SCREEN_REGION;
// TODO call also prePaintWindow() for all windows
@ -56,7 +57,26 @@ void Scene::paintScreen( int* mask, QRegion* region )
effects->paintScreen( *mask, *region, data, &wrapper );
}
void Scene::WrapperEffect::prePaintScreen( int*, QRegion* )
void Scene::updateTimeDiff()
{
if( last_time.isNull())
{
// has been idle for some time, time_diff would be huge
time_diff = 0;
}
else
time_diff = last_time.elapsed();
if( time_diff < 0 )
time_diff = 0;
last_time.start();;
}
void Scene::idle()
{
last_time = QTime();
}
void Scene::WrapperEffect::prePaintScreen( int*, QRegion*, int )
{
// nothing, no changes
}
@ -83,7 +103,7 @@ void Scene::paintGenericScreen( int orig_mask, ScreenPaintData )
QRegion damage = infiniteRegion();
WrapperEffect wrapper;
// preparation step
effects->prePaintWindow( w, &mask, &damage, &wrapper );
effects->prePaintWindow( w, &mask, &damage, time_diff, &wrapper );
paintWindow( w, mask, damage );
}
}
@ -109,7 +129,7 @@ void Scene::paintSimpleScreen( int orig_mask, QRegion region )
QRegion damage = region;
WrapperEffect wrapper;
// preparation step
effects->prePaintWindow( w, &mask, &damage, &wrapper );
effects->prePaintWindow( w, &mask, &damage, time_diff, &wrapper );
if( mask & PAINT_WINDOW_TRANSLUCENT )
phase2.prepend( Phase2Data( w, region, mask ));
if( mask & PAINT_WINDOW_OPAQUE )
@ -131,7 +151,7 @@ void Scene::paintSimpleScreen( int orig_mask, QRegion region )
}
}
void Scene::WrapperEffect::prePaintWindow( Scene::Window* , int*, QRegion* )
void Scene::WrapperEffect::prePaintWindow( Scene::Window* , int*, QRegion*, int )
{
// nothing, no changes
}

View file

@ -11,6 +11,8 @@ License. See the file "COPYING" for the exact licensing terms.
#ifndef KWIN_SCENE_H
#define KWIN_SCENE_H
#include <qdatetime.h>
#include "toplevel.h"
#include "utils.h"
@ -45,6 +47,8 @@ class Scene
PAINT_SCREEN_REGION = 1 << 3,
PAINT_SCREEN_TRANSFORMED = 1 << 4
};
// there's nothing to paint (adjust time_diff later)
void idle();
protected:
void paintScreen( int* mask, QRegion* region );
virtual void paintGenericScreen( int mask, ScreenPaintData data );
@ -52,6 +56,7 @@ class Scene
virtual void paintBackground( QRegion region ) = 0;
virtual void paintWindow( Window* w, int mask, QRegion region );
static QRegion infiniteRegion();
void updateTimeDiff();
struct Phase2Data
{
Phase2Data( Window* w, QRegion r, int m ) : window( w ), region( r ), mask( m ) {}
@ -60,6 +65,8 @@ class Scene
int mask;
};
QVector< Window* > stacking_order;
int time_diff;
QTime last_time;
Workspace* wspace;
class WrapperEffect;
};

View file

@ -44,6 +44,7 @@ License. See the file "COPYING" for the exact licensing terms.
#include "kwinadaptor.h"
#include "unmanaged.h"
#include "scene.h"
#include "effects.h"
#include <X11/extensions/shape.h>
#include <X11/keysym.h>
@ -489,6 +490,8 @@ Client* Workspace::createClient( Window w, bool is_mapped )
addClient( c, Allowed );
if( scene )
scene->windowAdded( c );
if( effects )
effects->windowAdded( c );
return c;
}
@ -503,6 +506,8 @@ Unmanaged* Workspace::createUnmanaged( Window w )
addUnmanaged( c, Allowed );
if( scene )
scene->windowAdded( c );
if( effects )
effects->windowAdded( c );
return c;
}
@ -571,6 +576,8 @@ void Workspace::removeClient( Client* c, allowed_t )
Q_ASSERT( clients.contains( c ) || desktops.contains( c ));
if( scene )
scene->windowDeleted( c );
if( effects )
effects->windowDeleted( c );
clients.removeAll( c );
desktops.removeAll( c );
unconstrained_stacking_order.removeAll( c );
@ -611,6 +618,8 @@ void Workspace::removeUnmanaged( Unmanaged* c, allowed_t )
assert( unmanaged.contains( c ));
if( scene )
scene->windowDeleted( c );
if( effects )
effects->windowDeleted( c );
unmanaged.removeAll( c );
}