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
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>
41 #include <QTextStream>
42 #include "workspace.h"
44 #include "unmanaged.h"
48 #include "scene_basic.h"
49 #include "scene_xrender.h"
50 #include "scene_opengl.h"
51 #include "compositingprefs.h"
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
67 #include <X11/extensions/Xrandr.h>
73 //****************************************
75 //****************************************
77 void Workspace::setupCompositing()
79 #ifdef KWIN_HAVE_COMPOSITING
82 if( !options
->useCompositing
&& getenv( "KWIN_COMPOSE") == NULL
)
84 kDebug( 1212 ) << "Compositing is turned off in options";
87 else if( !CompositingPrefs::compositingPossible() )
89 kError( 1212 ) << "Compositing is not possible";
92 CompositingType type
= options
->compositingMode
;
93 if( getenv( "KWIN_COMPOSE" ))
95 char c
= getenv( "KWIN_COMPOSE" )[ 0 ];
99 type
= OpenGLCompositing
;
102 type
= XRenderCompositing
;
105 kDebug( 1212 ) << "No compositing";
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
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())
130 break; // do not fall back to XRender for now, maybe in the future
132 #ifdef KWIN_HAVE_XRENDER_COMPOSITING
133 case XRenderCompositing
:
134 kDebug( 1212 ) << "XRender compositing";
135 scene
= new SceneXrender( this );
139 #ifndef KWIN_HAVE_COMPOSITING
140 kDebug( 1212 ) << "Compositing was not available at compile time";
142 kDebug( 1212 ) << "No compositing";
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";
157 if( options
->refreshRate
> 0 )
158 { // use manually configured refresh rate
159 rate
= options
->refreshRate
;
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
);
174 // 0Hz or less is invalid, so we fallback to a default rate
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 )
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
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)
204 kDebug( 1212 ) << "Compositing was not available at compile time";
208 void Workspace::finishCompositing()
210 #ifdef KWIN_HAVE_COMPOSITING
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();
236 repaints_region
= QRegion();
237 for( ClientList::ConstIterator it
= clients
.begin();
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)
252 void Workspace::lostCMSelection()
254 kDebug( 1212 ) << "Lost compositing manager selection";
258 void Workspace::addRepaint( int x
, int y
, int w
, int h
)
262 repaints_region
+= QRegion( x
, y
, w
, h
);
265 void Workspace::addRepaint( const QRect
& r
)
269 repaints_region
+= r
;
272 void Workspace::addRepaintFull()
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
287 if( lastCompositePaint
.elapsed() < 1 )
290 if(( repaints_region
.isEmpty() && !windowRepaintsPending()) // no damage
291 || !overlay_visible
) // nothing is visible anyway
296 // create a list of all windows in the stacking order
297 ToplevelList windows
;
298 foreach( Client
* c
, stacking_order
)
300 foreach( Unmanaged
* c
, unmanaged_stacking_order
)
302 foreach( Deleted
* c
, deleted
) // TODO remember stacking order somehow
304 foreach( EffectWindow
* c
, static_cast< EffectsHandlerImpl
* >( effects
)->elevatedWindows())
306 Toplevel
* t
= static_cast< EffectWindowImpl
* >( c
)->window();
307 windows
.removeAll( 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
;
320 foreach( Toplevel
* c
, tmp
)
321 if( c
->readyForPainting())
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();
341 bool Workspace::windowRepaintsPending() const
343 foreach( Toplevel
* c
, clients
)
344 if( !c
->repaints().isEmpty())
346 foreach( Toplevel
* c
, desktops
)
347 if( !c
->repaints().isEmpty())
349 foreach( Toplevel
* c
, unmanaged
)
350 if( !c
->repaints().isEmpty())
352 foreach( Toplevel
* c
, deleted
)
353 if( !c
->repaints().isEmpty())
358 bool Workspace::createOverlay()
360 assert( overlay
== None
);
361 if( !Extensions::compositeOverlayAvailable())
363 if( !Extensions::shapeInputAvailable()) // needed in setupOverlay()
365 #ifdef HAVE_XCOMPOSITE_OVERLAY
366 overlay
= XCompositeGetOverlayWindow( display(), rootWindow());
367 if( overlay
== None
)
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
);
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
)
393 #ifdef HAVE_XCOMPOSITE_OVERLAY
394 XCompositeReleaseOverlayWindow( display(), overlay
);
399 //****************************************
401 //****************************************
403 void Toplevel::setupCompositing()
405 #ifdef KWIN_HAVE_COMPOSITING
408 if( damage_handle
!= None
)
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 );
417 void Toplevel::finishCompositing()
419 #ifdef KWIN_HAVE_COMPOSITING
420 if( damage_handle
== None
)
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
;
435 void Toplevel::discardWindowPixmap()
438 if( window_pix
== None
)
440 XFreePixmap( display(), window_pix
);
442 if( effectWindow() != NULL
&& effectWindow()->sceneWindow() != NULL
)
443 effectWindow()->sceneWindow()->pixmapDiscarded();
446 Pixmap
Toplevel::createWindowPixmap()
448 #ifdef KWIN_HAVE_COMPOSITING
449 assert( compositing());
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
))
458 if( err
.error( false ))
460 if( attrs
.width
!= width() || attrs
.height
!= height() || attrs
.map_state
!= IsViewable
)
463 if( window_pix
== None
)
464 kDebug( 1212 ) << "Creating window pixmap failed: " << this;
472 void Toplevel::damageNotifyEvent( XDamageNotifyEvent
* e
)
474 addDamage( e
->area
.x
, e
->area
.y
, e
->area
.width
, e
->area
.height
);
476 while( XPending( display()))
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
);
491 void Client::damageNotifyEvent( XDamageNotifyEvent
* e
)
493 Toplevel::damageNotifyEvent( e
);
495 if( sync_counter
== None
) // cannot detect complete redraw, consider done now
496 ready_for_painting
= true;
498 ready_for_painting
= true; // no sync at all, consider done now
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
)
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
517 repaints_region
+= r
;
518 static_cast<EffectsHandlerImpl
*>(effects
)->windowDamaged( effectWindow(), r
);
521 void Toplevel::addDamageFull()
525 damage_region
= rect();
526 repaints_region
= rect();
527 static_cast<EffectsHandlerImpl
*>(effects
)->windowDamaged( effectWindow(), rect());
530 void Toplevel::resetDamage( const QRect
& 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
)
544 QRect
r( x
, y
, w
, h
);
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
)
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
);