~ubuntu-branches/ubuntu/gutsy/kdebase-workspace/gutsy-backports

« back to all changes in this revision

Viewing changes to kwin/scene_xrender.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jonathan Riddell
  • Date: 2007-09-05 20:45:14 UTC
  • Revision ID: james.westby@ubuntu.com-20070905204514-632hhspl0nvrc84i
Tags: upstream-3.93.0
ImportĀ upstreamĀ versionĀ 3.93.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*****************************************************************
 
2
 KWin - the KDE window manager
 
3
 This file is part of the KDE project.
 
4
 
 
5
Copyright (C) 2006 Lubos Lunak <l.lunak@kde.org>
 
6
 
 
7
You can Freely distribute this program under the GNU General Public
 
8
License. See the file "COPYING" for the exact licensing terms.
 
9
******************************************************************/
 
10
 
 
11
/*
 
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.
 
19
 
 
20
 Docs:
 
21
 
 
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
 
24
 
 
25
 XFixes (again, the protocol):
 
26
 http://gitweb.freedesktop.org/?p=xorg/proto/fixesproto.git;a=blob_plain;hb=HEAD;f=fixesproto.txt
 
27
 
 
28
*/
 
29
 
 
30
#include "scene_xrender.h"
 
31
 
 
32
#if defined(HAVE_XRENDER) && defined(HAVE_XFIXES)
 
33
 
 
34
#include "toplevel.h"
 
35
#include "client.h"
 
36
#include "deleted.h"
 
37
#include "effects.h"
 
38
 
 
39
#include <kxerrorhandler.h>
 
40
 
 
41
namespace KWin
 
42
{
 
43
 
 
44
//****************************************
 
45
// SceneXrender
 
46
//****************************************
 
47
 
 
48
// kDebug() support for the XserverRegion type
 
49
struct RegionDebug
 
50
   {   
 
51
   RegionDebug( XserverRegion r ) : rr( r ) {}   
 
52
   XserverRegion rr;   
 
53
   };   
 
54
 
 
55
#ifdef NDEBUG
 
56
inline
 
57
kndbgstream& operator<<( kndbgstream& stream, RegionDebug ) { return stream; }
 
58
#else
 
59
kdbgstream& operator<<( kdbgstream& stream, RegionDebug r )
 
60
    {       
 
61
    if( r.rr == None )
 
62
        return stream << "EMPTY";
 
63
    int num;
 
64
    XRectangle* rects = XFixesFetchRegion( display(), r.rr, &num );
 
65
    if( rects == NULL || num == 0 )
 
66
        return stream << "EMPTY";
 
67
    for( int i = 0;
 
68
         i < num;
 
69
         ++i )
 
70
       stream << "[" << rects[ i ].x << "+" << rects[ i ].y << " " << rects[ i ].width << "x" << rects[ i ].height << "]";
 
71
    return stream;
 
72
    }
 
73
#endif
 
74
 
 
75
Picture SceneXrender::buffer = None;
 
76
ScreenPaintData SceneXrender::screen_paint;
 
77
 
 
78
SceneXrender::SceneXrender( Workspace* ws )
 
79
    : Scene( ws )
 
80
    , front( None )
 
81
    , init_ok( false )
 
82
    {
 
83
    if( !Extensions::renderAvailable())
 
84
        {
 
85
        kDebug( 1212 ) << "No xrender extension available";
 
86
        return;
 
87
        }
 
88
    if( !Extensions::fixesRegionAvailable())
 
89
        {
 
90
        kDebug( 1212 ) << "No xfixes v3+ extension available";
 
91
        return;
 
92
        }
 
93
    KXErrorHandler xerr;
 
94
    // create XRender picture for the root window
 
95
    format = XRenderFindVisualFormat( display(), DefaultVisual( display(), DefaultScreen( display())));
 
96
    if( format == NULL )
 
97
        return; // error
 
98
    if( wspace->createOverlay())
 
99
        {
 
100
        wspace->setupOverlay( None );
 
101
        front = XRenderCreatePicture( display(), wspace->overlayWindow(), format, 0, NULL );
 
102
        }
 
103
    else
 
104
        {
 
105
        XRenderPictureAttributes pa;
 
106
        pa.subwindow_mode = IncludeInferiors;
 
107
        front = XRenderCreatePicture( display(), rootWindow(), format, CPSubwindowMode, &pa );
 
108
        }
 
109
    createBuffer();
 
110
    init_ok = !xerr.error( true );
 
111
    }
 
112
 
 
113
SceneXrender::~SceneXrender()
 
114
    {
 
115
    if( !init_ok )
 
116
        {
 
117
        // TODO this probably needs to clean up whatever has been created until the failure
 
118
        wspace->destroyOverlay();
 
119
        return;
 
120
        }
 
121
    XRenderFreePicture( display(), front );
 
122
    XRenderFreePicture( display(), buffer );
 
123
    wspace->destroyOverlay();
 
124
    foreach( Window* w, windows )
 
125
        delete w;
 
126
    }
 
127
 
 
128
bool SceneXrender::initFailed() const
 
129
    {
 
130
    return !init_ok;
 
131
    }
 
132
 
 
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()
 
136
    {
 
137
    if( buffer != None )
 
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
 
142
    }
 
143
 
 
144
// the entry point for painting
 
145
void SceneXrender::paint( QRegion damage, ToplevelList toplevels )
 
146
    {
 
147
    foreach( Toplevel* c, toplevels )
 
148
        {
 
149
        assert( windows.contains( c ));
 
150
        stacking_order.append( windows[ c ] );
 
151
        }
 
152
    int mask = 0;
 
153
    paintScreen( &mask, &damage );
 
154
    if( mask & PAINT_SCREEN_REGION )
 
155
        {
 
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 );
 
164
        XFlush( display());
 
165
        }
 
166
    else
 
167
        {
 
168
        // copy composed buffer to the root window
 
169
        XRenderComposite( display(), PictOpSrc, buffer, None, front, 0, 0, 0, 0, 0, 0, displayWidth(), displayHeight());
 
170
        XFlush( display());
 
171
        }
 
172
    // do cleanup
 
173
    stacking_order.clear();
 
174
    }
 
