• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

KWin

scene.cpp

Go to the documentation of this file.
00001 /********************************************************************
00002  KWin - the KDE window manager
00003  This file is part of the KDE project.
00004 
00005 Copyright (C) 2006 Lubos Lunak <l.lunak@kde.org>
00006 
00007 This program is free software; you can redistribute it and/or modify
00008 it under the terms of the GNU General Public License as published by
00009 the Free Software Foundation; either version 2 of the License, or
00010 (at your option) any later version.
00011 
00012 This program is distributed in the hope that it will be useful,
00013 but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 GNU General Public License for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with this program.  If not, see <http://www.gnu.org/licenses/>.
00019 *********************************************************************/
00020 
00021 /*
00022  (NOTE: The compositing code is work in progress. As such this design
00023  documentation may get outdated in some areas.)
00024 
00025  The base class for compositing, implementing shared functionality
00026  between the OpenGL and XRender backends.
00027  
00028  Design:
00029  
00030  When compositing is turned on, XComposite extension is used to redirect
00031  drawing of windows to pixmaps and XDamage extension is used to get informed
00032  about damage (changes) to window contents. This code is mostly in composite.cpp .
00033  
00034  Workspace::performCompositing() starts one painting pass. Painting is done
00035  by painting the screen, which in turn paints every window. Painting can be affected
00036  using effects, which are chained. E.g. painting a screen means that actually
00037  paintScreen() of the first effect is called, which possibly does modifications
00038  and calls next effect's paintScreen() and so on, until Scene::finalPaintScreen()
00039  is called.
00040  
00041  There are 3 phases of every paint (not necessarily done together):
00042  The pre-paint phase, the paint phase and the post-paint phase.
00043  
00044  The pre-paint phase is used to find out about how the painting will be actually
00045  done (i.e. what the effects will do). For example when only a part of the screen
00046  needs to be updated and no effect will do any transformation it is possible to use
00047  an optimized paint function. How the painting will be done is controlled
00048  by the mask argument, see PAINT_WINDOW_* and PAINT_SCREEN_* flags in scene.h .
00049  For example an effect that decides to paint a normal windows as translucent
00050  will need to modify the mask in its prePaintWindow() to include
00051  the PAINT_WINDOW_TRANSLUCENT flag. The paintWindow() function will then get
00052  the mask with this flag turned on and will also paint using transparency.
00053  
00054  The paint pass does the actual painting, based on the information collected
00055  using the pre-paint pass. After running through the effects' paintScreen()
00056  either paintGenericScreen() or optimized paintSimpleScreen() are called.
00057  Those call paintWindow() on windows (not necessarily all), possibly using
00058  clipping to optimize performance and calling paintWindow() first with only
00059  PAINT_WINDOW_OPAQUE to paint the opaque parts and then later
00060  with PAINT_WINDOW_TRANSLUCENT to paint the transparent parts. Function
00061  paintWindow() again goes through effects' paintWindow() until
00062  finalPaintWindow() is called, which calls the window's performPaint() to
00063  do the actual painting.
00064  
00065  The post-paint can be used for cleanups and is also used for scheduling
00066  repaints during the next painting pass for animations. Effects wanting to
00067  repaint certain parts can manually damage them during post-paint and repaint
00068  of these parts will be done during the next paint pass.
00069  
00070 */
00071 
00072 #include "scene.h"
00073 
00074 #include <X11/extensions/shape.h>
00075 
00076 #include "client.h"
00077 #include "deleted.h"
00078 #include "effects.h"
00079 
00080 #include <kephal/screens.h>
00081 
00082 namespace KWin
00083 {
00084 
00085 //****************************************
00086 // Scene
00087 //****************************************
00088 
00089 Scene* scene;
00090 
00091 Scene::Scene( Workspace* ws )
00092     : wspace( ws ),
00093     has_waitSync( false )
00094     {
00095     }
00096     
00097 Scene::~Scene()
00098     {
00099     }
00100 
00101 // returns mask and possibly modified region
00102 void Scene::paintScreen( int* mask, QRegion* region )
00103     {
00104     *mask = ( *region == QRegion( 0, 0, displayWidth(), displayHeight()))
00105         ? 0 : PAINT_SCREEN_REGION;
00106     updateTimeDiff();
00107     // preparation step
00108     static_cast<EffectsHandlerImpl*>(effects)->startPaint();
00109     ScreenPrePaintData pdata;
00110     pdata.mask = *mask;
00111     pdata.paint = *region;
00112     effects->prePaintScreen( pdata, time_diff );
00113     *mask = pdata.mask;
00114     *region = pdata.paint;
00115     if( *mask & ( PAINT_SCREEN_TRANSFORMED | PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS ))
00116         { // Region painting is not possible with transformations,
00117           // because screen damage doesn't match transformed positions.
00118         *mask &= ~PAINT_SCREEN_REGION;
00119         *region = infiniteRegion();
00120         }
00121     else if( *mask & PAINT_SCREEN_REGION )
00122         { // make sure not to go outside visible screen
00123         *region &= QRegion( 0, 0, displayWidth(), displayHeight());
00124         }
00125     else
00126         { // whole screen, not transformed, force region to be full
00127         *region = QRegion( 0, 0, displayWidth(), displayHeight());
00128         }
00129     painted_region = *region;
00130     if( *mask & PAINT_SCREEN_BACKGROUND_FIRST )
00131         paintBackground( *region );
00132     ScreenPaintData data;
00133     effects->paintScreen( *mask, *region, data );
00134     foreach( Window* w, stacking_order )
00135         effects->postPaintWindow( effectWindow( w ));
00136     effects->postPaintScreen();
00137     *region |= painted_region;
00138     // make sure not to go outside of the screen area
00139     *region &= QRegion( 0, 0, displayWidth(), displayHeight());
00140     // make sure all clipping is restored
00141     Q_ASSERT( !PaintClipper::clip());
00142     }
00143 
00144 // Compute time since the last painting pass.
00145 void Scene::updateTimeDiff()
00146     {
00147     if( last_time.isNull())
00148         {
00149         // Painting has been idle (optimized out) for some time,
00150         // which means time_diff would be huge and would break animations.
00151         // Simply set it to one (zero would mean no change at all and could
00152         // cause problems).
00153         time_diff = 1;
00154         }
00155     else
00156         time_diff = last_time.elapsed();
00157     if( time_diff < 0 ) // check time rollback
00158         time_diff = 1;
00159     last_time.start();;
00160     }
00161 
00162 // Painting pass is optimized away.
00163 void Scene::idle()
00164     {
00165     // Don't break time since last paint for the next pass.
00166     last_time = QTime();
00167     }
00168 
00169 // the function that'll be eventually called by paintScreen() above
00170 void Scene::finalPaintScreen( int mask, QRegion region, ScreenPaintData& data )
00171     {
00172     if( mask & ( PAINT_SCREEN_TRANSFORMED | PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS ))
00173         paintGenericScreen( mask, data );
00174     else
00175         paintSimpleScreen( mask, region );
00176     }
00177 
00178 // The generic painting code that can handle even transformations.
00179 // It simply paints bottom-to-top.
00180 void Scene::paintGenericScreen( int orig_mask, ScreenPaintData )
00181     {
00182     if( !( orig_mask & PAINT_SCREEN_BACKGROUND_FIRST ))
00183         paintBackground( infiniteRegion());
00184     QList< Phase2Data > phase2;
00185     foreach( Window* w, stacking_order ) // bottom to top
00186         {
00187         WindowPrePaintData data;
00188         data.mask = orig_mask | ( w->isOpaque() ? PAINT_WINDOW_OPAQUE : PAINT_WINDOW_TRANSLUCENT );
00189         w->resetPaintingEnabled();
00190         data.paint = infiniteRegion(); // no clipping, so doesn't really matter
00191         data.clip = QRegion();
00192         data.quads = w->buildQuads();
00193         // preparation step
00194         effects->prePaintWindow( effectWindow( w ), data, time_diff );
00195 #ifndef NDEBUG
00196         foreach( const WindowQuad &q, data.quads )
00197             if( q.isTransformed())
00198                 kFatal( 1212 ) << "Pre-paint calls are not allowed to transform quads!" ;
00199 #endif
00200         if( !w->isPaintingEnabled())
00201             continue;
00202         phase2.append( Phase2Data( w, infiniteRegion(), data.clip, data.mask, data.quads ));
00203         // transformations require window pixmap
00204         w->suspendUnredirect( data.mask
00205             & ( PAINT_WINDOW_TRANSLUCENT | PAINT_SCREEN_TRANSFORMED | PAINT_WINDOW_TRANSFORMED )); 
00206         }
00207 
00208     foreach( const Phase2Data &d, phase2 )
00209         paintWindow( d.window, d.mask, d.region, d.quads );
00210     }
00211 
00212 // The optimized case without any transformations at all.
00213 // It can paint only the requested region and can use clipping
00214 // to reduce painting and improve performance.
00215 void Scene::paintSimpleScreen( int orig_mask, QRegion region )
00216     {
00217     // TODO PAINT_WINDOW_* flags don't belong here, that's why it's in the assert,
00218     // perhaps the two enums should be separated
00219     assert(( orig_mask & ( PAINT_WINDOW_TRANSFORMED | PAINT_SCREEN_TRANSFORMED
00220         | PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS
00221         | PAINT_WINDOW_TRANSLUCENT | PAINT_WINDOW_OPAQUE )) == 0 );
00222     QHash< Window*, Phase2Data > phase2data;
00223     // Draw each opaque window top to bottom, subtracting the bounding rect of
00224     // each window from the clip region after it's been drawn.
00225     for( int i = stacking_order.count() - 1; // top to bottom
00226          i >= 0;
00227          --i )
00228         {
00229         Window* w = stacking_order[ i ];
00230         WindowPrePaintData data;
00231         data.mask = orig_mask | ( w->isOpaque() ? PAINT_WINDOW_OPAQUE : PAINT_WINDOW_TRANSLUCENT );
00232         w->resetPaintingEnabled();
00233         data.paint = region;
00234         data.clip = w->isOpaque() ? w->shape().translated( w->x(), w->y()) : QRegion();
00235         data.quads = w->buildQuads();
00236         // preparation step
00237         effects->prePaintWindow( effectWindow( w ), data, time_diff );
00238 #ifndef NDEBUG
00239         foreach( const WindowQuad &q, data.quads )
00240             if( q.isTransformed())
00241                 kFatal( 1212 ) << "Pre-paint calls are not allowed to transform quads!" ;
00242         if( data.mask & PAINT_WINDOW_TRANSFORMED )
00243             kFatal( 1212 ) << "PAINT_WINDOW_TRANSFORMED without PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS!";
00244 #endif
00245         if( !w->isPaintingEnabled())
00246             {
00247             w->suspendUnredirect( true );
00248             continue;
00249             }
00250         if( data.paint != region ) // prepaint added area to draw
00251             painted_region |= data.paint; // make sure it makes it to the screen
00252         // Schedule the window for painting
00253         phase2data[w] = Phase2Data( w, data.paint, data.clip, data.mask, data.quads );
00254         // no transformations, but translucency requires window pixmap
00255         w->suspendUnredirect( data.mask & PAINT_WINDOW_TRANSLUCENT );
00256         }
00257     // Do the actual painting
00258     // First opaque windows, top to bottom
00259     // This also calculates correct paint regions for windows, also taking
00260     //  care of clipping
00261     QRegion allclips;
00262     for( int i = stacking_order.count() - 1; i >= 0; --i )
00263         {
00264         Window* w = stacking_order[ i ];
00265         if( !phase2data.contains( w ))
00266             continue;
00267         Phase2Data d = phase2data[w];
00268         // Calculate correct paint region and take the clip region into account
00269         d.region = painted_region - allclips;
00270         allclips |= d.clip;
00271         if( d.mask & PAINT_WINDOW_TRANSLUCENT )
00272             {
00273             // For translucent windows, the paint region must contain the
00274             //  entire painted area, except areas clipped by opaque windows
00275             //  above the translucent window
00276             phase2data[w].region = d.region;
00277             }
00278         else
00279             {
00280             // Paint the opaque window
00281             paintWindow( d.window, d.mask, d.region, d.quads );
00282             }
00283         }
00284     // Fill any areas of the root window not covered by windows
00285     if( !( orig_mask & PAINT_SCREEN_BACKGROUND_FIRST ))
00286         paintBackground( painted_region - allclips );
00287     // Now walk the list bottom to top, drawing translucent windows.
00288     for( int i = 0; i < stacking_order.count(); i++ )
00289         {
00290         Window* w = stacking_order[ i ];
00291         if( !phase2data.contains( w ))
00292             continue;
00293         Phase2Data d = phase2data[w];
00294         if( d.mask & PAINT_WINDOW_TRANSLUCENT )
00295             paintWindow( d.window, d.mask, d.region, d.quads );
00296         }
00297     }
00298 
00299 void Scene::paintWindow( Window* w, int mask, QRegion region, WindowQuadList quads )
00300     {
00301     // no painting outside visible screen (and no transformations)
00302     region &= QRect( 0, 0, displayWidth(), displayHeight());
00303     if( region.isEmpty()) // completely clipped
00304         return;
00305 
00306     WindowPaintData data( w->window()->effectWindow());
00307     data.quads = quads;
00308     effects->paintWindow( effectWindow( w ), mask, region, data );
00309     }
00310 
00311 // the function that'll be eventually called by paintWindow() above
00312 void Scene::finalPaintWindow( EffectWindowImpl* w, int mask, QRegion region, WindowPaintData& data )
00313     {
00314     effects->drawWindow( w, mask, region, data );
00315     }
00316 
00317 // will be eventually called from drawWindow()
00318 void Scene::finalDrawWindow( EffectWindowImpl* w, int mask, QRegion region, WindowPaintData& data )
00319     {
00320     w->sceneWindow()->performPaint( mask, region, data );
00321     }
00322 
00323 QList< QPoint > Scene::selfCheckPoints() const
00324     {
00325     QList< QPoint > ret;
00326     // Use Kephal directly, we're interested in "real" screens, not depending on our config.
00327     // TODO: Does Kephal allow fake screens as well? We cannot use QDesktopWidget as it will cause a crash if
00328     //       the number of screens is different to what Kephal returns.
00329     for( int screen = 0;
00330          screen < Kephal::ScreenUtils::numScreens();
00331          ++screen )
00332         { // test top-left and bottom-right of every screen
00333         ret.append( Kephal::ScreenUtils::screenGeometry( screen ).topLeft());
00334         ret.append( Kephal::ScreenUtils::screenGeometry( screen ).bottomRight() + QPoint( -3 + 1, -2 + 1 )
00335             + QPoint( -1, 0 )); // intentionally moved one up, since the source windows will be one down
00336         }
00337     return ret;
00338     }
00339 
00340 //****************************************
00341 // Scene::Window
00342 //****************************************
00343 
00344 Scene::Window::Window( Toplevel * c )
00345     : toplevel( c )
00346     , filter( ImageFilterFast )
00347     , disable_painting( 0 )
00348     , shape_valid( false )
00349     , cached_quad_list( NULL )
00350     {
00351     }
00352 
00353 Scene::Window::~Window()
00354     {
00355     delete cached_quad_list;
00356     }
00357 
00358 void Scene::Window::discardShape()
00359     {
00360     // it is created on-demand and cached, simply
00361     // reset the flag
00362     shape_valid = false;
00363     delete cached_quad_list;
00364     cached_quad_list = NULL;
00365     }
00366 
00367 // Find out the shape of the window using the XShape extension
00368 // or if shape is not set then simply it's the window geometry.
00369 QRegion Scene::Window::shape() const
00370     {
00371     if( !shape_valid )
00372         {
00373         Client* c = dynamic_cast< Client* >( toplevel );
00374         if( toplevel->shape() || ( c != NULL && !c->mask().isEmpty()))
00375             {
00376             int count, order;
00377             XRectangle* rects = XShapeGetRectangles( display(), toplevel->frameId(),
00378                 ShapeBounding, &count, &order );
00379             if(rects)
00380                 {
00381                 shape_region = QRegion();
00382                 for( int i = 0;
00383                      i < count;
00384                      ++i )
00385                     shape_region += QRegion( rects[ i ].x, rects[ i ].y,
00386                         rects[ i ].width, rects[ i ].height );
00387                 XFree(rects);
00388                 // make sure the shape is sane (X is async, maybe even XShape is broken)
00389                 shape_region &= QRegion( 0, 0, width(), height());
00390                 }
00391             else
00392                 shape_region = QRegion();
00393             }
00394         else
00395             shape_region = QRegion( 0, 0, width(), height());
00396         shape_valid = true;
00397         }
00398     return shape_region;
00399     }
00400 
00401 bool Scene::Window::isVisible() const
00402     {
00403     if( dynamic_cast< Deleted* >( toplevel ) != NULL )
00404         return false;
00405     if( !toplevel->isOnCurrentDesktop())
00406         return false;
00407     if( Client* c = dynamic_cast< Client* >( toplevel ))
00408         return c->isShown( true );
00409     return true; // Unmanaged is always visible
00410     // TODO there may be transformations, so ignore this for now
00411     return !toplevel->geometry()
00412         .intersected( QRect( 0, 0, displayWidth(), displayHeight()))
00413         .isEmpty();
00414     }
00415 
00416 bool Scene::Window::isOpaque() const
00417     {
00418     return toplevel->opacity() == 1.0 && !toplevel->hasAlpha();
00419     }
00420 
00421 bool Scene::Window::isPaintingEnabled() const
00422     {
00423     return !disable_painting;
00424     }
00425 
00426 void Scene::Window::resetPaintingEnabled()
00427     {
00428     disable_painting = 0;
00429     if( dynamic_cast< Deleted* >( toplevel ) != NULL )
00430         disable_painting |= PAINT_DISABLED_BY_DELETE;
00431     if( !toplevel->isOnCurrentDesktop())
00432         disable_painting |= PAINT_DISABLED_BY_DESKTOP;
00433     if( Client* c = dynamic_cast< Client* >( toplevel ))
00434         {
00435         if( c->isMinimized() )
00436             disable_painting |= PAINT_DISABLED_BY_MINIMIZE;
00437         if( c->isHiddenInternal())
00438             disable_painting |= PAINT_DISABLED;
00439         }
00440     }
00441 
00442 void Scene::Window::enablePainting( int reason )
00443     {
00444     disable_painting &= ~reason;
00445     }
00446 
00447 void Scene::Window::disablePainting( int reason )
00448     {
00449     disable_painting |= reason;
00450     }
00451 
00452 WindowQuadList Scene::Window::buildQuads( bool force ) const
00453     {
00454     if( cached_quad_list != NULL && !force )
00455         return *cached_quad_list;
00456     WindowQuadList ret;
00457     if( toplevel->clientPos() == QPoint( 0, 0 ) && toplevel->clientSize() == toplevel->size())
00458         ret = makeQuads( WindowQuadContents, shape()); // has no decoration
00459     else
00460         {
00461         QRegion contents = shape() & QRect( toplevel->clientPos(), toplevel->clientSize());
00462         QRegion decoration = shape() - contents;
00463         ret = makeQuads( WindowQuadContents, contents );
00464         ret += makeQuads( WindowQuadDecoration, decoration );
00465         }
00466     effects->buildQuads( static_cast<Client*>( toplevel )->effectWindow(), ret );
00467     cached_quad_list = new WindowQuadList( ret );
00468     return ret;
00469     }
00470 
00471 WindowQuadList Scene::Window::makeQuads( WindowQuadType type, const QRegion& reg ) const
00472     {
00473     WindowQuadList ret;
00474     foreach( const QRect &r, reg.rects())
00475         {
00476         WindowQuad quad( type );
00477         // TODO asi mam spatne pravy dolni roh - bud tady, nebo v jinych castech
00478         quad[ 0 ] = WindowVertex( r.x(), r.y(), r.x(), r.y());
00479         quad[ 1 ] = WindowVertex( r.x() + r.width(), r.y(), r.x() + r.width(), r.y());
00480         quad[ 2 ] = WindowVertex( r.x() + r.width(), r.y() + r.height(), r.x() + r.width(), r.y() + r.height());
00481         quad[ 3 ] = WindowVertex( r.x(), r.y() + r.height(), r.x(), r.y() + r.height());
00482         ret.append( quad );
00483         }
00484     return ret;
00485     }
00486 
00487 } // namespace

KWin

Skip menu "KWin"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • KWin
  •   KWin Libraries
  • Libraries
  •   libkworkspace
  •   libsolidcontrol
  •   libtaskmanager
  • Plasma
  •   Animators
  •   Applets
  •   Engines
  • Solid Modules
Generated for API Reference by doxygen 1.5.7
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal