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 (NOTE: The compositing code is work in progress. As such this design
23 documentation may get outdated in some areas.)
25 The base class for compositing, implementing shared functionality
26 between the OpenGL and XRender backends.
30 When compositing is turned on, XComposite extension is used to redirect
31 drawing of windows to pixmaps and XDamage extension is used to get informed
32 about damage (changes) to window contents. This code is mostly in composite.cpp .
34 Workspace::performCompositing() starts one painting pass. Painting is done
35 by painting the screen, which in turn paints every window. Painting can be affected
36 using effects, which are chained. E.g. painting a screen means that actually
37 paintScreen() of the first effect is called, which possibly does modifications
38 and calls next effect's paintScreen() and so on, until Scene::finalPaintScreen()
41 There are 3 phases of every paint (not necessarily done together):
42 The pre-paint phase, the paint phase and the post-paint phase.
44 The pre-paint phase is used to find out about how the painting will be actually
45 done (i.e. what the effects will do). For example when only a part of the screen
46 needs to be updated and no effect will do any transformation it is possible to use
47 an optimized paint function. How the painting will be done is controlled
48 by the mask argument, see PAINT_WINDOW_* and PAINT_SCREEN_* flags in scene.h .
49 For example an effect that decides to paint a normal windows as translucent
50 will need to modify the mask in its prePaintWindow() to include
51 the PAINT_WINDOW_TRANSLUCENT flag. The paintWindow() function will then get
52 the mask with this flag turned on and will also paint using transparency.
54 The paint pass does the actual painting, based on the information collected
55 using the pre-paint pass. After running through the effects' paintScreen()
56 either paintGenericScreen() or optimized paintSimpleScreen() are called.
57 Those call paintWindow() on windows (not necessarily all), possibly using
58 clipping to optimize performance and calling paintWindow() first with only
59 PAINT_WINDOW_OPAQUE to paint the opaque parts and then later
60 with PAINT_WINDOW_TRANSLUCENT to paint the transparent parts. Function
61 paintWindow() again goes through effects' paintWindow() until
62 finalPaintWindow() is called, which calls the window's performPaint() to
63 do the actual painting.
65 The post-paint can be used for cleanups and is also used for scheduling
66 repaints during the next painting pass for animations. Effects wanting to
67 repaint certain parts can manually damage them during post-paint and repaint
68 of these parts will be done during the next paint pass.
74 #include <X11/extensions/shape.h>
80 #include <kephal/screens.h>
85 //****************************************
87 //****************************************
91 Scene::Scene( Workspace
* ws
)
101 // returns mask and possibly modified region
102 void Scene::paintScreen( int* mask
, QRegion
* region
)
104 *mask
= ( *region
== QRegion( 0, 0, displayWidth(), displayHeight()))
105 ? 0 : PAINT_SCREEN_REGION
;
108 static_cast<EffectsHandlerImpl
*>(effects
)->startPaint();
109 ScreenPrePaintData pdata
;
111 pdata
.paint
= *region
;
112 effects
->prePaintScreen( pdata
, time_diff
);
114 *region
= pdata
.paint
;
115 if( *mask
& ( PAINT_SCREEN_TRANSFORMED
| PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS
))
116 { // Region painting is not possible with transformations,
117 // because screen damage doesn't match transformed positions.
118 *mask
&= ~PAINT_SCREEN_REGION
;
119 *region
= infiniteRegion();
121 else if( *mask
& PAINT_SCREEN_REGION
)
122 { // make sure not to go outside visible screen
123 *region
&= QRegion( 0, 0, displayWidth(), displayHeight());
126 { // whole screen, not transformed, force region to be full
127 *region
= QRegion( 0, 0, displayWidth(), displayHeight());
129 painted_region
= *region
;
130 if( *mask
& PAINT_SCREEN_BACKGROUND_FIRST
)
131 paintBackground( *region
);
132 ScreenPaintData data
;
133 effects
->paintScreen( *mask
, *region
, data
);
134 foreach( Window
* w
, stacking_order
)
135 effects
->postPaintWindow( effectWindow( w
));
136 effects
->postPaintScreen();
137 *region
|= painted_region
;
138 // make sure not to go outside of the screen area
139 *region
&= QRegion( 0, 0, displayWidth(), displayHeight());
140 // make sure all clipping is restored
141 Q_ASSERT( !PaintClipper::clip());
144 // Compute time since the last painting pass.
145 void Scene::updateTimeDiff()
147 if( last_time
.isNull())
149 // Painting has been idle (optimized out) for some time,
150 // which means time_diff would be huge and would break animations.
151 // Simply set it to one (zero would mean no change at all and could
156 time_diff
= last_time
.elapsed();
157 if( time_diff
< 0 ) // check time rollback
162 // Painting pass is optimized away.
165 // Don't break time since last paint for the next pass.
169 // the function that'll be eventually called by paintScreen() above
170 void Scene::finalPaintScreen( int mask
, QRegion region
, ScreenPaintData
& data
)
172 if( mask
& ( PAINT_SCREEN_TRANSFORMED
| PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS
))
173 paintGenericScreen( mask
, data
);
175 paintSimpleScreen( mask
, region
);
178 // The generic painting code that can handle even transformations.
179 // It simply paints bottom-to-top.
180 void Scene::paintGenericScreen( int orig_mask
, ScreenPaintData
)
182 if( !( orig_mask
& PAINT_SCREEN_BACKGROUND_FIRST
))
183 paintBackground( infiniteRegion());
184 QList
< Phase2Data
> phase2
;
185 foreach( Window
* w
, stacking_order
) // bottom to top
187 WindowPrePaintData data
;
188 data
.mask
= orig_mask
| ( w
->isOpaque() ? PAINT_WINDOW_OPAQUE
: PAINT_WINDOW_TRANSLUCENT
);
189 w
->resetPaintingEnabled();
190 data
.paint
= infiniteRegion(); // no clipping, so doesn't really matter
191 data
.clip
= QRegion();
192 data
.quads
= w
->buildQuads();
194 effects
->prePaintWindow( effectWindow( w
), data
, time_diff
);
196 foreach( const WindowQuad
&q
, data
.quads
)
197 if( q
.isTransformed())
198 kFatal( 1212 ) << "Pre-paint calls are not allowed to transform quads!" ;
200 if( !w
->isPaintingEnabled())
202 phase2
.append( Phase2Data( w
, infiniteRegion(), data
.clip
, data
.mask
, data
.quads
));
203 // transformations require window pixmap
204 w
->suspendUnredirect( data
.mask
205 & ( PAINT_WINDOW_TRANSLUCENT
| PAINT_SCREEN_TRANSFORMED
| PAINT_WINDOW_TRANSFORMED
));
208 foreach( const Phase2Data
&d
, phase2
)
209 paintWindow( d
.window
, d
.mask
, d
.region
, d
.quads
);
212 // The optimized case without any transformations at all.
213 // It can paint only the requested region and can use clipping
214 // to reduce painting and improve performance.
215 void Scene::paintSimpleScreen( int orig_mask
, QRegion region
)
217 // TODO PAINT_WINDOW_* flags don't belong here, that's why it's in the assert,
218 // perhaps the two enums should be separated
219 assert(( orig_mask
& ( PAINT_WINDOW_TRANSFORMED
| PAINT_SCREEN_TRANSFORMED
220 | PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS
221 | PAINT_WINDOW_TRANSLUCENT
| PAINT_WINDOW_OPAQUE
)) == 0 );
222 QHash
< Window
*, Phase2Data
> phase2data
;
223 // Draw each opaque window top to bottom, subtracting the bounding rect of
224 // each window from the clip region after it's been drawn.
225 for( int i
= stacking_order
.count() - 1; // top to bottom
229 Window
* w
= stacking_order
[ i
];
230 WindowPrePaintData data
;
231 data
.mask
= orig_mask
| ( w
->isOpaque() ? PAINT_WINDOW_OPAQUE
: PAINT_WINDOW_TRANSLUCENT
);
232 w
->resetPaintingEnabled();
234 data
.clip
= w
->isOpaque() ? w
->shape().translated( w
->x(), w
->y()) : QRegion();
235 data
.quads
= w
->buildQuads();
237 effects
->prePaintWindow( effectWindow( w
), data
, time_diff
);
239 foreach( const WindowQuad
&q
, data
.quads
)
240 if( q
.isTransformed())
241 kFatal( 1212 ) << "Pre-paint calls are not allowed to transform quads!" ;
242 if( data
.mask
& PAINT_WINDOW_TRANSFORMED
)
243 kFatal( 1212 ) << "PAINT_WINDOW_TRANSFORMED without PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS!";
245 if( !w
->isPaintingEnabled())
247 w
->suspendUnredirect( true );
250 if( data
.paint
!= region
) // prepaint added area to draw
251 painted_region
|= data
.paint
; // make sure it makes it to the screen
252 // Schedule the window for painting
253 phase2data
[w
] = Phase2Data( w
, data
.paint
, data
.clip
, data
.mask
, data
.quads
);
254 // no transformations, but translucency requires window pixmap
255 w
->suspendUnredirect( data
.mask
& PAINT_WINDOW_TRANSLUCENT
);
257 // Do the actual painting
258 // First opaque windows, top to bottom
259 // This also calculates correct paint regions for windows, also taking
262 for( int i
= stacking_order
.count() - 1; i
>= 0; --i
)
264 Window
* w
= stacking_order
[ i
];
265 if( !phase2data
.contains( w
))
267 Phase2Data d
= phase2data
[w
];
268 // Calculate correct paint region and take the clip region into account
269 d
.region
= painted_region
- allclips
;
271 if( d
.mask
& PAINT_WINDOW_TRANSLUCENT
)
273 // For translucent windows, the paint region must contain the
274 // entire painted area, except areas clipped by opaque windows
275 // above the translucent window
276 phase2data
[w
].region
= d
.region
;
280 // Paint the opaque window
281 paintWindow( d
.window
, d
.mask
, d
.region
, d
.quads
);
284 // Fill any areas of the root window not covered by windows
285 if( !( orig_mask
& PAINT_SCREEN_BACKGROUND_FIRST
))
286 paintBackground( painted_region
- allclips
);
287 // Now walk the list bottom to top, drawing translucent windows.
288 for( int i
= 0; i
< stacking_order
.count(); i
++ )
290 Window
* w
= stacking_order
[ i
];
291 if( !phase2data
.contains( w
))
293 Phase2Data d
= phase2data
[w
];
294 if( d
.mask
& PAINT_WINDOW_TRANSLUCENT
)
295 paintWindow( d
.window
, d
.mask
, d
.region
, d
.quads
);
299 void Scene::paintWindow( Window
* w
, int mask
, QRegion region
, WindowQuadList quads
)
301 // no painting outside visible screen (and no transformations)
302 region
&= QRect( 0, 0, displayWidth(), displayHeight());
303 if( region
.isEmpty()) // completely clipped
306 WindowPaintData
data( w
->window()->effectWindow());
308 effects
->paintWindow( effectWindow( w
), mask
, region
, data
);
311 // the function that'll be eventually called by paintWindow() above
312 void Scene::finalPaintWindow( EffectWindowImpl
* w
, int mask
, QRegion region
, WindowPaintData
& data
)
314 effects
->drawWindow( w
, mask
, region
, data
);
317 // will be eventually called from drawWindow()
318 void Scene::finalDrawWindow( EffectWindowImpl
* w
, int mask
, QRegion region
, WindowPaintData
& data
)
320 w
->sceneWindow()->performPaint( mask
, region
, data
);
323 QList
< QPoint
> Scene::selfCheckPoints() const
326 // Use Kephal directly, we're interested in "real" screens, not depending on our config.
327 // TODO: Does Kephal allow fake screens as well? We cannot use QDesktopWidget as it will cause a crash if
328 // the number of screens is different to what Kephal returns.
330 screen
< Kephal::ScreenUtils::numScreens();
332 { // test top-left and bottom-right of every screen
333 ret
.append( Kephal::ScreenUtils::screenGeometry( screen
).topLeft());
334 ret
.append( Kephal::ScreenUtils::screenGeometry( screen
).bottomRight() + QPoint( -3 + 1, -2 + 1 )
335 + QPoint( -1, 0 )); // intentionally moved one up, since the source windows will be one down
340 //****************************************
342 //****************************************
344 Scene::Window::Window( Toplevel
* c
)
346 , filter( ImageFilterFast
)
347 , disable_painting( 0 )
348 , shape_valid( false )
349 , cached_quad_list( NULL
)
353 Scene::Window::~Window()
355 delete cached_quad_list
;
358 void Scene::Window::discardShape()
360 // it is created on-demand and cached, simply
363 delete cached_quad_list
;
364 cached_quad_list
= NULL
;
367 // Find out the shape of the window using the XShape extension
368 // or if shape is not set then simply it's the window geometry.
369 QRegion
Scene::Window::shape() const
373 Client
* c
= dynamic_cast< Client
* >( toplevel
);
374 if( toplevel
->shape() || ( c
!= NULL
&& !c
->mask().isEmpty()))
377 XRectangle
* rects
= XShapeGetRectangles( display(), toplevel
->frameId(),
378 ShapeBounding
, &count
, &order
);
381 shape_region
= QRegion();
385 shape_region
+= QRegion( rects
[ i
].x
, rects
[ i
].y
,
386 rects
[ i
].width
, rects
[ i
].height
);
388 // make sure the shape is sane (X is async, maybe even XShape is broken)
389 shape_region
&= QRegion( 0, 0, width(), height());
392 shape_region
= QRegion();
395 shape_region
= QRegion( 0, 0, width(), height());
401 bool Scene::Window::isVisible() const
403 if( dynamic_cast< Deleted
* >( toplevel
) != NULL
)
405 if( !toplevel
->isOnCurrentDesktop())
407 if( Client
* c
= dynamic_cast< Client
* >( toplevel
))
408 return c
->isShown( true );
409 return true; // Unmanaged is always visible
410 // TODO there may be transformations, so ignore this for now
411 return !toplevel
->geometry()
412 .intersected( QRect( 0, 0, displayWidth(), displayHeight()))
416 bool Scene::Window::isOpaque() const
418 return toplevel
->opacity() == 1.0 && !toplevel
->hasAlpha();
421 bool Scene::Window::isPaintingEnabled() const
423 return !disable_painting
;
426 void Scene::Window::resetPaintingEnabled()
428 disable_painting
= 0;
429 if( dynamic_cast< Deleted
* >( toplevel
) != NULL
)
430 disable_painting
|= PAINT_DISABLED_BY_DELETE
;
431 if( !toplevel
->isOnCurrentDesktop())
432 disable_painting
|= PAINT_DISABLED_BY_DESKTOP
;
433 if( Client
* c
= dynamic_cast< Client
* >( toplevel
))
435 if( c
->isMinimized() )
436 disable_painting
|= PAINT_DISABLED_BY_MINIMIZE
;
437 if( c
->isHiddenInternal())
438 disable_painting
|= PAINT_DISABLED
;
442 void Scene::Window::enablePainting( int reason
)
444 disable_painting
&= ~reason
;
447 void Scene::Window::disablePainting( int reason
)
449 disable_painting
|= reason
;
452 WindowQuadList
Scene::Window::buildQuads( bool force
) const
454 if( cached_quad_list
!= NULL
&& !force
)
455 return *cached_quad_list
;
457 if( toplevel
->clientPos() == QPoint( 0, 0 ) && toplevel
->clientSize() == toplevel
->size())
458 ret
= makeQuads( WindowQuadContents
, shape()); // has no decoration
461 QRegion contents
= shape() & QRect( toplevel
->clientPos(), toplevel
->clientSize());
462 QRegion decoration
= shape() - contents
;
463 ret
= makeQuads( WindowQuadContents
, contents
);
464 ret
+= makeQuads( WindowQuadDecoration
, decoration
);
466 effects
->buildQuads( static_cast<Client
*>( toplevel
)->effectWindow(), ret
);
467 cached_quad_list
= new WindowQuadList( ret
);
471 WindowQuadList
Scene::Window::makeQuads( WindowQuadType type
, const QRegion
& reg
) const
474 foreach( const QRect
&r
, reg
.rects())
476 WindowQuad
quad( type
);
477 // TODO asi mam spatne pravy dolni roh - bud tady, nebo v jinych castech
478 quad
[ 0 ] = WindowVertex( r
.x(), r
.y(), r
.x(), r
.y());
479 quad
[ 1 ] = WindowVertex( r
.x() + r
.width(), r
.y(), r
.x() + r
.width(), r
.y());
480 quad
[ 2 ] = WindowVertex( r
.x() + r
.width(), r
.y() + r
.height(), r
.x() + r
.width(), r
.y() + r
.height());
481 quad
[ 3 ] = WindowVertex( r
.x(), r
.y() + r
.height(), r
.x(), r
.y() + r
.height());