175
 
 
176
void SceneXrender::paintGenericScreen( int mask, ScreenPaintData data )
 
177
    {
 
178
    screen_paint = data; // save, transformations will be done when painting windows
 
179
    if( false ) // TODO never needed?
 
180
        Scene::paintGenericScreen( mask, data );
 
181
    else
 
182
        paintTransformedScreen( mask );
 
183
    }
 
184
 
 
185
/*
 
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.
 
191
 
 
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.
 
195
*/
 
196
void SceneXrender::paintTransformedScreen( int orig_mask )
 
197
    {
 
198
    QRegion region( 0, 0, displayWidth(), displayHeight());
 
199
    QList< Phase2Data > phase2;
 
200
    QRegion allclips;
 
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
 
204
         i >= 0;
 
205
         --i )
 
206
        {
 
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();
 
211
        data.paint = region;
 
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();
 
215
        // preparation step
 
216
        effects->prePaintWindow( effectWindow( w ), data, time_diff );
 
217
#ifndef NDEBUG
 
218
        foreach( WindowQuad q, data.quads )
 
219
            if( q.isTransformed())
 
220
                kFatal( 1212 ) << "Pre-paint calls are not allowed to transform quads!" ;
 
221
#endif
 
222
        if( !w->isPaintingEnabled())
 
223
            continue;
 
224
        data.paint -= allclips; // make sure to avoid already clipped areas
 
225
        if( data.paint.isEmpty()) // completely clipped
 
226
            continue;
 
227
        if( data.paint != region ) // prepaint added area to draw
 
228
            {
 
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
 
231
            }
 
232
        // If the window is transparent, the transparent part will be done
 
233
        // in the 2nd pass.
 
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 )
 
237
            {
 
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();
 
242
            }
 
243
        }
 
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.
 
249
    QRegion add_paint;
 
250
    foreach( Phase2Data d, phase2 )
 
251
        {
 
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;
 
257
        }
 
258
    }
 
259
 
 
260
// fill the screen background
 
261
void SceneXrender::paintBackground( QRegion region )
 
262
    {
 
263
    if( region != infiniteRegion())
 
264
        {
 
265
        XserverRegion background_region = toXserverRegion( region );
 
266
        XFixesSetPictureClipRegion( display(), buffer, 0, 0, background_region );
 
267
        XFixesDestroyRegion( display(), background_region );
 
268
        }
 
269
    XRenderColor col = { 0xffff, 0xffff, 0xffff, 0xffff };
 
270
    XRenderFillRectangle( display(), PictOpSrc, buffer, &col, 0, 0, displayWidth(), displayHeight());
 
271
    XFixesSetPictureClipRegion( display(), buffer, 0, 0, None );
 
272
    }
 
273
 
 
274
void SceneXrender::windowGeometryShapeChanged( Toplevel* c )
 
