add more spacing
[personal-kdebase.git] / workspace / kwin / scene.cpp
blob3acf357b99b182cdfb779f068e03d36c21a8dec9
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.
28 Design:
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()
39 is called.
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.
72 #include "scene.h"
74 #include <X11/extensions/shape.h>
76 #include "client.h"
77 #include "deleted.h"
78 #include "effects.h"
80 #include <kephal/screens.h>
82 namespace KWin
85 //****************************************
86 // Scene
87 //****************************************
89 Scene* scene;
91 Scene::Scene( Workspace* ws )
92 : wspace( ws ),
93 has_waitSync( false )
97 Scene::~Scene()
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;
106 updateTimeDiff();
107 // preparation step
108 static_cast<EffectsHandlerImpl*>(effects)->startPaint();
109 ScreenPrePaintData pdata;
110 pdata.mask = *mask;
111 pdata.paint = *region;
112 effects->prePaintScreen( pdata, time_diff );
113 *mask = pdata.mask;
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());
125 else
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
152 // cause problems).
153 time_diff = 1;
155 else
156 time_diff = last_time.elapsed();
157 if( time_diff < 0 ) // check time rollback
158 time_diff = 1;
159 last_time.start();;
162 // Painting pass is optimized away.
163 void Scene::idle()
165 // Don't break time since last paint for the next pass.
166 last_time = QTime();
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 );
174 else
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();
193 // preparation step
194 effects->prePaintWindow( effectWindow( w ), data, time_diff );
195 #ifndef NDEBUG
196 foreach( const WindowQuad &q, data.quads )
197 if( q.isTransformed())
198 kFatal( 1212 ) << "Pre-paint calls are not allowed to transform quads!" ;
199 #endif
200 if( !w->isPaintingEnabled())
201 continue;
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
226 i >= 0;
227 --i )
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();
233 data.paint = region;
234 data.clip = w->isOpaque() ? w->shape().translated( w->x(), w->y()) : QRegion();
235 data.quads = w->buildQuads();
236 // preparation step
237 effects->prePaintWindow( effectWindow( w ), data, time_diff );
238 #ifndef NDEBUG
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!";
244 #endif
245 if( !w->isPaintingEnabled())
247 w->suspendUnredirect( true );
248 continue;
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
260 // care of clipping
261 QRegion allclips;
262 for( int i = stacking_order.count() - 1; i >= 0; --i )
264 Window* w = stacking_order[ i ];
265 if( !phase2data.contains( w ))
266 continue;
267 Phase2Data d = phase2data[w];
268 // Calculate correct paint region and take the clip region into account
269 d.region = painted_region - allclips;
270 allclips |= d.clip;
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;
278 else
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 ))
292 continue;
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
304 return;
306 WindowPaintData data( w->window()->effectWindow());
307 data.quads = quads;
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
325 QList< QPoint > ret;
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.
329 for( int screen = 0;
330 screen < Kephal::ScreenUtils::numScreens();
331 ++screen )
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
337 return ret;
340 //****************************************
341 // Scene::Window
342 //****************************************
344 Scene::Window::Window( Toplevel * c )
345 : 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
361 // reset the flag
362 shape_valid = false;
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
371 if( !shape_valid )
373 Client* c = dynamic_cast< Client* >( toplevel );
374 if( toplevel->shape() || ( c != NULL && !c->mask().isEmpty()))
376 int count, order;
377 XRectangle* rects = XShapeGetRectangles( display(), toplevel->frameId(),
378 ShapeBounding, &count, &order );
379 if(rects)
381 shape_region = QRegion();
382 for( int i = 0;
383 i < count;
384 ++i )
385 shape_region += QRegion( rects[ i ].x, rects[ i ].y,
386 rects[ i ].width, rects[ i ].height );
387 XFree(rects);
388 // make sure the shape is sane (X is async, maybe even XShape is broken)
389 shape_region &= QRegion( 0, 0, width(), height());
391 else
392 shape_region = QRegion();
394 else
395 shape_region = QRegion( 0, 0, width(), height());
396 shape_valid = true;
398 return shape_region;
401 bool Scene::Window::isVisible() const
403 if( dynamic_cast< Deleted* >( toplevel ) != NULL )
404 return false;
405 if( !toplevel->isOnCurrentDesktop())
406 return false;
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()))
413 .isEmpty();
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;
456 WindowQuadList ret;
457 if( toplevel->clientPos() == QPoint( 0, 0 ) && toplevel->clientSize() == toplevel->size())
458 ret = makeQuads( WindowQuadContents, shape()); // has no decoration
459 else
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 );
468 return ret;
471 WindowQuadList Scene::Window::makeQuads( WindowQuadType type, const QRegion& reg ) const
473 WindowQuadList ret;
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());
482 ret.append( quad );
484 return ret;
487 } // namespace