• Skip to content
  • Skip to link menu
KDE 4.2 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 #include "notifications.h"
00056 
00057 #include <stdio.h>
00058 
00059 #include <QMenu>
00060 #include <kaction.h>
00061 #include <kactioncollection.h>
00062 #include <klocale.h>
00063 #include <kxerrorhandler.h>
00064 
00065 #include <X11/extensions/shape.h>
00066 
00067 #ifdef HAVE_XCOMPOSITE
00068 #include <X11/extensions/Xcomposite.h>
00069 #if XCOMPOSITE_MAJOR > 0 || XCOMPOSITE_MINOR >= 3
00070 #define HAVE_XCOMPOSITE_OVERLAY
00071 #endif
00072 #endif
00073 #ifdef HAVE_XRANDR
00074 #include <X11/extensions/Xrandr.h>
00075 #endif
00076 
00077 namespace KWin
00078 {
00079 
00080 //****************************************
00081 // Workspace
00082 //****************************************
00083 
00084 void Workspace::setupCompositing()
00085     {
00086 #ifdef KWIN_HAVE_COMPOSITING
00087     if( scene != NULL )
00088         return;
00089     if( !options->useCompositing && getenv( "KWIN_COMPOSE") == NULL )
00090         {
00091         kDebug( 1212 ) << "Compositing is turned off in options or disabled";
00092         return;
00093         }
00094     else if( compositingSuspended )
00095         {
00096         kDebug( 1212 ) << "Compositing is suspended";
00097         return;
00098         }
00099     else if( !CompositingPrefs::compositingPossible() )
00100         {
00101         kError( 1212 ) << "Compositing is not possible";
00102         return;
00103         }
00104     CompositingType type = options->compositingMode;
00105     if( getenv( "KWIN_COMPOSE" ))
00106         {
00107         char c = getenv( "KWIN_COMPOSE" )[ 0 ];
00108         switch( c )
00109             {
00110             case 'O':
00111                 type = OpenGLCompositing;
00112                 break;
00113             case 'X':
00114                 type = XRenderCompositing;
00115                 break;
00116             default:
00117                 kDebug( 1212 ) << "No compositing";
00118                 return;
00119             }
00120         }
00121 
00122     char selection_name[ 100 ];
00123     sprintf( selection_name, "_NET_WM_CM_S%d", DefaultScreen( display()));
00124     cm_selection = new KSelectionOwner( selection_name );
00125     connect( cm_selection, SIGNAL( lostOwnership()), SLOT( lostCMSelection()));
00126     cm_selection->claim( true ); // force claiming
00127 
00128     switch( type )
00129         {
00130         /*case 'B':
00131             kDebug( 1212 ) << "X compositing";
00132             scene = new SceneBasic( this );
00133           break; // don't fall through (this is a testing one) */
00134 #ifdef KWIN_HAVE_OPENGL_COMPOSITING
00135         case OpenGLCompositing:
00136             kDebug( 1212 ) << "OpenGL compositing";
00137             scene = new SceneOpenGL( this );
00138             if( !scene->initFailed())
00139                 break; // -->
00140             delete scene;
00141             scene = NULL;
00142             break; // do not fall back to XRender for now, maybe in the future
00143 #endif
00144 #ifdef KWIN_HAVE_XRENDER_COMPOSITING
00145         case XRenderCompositing:
00146             kDebug( 1212 ) << "XRender compositing";
00147             scene = new SceneXrender( this );
00148           break;
00149 #endif
00150         default:
00151 #ifndef KWIN_HAVE_COMPOSITING
00152             kDebug( 1212 ) << "Compositing was not available at compile time";
00153 #else
00154             kDebug( 1212 ) << "No compositing";
00155 #endif
00156             delete cm_selection;
00157           return;
00158         }
00159     if( scene == NULL || scene->initFailed())
00160         {
00161         kError( 1212 ) << "Failed to initialize compositing, compositing disabled";
00162         kError( 1212 ) << "Consult http://techbase.kde.org/Projects/KWin/4.0-release-notes#Setting_up";
00163         delete scene;
00164         scene = NULL;
00165         delete cm_selection;
00166         return;
00167         }
00168     int rate = 0;
00169     if( options->refreshRate > 0 )
00170         { // use manually configured refresh rate
00171         rate = options->refreshRate;
00172         }
00173 #ifdef HAVE_XRANDR
00174     else
00175         { // autoconfigure refresh rate based on XRandR info
00176         if( Extensions::randrAvailable() )
00177             {
00178             XRRScreenConfiguration *config;
00179 
00180             config = XRRGetScreenInfo( display(), rootWindow() );
00181             rate = XRRConfigCurrentRate( config );
00182             XRRFreeScreenConfigInfo( config );
00183             }
00184         }
00185 #endif
00186     // 0Hz or less is invalid, so we fallback to a default rate
00187     if( rate <= 0 )
00188         rate = 50;
00189     // QTimer gives us 1msec (1000Hz) at best, so we ignore anything higher;
00190     // however, additional throttling prevents very high rates from taking place anyway
00191     else if( rate > 1000 )
00192         rate = 1000;
00193     kDebug( 1212 ) << "Refresh rate " << rate << "Hz";
00194     compositeRate = 1000 / rate;
00195     lastCompositePaint.start();
00196     // fake a previous paint, so that the next starts right now
00197     nextPaintReference = QTime::currentTime().addMSecs( -compositeRate );
00198     compositeTimer.setSingleShot( true );
00199     checkCompositeTimer();
00200     composite_paint_times.clear();
00201     XCompositeRedirectSubwindows( display(), rootWindow(), CompositeRedirectManual );
00202     new EffectsHandlerImpl( scene->compositingType() ); // sets also the 'effects' pointer
00203     addRepaintFull();
00204     foreach( Client* c, clients )
00205         c->setupCompositing();
00206     foreach( Client* c, desktops )
00207         c->setupCompositing();
00208     foreach( Unmanaged* c, unmanaged )
00209         c->setupCompositing();
00210     foreach( Client* c, clients )
00211         scene->windowAdded( c );
00212     foreach( Client* c, desktops )
00213         scene->windowAdded( c );
00214     foreach( Unmanaged* c, unmanaged )
00215         scene->windowAdded( c );
00216     discardPopup(); // force re-creation of the Alt+F3 popup (opacity option)
00217 #else
00218     kDebug( 1212 ) << "Compositing was not available at compile time";
00219 #endif
00220     }
00221 
00222 void Workspace::finishCompositing()
00223     {
00224 #ifdef KWIN_HAVE_COMPOSITING
00225     if( scene == NULL )
00226         return;
00227     delete cm_selection;
00228     foreach( Client* c, clients )
00229         scene->windowClosed( c, NULL );
00230     foreach( Client* c, desktops )
00231         scene->windowClosed( c, NULL );
00232     foreach( Unmanaged* c, unmanaged )
00233         scene->windowClosed( c, NULL );
00234     foreach( Deleted* c, deleted )
00235         scene->windowDeleted( c );
00236     foreach( Client* c, clients )
00237         c->finishCompositing();
00238     foreach( Client* c, desktops )
00239         c->finishCompositing();
00240     foreach( Unmanaged* c, unmanaged )
00241         c->finishCompositing();
00242     foreach( Deleted* c, deleted )
00243         c->finishCompositing();
00244     XCompositeUnredirectSubwindows( display(), rootWindow(), CompositeRedirectManual );
00245     delete effects;
00246     effects = NULL;
00247     delete scene;
00248     scene = NULL;
00249     compositeTimer.stop();
00250     repaints_region = QRegion();
00251     for( ClientList::ConstIterator it = clients.constBegin();
00252          it != clients.constEnd();
00253          ++it )
00254         { // forward all opacity values to the frame in case there'll be other CM running
00255         if( (*it)->opacity() != 1.0 )
00256             {
00257             NETWinInfo2 i( display(), (*it)->frameId(), rootWindow(), 0 );
00258             i.setOpacity( static_cast< unsigned long >((*it)->opacity() * 0xffffffff ));
00259             }
00260         }
00261     discardPopup(); // force re-creation of the Alt+F3 popup (opacity option)
00262     // discard all Deleted windows (#152914)
00263     while( !deleted.isEmpty())
00264         deleted.first()->discard( Allowed );
00265 #endif
00266     }
00267 
00268 void Workspace::lostCMSelection()
00269     {
00270     kDebug( 1212 ) << "Lost compositing manager selection";
00271     finishCompositing();
00272     }
00273 
00274 // for the shortcut
00275 void Workspace::slotToggleCompositing()
00276     {
00277     suspendCompositing( !compositingSuspended );
00278     }
00279     
00280 void Workspace::suspendCompositing()
00281     {
00282     suspendCompositing( true );
00283     }
00284 
00285 void Workspace::suspendCompositing( bool suspend )
00286     {
00287     compositingSuspended = suspend;
00288     finishCompositing();
00289     setupCompositing(); // will do nothing if suspended
00290     }
00291 
00292 void Workspace::addRepaint( int x, int y, int w, int h )
00293     {
00294     if( !compositing())
00295         return;
00296     repaints_region += QRegion( x, y, w, h );
00297     checkCompositeTimer();
00298     }
00299 
00300 void Workspace::addRepaint( const QRect& r )
00301     {
00302     if( !compositing())
00303         return;
00304     repaints_region += r;
00305     checkCompositeTimer();
00306     }
00307     
00308 void Workspace::addRepaint( const QRegion& r )
00309     {
00310     if( !compositing())
00311         return;
00312     repaints_region += r;
00313     checkCompositeTimer();
00314     }
00315     
00316 void Workspace::addRepaintFull()
00317     {
00318     if( !compositing())
00319         return;
00320     repaints_region = QRegion( 0, 0, displayWidth(), displayHeight());
00321     checkCompositeTimer();
00322     }
00323 
00324 void Workspace::performCompositing()
00325     {
00326 #ifdef KWIN_HAVE_COMPOSITING
00327     // The event loop apparently tries to fire a QTimer as often as possible, even
00328     // at the expense of not processing many X events. This means that the composite
00329     // repaints can seriously impact performance of everything else, therefore throttle
00330     // them - leave at least 1msec time after one repaint is finished and next one
00331     // is started.
00332     if( lastCompositePaint.elapsed() < 1 )
00333         {
00334         compositeTimer.start( 1 );
00335         return;
00336         }
00337     if( !scene->waitSyncAvailable())
00338         nextPaintReference = QTime::currentTime();
00339     checkCursorPos();
00340     if((( repaints_region.isEmpty() && !windowRepaintsPending()) // no damage
00341         || !overlay_visible ) // nothing is visible anyway
00342         // HACK: don't idle during active full screen effect so that mouse events are not dropped (bug #177226)
00343         && !static_cast< EffectsHandlerImpl* >( effects )->activeFullScreenEffect() )
00344         {
00345         scene->idle();
00346         // Note: It would seem here we should undo suspended unredirect, but when scenes need
00347         // it for some reason, e.g. transformations or translucency, the next pass that does not
00348         // need this anymore and paints normally will also reset the suspended unredirect.
00349         // Otherwise the window would not be painted normally anyway.
00350         return;
00351         }
00352     // create a list of all windows in the stacking order
00353     ToplevelList windows = xStackingOrder();
00354     foreach( EffectWindow* c, static_cast< EffectsHandlerImpl* >( effects )->elevatedWindows())
00355         {
00356         Toplevel* t = static_cast< EffectWindowImpl* >( c )->window();
00357         windows.removeAll( t );
00358         windows.append( t );
00359         }
00360     // skip windows that are not yet ready for being painted
00361     ToplevelList tmp = windows;
00362     windows.clear();
00363 #if 0
00364     // There is a bug somewhere that prevents this from working properly (#160393), but additionally
00365     // this cannot be used so carelessly - needs protections against broken clients, the window
00366     // should not get focus before it's displayed, handle unredirected windows properly and so on.
00367     foreach( Toplevel* c, tmp )
00368         if( c->readyForPainting())
00369             windows.append( c );
00370 #else
00371     foreach( Toplevel* c, tmp )
00372         windows.append( c );
00373 #endif
00374     foreach( Toplevel* c, windows )
00375         { // This could be possibly optimized WRT obscuring, but that'd need being already
00376           // past prePaint() phase - probably not worth it.
00377           // TODO I think effects->transformWindowDamage() doesn't need to be called here,
00378           // pre-paint will extend painted window areas as necessary.
00379         repaints_region |= c->repaints().translated( c->pos());
00380         c->resetRepaints( c->rect());
00381         }
00382     QRegion repaints = repaints_region;
00383     // clear all repaints, so that post-pass can add repaints for the next repaint
00384     repaints_region = QRegion();
00385     QTime t = QTime::currentTime();
00386     scene->paint( repaints, windows );
00387     if( scene->waitSyncAvailable())
00388         {
00389         // If vsync is used, schedule the next repaint slightly in advance of the next sync,
00390         // so that there is still time for the drawing to take place. We have just synced, and
00391         // nextPaintReference is time from which multiples of compositeRate should be added,
00392         // so set it 10ms back (meaning next paint will be in 'compositeRate - 10').
00393         // However, make sure the reserve is smaller than the composite rate.
00394         int reserve = compositeRate <= 10 ? compositeRate - 1 : 10;
00395         nextPaintReference = QTime::currentTime().addMSecs( -reserve );
00396         }
00397     // Trigger at least one more pass even if there would be nothing to paint, so that scene->idle()
00398     // is called the next time. If there would be nothing pending, it will not restart the timer and
00399     // checkCompositeTime() would restart it again somewhen later, called from functions that
00400     // would again add something pending.
00401     checkCompositeTimer();
00402     checkCompositePaintTime( t.elapsed());
00403     lastCompositePaint.start();
00404 #endif
00405     }
00406 
00407 bool Workspace::windowRepaintsPending() const
00408     {
00409     foreach( Toplevel* c, clients )
00410         if( !c->repaints().isEmpty())
00411             return true;
00412     foreach( Toplevel* c, desktops )
00413         if( !c->repaints().isEmpty())
00414             return true;
00415     foreach( Toplevel* c, unmanaged )
00416         if( !c->repaints().isEmpty())
00417             return true;
00418     foreach( Toplevel* c, deleted )
00419         if( !c->repaints().isEmpty())
00420             return true;
00421     return false;
00422     }
00423 
00424 void Workspace::setCompositeTimer()
00425     {
00426     if( !compositing()) // should not really happen, but there may be e.g. some damage events still pending
00427         return;
00428     // The last paint set nextPaintReference as a reference time to which multiples of compositeRate
00429     // should be added for the next paint. qBound() for protection; system time can change without notice.
00430     compositeTimer.start( qBound( 0, nextPaintReference.msecsTo( QTime::currentTime() ), 250 ) % compositeRate );
00431     }
00432 
00433 bool Workspace::createOverlay()
00434     {
00435     assert( overlay == None );
00436     if( !Extensions::compositeOverlayAvailable())
00437         return false;
00438     if( !Extensions::shapeInputAvailable()) // needed in setupOverlay()
00439         return false;
00440 #ifdef HAVE_XCOMPOSITE_OVERLAY
00441     overlay = XCompositeGetOverlayWindow( display(), rootWindow());
00442     if( overlay == None )
00443         return false;
00444     return true;
00445 #else
00446     return false;
00447 #endif
00448     }
00449 
00450 void Workspace::checkCompositePaintTime( int msec )
00451     {
00452     if( options->disableCompositingChecks )
00453         return;
00454     composite_paint_times.prepend( msec );
00455     bool tooslow = false;
00456     // If last 3 paints were way too slow, disable and warn.
00457     // 1 second seems reasonable, it's not that difficult to get relatively high times
00458     // with high system load.
00459     const int MAX_LONG_PAINT = 1000;
00460     if( composite_paint_times.count() >= 3 && composite_paint_times[ 0 ] > MAX_LONG_PAINT
00461         && composite_paint_times[ 1 ] > MAX_LONG_PAINT && composite_paint_times[ 2 ] > MAX_LONG_PAINT )
00462         {
00463         kDebug( 1212 ) << "Too long paint times, suspending";
00464         tooslow = true;
00465         }
00466     // If last 15 seconds all paints (all of them) were quite slow, disable and warn too. Quite slow being 0,1s
00467     // should be reasonable, that's 10fps and having constant 10fps is bad.
00468     // This may possibly trigger also when activating an expensive effect, so this may need tweaking.
00469     const int MAX_SHORT_PAINT = 100;
00470     const int SHORT_TIME = 15000; // 15 sec
00471     int time = 0;
00472     foreach( int t, composite_paint_times )
00473         {
00474         if( t < MAX_SHORT_PAINT )
00475             break;
00476         time += t;
00477         if( time > SHORT_TIME ) // all paints in the given time were long
00478             {
00479             kDebug( 1212 ) << "Long paint times for long time, suspending";
00480             tooslow = true;
00481             break;
00482             }
00483         }
00484     if( composite_paint_times.count() > 1000 )
00485         composite_paint_times.removeLast();
00486     if( tooslow )
00487         {
00488         QTimer::singleShot( 0, this, SLOT( suspendCompositing()));
00489         QString shortcut, message;
00490         if( KAction* action = qobject_cast<KAction*>( keys->action("Suspend Compositing")))
00491             shortcut = action->globalShortcut().primary().toString(QKeySequence::NativeText);
00492         if (shortcut.isEmpty())
00493             message = i18n( "Compositing was too slow and has been suspended.\n"
00494                 "You can disable functionality checks in advanced compositing settings." );
00495         else
00496             message = i18n( "Compositing was too slow and has been suspended.\n"
00497                 "If this was only a temporary problem, you can resume using the '%1' shortcut.\n"
00498                 "You can also disable functionality checks in advanced compositing settings.", shortcut );
00499         Notify::raise( Notify::CompositingSlow, message );
00500         compositeTimer.start( 1000 ); // so that it doesn't trigger sooner than suspendCompositing()
00501         }
00502     }
00503 
00504 void Workspace::setupOverlay( Window w )
00505     {
00506     assert( overlay != None );
00507     assert( Extensions::shapeInputAvailable());
00508     XSetWindowBackgroundPixmap( display(), overlay, None );
00509     overlay_shape = QRegion();
00510     setOverlayShape( QRect( 0, 0, displayWidth(), displayHeight()));
00511     if( w != None )
00512         {
00513         XSetWindowBackgroundPixmap( display(), w, None );
00514         XShapeCombineRectangles( display(), w, ShapeInput, 0, 0, NULL, 0, ShapeSet, Unsorted );
00515         }
00516     XSelectInput( display(), overlay, VisibilityChangeMask );
00517     }
00518 
00519 void Workspace::showOverlay()
00520     {
00521     assert( overlay != None );
00522     if( overlay_shown )
00523         return;
00524     XMapSubwindows( display(), overlay );
00525     XMapWindow( display(), overlay );
00526     overlay_shown = true;
00527     }
00528 
00529 void Workspace::hideOverlay()
00530     {
00531     assert( overlay != None );
00532     XUnmapWindow( display(), overlay );
00533     overlay_shown = false;
00534     setOverlayShape( QRect( 0, 0, displayWidth(), displayHeight()));
00535     }
00536 
00537 void Workspace::setOverlayShape( const QRegion& reg )
00538     {
00539     // Avoid setting the same shape again, it causes flicker (apparently it is not a no-op
00540     // and triggers something).
00541     if( reg == overlay_shape )
00542         return;
00543     QVector< QRect > rects = reg.rects();
00544     XRectangle* xrects = new XRectangle[ rects.count() ];
00545     for( int i = 0;
00546          i < rects.count();
00547          ++i )
00548         {
00549         xrects[ i ].x = rects[ i ].x();
00550         xrects[ i ].y = rects[ i ].y();
00551         xrects[ i ].width = rects[ i ].width();
00552         xrects[ i ].height = rects[ i ].height();
00553         }
00554     XShapeCombineRectangles( display(), overlay, ShapeBounding, 0, 0,
00555         xrects, rects.count(), ShapeSet, Unsorted );
00556     delete[] xrects;
00557     XShapeCombineRectangles( display(), overlay, ShapeInput, 0, 0, NULL, 0, ShapeSet, Unsorted );
00558     overlay_shape = reg;
00559     }
00560 
00561 void Workspace::destroyOverlay()
00562     {
00563     if( overlay == None )
00564         return;
00565     // reset the overlay shape
00566     XRectangle rec = { 0, 0, displayWidth(), displayHeight() };
00567     XShapeCombineRectangles( display(), overlay, ShapeBounding, 0, 0, &rec, 1, ShapeSet, Unsorted );
00568     XShapeCombineRectangles( display(), overlay, ShapeInput, 0, 0, &rec, 1, ShapeSet, Unsorted );
00569 #ifdef HAVE_XCOMPOSITE_OVERLAY
00570     XCompositeReleaseOverlayWindow( display(), overlay );
00571 #endif
00572     overlay = None;
00573     overlay_shown = false;
00574     }
00575 
00576 bool Workspace::compositingActive()
00577     {
00578     return compositing();
00579     }
00580 
00581 // force is needed when the list of windows changes (e.g. a window goes away)
00582 void Workspace::checkUnredirect( bool force )
00583     {
00584     if( !compositing() || overlay == None || !options->unredirectFullscreen )
00585         return;
00586     if( force )
00587         forceUnredirectCheck = true;
00588     if( !unredirectTimer.isActive())
00589         unredirectTimer.start( 0 );
00590     }
00591 
00592 void Workspace::delayedCheckUnredirect()
00593     {
00594     if( !compositing() || overlay == None || !options->unredirectFullscreen )
00595         return;
00596     ToplevelList list;
00597     bool changed = forceUnredirectCheck;
00598     foreach( Client* c, clients )
00599         list.append( c );
00600     foreach( Unmanaged* c, unmanaged )
00601         list.append( c );
00602     foreach( Toplevel* c, list )
00603         {
00604         if( c->updateUnredirectedState())
00605             changed = true;
00606         }
00607     // no desktops, no Deleted ones
00608     if( !changed )
00609         return;
00610     forceUnredirectCheck = false;
00611     // Cut out parts from the overlay window where unredirected windows are,
00612     // so that they are actually visible.
00613     QRegion reg( 0, 0, displayWidth(), displayHeight());
00614     foreach( Toplevel* c, list )
00615         {
00616         if( c->unredirected())
00617             reg -= c->geometry();
00618         }
00619     setOverlayShape( reg );
00620     }
00621 
00622 //****************************************
00623 // Toplevel
00624 //****************************************
00625 
00626 void Toplevel::setupCompositing()
00627     {
00628 #ifdef KWIN_HAVE_COMPOSITING
00629     if( !compositing())
00630         return;
00631     if( damage_handle != None )
00632         return;
00633     damage_handle = XDamageCreate( display(), frameId(), XDamageReportRawRectangles );
00634     damage_region = QRegion( 0, 0, width(), height());
00635     effect_window = new EffectWindowImpl();
00636     effect_window->setWindow( this );
00637     unredirect = false;
00638     workspace()->checkUnredirect( true );
00639 #endif
00640     }
00641 
00642 void Toplevel::finishCompositing()
00643     {
00644 #ifdef KWIN_HAVE_COMPOSITING
00645     if( damage_handle == None )
00646         return;
00647     workspace()->checkUnredirect( true );
00648     if( effect_window->window() == this ) // otherwise it's already passed to Deleted, don't free data
00649         {
00650         discardWindowPixmap();
00651         delete effect_window;
00652         }
00653     XDamageDestroy( display(), damage_handle );
00654     damage_handle = None;
00655     damage_region = QRegion();
00656     repaints_region = QRegion();
00657     effect_window = NULL;
00658 #endif
00659     }
00660 
00661 void Toplevel::discardWindowPixmap()
00662     {
00663     addDamageFull();
00664     if( window_pix == None )
00665         return;
00666     XFreePixmap( display(), window_pix );
00667     window_pix = None;
00668     if( effectWindow() != NULL && effectWindow()->sceneWindow() != NULL )
00669         effectWindow()->sceneWindow()->pixmapDiscarded();
00670     }
00671 
00672 Pixmap Toplevel::createWindowPixmap()
00673     {
00674 #ifdef KWIN_HAVE_COMPOSITING
00675     assert( compositing());
00676     if( unredirected())
00677         return None;
00678     grabXServer();
00679     KXErrorHandler err;
00680     Pixmap pix = XCompositeNameWindowPixmap( display(), frameId());
00681     // check that the received pixmap is valid and actually matches what we
00682     // know about the window (i.e. size)
00683     XWindowAttributes attrs;
00684     if( !XGetWindowAttributes( display(), frameId(), &attrs )
00685         || err.error( false )
00686         || attrs.width != width() || attrs.height != height() || attrs.map_state != IsViewable )
00687         {
00688         kDebug( 1212 ) << "Creating window pixmap failed: " << this;
00689         XFreePixmap( display(), pix );
00690         pix = None;
00691         }
00692     ungrabXServer();
00693     return pix;
00694 #else
00695     return None;
00696 #endif
00697     }
00698 
00699 #ifdef HAVE_XDAMAGE
00700 void Toplevel::damageNotifyEvent( XDamageNotifyEvent* e )
00701     {
00702     QRegion damage( e->area.x, e->area.y, e->area.width, e->area.height );
00703     // compress
00704     int cnt = 1;
00705     while( XPending( display()))
00706         {
00707         XEvent e2;
00708         if( XPeekEvent( display(), &e2 ) && e2.type == Extensions::damageNotifyEvent()
00709             && e2.xany.window == frameId())
00710             {
00711             XNextEvent( display(), &e2 );
00712             if( cnt > 200 )
00713                 {
00714                 // If there are way too many damage events in the queue, just discard them
00715                 // and damage the whole window. Otherwise the X server can just overload
00716                 // us with a flood of damage events. Should be probably optimized
00717                 // in the X server, as this is rather lame.
00718                 damage = rect();
00719                 continue;
00720                 }
00721             XDamageNotifyEvent* e = reinterpret_cast< XDamageNotifyEvent* >( &e2 );
00722             QRect r( e->area.x, e->area.y, e->area.width, e->area.height );
00723             ++cnt;
00724             // If there are too many damaged rectangles, increase them
00725             // to be multiples of 100x100 px grid, since QRegion get quite
00726             // slow with many rectangles, and there is little to gain by using
00727             // many small rectangles (rather the opposite, several large should
00728             // be often faster).
00729             if( cnt > 50 )
00730                 {
00731                 r.setLeft( r.left() / 100 * 100 );
00732                 r.setRight(( r.right() + 99 ) / 100 * 100 );
00733                 r.setTop( r.top() / 100 * 100 );
00734                 r.setBottom(( r.bottom() + 99 ) / 100 * 100 );
00735                 }
00736             damage += r;
00737             continue;
00738             }
00739         break;
00740         }
00741     foreach( const QRect& r, damage.rects())
00742         addDamage( r ); 
00743     }
00744 
00745 void Client::damageNotifyEvent( XDamageNotifyEvent* e )
00746     {
00747     Toplevel::damageNotifyEvent( e );
00748 #ifdef HAVE_XSYNC
00749     if( sync_counter == None ) // cannot detect complete redraw, consider done now
00750         ready_for_painting = true;
00751 #else
00752     ready_for_painting = true; // no sync at all, consider done now
00753 #endif
00754     }
00755 #endif
00756 
00757 void Toplevel::addDamage( const QRect& r )
00758     {
00759     addDamage( r.x(), r.y(), r.width(), r.height());
00760     }
00761 
00762 void Toplevel::addDamage( int x, int y, int w, int h )
00763     {
00764     if( !compositing())
00765         return;
00766     QRect r( x, y, w, h );
00767     // resizing the decoration may lag behind a bit and when shrinking there
00768     // may be a damage event coming with size larger than the current window size
00769     r &= rect();
00770     damage_region += r;
00771     repaints_region += r;
00772     static_cast<EffectsHandlerImpl*>(effects)->windowDamaged( effectWindow(), r );
00773     workspace()->checkCompositeTimer();
00774     }
00775 
00776 void Toplevel::addDamageFull()
00777     {
00778     if( !compositing())
00779         return;
00780     damage_region = rect();
00781     repaints_region = rect();
00782     static_cast<EffectsHandlerImpl*>(effects)->windowDamaged( effectWindow(), rect());
00783     workspace()->checkCompositeTimer();
00784     }
00785 
00786 void Toplevel::resetDamage( const QRect& r )
00787     {
00788     damage_region -= r;
00789     }
00790 
00791 void Toplevel::addRepaint( const QRect& r )
00792     {
00793     addRepaint( r.x(), r.y(), r.width(), r.height());
00794     }
00795 
00796 void Toplevel::addRepaint( int x, int y, int w, int h )
00797     {
00798     if( !compositing())
00799         return;
00800     QRect r( x, y, w, h );
00801     r &= rect();
00802     repaints_region += r;
00803     workspace()->checkCompositeTimer();
00804     }
00805 
00806 void Toplevel::addRepaintFull()
00807     {
00808     repaints_region = rect();
00809     workspace()->checkCompositeTimer();
00810     }
00811 
00812 void Toplevel::resetRepaints( const QRect& r )
00813     {
00814     repaints_region -= r;
00815     }
00816 
00817 void Toplevel::addWorkspaceRepaint( int x, int y, int w, int h )
00818     {
00819     addWorkspaceRepaint( QRect( x, y, w, h ));
00820     }
00821 
00822 void Toplevel::addWorkspaceRepaint( const QRect& r2 )
00823     {
00824     if( !compositing())
00825         return;
00826     if( effectWindow() == NULL ) // TODO - this can happen during window destruction
00827         return workspace()->addRepaint( r2 );
00828     QRect r = effects->transformWindowDamage( effectWindow(), r2 );
00829     workspace()->addRepaint( r );
00830     }
00831 
00832 bool Toplevel::updateUnredirectedState()
00833     {
00834     assert( compositing());
00835     bool should = shouldUnredirect() && !unredirectSuspend && !shape() && !hasAlpha() && opacity() == 1.0 &&
00836         !static_cast<EffectsHandlerImpl*>( effects )->activeFullScreenEffect();
00837     if( should && !unredirect )
00838         {
00839         unredirect = true;
00840         kDebug( 1212 ) << "Unredirecting:" << this;
00841 #ifdef HAVE_XCOMPOSITE
00842         XCompositeUnredirectWindow( display(), frameId(), CompositeRedirectManual );
00843 #endif
00844         return true;
00845         }
00846     else if( !should && unredirect )
00847         {
00848         unredirect = false;
00849         kDebug( 1212 ) << "Redirecting:" << this;
00850 #ifdef HAVE_XCOMPOSITE
00851         XCompositeRedirectWindow( display(), frameId(), CompositeRedirectManual );
00852 #endif
00853         discardWindowPixmap();
00854         return true;
00855         }
00856     return false;
00857     }
00858 
00859 void Toplevel::suspendUnredirect( bool suspend )
00860     {
00861     if( unredirectSuspend == suspend )
00862         return;
00863     unredirectSuspend = suspend;
00864     workspace()->checkUnredirect();
00865     }
00866 
00867 //****************************************
00868 // Client
00869 //****************************************
00870 
00871 void Client::setupCompositing()
00872     {
00873     Toplevel::setupCompositing();
00874     updateVisibility(); // for internalKeep()
00875     }
00876 
00877 void Client::finishCompositing()
00878     {
00879     Toplevel::finishCompositing();
00880     updateVisibility();
00881     }
00882 
00883 bool Client::shouldUnredirect() const
00884     {
00885     if( isActiveFullScreen())
00886         {
00887         ToplevelList stacking = workspace()->xStackingOrder();
00888         for( int pos = stacking.count() - 1;
00889              pos >= 0;
00890              --pos )
00891             {
00892             Toplevel* c = stacking.at( pos );
00893             if( c == this ) // is not covered by any other window, ok to unredirect
00894                 return true;
00895             if( c->geometry().intersects( geometry()))
00896                 return false;
00897             }
00898         abort();
00899         }
00900     return false;
00901     }
00902 
00903 //****************************************
00904 // Unmanaged
00905 //****************************************
00906 
00907 bool Unmanaged::shouldUnredirect() const
00908     {
00909     // the pixmap is needed for the login effect, a nicer solution would be the login effect increasing
00910     // refcount for the window pixmap (which would prevent unredirect), avoiding this hack
00911     if( resourceClass() == "ksplashx" || resourceClass() == "ksplashsimple" )
00912         return false;
00913 // it must cover whole display or one xinerama screen, and be the topmost there
00914     if( geometry() == workspace()->clientArea( FullArea, geometry().center(), workspace()->currentDesktop())
00915         || geometry() == workspace()->clientArea( ScreenArea, geometry().center(), workspace()->currentDesktop()))
00916         {
00917         ToplevelList stacking = workspace()->xStackingOrder();
00918         for( int pos = stacking.count() - 1;
00919              pos >= 0;
00920              --pos )
00921             {
00922             Toplevel* c = stacking.at( pos );
00923             if( c == this ) // is not covered by any other window, ok to unredirect
00924                 return true;
00925             if( c->geometry().intersects( geometry()))
00926                 return false;
00927             }
00928         abort();
00929         }
00930     return false;
00931     }
00932 
00933 //****************************************
00934 // Deleted
00935 //****************************************
00936 
00937 bool Deleted::shouldUnredirect() const
00938     {
00939     return false;
00940     }
00941 
00942 } // 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