275
    {
 
276
    if( !windows.contains( c )) // this is ok, shape is not valid by default
 
277
        return;
 
278
    Window* w = windows[ c ];
 
279
    w->discardPicture();
 
280
    w->discardShape();
 
281
    w->discardAlpha();
 
282
    }
 
283
    
 
284
void SceneXrender::windowOpacityChanged( Toplevel* c )
 
285
    {
 
286
    if( !windows.contains( c )) // this is ok, alpha is created on demand
 
287
        return;
 
288
    Window* w = windows[ c ];
 
289
    w->discardAlpha();
 
290
    }
 
291
 
 
292
void SceneXrender::windowClosed( Toplevel* c, Deleted* deleted )
 
293
    {
 
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;
 
300
        }
 
301
    else
 
302
        {
 
303
        delete windows.take( c );
 
304
        c->effectWindow()->setSceneWindow( NULL );
 
305
        }
 
306
    }
 
307
 
 
308
void SceneXrender::windowDeleted( Deleted* c )
 
309
    {
 
310
    assert( windows.contains( c ));
 
311
    delete windows.take( c );
 
312
    c->effectWindow()->setSceneWindow( NULL );
 
313
    }
 
314
 
 
315
void SceneXrender::windowAdded( Toplevel* c )
 
316
    {
 
317
    assert( !windows.contains( c ));
 
318
    windows[ c ] = new Window( c );
 
319
    }
 
320
 
 
321
// Convert QRegion to XserverRegion. This code uses XserverRegion
 
322
// only when really necessary as the shared implementation uses
 
323
// QRegion.
 
324
XserverRegion SceneXrender::toXserverRegion( QRegion region )
 
325
    {
 
326
    QVector< QRect > rects = region.rects();
 
327
    XRectangle* xr = new XRectangle[ rects.count() ];
 
328
    for( int i = 0;
 
329
         i < rects.count();
 
330
         ++i )
 
331
        {
 
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();
 
336
        }
 
337
    XserverRegion ret = XFixesCreateRegion( display(), xr, rects.count());
 
338
    delete[] xr;
 
339
    return ret;
 
340
    }
 
341
 
 
342
//****************************************
 
343
// SceneXrender::Window
 
344
//****************************************
 
345
 
 
346
SceneXrender::Window::Window( Toplevel* c )
 
347
    : Scene::Window( c )
 
348
    , _picture( None )
 
349
    , format( XRenderFindVisualFormat( display(), c->visual()))
 
350
    , alpha( None )
 
351
    {
 
352
    }
 
353
 
 
354
SceneXrender::Window::~Window()
 
355
    {
 
356
    discardPicture();
 
357
    discardAlpha();
 
358
    discardShape();
 
359
    }
 
360
 
 
361
// Create XRender picture for the pixmap with the window contents.
 
362
Picture SceneXrender::Window::picture()
 
363
    {
 
364
    if( !toplevel->damage().isEmpty() && _picture != None )
 
365
        {
 
366
        XRenderFreePicture( display(), _picture );
 
367
        _picture = None;
 
368
        }
 
369
    if( _picture == None && format != NULL )
 
370
        {
 
371
        // Get the pixmap with the window contents.
 
372
        Pixmap pix = toplevel->windowPixmap();
 
373
        if( pix == None )
 
374
            return None;
 
375
        _picture = XRenderCreatePicture( display(), pix, format, 0, 0 );
 
376
        toplevel->resetDamage( toplevel->rect());
 
377
        }
 
378
    return _picture;
 
379
    }
 
380
 
 
381
 
 
382
void SceneXrender::Window::discardPicture()
 
383
    {
 
384
    if( _picture != None )
 
385
        XRenderFreePicture( display(), _picture );
 
386
    _picture = None;
 
387
    }
 
388
 
 
389
void SceneXrender::Window::discardAlpha()
 
390
    {
 
391
    if( alpha != None )
 
392
        XRenderFreePicture( display(), alpha );
 
393
    alpha = None;
 
394
    }
 
395
 
 
396
// Create XRender picture for the alpha mask.
 
397
Picture SceneXrender::Window::alphaMask( float opacity )
 
