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 This is the XRender-based compositing code. The primary compositing
23 backend is the OpenGL-based one, which should be more powerful
24 and also possibly better documented. This backend is mostly for cases
25 when the OpenGL backend cannot be used for some reason (insufficient
26 performance, no usable OpenGL support at all, etc.)
27 The plan is to keep it around as long as needed/possible, but if it
28 proves to be too much hassle it will be dropped in the future.
32 XRender (the protocol, but the function calls map to it):
33 http://gitweb.freedesktop.org/?p=xorg/proto/renderproto.git;a=blob_plain;hb=HEAD;f=renderproto.txt
35 XFixes (again, the protocol):
36 http://gitweb.freedesktop.org/?p=xorg/proto/fixesproto.git;a=blob_plain;hb=HEAD;f=fixesproto.txt
40 #include "scene_xrender.h"
42 #ifdef KWIN_HAVE_XRENDER_COMPOSITING
48 #include "kwinxrenderutils.h"
50 #include <X11/extensions/Xcomposite.h>
52 #include <kxerrorhandler.h>
57 //****************************************
59 //****************************************
61 // kDebug() support for the XserverRegion type
64 RegionDebug( XserverRegion r
) : rr( r
) {}
68 kdbgstream
& operator<<( kdbgstream
& stream
, RegionDebug r
)
71 return stream
<< "EMPTY";
73 XRectangle
* rects
= XFixesFetchRegion( display(), r
.rr
, &num
);
74 if( rects
== NULL
|| num
== 0 )
75 return stream
<< "EMPTY";
79 stream
<< "[" << rects
[ i
].x
<< "+" << rects
[ i
].y
<< " " << rects
[ i
].width
<< "x" << rects
[ i
].height
<< "]";
83 Picture
SceneXrender::buffer
= None
;
84 ScreenPaintData
SceneXrender::screen_paint
;
86 SceneXrender::SceneXrender( Workspace
* ws
)
91 if( !Extensions::renderAvailable())
93 kError( 1212 ) << "No XRender extension available";
96 if( !Extensions::fixesRegionAvailable())
98 kError( 1212 ) << "No XFixes v3+ extension available";
102 if( wspace
->createOverlay())
104 wspace
->setupOverlay( None
);
105 XWindowAttributes attrs
;
106 XGetWindowAttributes( display(), wspace
->overlayWindow(), &attrs
);
107 format
= XRenderFindVisualFormat( display(), attrs
.visual
);
110 kError( 1212 ) << "Failed to find XRender format for overlay window";
113 front
= XRenderCreatePicture( display(), wspace
->overlayWindow(), format
, 0, NULL
);
117 // create XRender picture for the root window
118 format
= XRenderFindVisualFormat( display(), DefaultVisual( display(), DefaultScreen( display())));
121 kError( 1212 ) << "Failed to find XRender format for root window";
124 XRenderPictureAttributes pa
;
125 pa
.subwindow_mode
= IncludeInferiors
;
126 front
= XRenderCreatePicture( display(), rootWindow(), format
, CPSubwindowMode
, &pa
);
129 if( xerr
.error( true ))
131 kError( 1212 ) << "XRender compositing setup failed";
139 SceneXrender::~SceneXrender()
143 // TODO this probably needs to clean up whatever has been created until the failure
144 wspace
->destroyOverlay();
147 XRenderFreePicture( display(), front
);
148 XRenderFreePicture( display(), buffer
);
150 wspace
->destroyOverlay();
151 foreach( Window
* w
, windows
)
155 bool SceneXrender::initFailed() const
160 // Create the compositing buffer. The root window is not double-buffered,
161 // so it is done manually using this buffer,
162 void SceneXrender::createBuffer()
164 Pixmap pixmap
= XCreatePixmap( display(), rootWindow(), displayWidth(), displayHeight(), DefaultDepth( display(), DefaultScreen( display())));
165 buffer
= XRenderCreatePicture( display(), pixmap
, format
, 0, 0 );
166 XFreePixmap( display(), pixmap
); // The picture owns the pixmap now
169 // Just like SceneOpenGL::selfCheck()
170 bool SceneXrender::selfCheck()
172 QImage
img( 3, 2, QImage::Format_RGB32
);
173 img
.setPixel( 0, 0, QColor( Qt::red
).rgb());
174 img
.setPixel( 1, 0, QColor( Qt::green
).rgb());
175 img
.setPixel( 2, 0, QColor( Qt::blue
).rgb());
176 img
.setPixel( 0, 1, QColor( Qt::white
).rgb());
177 img
.setPixel( 1, 1, QColor( Qt::black
).rgb());
178 img
.setPixel( 2, 1, QColor( Qt::white
).rgb());
179 QPixmap pix
= QPixmap::fromImage( img
);
180 QList
< QPoint
> points
= selfCheckPoints();
182 foreach( const QPoint
& p
, points
)
183 reg
|= QRect( p
, pix
.size());
184 if( wspace
->overlayWindow())
185 { // avoid covering the whole screen too soon
186 wspace
->setOverlayShape( reg
);
187 wspace
->showOverlay();
189 foreach( const QPoint
& p
, points
)
191 XSetWindowAttributes wa
;
192 wa
.override_redirect
= True
;
193 ::Window window
= XCreateWindow( display(), rootWindow(), 0, 0, 3, 2, 0, QX11Info::appDepth(),
194 CopyFromParent
, CopyFromParent
, CWOverrideRedirect
, &wa
);
195 XSetWindowBackgroundPixmap( display(), window
, pix
.handle());
196 XClearWindow( display(), window
);
197 XMapWindow( display(), window
);
198 // move the window one down to where the result will be rendered too, just in case
199 // the render would fail completely and eventual check would try to read this window's contents
200 XMoveWindow( display(), window
, p
.x() + 1, p
.y());
201 XCompositeRedirectWindow( display(), window
, CompositeRedirectManual
);
202 Pixmap wpix
= XCompositeNameWindowPixmap( display(), window
);
203 XWindowAttributes attrs
;
204 XGetWindowAttributes( display(), window
, &attrs
);
205 XRenderPictFormat
* format
= XRenderFindVisualFormat( display(), attrs
.visual
);
206 Picture pic
= XRenderCreatePicture( display(), wpix
, format
, 0, 0 );
207 QRect
rect( p
.x(), p
.y(), 3, 2 );
208 XRenderComposite( display(), PictOpSrc
, pic
, None
, buffer
, 0, 0, 0, 0,
209 rect
.x(), rect
.y(), rect
.width(), rect
.height());
210 XFreePixmap( display(), wpix
);
211 XDestroyWindow( display(), window
);
213 flushBuffer( PAINT_SCREEN_REGION
, reg
);
215 foreach( const QPoint
& p
, points
)
217 QPixmap pix
= QPixmap::grabWindow( rootWindow(), p
.x(), p
.y(), 3, 2 );
218 QImage img
= pix
.toImage();
219 // kDebug(1212) << "P:" << QColor( img.pixel( 0, 0 )).name();
220 // kDebug(1212) << "P:" << QColor( img.pixel( 1, 0 )).name();
221 // kDebug(1212) << "P:" << QColor( img.pixel( 2, 0 )).name();
222 // kDebug(1212) << "P:" << QColor( img.pixel( 0, 1 )).name();
223 // kDebug(1212) << "P:" << QColor( img.pixel( 1, 1 )).name();
224 // kDebug(1212) << "P:" << QColor( img.pixel( 2, 1 )).name();
225 if( img
.pixel( 0, 0 ) != QColor( Qt::red
).rgb()
226 || img
.pixel( 1, 0 ) != QColor( Qt::green
).rgb()
227 || img
.pixel( 2, 0 ) != QColor( Qt::blue
).rgb()
228 || img
.pixel( 0, 1 ) != QColor( Qt::white
).rgb()
229 || img
.pixel( 1, 1 ) != QColor( Qt::black
).rgb()
230 || img
.pixel( 2, 1 ) != QColor( Qt::white
).rgb())
232 kError( 1212 ) << "Compositing self-check failed, disabling compositing.";
237 if( wspace
->overlayWindow())
238 wspace
->hideOverlay();
240 kDebug( 1212 ) << "Compositing self-check passed.";
241 if( !ok
&& options
->disableCompositingChecks
)
243 kWarning( 1212 ) << "Compositing checks disabled, proceeding regardless of self-check failure.";
250 // the entry point for painting
251 void SceneXrender::paint( QRegion damage
, ToplevelList toplevels
)
253 foreach( Toplevel
* c
, toplevels
)
255 assert( windows
.contains( c
));
256 stacking_order
.append( windows
[ c
] );
259 paintScreen( &mask
, &damage
);
260 if( wspace
->overlayWindow()) // show the window only after the first pass, since
261 wspace
->showOverlay(); // that pass may take long
262 flushBuffer( mask
, damage
);
264 stacking_order
.clear();
267 void SceneXrender::flushBuffer( int mask
, QRegion damage
)
269 if( mask
& PAINT_SCREEN_REGION
)
271 // Use the damage region as the clip region for the root window
272 XserverRegion front_region
= toXserverRegion( damage
);
273 XFixesSetPictureClipRegion( display(), front
, 0, 0, front_region
);
274 XFixesDestroyRegion( display(), front_region
);
275 // copy composed buffer to the root window
276 XFixesSetPictureClipRegion( display(), buffer
, 0, 0, None
);
277 XRenderComposite( display(), PictOpSrc
, buffer
, None
, front
, 0, 0, 0, 0, 0, 0, displayWidth(), displayHeight());
278 XFixesSetPictureClipRegion( display(), front
, 0, 0, None
);
283 // copy composed buffer to the root window
284 XRenderComposite( display(), PictOpSrc
, buffer
, None
, front
, 0, 0, 0, 0, 0, 0, displayWidth(), displayHeight());
289 void SceneXrender::paintGenericScreen( int mask
, ScreenPaintData data
)
291 screen_paint
= data
; // save, transformations will be done when painting windows
292 if( true ) // as long as paintTransformedScreen() doesn't work properly
293 Scene::paintGenericScreen( mask
, data
);
295 paintTransformedScreen( mask
);
299 TODO currently broken
300 Try to do optimized painting even with transformations. Since only scaling
301 and translation are supported by the painting code, clipping can be done
302 manually to avoid having to paint everything in every pass. Whole screen
303 still need to be painted but e.g. obscured windows don't. So this below
304 is basically paintSimpleScreen() with extra code to compute clipping correctly.
306 This code assumes that the only transformations possible with XRender are those
307 provided by Window/ScreenPaintData, In the (very unlikely?) case more is needed
308 then paintGenericScreen() needs to be used.
310 void SceneXrender::paintTransformedScreen( int orig_mask
)
312 QRegion
region( 0, 0, displayWidth(), displayHeight());
313 QList
< Phase2Data
> phase2
;
315 // Draw each opaque window top to bottom, subtracting the bounding rect of
316 // each window from the clip region after it's been drawn.
317 for( int i
= stacking_order
.count() - 1; // top to bottom
321 Window
* w
= static_cast< Window
* >( stacking_order
[ i
] );
322 WindowPrePaintData data
;
323 data
.mask
= orig_mask
| ( w
->isOpaque() ? PAINT_WINDOW_OPAQUE
: PAINT_WINDOW_TRANSLUCENT
);
324 w
->resetPaintingEnabled();
326 // TODO this is wrong, transformedShape() should be used here, but is not known yet
327 data
.clip
= w
->isOpaque() ? region
: QRegion();
328 data
.quads
= w
->buildQuads();
330 effects
->prePaintWindow( effectWindow( w
), data
, time_diff
);
332 foreach( const WindowQuad
&q
, data
.quads
)
333 if( q
.isTransformed())
334 kFatal( 1212 ) << "Pre-paint calls are not allowed to transform quads!" ;
336 if( !w
->isPaintingEnabled())
338 data
.paint
-= allclips
; // make sure to avoid already clipped areas
339 if( data
.paint
.isEmpty()) // completely clipped
341 if( data
.paint
!= region
) // prepaint added area to draw
343 region
|= data
.paint
; // make sure other windows in that area get painted too
344 painted_region
|= data
.paint
; // make sure it makes it to the screen
346 // If the window is transparent, the transparent part will be done
348 if( data
.mask
& PAINT_WINDOW_TRANSLUCENT
)
349 phase2
.prepend( Phase2Data( w
, data
.paint
, data
.clip
, data
.mask
, data
.quads
));
350 if( data
.mask
& PAINT_WINDOW_OPAQUE
)
352 w
->setTransformedShape( QRegion());
353 paintWindow( w
, data
.mask
, data
.paint
, data
.quads
);
354 // The window can clip by its opaque parts the windows below.
355 region
-= w
->transformedShape();
357 // translucency or window transformed require window pixmap
358 w
->suspendUnredirect( data
.mask
& ( PAINT_WINDOW_TRANSLUCENT
| PAINT_WINDOW_TRANSFORMED
));
360 if( !( orig_mask
& PAINT_SCREEN_BACKGROUND_FIRST
))
361 paintBackground( region
); // Fill any areas of the root window not covered by windows
362 // Now walk the list bottom to top, drawing translucent windows.
363 // That we draw bottom to top is important now since we're drawing translucent objects
364 // and also are clipping only by opaque windows.
366 foreach( const Phase2Data
&d
, phase2
)
368 Scene::Window
* w
= d
.window
;
369 paintWindow( w
, d
.mask
, d
.region
| add_paint
, d
.quads
);
370 // It is necessary to also add paint regions of windows below, because their
371 // pre-paint's might have extended the paint area, so those areas need to be painted too.
372 add_paint
|= d
.region
;
376 // fill the screen background
377 void SceneXrender::paintBackground( QRegion region
)
379 PaintClipper
pc( region
);
380 for( PaintClipper::Iterator iterator
;
384 XRenderColor col
= { 0, 0, 0, 0xffff }; // black
385 XRenderFillRectangle( display(), PictOpSrc
, buffer
, &col
, 0, 0, displayWidth(), displayHeight());
389 void SceneXrender::windowGeometryShapeChanged( Toplevel
* c
)
391 if( !windows
.contains( c
)) // this is ok, shape is not valid by default
393 Window
* w
= windows
[ c
];
399 void SceneXrender::windowOpacityChanged( Toplevel
* c
)
401 if( !windows
.contains( c
)) // this is ok, alpha is created on demand
403 Window
* w
= windows
[ c
];
407 void SceneXrender::windowClosed( Toplevel
* c
, Deleted
* deleted
)
409 assert( windows
.contains( c
));
410 if( deleted
!= NULL
)
411 { // replace c with deleted
412 Window
* w
= windows
.take( c
);
413 w
->updateToplevel( deleted
);
414 windows
[ deleted
] = w
;
418 delete windows
.take( c
);
419 c
->effectWindow()->setSceneWindow( NULL
);
423 void SceneXrender::windowDeleted( Deleted
* c
)
425 assert( windows
.contains( c
));
426 delete windows
.take( c
);
427 c
->effectWindow()->setSceneWindow( NULL
);
430 void SceneXrender::windowAdded( Toplevel
* c
)
432 assert( !windows
.contains( c
));
433 windows
[ c
] = new Window( c
);
434 c
->effectWindow()->setSceneWindow( windows
[ c
]);
437 //****************************************
438 // SceneXrender::Window
439 //****************************************
441 SceneXrender::Window::Window( Toplevel
* c
)
444 , format( XRenderFindVisualFormat( display(), c
->visual()))
449 SceneXrender::Window::~Window()
456 // Create XRender picture for the pixmap with the window contents.
457 Picture
SceneXrender::Window::picture()
459 if( !toplevel
->damage().isEmpty() && _picture
!= None
)
461 XRenderFreePicture( display(), _picture
);
464 if( _picture
== None
&& format
!= NULL
)
466 // Get the pixmap with the window contents.
467 Pixmap pix
= toplevel
->windowPixmap();
470 _picture
= XRenderCreatePicture( display(), pix
, format
, 0, 0 );
471 toplevel
->resetDamage( toplevel
->rect());
477 void SceneXrender::Window::discardPicture()
479 if( _picture
!= None
)
480 XRenderFreePicture( display(), _picture
);
484 void SceneXrender::Window::discardAlpha()
487 XRenderFreePicture( display(), alpha
);
491 // Create XRender picture for the alpha mask.
492 Picture
SceneXrender::Window::alphaMask( double opacity
)
494 if( isOpaque() && opacity
== 1.0 )
496 if( alpha
!= None
&& alpha_cached_opacity
!= opacity
)
499 XRenderFreePicture( display(), alpha
);
505 { // no need to create alpha mask
506 alpha_cached_opacity
= 1.0;
509 // Create a 1x1 8bpp pixmap containing the given opacity in the alpha channel.
510 Pixmap pixmap
= XCreatePixmap( display(), rootWindow(), 1, 1, 8 );
511 XRenderPictFormat
* format
= XRenderFindStandardFormat( display(), PictStandardA8
);
512 XRenderPictureAttributes pa
;
514 alpha
= XRenderCreatePicture( display(), pixmap
, format
, CPRepeat
, &pa
);
515 XFreePixmap( display(), pixmap
);
517 col
.alpha
= int( opacity
* 0xffff );
518 alpha_cached_opacity
= opacity
;
519 XRenderFillRectangle( display(), PictOpSrc
, alpha
, &col
, 0, 0, 1, 1 );
524 void SceneXrender::Window::performPaint( int mask
, QRegion region
, WindowPaintData data
)
526 setTransformedShape( QRegion()); // maybe nothing will be painted
527 // check if there is something to paint
528 bool opaque
= isOpaque() && data
.opacity
== 1.0;
529 /* HACK: It seems this causes painting glitches, disable temporarily
530 if(( mask & PAINT_WINDOW_OPAQUE ) ^ ( mask & PAINT_WINDOW_TRANSLUCENT ))
531 { // We are only painting either opaque OR translucent windows, not both
532 if( mask & PAINT_WINDOW_OPAQUE && !opaque )
533 return; // Only painting opaque and window is translucent
534 if( mask & PAINT_WINDOW_TRANSLUCENT && opaque )
535 return; // Only painting translucent and window is opaque
537 Picture pic
= picture(); // get XRender picture
538 if( pic
== None
) // The render format can be null for GL and/or Xv visuals
540 // set picture filter
541 if( options
->xrenderSmoothScale
) // only when forced, it's slow
543 if( mask
& PAINT_WINDOW_TRANSFORMED
)
544 filter
= ImageFilterGood
;
545 else if( mask
& PAINT_SCREEN_TRANSFORMED
)
546 filter
= ImageFilterGood
;
548 filter
= ImageFilterFast
;
551 filter
= ImageFilterFast
;
552 // do required transformations
553 int x
= toplevel
->x();
554 int y
= toplevel
->y();
555 int width
= toplevel
->width();
556 int height
= toplevel
->height();
559 transformed_shape
= shape();
560 if( mask
& PAINT_WINDOW_TRANSFORMED
)
562 xscale
*= data
.xScale
;
563 yscale
*= data
.yScale
;
564 x
+= data
.xTranslate
;
565 y
+= data
.yTranslate
;
567 if( mask
& PAINT_SCREEN_TRANSFORMED
)
569 xscale
*= screen_paint
.xScale
;
570 yscale
*= screen_paint
.yScale
;
571 x
= int( x
* screen_paint
.xScale
);
572 y
= int( y
* screen_paint
.yScale
);
573 x
+= screen_paint
.xTranslate
;
574 y
+= screen_paint
.yTranslate
;
576 if( yscale
!= 1 || xscale
!= 1 )
578 XTransform xform
= {{
579 { XDoubleToFixed( 1 / xscale
), XDoubleToFixed( 0 ), XDoubleToFixed( 0 ) },
580 { XDoubleToFixed( 0 ), XDoubleToFixed( 1 / yscale
), XDoubleToFixed( 0 ) },
581 { XDoubleToFixed( 0 ), XDoubleToFixed( 0 ), XDoubleToFixed( 1 ) }
583 XRenderSetPictureTransform( display(), pic
, &xform
);
584 width
= (int)(width
* xscale
);
585 height
= (int)(height
* yscale
);
586 if( filter
== ImageFilterGood
)
587 XRenderSetPictureFilter( display(), pic
, const_cast< char* >( "good" ), NULL
, 0 );
588 // transform the shape for clipping in paintTransformedScreen()
589 QVector
< QRect
> rects
= transformed_shape
.rects();
594 QRect
& r
= rects
[ i
];
595 r
= QRect( int( r
.x() * xscale
), int( r
.y() * yscale
),
596 int( r
.width() * xscale
), int( r
.height() * xscale
));
598 transformed_shape
.setRects( rects
.constData(), rects
.count());
600 transformed_shape
.translate( x
, y
);
601 PaintClipper
pcreg( region
); // clip by the region to paint
602 PaintClipper
pc( transformed_shape
); // clip by window's shape
603 for( PaintClipper::Iterator iterator
;
609 XRenderComposite( display(), PictOpSrc
, pic
, None
, buffer
, 0, 0, 0, 0,
610 x
, y
, width
, height
);
611 // fake brightness change by overlaying black
612 XRenderColor col
= { 0, 0, 0, 0xffff * ( 1 - data
.brightness
) };
613 XRenderFillRectangle( display(), PictOpOver
, buffer
, &col
, x
, y
, width
, height
);
617 Picture alpha
= alphaMask( data
.opacity
);
618 XRenderComposite( display(), PictOpOver
, pic
, alpha
, buffer
, 0, 0, 0, 0,
619 x
, y
, width
, height
);
620 // fake brightness change by overlaying black
621 XRenderColor col
= { 0, 0, 0, 0xffff * ( 1 - data
.brightness
) * data
.opacity
};
622 XRenderFillRectangle( display(), PictOpOver
, buffer
, &col
, x
, y
, width
, height
);
623 transformed_shape
= QRegion();
626 if( xscale
!= 1 || yscale
!= 1 )
628 XTransform xform
= {{
629 { XDoubleToFixed( 1 ), XDoubleToFixed( 0 ), XDoubleToFixed( 0 ) },
630 { XDoubleToFixed( 0 ), XDoubleToFixed( 1 ), XDoubleToFixed( 0 ) },
631 { XDoubleToFixed( 0 ), XDoubleToFixed( 0 ), XDoubleToFixed( 1 ) }
633 XRenderSetPictureTransform( display(), pic
, &xform
);
634 if( filter
== ImageFilterGood
)
635 XRenderSetPictureFilter( display(), pic
, const_cast< char* >( "fast" ), NULL
, 0 );