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

KWin

composite.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  Code related to compositing (redirecting windows to pixmaps and tracking
00023  window damage).
00024  
00025  Docs:
00026  
00027  XComposite (the protocol, but the function calls map to it):
00028  http://gitweb.freedesktop.org/?p=xorg/proto/compositeproto.git;a=blob_plain;hb=HEAD;f=compositeproto.txt
00029  
00030  XDamage (again the protocol):
00031  http://gitweb.freedesktop.org/?p=xorg/proto/damageproto.git;a=blob_plain;hb=HEAD;f=damageproto.txt
00032 
00033  Paper including basics on compositing, XGL vs AIGLX, XRender vs OpenGL, etc.:
00034  http://www.vis.uni-stuttgart.de/~hopf/pub/LinuxTag2007_compiz_NextGenerationDesktop_Paper.pdf
00035  
00036  Composite HOWTO from Fredrik:
00037  http://ktown.kde.org/~fredrik/composite_howto.html
00038 
00039 */
00040 
00041 #include <config-X11.h>
00042 
00043 #include "utils.h"
00044 #include <QTextStream>
00045 #include "workspace.h"
00046 #include "client.h"
00047 #include "unmanaged.h"
00048 #include "deleted.h"
00049 #include "effects.h"
00050 #include "scene.h"
00051 #include "scene_basic.h"
00052 #include "scene_xrender.h"
00053 #include "scene_opengl.h"
00054 #include "compositingprefs.h"
00055 
00056 #include <stdio.h>
00057 
00058 #include <QMenu>
00059 #include <kxerrorhandler.h>
00060 
00061 #include <X11/extensions/shape.h>
00062 
00063 #ifdef HAVE_XCOMPOSITE
00064 #include <X11/extensions/Xcomposite.h>
00065 #if XCOMPOSITE_MAJOR > 0 || XCOMPOSITE_MINOR >= 3
00066 #define HAVE_XCOMPOSITE_OVERLAY
00067 #endif
00068 #endif
00069 #ifdef HAVE_XRANDR
00070 #include <X11/extensions/Xrandr.h>
00071 #endif
00072 
00073 namespace KWin
00074 {
00075 
00076 //****************************************
00077 // Workspace
00078 //****************************************
00079 
00080 void Workspace::setupCompositing()
00081     {
00082 #ifdef KWIN_HAVE_COMPOSITING
00083     if( scene != NULL )
00084         return;
00085     if( !options->useCompositing && getenv( "KWIN_COMPOSE") == NULL )
00086         {
00087         kDebug( 1212 ) << "Compositing is turned off in options";
00088         return;
00089         }
00090     else if( compositingSuspended )
00091         {
00092         kDebug( 1212 ) << "Compositing is suspended";
00093         return;
00094         }
00095     else if( !CompositingPrefs::compositingPossible() )
00096         {
00097         kError( 1212 ) << "Compositing is not possible";
00098         return;
00099         }
00100     CompositingType type = options->compositingMode;
00101     if( getenv( "KWIN_COMPOSE" ))
00102         {
00103         char c = getenv( "KWIN_COMPOSE" )[ 0 ];
00104         switch( c )
00105             {
00106             case 'O':
00107                 type = OpenGLCompositing;
00108                 break;
00109             case 'X':
00110                 type = XRenderCompositing;
00111                 break;
00112             default:
00113                 kDebug( 1212 ) << "No compositing";
00114                 return;
00115             }
00116         }
00117 
00118     char selection_name[ 100 ];
00119     sprintf( selection_name, "_NET_WM_CM_S%d", DefaultScreen( display()));
00120     cm_selection = new KSelectionOwner( selection_name );
00121     connect( cm_selection, SIGNAL( lostOwnership()), SLOT( lostCMSelection()));
00122     cm_selection->claim( true ); // force claiming
00123 
00124     switch( type )
00125         {
00126         /*case 'B':
00127             kDebug( 1212 ) << "X compositing";
00128             scene = new SceneBasic( this );
00129           break; // don't fall through (this is a testing one) */
00130 #ifdef KWIN_HAVE_OPENGL_COMPOSITING
00131         case OpenGLCompositing:
00132             kDebug( 1212 ) << "OpenGL compositing";
00133             scene = new SceneOpenGL( this );
00134             if( !scene->initFailed())
00135                 break; // -->
00136             delete scene;
00137             scene = NULL;
00138             break; // do not fall back to XRender for now, maybe in the future
00139 #endif
00140 #ifdef KWIN_HAVE_XRENDER_COMPOSITING
00141         case XRenderCompositing:
00142             kDebug( 1212 ) << "XRender compositing";
00143             scene = new SceneXrender( this );
00144           break;
00145 #endif
00146         default:
00147 #ifndef KWIN_HAVE_COMPOSITING
00148             kDebug( 1212 ) << "Compositing was not available at compile time";
00149 #else
00150             kDebug( 1212 ) << "No compositing";
00151 #endif
00152             delete cm_selection;
00153           return;
00154         }
00155     if( scene == NULL || scene->initFailed())
00156         {
00157         kError( 1212 ) << "Failed to initialize compositing, compositing disabled";
00158         kError( 1212 ) << "Consult http://techbase.kde.org/Projects/KWin/4.0-release-notes#Setting_up";
00159         delete scene;
00160         scene = NULL;
00161         delete cm_selection;
00162         return;
00163         }
00164     int rate = 0;
00165     if( options->refreshRate > 0 )
00166         { // use manually configured refresh rate
00167         rate = options->refreshRate;
00168         }
00169 #ifdef HAVE_XRANDR
00170     else
00171         { // autoconfigure refresh rate based on XRandR info
00172         if( Extensions::randrAvailable() )
00173             {
00174             XRRScreenConfiguration *config;
00175 
00176             config = XRRGetScreenInfo( display(), rootWindow() );
00177             rate = XRRConfigCurrentRate( config );
00178             XRRFreeScreenConfigInfo( config );
00179             }
00180         }
00181 #endif
00182     // 0Hz or less is invalid, so we fallback to a default rate
00183     if( rate <= 0 )
00184         rate = 50;
00185     // QTimer gives us 1msec (1000Hz) at best, so we ignore anything higher;
00186     // however, additional throttling makes prevents very high rates from taking place anyway
00187     else if( rate > 1000 )
00188         rate = 1000;
00189     kDebug( 1212 ) << "Refresh rate " << rate << "Hz";
00190     compositeRate = 1000 / rate;
00191     compositeTimer.start( compositeRate );
00192     lastCompositePaint.start();
00193     XCompositeRedirectSubwindows( display(), rootWindow(), CompositeRedirectManual );
00194     new EffectsHandlerImpl( scene->compositingType() ); // sets also the 'effects' pointer
00195     addRepaintFull();
00196     foreach( Client* c, clients )
00197         c->setupCompositing();
00198     foreach( Client* c, desktops )
00199         c->setupCompositing();
00200     foreach( Unmanaged* c, unmanaged )
00201         c->setupCompositing();
00202     foreach( Client* c, clients )
00203         scene->windowAdded( c );
00204     foreach( Client* c, desktops )
00205         scene->windowAdded( c );
00206     foreach( Unmanaged* c, unmanaged )
00207         scene->windowAdded( c );
00208     delete popup; // force re-creation of the Alt+F3 popup (opacity option)
00209     popup = NULL;
00210 #else
00211     kDebug( 1212 ) << "Compositing was not available at compile time";
00212 #endif
00213     }
00214 
00215 void Workspace::finishCompositing()
00216     {
00217 #ifdef KWIN_HAVE_COMPOSITING
00218     if( scene == NULL )
00219         return;
00220     delete cm_selection;
00221     foreach( Client* c, clients )
00222         scene->windowClosed( c, NULL );
00223     foreach( Client* c, desktops )
00224         scene->windowClosed( c, NULL );
00225     foreach( Unmanaged* c, unmanaged )
00226         scene->windowClosed( c, NULL );
00227     foreach( Deleted* c, deleted )
00228         scene->windowDeleted( c );
00229     foreach( Client* c, clients )
00230         c->finishCompositing();
00231     foreach( Client* c, desktops )
00232         c->finishCompositing();
00233     foreach( Unmanaged* c, unmanaged )
00234         c->finishCompositing();
00235     foreach( Deleted* c, deleted )
00236         c->finishCompositing();
00237     XCompositeUnredirectSubwindows( display(), rootWindow(), CompositeRedirectManual );
00238     compositeTimer.stop();
00239     delete effects;
00240     effects = NULL;
00241     delete scene;
00242     scene = NULL;
00243     repaints_region = QRegion();
00244     for( ClientList::ConstIterator it = clients.begin();
00245          it != clients.end();
00246          ++it )
00247         { // forward all opacity values to the frame in case there'll be other CM running
00248         if( (*it)->opacity() != 1.0 )
00249             {
00250             NETWinInfo i( display(), (*it)->frameId(), rootWindow(), 0 );
00251             i.setOpacity( static_cast< unsigned long >((*it)->opacity() * 0xffffffff ));
00252             }
00253         }
00254     delete popup; // force re-creation of the Alt+F3 popup (opacity option)
00255     popup = NULL;
00256     // discard all Deleted windows (#152914)
00257     while( !deleted.isEmpty())
00258         deleted.first()->discard( Allowed );
00259 #endif
00260     }
00261 
00262 void Workspace::lostCMSelection()
00263     {
00264     kDebug( 1212 ) << "Lost compositing manager selection";
00265     finishCompositing();
00266     }
00267 
00268 // for the shortcut
00269 void Workspace::slotToggleCompositing()
00270     {
00271     compositingSuspended = !compositingSuspended;
00272     finishCompositing();
00273     setupCompositing(); // will do nothing if suspended
00274     }
00275 
00276 void Workspace::addRepaint( int x, int y, int w, int h )
00277     {
00278     if( !compositing())
00279         return;
00280     repaints_region += QRegion( x, y, w, h );
00281     }
00282 
00283 void Workspace::addRepaint( const QRect& r )
00284     {
00285     if( !compositing())
00286         return;
00287     repaints_region += r;
00288     }
00289     
00290 void Workspace::addRepaint( const QRegion& r )
00291     {
00292     if( !compositing())
00293         return;
00294     repaints_region += r;
00295     }
00296     
00297 void Workspace::addRepaintFull()
00298     {
00299     if( !compositing())
00300         return;
00301     repaints_region = QRegion( 0, 0, displayWidth(), displayHeight());
00302     }
00303 
00304 void Workspace::performCompositing()
00305     {
00306 #ifdef KWIN_HAVE_COMPOSITING
00307     // The event loop apparently tries to fire a QTimer as often as possible, even
00308     // at the expense of not processing many X events. This means that the composite
00309     // repaints can seriously impact performance of everything else, therefore throttle
00310     // them - leave at least 1msec time after one repaint is finished and next one
00311     // is started.
00312     if( lastCompositePaint.elapsed() < 1 )
00313         return;
00314     checkCursorPos();
00315     if(( repaints_region.isEmpty() && !windowRepaintsPending()) // no damage
00316         || !overlay_visible ) // nothing is visible anyway
00317         {
00318         scene->idle();
00319         return;
00320         }
00321     // create a list of all windows in the stacking order
00322     ToplevelList windows = compositingStackingOrder();
00323     foreach( EffectWindow* c, static_cast< EffectsHandlerImpl* >( effects )->elevatedWindows())
00324         {
00325         Toplevel* t = static_cast< EffectWindowImpl* >( c )->window();
00326         windows.removeAll( t );
00327         windows.append( t );
00328         }
00329     // skip windows that are not yet ready for being painted
00330     ToplevelList tmp = windows;
00331     windows.clear();
00332 #if 0
00333     // There is a bug somewhere that prevents this from working properly (#160393), but additionally
00334     // this cannot be used so carelessly - needs protections against broken clients, the window
00335     // should not get focus before it's displayed and so on.
00336     foreach( Toplevel* c, tmp )
00337         if( c->readyForPainting())
00338             windows.append( c );
00339 #else
00340     foreach( Toplevel* c, tmp )
00341         windows.append( c );
00342 #endif
00343     foreach( Toplevel* c, windows )
00344         { // This could be possibly optimized WRT obscuring, but that'd need being already
00345           // past prePaint() phase - probably not worth it.
00346           // TODO I think effects->transformWindowDamage() doesn't need to be called here,
00347           // pre-paint will extend painted window areas as necessary.
00348         repaints_region |= c->repaints().translated( c->pos());
00349         c->resetRepaints( c->rect());
00350         }
00351     QRegion repaints = repaints_region;
00352     // clear all repaints, so that post-pass can add repaints for the next repaint
00353     repaints_region = QRegion();
00354     scene->paint( repaints, windows );
00355     if( scene->waitSyncAvailable())
00356         {
00357         // if vsync is used, schedule the next repaint slightly in advance of the next sync,
00358         // so that there is still time for the drawing to take place
00359         int untilNextSync = compositeRate - ( lastCompositePaint.elapsed() % compositeRate );
00360         compositeTimer.start( qMax( 1, untilNextSync - 10 )); // 10 ms in advance - TODO maybe less?
00361         }
00362     lastCompositePaint.start();
00363 #endif
00364     }
00365 
00366 bool Workspace::windowRepaintsPending() const
00367     {
00368     foreach( Toplevel* c, clients )
00369         if( !c->repaints().isEmpty())
00370             return true;
00371     foreach( Toplevel* c, desktops )
00372         if( !c->repaints().isEmpty())
00373             return true;
00374     foreach( Toplevel* c, unmanaged )
00375         if( !c->repaints().isEmpty())
00376             return true;
00377     foreach( Toplevel* c, deleted )
00378         if( !c->repaints().isEmpty())
00379             return true;
00380     return false;
00381     }
00382 
00383 bool Workspace::createOverlay()
00384     {
00385     assert( overlay == None );
00386     if( !Extensions::compositeOverlayAvailable())
00387         return false;
00388     if( !Extensions::shapeInputAvailable()) // needed in setupOverlay()
00389         return false;
00390 #ifdef HAVE_XCOMPOSITE_OVERLAY
00391     overlay = XCompositeGetOverlayWindow( display(), rootWindow());
00392     if( overlay == None )
00393         return false;
00394     return true;
00395 #else
00396     return false;
00397 #endif
00398     }
00399 
00400 void Workspace::setupOverlay( Window w )
00401     {
00402     assert( overlay != None );
00403     assert( Extensions::shapeInputAvailable());
00404     XShapeCombineRectangles( display(), overlay, ShapeInput, 0, 0, NULL, 0, ShapeSet, Unsorted );
00405     if( w != None )
00406         XShapeCombineRectangles( display(), w, ShapeInput, 0, 0, NULL, 0, ShapeSet, Unsorted );
00407     XSelectInput( display(), overlay, VisibilityChangeMask );
00408     }
00409 
00410 void Workspace::showOverlay()
00411     {
00412     assert( overlay != None );
00413     if( overlay_shown )
00414         return;
00415     XMapSubwindows( display(), overlay );
00416     XMapWindow( display(), overlay );
00417     overlay_shown = true;
00418     }
00419 
00420 void Workspace::destroyOverlay()
00421     {
00422     if( overlay == None )
00423         return;
00424 #ifdef HAVE_XCOMPOSITE_OVERLAY
00425     XCompositeReleaseOverlayWindow( display(), overlay );
00426 #endif
00427     overlay = None;
00428     overlay_shown = false;
00429     }
00430 
00431 //****************************************
00432 // Toplevel
00433 //****************************************
00434 
00435 void Toplevel::setupCompositing()
00436     {
00437 #ifdef KWIN_HAVE_COMPOSITING
00438     if( !compositing())
00439         return;
00440     if( damage_handle != None )
00441         return;
00442     damage_handle = XDamageCreate( display(), frameId(), XDamageReportRawRectangles );
00443     damage_region = QRegion( 0, 0, width(), height());
00444     effect_window = new EffectWindowImpl();
00445     effect_window->setWindow( this );
00446 #endif
00447     }
00448 
00449 void Toplevel::finishCompositing()
00450     {
00451 #ifdef KWIN_HAVE_COMPOSITING
00452     if( damage_handle == None )
00453         return;
00454     if( effect_window->window() == this ) // otherwise it's already passed to Deleted, don't free data
00455         {
00456         discardWindowPixmap();
00457         delete effect_window;
00458         }
00459     XDamageDestroy( display(), damage_handle );
00460     damage_handle = None;
00461     damage_region = QRegion();
00462     repaints_region = QRegion();
00463     effect_window = NULL;
00464 #endif
00465     }
00466 
00467 void Toplevel::discardWindowPixmap()
00468     {
00469     addDamageFull();
00470     if( window_pix == None )
00471         return;
00472     XFreePixmap( display(), window_pix );
00473     window_pix = None;
00474     if( effectWindow() != NULL && effectWindow()->sceneWindow() != NULL )
00475         effectWindow()->sceneWindow()->pixmapDiscarded();
00476     }
00477 
00478 Pixmap Toplevel::createWindowPixmap()
00479     {
00480 #ifdef KWIN_HAVE_COMPOSITING
00481     assert( compositing());
00482     grabXServer();
00483     KXErrorHandler err;
00484     window_pix = XCompositeNameWindowPixmap( display(), frameId());
00485     // check that the received pixmap is valid and actually matches what we
00486     // know about the window (i.e. size)
00487     XWindowAttributes attrs;
00488     if( !XGetWindowAttributes( display(), frameId(), &attrs )
00489         || err.error( false )
00490         || attrs.width != width() || attrs.height != height() || attrs.map_state != IsViewable )
00491         {
00492         kDebug( 1212 ) << "Creating window pixmap failed: " << this;
00493         XFreePixmap( display(), window_pix );
00494         window_pix = None;
00495         }
00496     ungrabXServer();
00497     return window_pix;
00498 #else
00499     return None;
00500 #endif
00501     }
00502 
00503 #ifdef HAVE_XDAMAGE
00504 void Toplevel::damageNotifyEvent( XDamageNotifyEvent* e )
00505     {
00506     QRegion damage( e->area.x, e->area.y, e->area.width, e->area.height );
00507     // compress
00508     int cnt = 1;
00509     while( XPending( display()))
00510         {
00511         XEvent e2;
00512         if( XPeekEvent( display(), &e2 ) && e2.type == Extensions::damageNotifyEvent()
00513             && e2.xany.window == frameId())
00514             {
00515             XNextEvent( display(), &e2 );
00516             if( cnt > 200 )
00517                 {
00518                 // If there are way too many damage events in the queue, just discard them
00519                 // and damage the whole window. Otherwise the X server can just overload
00520                 // us with a flood of damage events. Should be probably optimized
00521                 // in the X server, as this is rather lame.
00522                 damage = rect();
00523                 continue;
00524                 }
00525             XDamageNotifyEvent* e = reinterpret_cast< XDamageNotifyEvent* >( &e2 );
00526             QRect r( e->area.x, e->area.y, e->area.width, e->area.height );
00527             ++cnt;
00528             // If there are too many damaged rectangles, increase them
00529             // to be multiples of 100x100 px grid, since QRegion get quite
00530             // slow with many rectangles, and there is little to gain by using
00531             // many small rectangles (rather the opposite, several large should
00532             // be often faster).
00533             if( cnt > 50 )
00534                 {
00535                 r.setLeft( r.left() / 100 * 100 );
00536                 r.setRight(( r.right() + 99 ) / 100 * 100 );
00537                 r.setTop( r.top() / 100 * 100 );
00538                 r.setBottom(( r.bottom() + 99 ) / 100 * 100 );
00539                 }
00540             damage += r;
00541             continue;
00542             }
00543         break;
00544         }
00545     foreach( QRect r, damage.rects())
00546         addDamage( r ); 
00547     }
00548 
00549 void Client::damageNotifyEvent( XDamageNotifyEvent* e )
00550     {
00551     Toplevel::damageNotifyEvent( e );
00552 #ifdef HAVE_XSYNC
00553     if( sync_counter == None ) // cannot detect complete redraw, consider done now
00554         ready_for_painting = true;
00555 #else
00556     ready_for_painting = true; // no sync at all, consider done now
00557 #endif
00558     }
00559 #endif
00560 
00561 void Toplevel::addDamage( const QRect& r )
00562     {
00563     addDamage( r.x(), r.y(), r.width(), r.height());
00564     }
00565 
00566 void Toplevel::addDamage( int x, int y, int w, int h )
00567     {
00568     if( !compositing())
00569         return;
00570     QRect r( x, y, w, h );
00571     // resizing the decoration may lag behind a bit and when shrinking there
00572     // may be a damage event coming with size larger than the current window size
00573     r &= rect();
00574     damage_region += r;
00575     repaints_region += r;
00576     static_cast<EffectsHandlerImpl*>(effects)->windowDamaged( effectWindow(), r );
00577     }
00578 
00579 void Toplevel::addDamageFull()
00580     {
00581     if( !compositing())
00582         return;
00583     damage_region = rect();
00584     repaints_region = rect();
00585     static_cast<EffectsHandlerImpl*>(effects)->windowDamaged( effectWindow(), rect());
00586     }
00587 
00588 void Toplevel::resetDamage( const QRect& r )
00589     {
00590     damage_region -= r;
00591     }
00592 
00593 void Toplevel::addRepaint( const QRect& r )
00594     {
00595     addRepaint( r.x(), r.y(), r.width(), r.height());
00596     }
00597 
00598 void Toplevel::addRepaint( int x, int y, int w, int h )
00599     {
00600     if( !compositing())
00601         return;
00602     QRect r( x, y, w, h );
00603     r &= rect();
00604     repaints_region += r;
00605     }
00606 
00607 void Toplevel::addRepaintFull()
00608     {
00609     repaints_region = rect();
00610     }
00611 
00612 void Toplevel::resetRepaints( const QRect& r )
00613     {
00614     repaints_region -= r;
00615     }
00616 
00617 void Toplevel::addWorkspaceRepaint( int x, int y, int w, int h )
00618     {
00619     addWorkspaceRepaint( QRect( x, y, w, h ));
00620     }
00621 
00622 void Toplevel::addWorkspaceRepaint( const QRect& r2 )
00623     {
00624     if( !compositing())
00625         return;
00626     if( effectWindow() == NULL ) // TODO - this can happen during window destruction
00627         return workspace()->addRepaint( r2 );
00628     QRect r = effects->transformWindowDamage( effectWindow(), r2 );
00629     workspace()->addRepaint( r );
00630     }
00631 
00632 } // 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
  •   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