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
You can Freely distribute this program under the GNU General Public
8
License. See the file "COPYING" for the exact licensing terms.
9
******************************************************************/
12
This is the XRender-based compositing code. The primary compositing
13
backend is the OpenGL-based one, which should be more powerful
14
and also possibly better documented. This backend is mostly for cases
15
when the OpenGL backend cannot be used for some reason (insufficient
16
performance, no usable OpenGL support at all, etc.)
17
The plan is to keep it around as long as needed/possible, but if it
18
proves to be too much hassle it will be dropped in the future.
22
XRender (the protocol, but the function calls map to it):
23
http://gitweb.freedesktop.org/?p=xorg/proto/renderproto.git;a=blob_plain;hb=HEAD;f=renderproto.txt
25
XFixes (again, the protocol):
26
http://gitweb.freedesktop.org/?p=xorg/proto/fixesproto.git;a=blob_plain;hb=HEAD;f=fixesproto.txt
30
#include "scene_xrender.h"
32
#if defined(HAVE_XRENDER) && defined(HAVE_XFIXES)
39
#include <kxerrorhandler.h>
44
//****************************************
46
//****************************************
48
// kDebug() support for the XserverRegion type
51
RegionDebug( XserverRegion r ) : rr( r ) {}
57
kndbgstream& operator<<( kndbgstream& stream, RegionDebug ) { return stream; }
59
kdbgstream& operator<<( kdbgstream& stream, RegionDebug r )
62
return stream << "EMPTY";
64
XRectangle* rects = XFixesFetchRegion( display(), r.rr, &num );
65
if( rects == NULL || num == 0 )
66
return stream << "EMPTY";
70
stream << "[" << rects[ i ].x << "+" << rects[ i ].y << " " << rects[ i ].width << "x" << rects[ i ].height << "]";
75
Picture SceneXrender::buffer = None;
76
ScreenPaintData SceneXrender::screen_paint;
78
SceneXrender::SceneXrender( Workspace* ws )
83
if( !Extensions::renderAvailable())
85
kDebug( 1212 ) << "No xrender extension available";
88
if( !Extensions::fixesRegionAvailable())
90
kDebug( 1212 ) << "No xfixes v3+ extension available";
94
// create XRender picture for the root window
95
format = XRenderFindVisualFormat( display(), DefaultVisual( display(), DefaultScreen( display())));
98
if( wspace->createOverlay())
100
wspace->setupOverlay( None );
101
front = XRenderCreatePicture( display(), wspace->overlayWindow(), format, 0, NULL );
105
XRenderPictureAttributes pa;
106
pa.subwindow_mode = IncludeInferiors;
107
front = XRenderCreatePicture( display(), rootWindow(), format, CPSubwindowMode, &pa );
110
init_ok = !xerr.error( true );
113
SceneXrender::~SceneXrender()
117
// TODO this probably needs to clean up whatever has been created until the failure
118
wspace->destroyOverlay();
121
XRenderFreePicture( display(), front );
122
XRenderFreePicture( display(), buffer );
123
wspace->destroyOverlay();
124
foreach( Window* w, windows )
128
bool SceneXrender::initFailed() const
133
// Create the compositing buffer. The root window is not double-buffered,
134
// so it is done manually using this buffer,
135
void SceneXrender::createBuffer()
138
XRenderFreePicture( display(), buffer );
139
Pixmap pixmap = XCreatePixmap( display(), rootWindow(), displayWidth(), displayHeight(), DefaultDepth( display(), DefaultScreen( display())));
140
buffer = XRenderCreatePicture( display(), pixmap, format, 0, 0 );
141
XFreePixmap( display(), pixmap ); // The picture owns the pixmap now
144
// the entry point for painting
145
void SceneXrender::paint( QRegion damage, ToplevelList toplevels )
147
foreach( Toplevel* c, toplevels )
149
assert( windows.contains( c ));
150
stacking_order.append( windows[ c ] );
153
paintScreen( &mask, &damage );
154
if( mask & PAINT_SCREEN_REGION )
156
// Use the damage region as the clip region for the root window
157
XserverRegion front_region = toXserverRegion( damage );
158
XFixesSetPictureClipRegion( display(), front, 0, 0, front_region );
159
XFixesDestroyRegion( display(), front_region );
160
// copy composed buffer to the root window
161
XFixesSetPictureClipRegion( display(), buffer, 0, 0, None );
162
XRenderComposite( display(), PictOpSrc, buffer, None, front, 0, 0, 0, 0, 0, 0, displayWidth(), displayHeight());
163
XFixesSetPictureClipRegion( display(), front, 0, 0, None );
168
// copy composed buffer to the root window
169
XRenderComposite( display(), PictOpSrc, buffer, None, front, 0, 0, 0, 0, 0, 0, displayWidth(), displayHeight());
173
stacking_order.clear();
176
void SceneXrender::paintGenericScreen( int mask, ScreenPaintData data )
178
screen_paint = data; // save, transformations will be done when painting windows
179
if( false ) // TODO never needed?
180
Scene::paintGenericScreen( mask, data );
182
paintTransformedScreen( mask );
186
Try to do optimized painting even with transformations. Since only scaling
187
and translation are supported by the painting code, clipping can be done
188
manually to avoid having to paint everything in every pass. Whole screen
189
still need to be painted but e.g. obscured windows don't. So this below
190
is basically paintSimpleScreen() with extra code to compute clipping correctly.
192
This code assumes that the only transformations possible with XRender are those
193
provided by Window/ScreenPaintData, In the (very unlikely?) case more is needed
194
then paintGenericScreen() needs to be used.
196
void SceneXrender::paintTransformedScreen( int orig_mask )
198
QRegion region( 0, 0, displayWidth(), displayHeight());
199
QList< Phase2Data > phase2;
201
// Draw each opaque window top to bottom, subtracting the bounding rect of
202
// each window from the clip region after it's been drawn.
203
for( int i = stacking_order.count() - 1; // top to bottom
207
Window* w = static_cast< Window* >( stacking_order[ i ] );
208
WindowPrePaintData data;
209
data.mask = orig_mask | ( w->isOpaque() ? PAINT_WINDOW_OPAQUE : PAINT_WINDOW_TRANSLUCENT );
210
w->resetPaintingEnabled();
212
// TODO this is wrong, transformedShape() should be used here, but is not known yet
213
data.clip = w->isOpaque() ? region : QRegion();
214
data.quads = w->buildQuads();
216
effects->prePaintWindow( effectWindow( w ), data, time_diff );
218
foreach( WindowQuad q, data.quads )
219
if( q.isTransformed())
220
kFatal( 1212 ) << "Pre-paint calls are not allowed to transform quads!" ;
222
if( !w->isPaintingEnabled())
224
data.paint -= allclips; // make sure to avoid already clipped areas
225
if( data.paint.isEmpty()) // completely clipped
227
if( data.paint != region ) // prepaint added area to draw
229
region |= data.paint; // make sure other windows in that area get painted too
230
painted_region |= data.paint; // make sure it makes it to the screen
232
// If the window is transparent, the transparent part will be done
234
if( data.mask & PAINT_WINDOW_TRANSLUCENT )
235
phase2.prepend( Phase2Data( w, data.paint, data.mask, data.quads ));
236
if( data.mask & PAINT_WINDOW_OPAQUE )
238
w->setTransformedShape( QRegion());
239
paintWindow( w, data.mask, data.paint, data.quads );
240
// The window can clip by its opaque parts the windows below.
241
region -= w->transformedShape();
244
if( !( orig_mask & PAINT_SCREEN_BACKGROUND_FIRST ))
245
paintBackground( region ); // Fill any areas of the root window not covered by windows
246
// Now walk the list bottom to top, drawing translucent windows.
247
// That we draw bottom to top is important now since we're drawing translucent objects
248
// and also are clipping only by opaque windows.
250
foreach( Phase2Data d, phase2 )
252
Scene::Window* w = d.window;
253
paintWindow( w, d.mask, d.region | add_paint, d.quads );
254
// It is necessary to also add paint regions of windows below, because their
255
// pre-paint's might have extended the paint area, so those areas need to be painted too.
256
add_paint |= d.region;
260
// fill the screen background
261
void SceneXrender::paintBackground( QRegion region )
263
if( region != infiniteRegion())
265
XserverRegion background_region = toXserverRegion( region );
266
XFixesSetPictureClipRegion( display(), buffer, 0, 0, background_region );
267
XFixesDestroyRegion( display(), background_region );
269
XRenderColor col = { 0xffff, 0xffff, 0xffff, 0xffff };
270
XRenderFillRectangle( display(), PictOpSrc, buffer, &col, 0, 0, displayWidth(), displayHeight());
271
XFixesSetPictureClipRegion( display(), buffer, 0, 0, None );
274
void SceneXrender::windowGeometryShapeChanged( Toplevel* c )
276
if( !windows.contains( c )) // this is ok, shape is not valid by default
278
Window* w = windows[ c ];
284
void SceneXrender::windowOpacityChanged( Toplevel* c )
286
if( !windows.contains( c )) // this is ok, alpha is created on demand
288
Window* w = windows[ c ];
292
void SceneXrender::windowClosed( Toplevel* c, Deleted* deleted )
294
assert( windows.contains( c ));
295
if( deleted != NULL )
296
{ // replace c with deleted
297
Window* w = windows.take( c );
298
w->updateToplevel( deleted );
299
windows[ deleted ] = w;
303
delete windows.take( c );
304
c->effectWindow()->setSceneWindow( NULL );
308
void SceneXrender::windowDeleted( Deleted* c )
310
assert( windows.contains( c ));
311
delete windows.take( c );
312
c->effectWindow()->setSceneWindow( NULL );
315
void SceneXrender::windowAdded( Toplevel* c )
317
assert( !windows.contains( c ));
318
windows[ c ] = new Window( c );
321
// Convert QRegion to XserverRegion. This code uses XserverRegion
322
// only when really necessary as the shared implementation uses
324
XserverRegion SceneXrender::toXserverRegion( QRegion region )
326
QVector< QRect > rects = region.rects();
327
XRectangle* xr = new XRectangle[ rects.count() ];
332
xr[ i ].x = rects[ i ].x();
333
xr[ i ].y = rects[ i ].y();
334
xr[ i ].width = rects[ i ].width();
335
xr[ i ].height = rects[ i ].height();
337
XserverRegion ret = XFixesCreateRegion( display(), xr, rects.count());
342
//****************************************
343
// SceneXrender::Window
344
//****************************************
346
SceneXrender::Window::Window( Toplevel* c )
349
, format( XRenderFindVisualFormat( display(), c->visual()))
354
SceneXrender::Window::~Window()
361
// Create XRender picture for the pixmap with the window contents.
362
Picture SceneXrender::Window::picture()
364
if( !toplevel->damage().isEmpty() && _picture != None )
366
XRenderFreePicture( display(), _picture );
369
if( _picture == None && format != NULL )
371
// Get the pixmap with the window contents.
372
Pixmap pix = toplevel->windowPixmap();
375
_picture = XRenderCreatePicture( display(), pix, format, 0, 0 );
376
toplevel->resetDamage( toplevel->rect());
382
void SceneXrender::Window::discardPicture()
384
if( _picture != None )
385
XRenderFreePicture( display(), _picture );
389
void SceneXrender::Window::discardAlpha()
392
XRenderFreePicture( display(), alpha );
396
// Create XRender picture for the alpha mask.
397
Picture SceneXrender::Window::alphaMask( float opacity )
399
if( isOpaque() && opacity == 1.0 )
401
if( alpha != None && alpha_cached_opacity != opacity )
404
XRenderFreePicture( display(), alpha );
410
{ // no need to create alpha mask
411
alpha_cached_opacity = 1.0;
414
// Create a 1x1 8bpp pixmap containing the given opacity in the alpha channel.
415
Pixmap pixmap = XCreatePixmap( display(), rootWindow(), 1, 1, 8 );
416
XRenderPictFormat* format = XRenderFindStandardFormat( display(), PictStandardA8 );
417
XRenderPictureAttributes pa;
419
alpha = XRenderCreatePicture( display(), pixmap, format, CPRepeat, &pa );
420
XFreePixmap( display(), pixmap );
422
col.alpha = int( opacity * 0xffff );
423
alpha_cached_opacity = opacity;
424
XRenderFillRectangle( display(), PictOpSrc, alpha, &col, 0, 0, 1, 1 );
429
void SceneXrender::Window::performPaint( int mask, QRegion region, WindowPaintData data )
431
setTransformedShape( QRegion()); // maybe nothing will be painted
432
// check if there is something to paint
433
bool opaque = isOpaque() && data.opacity == 1.0;
434
if( mask & ( PAINT_WINDOW_OPAQUE | PAINT_WINDOW_TRANSLUCENT ))
436
else if( mask & PAINT_WINDOW_OPAQUE )
441
else if( mask & PAINT_WINDOW_TRANSLUCENT )
446
if( region != infiniteRegion())
448
XserverRegion clip_region = toXserverRegion( region );
449
XFixesSetPictureClipRegion( display(), buffer, 0, 0, clip_region );
450
XFixesDestroyRegion( display(), clip_region );
452
Picture pic = picture(); // get XRender picture
453
if( pic == None ) // The render format can be null for GL and/or Xv visuals
455
// set picture filter
456
if( options->smoothScale > 0 ) // only when forced, it's slow
458
if( mask & PAINT_WINDOW_TRANSFORMED )
459
filter = ImageFilterGood;
460
else if( mask & PAINT_SCREEN_TRANSFORMED )
461
filter = ImageFilterGood;
463
filter = ImageFilterFast;
466
filter = ImageFilterFast;
467
// do required transformations
468
int x = toplevel->x();
469
int y = toplevel->y();
470
int width = toplevel->width();
471
int height = toplevel->height();
474
transformed_shape = shape();
475
if( mask & PAINT_WINDOW_TRANSFORMED )
477
xscale *= data.xScale;
478
yscale *= data.yScale;
479
x += data.xTranslate;
480
y += data.yTranslate;
482
if( mask & PAINT_SCREEN_TRANSFORMED )
484
xscale *= screen_paint.xScale;
485
yscale *= screen_paint.yScale;
486
x = int( x * screen_paint.xScale );
487
y = int( y * screen_paint.yScale );
488
x += screen_paint.xTranslate;
489
y += screen_paint.yTranslate;
491
if( yscale != 1 || xscale != 1 )
493
XTransform xform = {{
494
{ XDoubleToFixed( 1 / xscale ), XDoubleToFixed( 0 ), XDoubleToFixed( 0 ) },
495
{ XDoubleToFixed( 0 ), XDoubleToFixed( 1 / yscale ), XDoubleToFixed( 0 ) },
496
{ XDoubleToFixed( 0 ), XDoubleToFixed( 0 ), XDoubleToFixed( 1 ) }
498
XRenderSetPictureTransform( display(), pic, &xform );
499
width = (int)(width * xscale);
500
height = (int)(height * yscale);
501
if( filter == ImageFilterGood )
502
XRenderSetPictureFilter( display(), pic, const_cast< char* >( "good" ), NULL, 0 );
503
// transform the shape for clipping in paintTransformedScreen()
504
QVector< QRect > rects = transformed_shape.rects();
509
QRect& r = rects[ i ];
510
r = QRect( int( r.x() * xscale ), int( r.y() * yscale ),
511
int( r.width() * xscale ), int( r.height() * xscale ));
513
transformed_shape.setRects( rects.constData(), rects.count());
515
if( x != toplevel->x() || y != toplevel->y())
516
transformed_shape.translate( x, y );
519
XRenderComposite( display(), PictOpSrc, pic, None, buffer, 0, 0, 0, 0,
520
x, y, width, height);
524
Picture alpha = alphaMask( data.opacity );
525
XRenderComposite( display(), PictOpOver, pic, alpha, buffer, 0, 0, 0, 0,
526
x, y, width, height);
527
transformed_shape = QRegion();
529
if( xscale != 1 || yscale != 1 )
531
XTransform xform = {{
532
{ XDoubleToFixed( 1 ), XDoubleToFixed( 0 ), XDoubleToFixed( 0 ) },
533
{ XDoubleToFixed( 0 ), XDoubleToFixed( 1 ), XDoubleToFixed( 0 ) },
534
{ XDoubleToFixed( 0 ), XDoubleToFixed( 0 ), XDoubleToFixed( 1 ) }
536
XRenderSetPictureTransform( display(), pic, &xform );
537
if( filter == ImageFilterGood )
538
XRenderSetPictureFilter( display(), pic, const_cast< char* >( "fast" ), NULL, 0 );
540
XFixesSetPictureClipRegion( display(), buffer, 0, 0, None );