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

KWin

scene_xrender.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  This is the XRender-based compositing code. The primary compositing
00023  backend is the OpenGL-based one, which should be more powerful
00024  and also possibly better documented. This backend is mostly for cases
00025  when the OpenGL backend cannot be used for some reason (insufficient
00026  performance, no usable OpenGL support at all, etc.)
00027  The plan is to keep it around as long as needed/possible, but if it
00028  proves to be too much hassle it will be dropped in the future.
00029  
00030  Docs:
00031  
00032  XRender (the protocol, but the function calls map to it):
00033  http://gitweb.freedesktop.org/?p=xorg/proto/renderproto.git;a=blob_plain;hb=HEAD;f=renderproto.txt
00034  
00035  XFixes (again, the protocol):
00036  http://gitweb.freedesktop.org/?p=xorg/proto/fixesproto.git;a=blob_plain;hb=HEAD;f=fixesproto.txt
00037 
00038 */
00039 
00040 #include "scene_xrender.h"
00041 
00042 #ifdef KWIN_HAVE_XRENDER_COMPOSITING
00043 
00044 #include "toplevel.h"
00045 #include "client.h"
00046 #include "deleted.h"
00047 #include "effects.h"
00048 #include "kwinxrenderutils.h"
00049 
00050 #include <kxerrorhandler.h>
00051 
00052 namespace KWin
00053 {
00054 
00055 //****************************************
00056 // SceneXrender
00057 //****************************************
00058 
00059 // kDebug() support for the XserverRegion type
00060 struct RegionDebug
00061    {   
00062    RegionDebug( XserverRegion r ) : rr( r ) {}   
00063    XserverRegion rr;   
00064    };   
00065 
00066 kdbgstream& operator<<( kdbgstream& stream, RegionDebug r )
00067     {       
00068     if( r.rr == None )
00069         return stream << "EMPTY";
00070     int num;
00071     XRectangle* rects = XFixesFetchRegion( display(), r.rr, &num );
00072     if( rects == NULL || num == 0 )
00073         return stream << "EMPTY";
00074     for( int i = 0;
00075          i < num;
00076          ++i )
00077        stream << "[" << rects[ i ].x << "+" << rects[ i ].y << " " << rects[ i ].width << "x" << rects[ i ].height << "]";
00078     return stream;
00079     }
00080 
00081 Picture SceneXrender::buffer = None;
00082 ScreenPaintData SceneXrender::screen_paint;
00083 
00084 SceneXrender::SceneXrender( Workspace* ws )
00085     : Scene( ws )
00086     , front( None )
00087     , init_ok( false )
00088     {
00089     if( !Extensions::renderAvailable())
00090         {
00091         kError( 1212 ) << "No XRender extension available";
00092         return;
00093         }
00094     if( !Extensions::fixesRegionAvailable())
00095         {
00096         kError( 1212 ) << "No XFixes v3+ extension available";
00097         return;
00098         }
00099     KXErrorHandler xerr;
00100     if( wspace->createOverlay())
00101         {
00102         wspace->setupOverlay( None );
00103         XWindowAttributes attrs;
00104         XGetWindowAttributes( display(), wspace->overlayWindow(), &attrs );
00105         format = XRenderFindVisualFormat( display(), attrs.visual );
00106         if( format == NULL )
00107             {
00108             kError( 1212 ) << "Failed to find XRender format for overlay window";
00109             return;
00110             }
00111         front = XRenderCreatePicture( display(), wspace->overlayWindow(), format, 0, NULL );
00112         }
00113     else
00114         {
00115         // create XRender picture for the root window
00116         format = XRenderFindVisualFormat( display(), DefaultVisual( display(), DefaultScreen( display())));
00117         if( format == NULL )
00118             {
00119             kError( 1212 ) << "Failed to find XRender format for root window";
00120             return; // error
00121             }
00122         XRenderPictureAttributes pa;
00123         pa.subwindow_mode = IncludeInferiors;
00124         front = XRenderCreatePicture( display(), rootWindow(), format, CPSubwindowMode, &pa );
00125         }
00126     createBuffer();
00127     init_ok = !xerr.error( true );
00128     if( !init_ok )
00129         kError( 1212 ) << "XRender compositing setup failed";
00130     }
00131 
00132 SceneXrender::~SceneXrender()
00133     {
00134     if( !init_ok )
00135         {
00136         // TODO this probably needs to clean up whatever has been created until the failure
00137         wspace->destroyOverlay();
00138         return;
00139         }
00140     XRenderFreePicture( display(), front );
00141     XRenderFreePicture( display(), buffer );
00142     buffer = None;
00143     wspace->destroyOverlay();
00144     foreach( Window* w, windows )
00145         delete w;
00146     }
00147 
00148 bool SceneXrender::initFailed() const
00149     {
00150     return !init_ok;
00151     }
00152 
00153 // Create the compositing buffer. The root window is not double-buffered,
00154 // so it is done manually using this buffer,
00155 void SceneXrender::createBuffer()
00156     {
00157     Pixmap pixmap = XCreatePixmap( display(), rootWindow(), displayWidth(), displayHeight(), DefaultDepth( display(), DefaultScreen( display())));
00158     buffer = XRenderCreatePicture( display(), pixmap, format, 0, 0 );
00159     XFreePixmap( display(), pixmap ); // The picture owns the pixmap now
00160     }
00161 
00162 // the entry point for painting
00163 void SceneXrender::paint( QRegion damage, ToplevelList toplevels )
00164     {
00165     foreach( Toplevel* c, toplevels )
00166         {
00167         assert( windows.contains( c ));
00168         stacking_order.append( windows[ c ] );
00169         }
00170     int mask = 0;
00171     paintScreen( &mask, &damage );
00172     if( wspace->overlayWindow()) // show the window only after the first pass, since
00173         wspace->showOverlay();   // that pass may take long
00174     if( mask & PAINT_SCREEN_REGION )
00175         {
00176         // Use the damage region as the clip region for the root window
00177         XserverRegion front_region = toXserverRegion( damage );
00178         XFixesSetPictureClipRegion( display(), front, 0, 0, front_region );
00179         XFixesDestroyRegion( display(), front_region );
00180         // copy composed buffer to the root window
00181         XFixesSetPictureClipRegion( display(), buffer, 0, 0, None );
00182         XRenderComposite( display(), PictOpSrc, buffer, None, front, 0, 0, 0, 0, 0, 0, displayWidth(), displayHeight());
00183         XFixesSetPictureClipRegion( display(), front, 0, 0, None );
00184         XFlush( display());
00185         }
00186     else
00187         {
00188         // copy composed buffer to the root window
00189         XRenderComposite( display(), PictOpSrc, buffer, None, front, 0, 0, 0, 0, 0, 0, displayWidth(), displayHeight());
00190         XFlush( display());
00191         }
00192     // do cleanup
00193     stacking_order.clear();
00194     }
00195 
00196 void SceneXrender::paintGenericScreen( int mask, ScreenPaintData data )
00197     {
00198     screen_paint = data; // save, transformations will be done when painting windows
00199     if( true ) // as long as paintTransformedScreen() doesn't work properly
00200         Scene::paintGenericScreen( mask, data );
00201     else
00202         paintTransformedScreen( mask );
00203     }
00204 
00205 /*
00206  TODO currently broken
00207  Try to do optimized painting even with transformations. Since only scaling
00208  and translation are supported by the painting code, clipping can be done
00209  manually to avoid having to paint everything in every pass. Whole screen
00210  still need to be painted but e.g. obscured windows don't. So this below
00211  is basically paintSimpleScreen() with extra code to compute clipping correctly.
00212  
00213  This code assumes that the only transformations possible with XRender are those
00214  provided by Window/ScreenPaintData, In the (very unlikely?) case more is needed
00215  then paintGenericScreen() needs to be used.
00216 */
00217 void SceneXrender::paintTransformedScreen( int orig_mask )
00218     {
00219     QRegion region( 0, 0, displayWidth(), displayHeight());
00220     QList< Phase2Data > phase2;
00221     QRegion allclips;
00222     // Draw each opaque window top to bottom, subtracting the bounding rect of
00223     // each window from the clip region after it's been drawn.
00224     for( int i = stacking_order.count() - 1; // top to bottom
00225          i >= 0;
00226          --i )
00227         {
00228         Window* w = static_cast< Window* >( stacking_order[ i ] );
00229         WindowPrePaintData data;
00230         data.mask = orig_mask | ( w->isOpaque() ? PAINT_WINDOW_OPAQUE : PAINT_WINDOW_TRANSLUCENT );
00231         w->resetPaintingEnabled();
00232         data.paint = region;
00233         // TODO this is wrong, transformedShape() should be used here, but is not known yet
00234         data.clip = w->isOpaque() ? region : 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 #endif
00243         if( !w->isPaintingEnabled())
00244             continue;
00245         data.paint -= allclips; // make sure to avoid already clipped areas
00246         if( data.paint.isEmpty()) // completely clipped
00247             continue;
00248         if( data.paint != region ) // prepaint added area to draw
00249             {
00250             region |= data.paint; // make sure other windows in that area get painted too
00251             painted_region |= data.paint; // make sure it makes it to the screen
00252             }
00253         // If the window is transparent, the transparent part will be done
00254         // in the 2nd pass.
00255         if( data.mask & PAINT_WINDOW_TRANSLUCENT )
00256             phase2.prepend( Phase2Data( w, data.paint, data.clip, data.mask, data.quads ));
00257         if( data.mask & PAINT_WINDOW_OPAQUE )
00258             {
00259             w->setTransformedShape( QRegion());
00260             paintWindow( w, data.mask, data.paint, data.quads );
00261             // The window can clip by its opaque parts the windows below.
00262             region -= w->transformedShape();
00263             }
00264         }
00265     if( !( orig_mask & PAINT_SCREEN_BACKGROUND_FIRST ))
00266         paintBackground( region ); // Fill any areas of the root window not covered by windows
00267     // Now walk the list bottom to top, drawing translucent windows.
00268     // That we draw bottom to top is important now since we're drawing translucent objects
00269     // and also are clipping only by opaque windows.
00270     QRegion add_paint;
00271     foreach( const Phase2Data &d, phase2 )
00272         {
00273         Scene::Window* w = d.window;
00274         paintWindow( w, d.mask, d.region | add_paint, d.quads );
00275         // It is necessary to also add paint regions of windows below, because their
00276         // pre-paint's might have extended the paint area, so those areas need to be painted too.
00277         add_paint |= d.region;
00278         }
00279     }
00280 
00281 // fill the screen background
00282 void SceneXrender::paintBackground( QRegion region )
00283     {
00284     PaintClipper pc( region );
00285     for( PaintClipper::Iterator iterator;
00286          !iterator.isDone();
00287          iterator.next())
00288         {
00289         XRenderColor col = { 0, 0, 0, 0xffff }; // black
00290         XRenderFillRectangle( display(), PictOpSrc, buffer, &col, 0, 0, displayWidth(), displayHeight());
00291         }
00292     }
00293 
00294 void SceneXrender::windowGeometryShapeChanged( Toplevel* c )
00295     {
00296     if( !windows.contains( c )) // this is ok, shape is not valid by default
00297         return;
00298     Window* w = windows[ c ];
00299     w->discardPicture();
00300     w->discardShape();
00301     w->discardAlpha();
00302     }
00303     
00304 void SceneXrender::windowOpacityChanged( Toplevel* c )
00305     {
00306     if( !windows.contains( c )) // this is ok, alpha is created on demand
00307         return;
00308     Window* w = windows[ c ];
00309     w->discardAlpha();
00310     }
00311 
00312 void SceneXrender::windowClosed( Toplevel* c, Deleted* deleted )
00313     {
00314     assert( windows.contains( c ));
00315     if( deleted != NULL )
00316         { // replace c with deleted
00317         Window* w = windows.take( c );
00318         w->updateToplevel( deleted );
00319         windows[ deleted ] = w;
00320         }
00321     else
00322         {
00323         delete windows.take( c );
00324         c->effectWindow()->setSceneWindow( NULL );
00325         }
00326     }
00327 
00328 void SceneXrender::windowDeleted( Deleted* c )
00329     {
00330     assert( windows.contains( c ));
00331     delete windows.take( c );
00332     c->effectWindow()->setSceneWindow( NULL );
00333     }
00334 
00335 void SceneXrender::windowAdded( Toplevel* c )
00336     {
00337     assert( !windows.contains( c ));
00338     windows[ c ] = new Window( c );
00339     c->effectWindow()->setSceneWindow( windows[ c ]);
00340     }
00341 
00342 //****************************************
00343 // SceneXrender::Window
00344 //****************************************
00345 
00346 SceneXrender::Window::Window( Toplevel* c )
00347     : Scene::Window( c )
00348     , _picture( None )
00349     , format( XRenderFindVisualFormat( display(), c->visual()))
00350     , alpha( None )
00351     {
00352     }
00353 
00354 SceneXrender::Window::~Window()
00355     {
00356     discardPicture();
00357     discardAlpha();
00358     discardShape();
00359     }
00360 
00361 // Create XRender picture for the pixmap with the window contents.
00362 Picture SceneXrender::Window::picture()
00363     {
00364     if( !toplevel->damage().isEmpty() && _picture != None )
00365         {
00366         XRenderFreePicture( display(), _picture );
00367         _picture = None;
00368         }
00369     if( _picture == None && format != NULL )
00370         {
00371         // Get the pixmap with the window contents.
00372         Pixmap pix = toplevel->windowPixmap();
00373         if( pix == None )
00374             return None;
00375         _picture = XRenderCreatePicture( display(), pix, format, 0, 0 );
00376         toplevel->resetDamage( toplevel->rect());
00377         }
00378     return _picture;
00379     }
00380 
00381 
00382 void SceneXrender::Window::discardPicture()
00383     {
00384     if( _picture != None )
00385         XRenderFreePicture( display(), _picture );
00386     _picture = None;
00387     }
00388 
00389 void SceneXrender::Window::discardAlpha()
00390     {
00391     if( alpha != None )
00392         XRenderFreePicture( display(), alpha );
00393     alpha = None;
00394     }
00395 
00396 // Create XRender picture for the alpha mask.
00397 Picture SceneXrender::Window::alphaMask( double opacity )
00398     {
00399     if( isOpaque() && opacity == 1.0 )
00400         return None;
00401     if( alpha != None && alpha_cached_opacity != opacity )
00402         {
00403         if( alpha != None )
00404             XRenderFreePicture( display(), alpha );
00405         alpha = None;
00406         }
00407     if( alpha != None )
00408         return alpha;
00409     if( opacity == 1.0 )
00410         { // no need to create alpha mask
00411         alpha_cached_opacity = 1.0;
00412         return None;
00413         }
00414     // Create a 1x1 8bpp pixmap containing the given opacity in the alpha channel.
00415     Pixmap pixmap = XCreatePixmap( display(), rootWindow(), 1, 1, 8 );
00416     XRenderPictFormat* format = XRenderFindStandardFormat( display(), PictStandardA8 );
00417     XRenderPictureAttributes pa;
00418     pa.repeat = True;
00419     alpha = XRenderCreatePicture( display(), pixmap, format, CPRepeat, &pa );
00420     XFreePixmap( display(), pixmap );
00421     XRenderColor col;
00422     col.alpha = int( opacity * 0xffff );
00423     alpha_cached_opacity = opacity;
00424     XRenderFillRectangle( display(), PictOpSrc, alpha, &col, 0, 0, 1, 1 );
00425     return alpha;
00426     }
00427 
00428 // paint the window
00429 void SceneXrender::Window::performPaint( int mask, QRegion region, WindowPaintData data )
00430     {
00431     setTransformedShape( QRegion()); // maybe nothing will be painted
00432     // check if there is something to paint
00433     bool opaque = isOpaque() && data.opacity == 1.0;
00434     if( mask & ( PAINT_WINDOW_OPAQUE | PAINT_WINDOW_TRANSLUCENT ))
00435         {}
00436     else if( mask & PAINT_WINDOW_OPAQUE )
00437         {
00438         if( !opaque )
00439             return;
00440         }
00441     else if( mask & PAINT_WINDOW_TRANSLUCENT )
00442         {
00443         if( opaque )
00444             return;
00445         }
00446     Picture pic = picture(); // get XRender picture
00447     if( pic == None ) // The render format can be null for GL and/or Xv visuals
00448         return;
00449     // set picture filter
00450     if( options->xrenderSmoothScale ) // only when forced, it's slow
00451         {
00452         if( mask & PAINT_WINDOW_TRANSFORMED )
00453             filter = ImageFilterGood;
00454         else if( mask & PAINT_SCREEN_TRANSFORMED )
00455             filter = ImageFilterGood;
00456         else
00457             filter = ImageFilterFast;
00458         }
00459     else
00460         filter = ImageFilterFast;
00461     // do required transformations
00462     int x = toplevel->x();
00463     int y = toplevel->y();
00464     int width = toplevel->width();
00465     int height = toplevel->height();
00466     double xscale = 1;
00467     double yscale = 1;
00468     transformed_shape = shape();
00469     if( mask & PAINT_WINDOW_TRANSFORMED )
00470         {
00471         xscale *= data.xScale;
00472         yscale *= data.yScale;
00473         x += data.xTranslate;
00474         y += data.yTranslate;
00475         }
00476     if( mask & PAINT_SCREEN_TRANSFORMED )
00477         {
00478         xscale *= screen_paint.xScale;
00479         yscale *= screen_paint.yScale;
00480         x = int( x * screen_paint.xScale );
00481         y = int( y * screen_paint.yScale );
00482         x += screen_paint.xTranslate;
00483         y += screen_paint.yTranslate;
00484         }
00485     if( yscale != 1 || xscale != 1 )
00486         {
00487         XTransform xform = {{
00488             { XDoubleToFixed( 1 / xscale ), XDoubleToFixed( 0 ), XDoubleToFixed( 0 ) },
00489             { XDoubleToFixed( 0 ), XDoubleToFixed( 1 / yscale ), XDoubleToFixed( 0 ) },
00490             { XDoubleToFixed( 0 ), XDoubleToFixed( 0 ), XDoubleToFixed( 1 ) }
00491         }};
00492         XRenderSetPictureTransform( display(), pic, &xform );
00493         width = (int)(width * xscale);
00494         height = (int)(height * yscale);
00495         if( filter == ImageFilterGood )
00496             XRenderSetPictureFilter( display(), pic, const_cast< char* >( "good" ), NULL, 0 );
00497         // transform the shape for clipping in paintTransformedScreen()
00498         QVector< QRect > rects = transformed_shape.rects();
00499         for( int i = 0;
00500              i < rects.count();
00501              ++i )
00502             {
00503             QRect& r = rects[ i ];
00504             r = QRect( int( r.x() * xscale ), int( r.y() * yscale ),
00505                 int( r.width() * xscale ), int( r.height() * xscale ));
00506             }
00507         transformed_shape.setRects( rects.constData(), rects.count());
00508         }
00509     transformed_shape.translate( x, y );
00510     PaintClipper pcreg( region ); // clip by the region to paint
00511     PaintClipper pc( transformed_shape ); // clip by window's shape
00512     for( PaintClipper::Iterator iterator;
00513          !iterator.isDone();
00514          iterator.next())
00515         {
00516         if( opaque )
00517             {
00518             XRenderComposite( display(), PictOpSrc, pic, None, buffer, 0, 0, 0, 0,
00519                 x, y, width, height);
00520             // fake brightness change by overlaying black
00521             XRenderColor col = { 0, 0, 0, 0xffff * ( 1 - data.brightness ) };
00522             XRenderFillRectangle( display(), PictOpOver, buffer, &col, x, y, width, height );
00523             }
00524         else
00525             {
00526             Picture alpha = alphaMask( data.opacity );
00527             XRenderComposite( display(), PictOpOver, pic, alpha, buffer, 0, 0, 0, 0,
00528                 x, y, width, height);
00529             // fake brightness change by overlaying black
00530             XRenderColor col = { 0, 0, 0, 0xffff * ( 1 - data.brightness ) * data.opacity };
00531             XRenderFillRectangle( display(), PictOpOver, buffer, &col, x, y, width, height );
00532             transformed_shape = QRegion();
00533             }
00534         }
00535     if( xscale != 1 || yscale != 1 )
00536         {
00537         XTransform xform = {{
00538             { XDoubleToFixed( 1 ), XDoubleToFixed( 0 ), XDoubleToFixed( 0 ) },
00539             { XDoubleToFixed( 0 ), XDoubleToFixed( 1 ), XDoubleToFixed( 0 ) },
00540             { XDoubleToFixed( 0 ), XDoubleToFixed( 0 ), XDoubleToFixed( 1 ) }
00541         }};
00542         XRenderSetPictureTransform( display(), pic, &xform );
00543         if( filter == ImageFilterGood )
00544             XRenderSetPictureFilter( display(), pic, const_cast< char* >( "fast" ), NULL, 0 );
00545         }
00546     }
00547 
00548 } // namespace
00549 #endif

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
  •   libplasma
  •   libsolidcontrol
  •   libtaskmanager
  • Plasma
  •   Animators
  •   Applets
  •   Engines
  • Solid Modules
Generated for API Reference by doxygen 1.5.4
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