398
    {
 
399
    if( isOpaque() && opacity == 1.0 )
 
400
        return None;
 
401
    if( alpha != None && alpha_cached_opacity != opacity )
 
402
        {
 
403
        if( alpha != None )
 
404
            XRenderFreePicture( display(), alpha );
 
405
        alpha = None;
 
406
        }
 
407
    if( alpha != None )
 
408
        return alpha;
 
409
    if( opacity == 1.0 )
 
410
        { // no need to create alpha mask
 
411
        alpha_cached_opacity = 1.0;
 
412
        return None;
 
413
        }
 
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;
 
418
    pa.repeat = True;
 
419
    alpha = XRenderCreatePicture( display(), pixmap, format, CPRepeat, &pa );
 
420
    XFreePixmap( display(), pixmap );
 
421
    XRenderColor col;
 
422
    col.alpha = int( opacity * 0xffff );
 
423
    alpha_cached_opacity = opacity;
 
424
    XRenderFillRectangle( display(), PictOpSrc, alpha, &col, 0, 0, 1, 1 );
 
425
    return alpha;
 
426
    }
 
427
 
 
428
// paint the window
 
429
void SceneXrender::Window::performPaint( int mask, QRegion region, WindowPaintData data )
 
430
    {
 
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 ))
 
435
        {}
 
436
    else if( mask & PAINT_WINDOW_OPAQUE )
 
437
        {
 
438
        if( !opaque )
 
439
            return;
 
440
        }
 
441
    else if( mask & PAINT_WINDOW_TRANSLUCENT )
 
442
        {
 
443
        if( opaque )
 
444
            return;
 
445
        }
 
446
    if( region != infiniteRegion())
 
447
        {
 
448
        XserverRegion clip_region = toXserverRegion( region );
 
449
        XFixesSetPictureClipRegion( display(), buffer, 0, 0, clip_region );
 
450
        XFixesDestroyRegion( display(), clip_region );
 
451
        }
 
452
    Picture pic = picture(); // get XRender picture
 
453
    if( pic == None ) // The render format can be null for GL and/or Xv visuals
 
454
        return;
 
455
    // set picture filter
 
456
    if( options->smoothScale > 0 ) // only when forced, it's slow
 
457
        {
 
458
        if( mask & PAINT_WINDOW_TRANSFORMED )
 
459
            filter = ImageFilterGood;
 
460
        else if( mask & PAINT_SCREEN_TRANSFORMED )
 
461
            filter = ImageFilterGood;
 
462
        else
 
463
            filter = ImageFilterFast;
 
464
        }
 
465
    else
 
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();
 
472
    float xscale = 1;
 
473
    float yscale = 1;
 
474
    transformed_shape = shape();
 
475
    if( mask & PAINT_WINDOW_TRANSFORMED )
 
476
        {
 
477
        xscale *= data.xScale;
 
478
        yscale *= data.yScale;
 
479
        x += data.xTranslate;
 
480
        y += data.yTranslate;
 
481
        }
 
482
    if( mask & PAINT_SCREEN_TRANSFORMED )
 
483
        {
 
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;
 
490
        }
 
491
    if( yscale != 1 || xscale != 1 )
 
492
        {
 
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 ) }
 
497
        }};
 
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();
 
505
        for( int i = 0;
 
506
             i < rects.count();
 
507
             ++i )
 
508
            {
 
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 ));
 
512
            }
 
513
        transformed_shape.setRects( rects.constData(), rects.count());
 
514
        }
 
515
    if( x != toplevel->x() || y != toplevel->y())
 
516
        transformed_shape.translate( x, y );
 
517
    if( opaque )
 
518
        {
 
519
        XRenderComposite( display(), PictOpSrc, pic, None, buffer, 0, 0, 0, 0,
 
520
            x, y, width, height);
 
521
        }
 
522
    else
 
523
        {
 
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();
 
528
        }
 
529
    if( xscale != 1 || yscale != 1 )
 
530
        {
 
531
        XTransform xform = {{
 
532
            { XDoubleToFixed( 1 ), XDoubleToFixed( 0 ), XDoubleToFixed( 0 ) },
 
533
            { XDoubleToFixed( 0 ), XDoubleToFixed( 1 ), XDoubleToFixed( 0 ) },
 
534
            { XDoubleToFixed( 0 ), XDoubleToFixed( 0 ), XDoubleToFixed( 1 ) }
 
535
        }};
 
536
        XRenderSetPictureTransform( display(), pic, &xform );
 
537
        if( filter == ImageFilterGood )
 
538
            XRenderSetPictureFilter( display(), pic, const_cast< char* >( "fast" ), NULL, 0 );
 
539
        }
 
540
    XFixesSetPictureClipRegion( display(), buffer, 0, 0, None );
 
541
    }
 
542
 
 
543
} // namespace
 
544
#endif