Make a branch to make krunner Good Enough For Aaron™.
[kdebase/uwolfer.git] / workspace / kwin / composite.cpp
blobed88cae2585d90bd54b20d2ece54bdfebbe7f922
1 /********************************************************************
2 KWin - the KDE window manager
3 This file is part of the KDE project.
5 Copyright (C) 2006 Lubos Lunak <l.lunak@kde.org>
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *********************************************************************/
22 Code related to compositing (redirecting windows to pixmaps and tracking
23 window damage).
25 Docs:
27 XComposite (the protocol, but the function calls map to it):
28 http://gitweb.freedesktop.org/?p=xorg/proto/compositeproto.git;a=blob_plain;hb=HEAD;f=compositeproto.txt
30 XDamage (again the protocol):
31 http://gitweb.freedesktop.org/?p=xorg/proto/damageproto.git;a=blob_plain;hb=HEAD;f=damageproto.txt
33 Composite HOWTO from Fredrik:
34 http://ktown.kde.org/~fredrik/composite_howto.html
38 #include <config-X11.h>
40 #include "utils.h"
41 #include <QTextStream>
42 #include "workspace.h"
43 #include "client.h"
44 #include "unmanaged.h"
45 #include "deleted.h"
46 #include "effects.h"
47 #include "scene.h"
48 #include "scene_basic.h"
49 #include "scene_xrender.h"
50 #include "scene_opengl.h"
51 #include "compositingprefs.h"
53 #include <stdio.h>
55 #include <QMenu>
56 #include <kxerrorhandler.h>
58 #include <X11/extensions/shape.h>
60 #ifdef HAVE_XCOMPOSITE
61 #include <X11/extensions/Xcomposite.h>
62 #if XCOMPOSITE_MAJOR > 0 || XCOMPOSITE_MINOR >= 3
63 #define HAVE_XCOMPOSITE_OVERLAY
64 #endif
65 #endif
66 #ifdef HAVE_XRANDR
67 #include <X11/extensions/Xrandr.h>
68 #endif
70 namespace KWin
73 //****************************************
74 // Workspace
75 //****************************************
77 void Workspace::setupCompositing()
79 #ifdef KWIN_HAVE_COMPOSITING
80 if( scene != NULL )
81 return;
82 if( !options->useCompositing && getenv( "KWIN_COMPOSE") == NULL )
84 kDebug( 1212 ) << "Compositing is turned off in options";
85 return;
87 else if( !CompositingPrefs::compositingPossible() )
89 kError( 1212 ) << "Compositing is not possible";
90 return;
92 CompositingType type = options->compositingMode;
93 if( getenv( "KWIN_COMPOSE" ))
95 char c = getenv( "KWIN_COMPOSE" )[ 0 ];
96 switch( c )
98 case 'O':
99 type = OpenGLCompositing;
100 break;
101 case 'X':
102 type = XRenderCompositing;
103 break;
104 default:
105 kDebug( 1212 ) << "No compositing";
106 return;
110 char selection_name[ 100 ];
111 sprintf( selection_name, "_NET_WM_CM_S%d", DefaultScreen( display()));
112 cm_selection = new KSelectionOwner( selection_name );
113 connect( cm_selection, SIGNAL( lostOwnership()), SLOT( lostCMSelection()));
114 cm_selection->claim( true ); // force claiming
116 switch( type )
118 /*case 'B':
119 kDebug( 1212 ) << "X compositing";
120 scene = new SceneBasic( this );
121 break; // don't fall through (this is a testing one) */
122 #ifdef KWIN_HAVE_OPENGL_COMPOSITING
123 case OpenGLCompositing:
124 kDebug( 1212 ) << "OpenGL compositing";
125 scene = new SceneOpenGL( this );
126 if( !scene->initFailed())
127 break; // -->
128 delete scene;
129 scene = NULL;
130 break; // do not fall back to XRender for now, maybe in the future
131 #endif
132 #ifdef KWIN_HAVE_XRENDER_COMPOSITING
133 case XRenderCompositing:
134 kDebug( 1212 ) << "XRender compositing";
135 scene = new SceneXrender( this );
136 break;
137 #endif
138 default:
139 #ifndef KWIN_HAVE_COMPOSITING
140 kDebug( 1212 ) << "Compositing was not available at compile time";
141 #else
142 kDebug( 1212 ) << "No compositing";
143 #endif
144 delete cm_selection;
145 return;
147 if( scene == NULL || scene->initFailed())
149 kError( 1212 ) << "Failed to initialize compositing, compositing disabled";
150 kError( 1212 ) << "Consult http://techbase.kde.org/Projects/KWin/4.0-release-notes#Setting_up";
151 delete scene;
152 scene = NULL;
153 delete cm_selection;
154 return;
156 int rate = 0;
157 if( options->refreshRate > 0 )
158 { // use manually configured refresh rate
159 rate = options->refreshRate;
161 #ifdef HAVE_XRANDR
162 else
163 { // autoconfigure refresh rate based on XRandR info
164 if( Extensions::randrAvailable() )
166 XRRScreenConfiguration *config;
168 config = XRRGetScreenInfo( display(), rootWindow() );
169 rate = XRRConfigCurrentRate( config );
170 XRRFreeScreenConfigInfo( config );
173 #endif
174 // 0Hz or less is invalid, so we fallback to a default rate
175 if( rate <= 0 )
176 rate = 50;
177 // QTimer gives us 1msec (1000Hz) at best, so we ignore anything higher;
178 // however, since compositing is limited to no more than once per 5msec,
179 // 200Hz to 1000Hz are effectively identical
180 else if( rate > 1000 )
181 rate = 1000;
182 kDebug( 1212 ) << "Refresh rate " << rate << "Hz";
183 compositeRate = 1000 / rate;
184 compositeTimer.start( compositeRate );
185 lastCompositePaint.start();
186 XCompositeRedirectSubwindows( display(), rootWindow(), CompositeRedirectManual );
187 new EffectsHandlerImpl( scene->compositingType() ); // sets also the 'effects' pointer
188 addRepaintFull();
189 foreach( Client* c, clients )
190 c->setupCompositing();
191 foreach( Client* c, desktops )
192 c->setupCompositing();
193 foreach( Unmanaged* c, unmanaged )
194 c->setupCompositing();
195 foreach( Client* c, clients )
196 scene->windowAdded( c );
197 foreach( Client* c, desktops )
198 scene->windowAdded( c );
199 foreach( Unmanaged* c, unmanaged )
200 scene->windowAdded( c );
201 delete popup; // force re-creation of the Alt+F3 popup (opacity option)
202 popup = NULL;
203 #else
204 kDebug( 1212 ) << "Compositing was not available at compile time";
205 #endif
208 void Workspace::finishCompositing()
210 #ifdef KWIN_HAVE_COMPOSITING
211 if( scene == NULL )
212 return;
213 delete cm_selection;
214 foreach( Client* c, clients )
215 scene->windowClosed( c, NULL );
216 foreach( Client* c, desktops )
217 scene->windowClosed( c, NULL );
218 foreach( Unmanaged* c, unmanaged )
219 scene->windowClosed( c, NULL );
220 foreach( Deleted* c, deleted )
221 scene->windowDeleted( c );
222 foreach( Client* c, clients )
223 c->finishCompositing();
224 foreach( Client* c, desktops )
225 c->finishCompositing();
226 foreach( Unmanaged* c, unmanaged )
227 c->finishCompositing();
228 foreach( Deleted* c, deleted )
229 c->finishCompositing();
230 XCompositeUnredirectSubwindows( display(), rootWindow(), CompositeRedirectManual );
231 compositeTimer.stop();
232 delete effects;
233 effects = NULL;
234 delete scene;
235 scene = NULL;
236 repaints_region = QRegion();
237 for( ClientList::ConstIterator it = clients.begin();
238 it != clients.end();
239 ++it )
240 { // forward all opacity values to the frame in case there'll be other CM running
241 if( (*it)->opacity() != 1.0 )
243 NETWinInfo i( display(), (*it)->frameId(), rootWindow(), 0 );
244 i.setOpacity( static_cast< unsigned long >((*it)->opacity() * 0xffffffff ));
247 delete popup; // force re-creation of the Alt+F3 popup (opacity option)
248 popup = NULL;
249 #endif
252 void Workspace::lostCMSelection()
254 kDebug( 1212 ) << "Lost compositing manager selection";
255 finishCompositing();
258 void Workspace::addRepaint( int x, int y, int w, int h )
260 if( !compositing())
261 return;
262 repaints_region += QRegion( x, y, w, h );
265 void Workspace::addRepaint( const QRect& r )
267 if( !compositing())
268 return;
269 repaints_region += r;
272 void Workspace::addRepaintFull()
274 if( !compositing())
275 return;
276 repaints_region = QRegion( 0, 0, displayWidth(), displayHeight());
279 void Workspace::performCompositing()
281 #ifdef KWIN_HAVE_COMPOSITING
282 // The event loop apparently tries to fire a QTimer as often as possible, even
283 // at the expense of not processing many X events. This means that the composite
284 // repaints can seriously impact performance of everything else, therefore throttle
285 // them - leave at least 1msec time after one repaint is finished and next one
286 // is started.
287 if( lastCompositePaint.elapsed() < 1 )
288 return;
289 checkCursorPos();
290 if(( repaints_region.isEmpty() && !windowRepaintsPending()) // no damage
291 || !overlay_visible ) // nothing is visible anyway
293 scene->idle();
294 return;
296 // create a list of all windows in the stacking order
297 ToplevelList windows;
298 foreach( Client* c, stacking_order )
299 windows.append( c );
300 foreach( Unmanaged* c, unmanaged_stacking_order )
301 windows.append( c );
302 foreach( Deleted* c, deleted ) // TODO remember stacking order somehow
303 windows.append( c );
304 foreach( EffectWindow* c, static_cast< EffectsHandlerImpl* >( effects )->elevatedWindows())
306 Toplevel* t = static_cast< EffectWindowImpl* >( c )->window();
307 windows.removeAll( t );
308 windows.append( t );
310 foreach( Toplevel* c, windows )
311 { // This could be possibly optimized WRT obscuring, but that'd need being already
312 // past prePaint() phase - probably not worth it.
313 // TODO I think effects->transformWindowDamage() doesn't need to be called here,
314 // pre-paint will extend painted window areas as necessary.
315 repaints_region |= c->repaints().translated( c->pos());
316 c->resetRepaints( c->rect());
318 ToplevelList tmp = windows;
319 windows.clear();
320 foreach( Toplevel* c, tmp )
321 if( c->readyForPainting())
322 windows.append( c );
323 QRegion repaints = repaints_region;
324 // clear all repaints, so that post-pass can add repaints for the next repaint
325 repaints_region = QRegion();
326 scene->paint( repaints, windows );
327 if( scene->waitSyncAvailable() && options->glVSync )
328 { // if we're using vsync, then time the next paint pass to
329 // before the next available sync
330 int paintTime = ( lastCompositePaint.elapsed() % compositeRate ) +
331 ( compositeRate / 2 );
332 if( paintTime >= compositeRate )
333 compositeTimer.start( paintTime );
334 else if( paintTime < compositeRate )
335 compositeTimer.start( compositeRate - paintTime );
337 lastCompositePaint.start();
338 #endif
341 bool Workspace::windowRepaintsPending() const
343 foreach( Toplevel* c, clients )
344 if( !c->repaints().isEmpty())
345 return true;
346 foreach( Toplevel* c, desktops )
347 if( !c->repaints().isEmpty())
348 return true;
349 foreach( Toplevel* c, unmanaged )
350 if( !c->repaints().isEmpty())
351 return true;
352 foreach( Toplevel* c, deleted )
353 if( !c->repaints().isEmpty())
354 return true;
355 return false;
358 bool Workspace::createOverlay()
360 assert( overlay == None );
361 if( !Extensions::compositeOverlayAvailable())
362 return false;
363 if( !Extensions::shapeInputAvailable()) // needed in setupOverlay()
364 return false;
365 #ifdef HAVE_XCOMPOSITE_OVERLAY
366 overlay = XCompositeGetOverlayWindow( display(), rootWindow());
367 if( overlay == None )
368 return false;
369 return true;
370 #else
371 return false;
372 #endif
375 void Workspace::setupOverlay( Window w )
377 assert( overlay != None );
378 assert( Extensions::shapeInputAvailable());
379 XShapeCombineRectangles( display(), overlay, ShapeInput, 0, 0, NULL, 0, ShapeSet, Unsorted );
380 if( w != None )
382 XShapeCombineRectangles( display(), w, ShapeInput, 0, 0, NULL, 0, ShapeSet, Unsorted );
383 XMapWindow( display(), w );
385 XMapRaised( display(), overlay );
386 XSelectInput( display(), overlay, VisibilityChangeMask );
389 void Workspace::destroyOverlay()
391 if( overlay == None )
392 return;
393 #ifdef HAVE_XCOMPOSITE_OVERLAY
394 XCompositeReleaseOverlayWindow( display(), overlay );
395 #endif
396 overlay = None;
399 //****************************************
400 // Toplevel
401 //****************************************
403 void Toplevel::setupCompositing()
405 #ifdef KWIN_HAVE_COMPOSITING
406 if( !compositing())
407 return;
408 if( damage_handle != None )
409 return;
410 damage_handle = XDamageCreate( display(), frameId(), XDamageReportRawRectangles );
411 damage_region = QRegion( 0, 0, width(), height());
412 effect_window = new EffectWindowImpl();
413 effect_window->setWindow( this );
414 #endif
417 void Toplevel::finishCompositing()
419 #ifdef KWIN_HAVE_COMPOSITING
420 if( damage_handle == None )
421 return;
422 if( effect_window->window() == this ) // otherwise it's already passed to Deleted, don't free data
424 discardWindowPixmap();
425 delete effect_window;
427 XDamageDestroy( display(), damage_handle );
428 damage_handle = None;
429 damage_region = QRegion();
430 repaints_region = QRegion();
431 effect_window = NULL;
432 #endif
435 void Toplevel::discardWindowPixmap()
437 addDamageFull();
438 if( window_pix == None )
439 return;
440 XFreePixmap( display(), window_pix );
441 window_pix = None;
442 if( effectWindow() != NULL && effectWindow()->sceneWindow() != NULL )
443 effectWindow()->sceneWindow()->pixmapDiscarded();
446 Pixmap Toplevel::createWindowPixmap()
448 #ifdef KWIN_HAVE_COMPOSITING
449 assert( compositing());
450 grabXServer();
451 KXErrorHandler err;
452 window_pix = XCompositeNameWindowPixmap( display(), frameId());
453 // check that the received pixmap is valid and actually matches what we
454 // know about the window (i.e. size)
455 XWindowAttributes attrs;
456 if( !XGetWindowAttributes( display(), frameId(), &attrs ))
457 window_pix = None;
458 if( err.error( false ))
459 window_pix = None;
460 if( attrs.width != width() || attrs.height != height() || attrs.map_state != IsViewable )
461 window_pix = None;
462 ungrabXServer();
463 if( window_pix == None )
464 kDebug( 1212 ) << "Creating window pixmap failed: " << this;
465 return window_pix;
466 #else
467 return None;
468 #endif
471 #ifdef HAVE_XDAMAGE
472 void Toplevel::damageNotifyEvent( XDamageNotifyEvent* e )
474 addDamage( e->area.x, e->area.y, e->area.width, e->area.height );
475 // compress
476 while( XPending( display()))
478 XEvent e2;
479 if( XPeekEvent( display(), &e2 ) && e2.type == Extensions::damageNotifyEvent()
480 && e2.xany.window == frameId())
482 XNextEvent( display(), &e2 );
483 XDamageNotifyEvent* e = reinterpret_cast< XDamageNotifyEvent* >( &e2 );
484 addDamage( e->area.x, e->area.y, e->area.width, e->area.height );
485 continue;
487 break;
491 void Client::damageNotifyEvent( XDamageNotifyEvent* e )
493 Toplevel::damageNotifyEvent( e );
494 #ifdef HAVE_XSYNC
495 if( sync_counter == None ) // cannot detect complete redraw, consider done now
496 ready_for_painting = true;
497 #else
498 ready_for_painting = true; // no sync at all, consider done now
499 #endif
501 #endif
503 void Toplevel::addDamage( const QRect& r )
505 addDamage( r.x(), r.y(), r.width(), r.height());
508 void Toplevel::addDamage( int x, int y, int w, int h )
510 if( !compositing())
511 return;
512 QRect r( x, y, w, h );
513 // resizing the decoration may lag behind a bit and when shrinking there
514 // may be a damage event coming with size larger than the current window size
515 r &= rect();
516 damage_region += r;
517 repaints_region += r;
518 static_cast<EffectsHandlerImpl*>(effects)->windowDamaged( effectWindow(), r );
521 void Toplevel::addDamageFull()
523 if( !compositing())
524 return;
525 damage_region = rect();
526 repaints_region = rect();
527 static_cast<EffectsHandlerImpl*>(effects)->windowDamaged( effectWindow(), rect());
530 void Toplevel::resetDamage( const QRect& r )
532 damage_region -= r;
535 void Toplevel::addRepaint( const QRect& r )
537 addRepaint( r.x(), r.y(), r.width(), r.height());
540 void Toplevel::addRepaint( int x, int y, int w, int h )
542 if( !compositing())
543 return;
544 QRect r( x, y, w, h );
545 r &= rect();
546 repaints_region += r;
549 void Toplevel::addRepaintFull()
551 repaints_region = rect();
554 void Toplevel::resetRepaints( const QRect& r )
556 repaints_region -= r;
559 void Toplevel::addWorkspaceRepaint( int x, int y, int w, int h )
561 addWorkspaceRepaint( QRect( x, y, w, h ));
564 void Toplevel::addWorkspaceRepaint( const QRect& r2 )
566 if( !compositing())
567 return;
568 if( effectWindow() == NULL ) // TODO - this can happen during window destruction
569 return workspace()->addRepaint( r2 );
570 QRect r = effects->transformWindowDamage( effectWindow(), r2 );
571 workspace()->addRepaint( r );
574 } // namespace