diff --git a/CMakeLists.txt b/CMakeLists.txt index 35b503a92d..8790e28efa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,10 +71,11 @@ set(kwin_KDEINIT_SRCS effects/dialogparent.cpp effects/showfps.cpp effects/zoom.cpp - effects/test_input.cpp effects/presentwindows.cpp effects/minimizeanimation.cpp effects/desktopchangeslide.cpp + effects/test_input.cpp + effects/test_thumbnail.cpp ) if( CAPTURY_FOUND ) diff --git a/client.cpp b/client.cpp index 87d243cffc..97100121ac 100644 --- a/client.cpp +++ b/client.cpp @@ -281,6 +281,7 @@ void Client::updateDecoration( bool check_workspace_pos, bool force ) || ( decoration != NULL && !noBorder()))) return; bool do_show = false; + QRect oldgeom = geometry(); blockGeometryUpdates( true ); if( force ) destroyDecoration(); @@ -304,6 +305,8 @@ void Client::updateDecoration( bool check_workspace_pos, bool force ) discardWindowPixmap(); if( scene != NULL ) scene->windowGeometryShapeChanged( this ); + if( effects != NULL ) + effects->windowGeometryShapeChanged( effectWindow(), oldgeom ); } else destroyDecoration(); @@ -317,6 +320,7 @@ void Client::updateDecoration( bool check_workspace_pos, bool force ) void Client::destroyDecoration() { + QRect oldgeom = geometry(); if( decoration != NULL ) { delete decoration; @@ -334,6 +338,8 @@ void Client::destroyDecoration() discardWindowPixmap(); if( scene != NULL && !deleting ) scene->windowGeometryShapeChanged( this ); + if( effects != NULL && !deleting ) + effects->windowGeometryShapeChanged( effectWindow(), oldgeom ); } } @@ -462,6 +468,8 @@ void Client::updateShape() addDamageFull(); if( scene != NULL ) scene->windowGeometryShapeChanged( this ); + if( effects != NULL ) + effects->windowGeometryShapeChanged( effectWindow(), geometry()); // workaround for #19644 - shaped windows shouldn't have decoration if( shape() && !noBorder()) { @@ -500,6 +508,8 @@ void Client::setMask( const QRegion& reg, int mode ) addDamageFull(); if( scene != NULL ) scene->windowGeometryShapeChanged( this ); + if( effects != NULL ) + effects->windowGeometryShapeChanged( effectWindow(), geometry()); } QRegion Client::mask() const diff --git a/composite.cpp b/composite.cpp index 08c0217da0..f835a36873 100644 --- a/composite.cpp +++ b/composite.cpp @@ -429,6 +429,7 @@ void Toplevel::addDamage( int x, int y, int w, int h ) r &= rect(); damage_region += r; repaints_region += r; + effects->windowDamaged( effectWindow(), r ); } void Toplevel::addDamageFull() @@ -437,6 +438,7 @@ void Toplevel::addDamageFull() return; damage_region = rect(); repaints_region = rect(); + effects->windowDamaged( effectWindow(), rect()); } void Toplevel::resetDamage( const QRect& r ) diff --git a/effects.cpp b/effects.cpp index 27da168b68..9eca6d30ca 100644 --- a/effects.cpp +++ b/effects.cpp @@ -35,6 +35,7 @@ License. See the file "COPYING" for the exact licensing terms. #include "effects/zoom.h" #include "effects/test_input.h" +#include "effects/test_thumbnail.h" namespace KWinInternal { @@ -87,6 +88,14 @@ void Effect::desktopChanged( int ) { } +void Effect::windowDamaged( EffectWindow*, const QRect& ) + { + } + +void Effect::windowGeometryShapeChanged( EffectWindow*, const QRect& ) + { + } + void Effect::prePaintScreen( int* mask, QRegion* region, int time ) { effects->prePaintScreen( mask, region, time ); @@ -117,13 +126,35 @@ void Effect::postPaintWindow( EffectWindow* w ) effects->postPaintWindow( w ); } +void Effect::drawWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data ) + { + effects->drawWindow( w, mask, region, data ); + } + +void Effect::setPositionTransformations( WindowPaintData& data, QRect& region, EffectWindow* w, + const QRect& r, Qt::AspectRatioMode aspect ) + { + QSize size = w->size(); + size.scale( r.size(), aspect ); + data.xScale = size.width() / double( w->width()); + data.yScale = size.height() / double( w->height()); + int width = int( w->width() * data.xScale ); + int height = int( w->height() * data.yScale ); + int x = r.x() + ( r.width() - width ) / 2; + int y = r.y() + ( r.height() - height ) / 2; + region = QRect( x, y, width, height ); + data.xTranslate = x - w->x(); + data.yTranslate = y - w->y(); + } + //**************************************** // EffectsHandler //**************************************** EffectsHandler::EffectsHandler() - : current_paint_window( 0 ) - , current_paint_screen( 0 ) + : current_paint_screen( 0 ) + , current_paint_window( 0 ) + , current_draw_window( 0 ) { if( !compositing()) return; @@ -147,7 +178,9 @@ EffectsHandler::EffectsHandler() registerEffect("ScaleIn", new GenericEffectFactory); registerEffect("DialogParent", new GenericEffectFactory); registerEffect("DesktopChangeSlide", new GenericEffectFactory); + registerEffect("TestInput", new GenericEffectFactory); + registerEffect("TestThumbnail", new GenericEffectFactory); QStringList::const_iterator effectsIterator; for( effectsIterator = options->defaultEffects.constBegin(); @@ -224,11 +257,26 @@ void EffectsHandler::desktopChanged( int old ) ep.second->desktopChanged( old ); } +void EffectsHandler::windowDamaged( EffectWindow* w, const QRect& r ) + { + foreach( EffectPair ep, loaded_effects ) + ep.second->windowDamaged( w, r ); + } + +void EffectsHandler::windowGeometryShapeChanged( EffectWindow* w, const QRect& old ) + { + if( w == NULL ) // during late cleanup effectWindow() may be already NULL + return; // in some functions that may still call this + foreach( EffectPair ep, loaded_effects ) + ep.second->windowGeometryShapeChanged( w, old ); + } + // start another painting pass void EffectsHandler::startPaint() { assert( current_paint_screen == 0 ); assert( current_paint_window == 0 ); + assert( current_draw_window == 0 ); } // the idea is that effects call this function again which calls the next one @@ -294,6 +342,17 @@ void EffectsHandler::postPaintWindow( EffectWindow* w ) // no special final code } +void EffectsHandler::drawWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data ) + { + if( current_draw_window < loaded_effects.size()) + { + loaded_effects[current_draw_window++].second->drawWindow( w, mask, region, data ); + --current_draw_window; + } + else + scene->finalDrawWindow( w, mask, region, data ); + } + Window EffectsHandler::createInputWindow( Effect* e, int x, int y, int w, int h, const QCursor& cursor ) { XSetWindowAttributes attrs; @@ -400,6 +459,7 @@ void EffectsHandler::loadEffect( const QString& name ) { assert( current_paint_screen == 0 ); assert( current_paint_window == 0 ); + assert( current_draw_window == 0 ); for(QVector< EffectPair >::const_iterator it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); it++) { @@ -426,6 +486,7 @@ void EffectsHandler::unloadEffect( const QString& name ) { assert( current_paint_screen == 0 ); assert( current_paint_window == 0 ); + assert( current_draw_window == 0 ); for( QVector< EffectPair >::iterator it = loaded_effects.begin(); it != loaded_effects.end(); it++) { diff --git a/effects.h b/effects.h index f66d738927..3b3bb3a50d 100644 --- a/effects.h +++ b/effects.h @@ -69,8 +69,12 @@ class Effect virtual void paintScreen( int mask, QRegion region, ScreenPaintData& data ); virtual void postPaintScreen(); virtual void prePaintWindow( EffectWindow* w, int* mask, QRegion* region, int time ); + // paintWindow() can do various transformations virtual void paintWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data ); virtual void postPaintWindow( EffectWindow* w ); + // drawWindow() is used even for thumbnails etc. - it can alter the window itself where it + // makes sense (e.g.darkening out unresponsive windows), but it cannot do transformations + virtual void drawWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data ); // called when moved/resized or once after it's finished virtual void windowUserMovedResized( EffectWindow* c, bool first, bool last ); virtual void windowOpacityChanged( EffectWindow* c, double old_opacity ); @@ -82,12 +86,18 @@ class Effect virtual void windowUnminimized( EffectWindow* c ); virtual void windowInputMouseEvent( Window w, QEvent* e ); virtual void desktopChanged( int old ); + virtual void windowDamaged( EffectWindow* w, const QRect& r ); + virtual void windowGeometryShapeChanged( EffectWindow* w, const QRect& old ); // Interpolates between x and y static float interpolate(float x, float y, float a) { return x * (1 - a) + y * a; } + // helper to set WindowPaintData and QRegion to necessary transformations so that + // a following drawWindow() would put the window at the requested geometry (useful for thumbnails) + static void setPositionTransformations( WindowPaintData& data, QRect& region, EffectWindow* w, + const QRect& r, Qt::AspectRatioMode aspect ); protected: Workspace* workspace() const; @@ -125,6 +135,7 @@ class EffectsHandler void prePaintWindow( EffectWindow* w, int* mask, QRegion* region, int time ); void paintWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data ); void postPaintWindow( EffectWindow* w ); + void drawWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data ); // Functions for handling input - e.g. when an Expose-like effect is shown, an input window // covering the whole screen is created and all mouse events will be intercepted by it. // The effect's windowInputMouseEvent() will get called with such events. @@ -148,6 +159,9 @@ class EffectsHandler bool checkInputWindowEvent( XEvent* e ); void checkInputWindowStacking(); void desktopChanged( int old ); + void windowDamaged( EffectWindow* w, const QRect& r ); + void windowGeometryShapeChanged( EffectWindow* w, const QRect& old ); + void registerEffect( const QString& name, EffectFactory* factory ); void loadEffect( const QString& name ); void unloadEffect( const QString& name ); @@ -158,8 +172,9 @@ class EffectsHandler typedef QPair< Effect*, Window > InputWindowPair; QList< InputWindowPair > input_windows; QMap< QString, EffectFactory* > effect_factories; - int current_paint_window; int current_paint_screen; + int current_paint_window; + int current_draw_window; }; // This class is a representation of a window used by/for Effect classes. diff --git a/effects/test_thumbnail.cpp b/effects/test_thumbnail.cpp new file mode 100644 index 0000000000..f78c08ffd4 --- /dev/null +++ b/effects/test_thumbnail.cpp @@ -0,0 +1,75 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2007 Lubos Lunak + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +/* + + Testing of painting a window more than once. The active window is painted + once more as a thumbnail in the bottom-right corner of the screen. + +*/ + +#include "test_thumbnail.h" + +namespace KWinInternal +{ + +TestThumbnailEffect::TestThumbnailEffect() + : active_window( NULL ) + { + } + +void TestThumbnailEffect::paintScreen( int mask, QRegion region, ScreenPaintData& data ) + { + effects->paintScreen( mask, region, data ); + if( active_window != NULL && region.contains( thumbnailRect())) + { + WindowPaintData data; + QRect region; + setPositionTransformations( data, region, active_window, thumbnailRect(), Qt::KeepAspectRatio ); + effects->drawWindow( active_window, + Scene::PAINT_WINDOW_OPAQUE | Scene::PAINT_WINDOW_TRANSLUCENT | Scene::PAINT_WINDOW_TRANSFORMED, + region, data ); + } + } + +void TestThumbnailEffect::windowActivated( EffectWindow* act ) + { + active_window = act; + workspace()->addRepaint( thumbnailRect()); + } + +void TestThumbnailEffect::windowDamaged( EffectWindow* w, const QRect& ) + { + if( w == active_window ) + workspace()->addRepaint( thumbnailRect()); + // TODO maybe just the relevant part of the area should be repainted? + } + +void TestThumbnailEffect::windowGeometryShapeChanged( EffectWindow* w, const QRect& old ) + { + if( w == active_window && w->size() != old.size()) + workspace()->addRepaint( thumbnailRect()); + } + +void TestThumbnailEffect::windowClosed( EffectWindow* w ) + { + if( w == active_window ) + { + active_window = NULL; + workspace()->addRepaint( thumbnailRect()); + } + } + +QRect TestThumbnailEffect::thumbnailRect() const + { + return QRect( displayWidth() - 100, displayHeight() - 100, 100, 100 ); + } + +} // namespace diff --git a/effects/test_thumbnail.h b/effects/test_thumbnail.h new file mode 100644 index 0000000000..7f0115f4ba --- /dev/null +++ b/effects/test_thumbnail.h @@ -0,0 +1,42 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2007 Lubos Lunak + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +/* + + Testing of painting a window more than once. + +*/ + +#ifndef KWIN_TEST_THUMBNAIL_H +#define KWIN_TEST_THUMBNAIL_H + +#include + +namespace KWinInternal +{ + +class TestThumbnailEffect + : public Effect + { + public: + TestThumbnailEffect(); + virtual void paintScreen( int mask, QRegion region, ScreenPaintData& data ); + virtual void windowActivated( EffectWindow* w ); + virtual void windowDamaged( EffectWindow* w, const QRect& damage ); + virtual void windowGeometryShapeChanged( EffectWindow* w, const QRect& old ); + virtual void windowClosed( EffectWindow* w ); + private: + QRect thumbnailRect() const; + EffectWindow* active_window; + }; + +} // namespace + +#endif diff --git a/events.cpp b/events.cpp index a9dffde648..63aa1506e6 100644 --- a/events.cpp +++ b/events.cpp @@ -1636,6 +1636,8 @@ bool Unmanaged::windowEvent( XEvent* e ) addDamageFull(); if( scene != NULL ) scene->windowGeometryShapeChanged( this ); + if( effects != NULL ) + effects->windowGeometryShapeChanged( effectWindow(), geometry()); } #ifdef HAVE_XDAMAGE if( e->type == Extensions::damageNotifyEvent()) @@ -1664,10 +1666,13 @@ void Unmanaged::configureNotifyEvent( XConfigureEvent* e ) if( newgeom == geom ) return; workspace()->addRepaint( geometry()); // damage old area + QRect old = geom; geom = newgeom; discardWindowPixmap(); if( scene != NULL ) scene->windowGeometryShapeChanged( this ); + if( effects != NULL ) + effects->windowGeometryShapeChanged( effectWindow(), old ); } // **************************************** diff --git a/geometry.cpp b/geometry.cpp index 219fb91d1e..4056106aa0 100644 --- a/geometry.cpp +++ b/geometry.cpp @@ -1704,6 +1704,8 @@ void Client::setGeometry( int x, int y, int w, int h, ForceGeometry_t force ) discardWindowPixmap(); if( scene != NULL ) scene->windowGeometryShapeChanged( this ); + if( effects != NULL ) + effects->windowGeometryShapeChanged( effectWindow(), geom_before_block ); } workspace()->addRepaint( geom_before_block ); geom_before_block = geom; @@ -1764,6 +1766,8 @@ void Client::plainResize( int w, int h, ForceGeometry_t force ) discardWindowPixmap(); if( scene != NULL ) scene->windowGeometryShapeChanged( this ); + if( effects != NULL ) + effects->windowGeometryShapeChanged( effectWindow(), geom_before_block ); workspace()->addRepaint( geom_before_block ); geom_before_block = geom; } diff --git a/scene.cpp b/scene.cpp index 4d74a4ee91..92d49f82e8 100644 --- a/scene.cpp +++ b/scene.cpp @@ -230,6 +230,12 @@ void Scene::paintWindow( Window* w, int mask, QRegion region ) // the function that'll be eventually called by paintWindow() above void Scene::finalPaintWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data ) + { + effects->drawWindow( w, mask, region, data ); + } + +// will be eventually called from drawWindow() +void Scene::finalDrawWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data ) { w->sceneWindow()->performPaint( mask, region, data ); } diff --git a/scene.h b/scene.h index 70e90ab324..c165e8f957 100644 --- a/scene.h +++ b/scene.h @@ -90,6 +90,8 @@ class Scene void finalPaintWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data ); // shared implementation, starts painting the window virtual void paintWindow( Window* w, int mask, QRegion region ); + // called after all effects had their drawWindow() called + void finalDrawWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data ); // infinite region, i.e. everything static QRegion infiniteRegion(); // compute time since the last repaint diff --git a/scene_opengl.cpp b/scene_opengl.cpp index 8e67134e28..fa19c19044 100644 --- a/scene_opengl.cpp +++ b/scene_opengl.cpp @@ -1140,7 +1140,8 @@ void SceneOpenGL::Window::performPaint( int mask, QRegion region, WindowPaintDat // paint only requested areas if( region != infiniteRegion()) // avoid integer overflow region.translate( -x(), -y()); - region &= shape(); + if(( mask & ( PAINT_SCREEN_TRANSFORMED | PAINT_WINDOW_TRANSFORMED )) == 0 ) + region &= shape(); if( region.isEmpty()) return; if( !bindTexture())