diff --git a/COMPOSITE_TODO b/COMPOSITE_TODO index 717d85d5f7..3513fd67dd 100644 --- a/COMPOSITE_TODO +++ b/COMPOSITE_TODO @@ -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 diff --git a/composite.cpp b/composite.cpp index e014f46ab3..018ba368a1 100644 --- a/composite.cpp +++ b/composite.cpp @@ -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(); } diff --git a/effects.cpp b/effects.cpp index d703c10e8b..9b023cd686 100644 --- a/effects.cpp +++ b/effects.cpp @@ -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; } diff --git a/effects.h b/effects.h index 53cbbfc612..3e06916e96 100644 --- a/effects.h +++ b/effects.h @@ -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 ); }; diff --git a/scene.cpp b/scene.cpp index d780a5659f..c472f0d9a4 100644 --- a/scene.cpp +++ b/scene.cpp @@ -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 +#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 } diff --git a/scene.h b/scene.h index 16775f8f3c..2dd13d9dcb 100644 --- a/scene.h +++ b/scene.h @@ -11,6 +11,8 @@ License. See the file "COPYING" for the exact licensing terms. #ifndef KWIN_SCENE_H #define KWIN_SCENE_H +#include + #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; }; diff --git a/workspace.cpp b/workspace.cpp index 4b65df1ce2..e404b7ddd3 100644 --- a/workspace.cpp +++ b/workspace.cpp @@ -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 #include @@ -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 ); }