not quite so much needs to be delayed to the init() function
[personal-kdebase.git] / workspace / kwin / composite.cpp
blob3579c42ff4b52ec552eef7ec84361bf5a346da4c
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 Code related to compositing (redirecting windows to pixmaps and tracking
23 window damage).
25 Docs:
27 XComposite (the protocol, but the function calls map to it):
28 http://gitweb.freedesktop.org/?p=xorg/proto/compositeproto.git;a=blob_plain;hb=HEAD;f=compositeproto.txt
30 XDamage (again the protocol):
31 http://gitweb.freedesktop.org/?p=xorg/proto/damageproto.git;a=blob_plain;hb=HEAD;f=damageproto.txt
33 Paper including basics on compositing, XGL vs AIGLX, XRender vs OpenGL, etc.:
34 http://www.vis.uni-stuttgart.de/~hopf/pub/LinuxTag2007_compiz_NextGenerationDesktop_Paper.pdf
36 Composite HOWTO from Fredrik:
37 http://ktown.kde.org/~fredrik/composite_howto.html
41 #include <config-X11.h>
43 #include "utils.h"
44 #include <QTextStream>
45 #include "workspace.h"
46 #include "client.h"
47 #include "unmanaged.h"
48 #include "deleted.h"
49 #include "effects.h"
50 #include "scene.h"
51 #include "scene_basic.h"
52 #include "scene_xrender.h"
53 #include "scene_opengl.h"
54 #include "compositingprefs.h"
55 #include "notifications.h"
57 #include <stdio.h>
59 #include <QMenu>
60 #include <kaction.h>
61 #include <kactioncollection.h>
62 #include <klocale.h>
63 #include <kxerrorhandler.h>
65 #include <X11/extensions/shape.h>
67 #ifdef HAVE_XCOMPOSITE
68 #include <X11/extensions/Xcomposite.h>
69 #if XCOMPOSITE_MAJOR > 0 || XCOMPOSITE_MINOR >= 3
70 #define HAVE_XCOMPOSITE_OVERLAY
71 #endif
72 #endif
73 #ifdef HAVE_XRANDR
74 #include <X11/extensions/Xrandr.h>
75 #endif
77 namespace KWin
80 //****************************************
81 // Workspace
82 //****************************************
84 void Workspace::setupCompositing()
86 #ifdef KWIN_HAVE_COMPOSITING
87 if( scene != NULL )
88 return;
89 if( !options->useCompositing && getenv( "KWIN_COMPOSE") == NULL )
91 kDebug( 1212 ) << "Compositing is turned off in options or disabled";
92 return;
94 else if( compositingSuspended )
96 kDebug( 1212 ) << "Compositing is suspended";
97 return;
99 else if( !CompositingPrefs::compositingPossible() )
101 kError( 1212 ) << "Compositing is not possible";
102 return;
104 CompositingType type = options->compositingMode;
105 if( getenv( "KWIN_COMPOSE" ))
107 char c = getenv( "KWIN_COMPOSE" )[ 0 ];
108 switch( c )
110 case 'O':
111 type = OpenGLCompositing;
112 break;
113 case 'X':
114 type = XRenderCompositing;
115 break;
116 default:
117 kDebug( 1212 ) << "No compositing";
118 return;
122 char selection_name[ 100 ];
123 sprintf( selection_name, "_NET_WM_CM_S%d", DefaultScreen( display()));
124 cm_selection = new KSelectionOwner( selection_name );
125 connect( cm_selection, SIGNAL( lostOwnership()), SLOT( lostCMSelection()));
126 cm_selection->claim( true ); // force claiming
128 switch( type )
130 /*case 'B':
131 kDebug( 1212 ) << "X compositing";
132 scene = new SceneBasic( this );
133 break; // don't fall through (this is a testing one) */
134 #ifdef KWIN_HAVE_OPENGL_COMPOSITING
135 case OpenGLCompositing:
136 kDebug( 1212 ) << "OpenGL compositing";
137 scene = new SceneOpenGL( this );
138 if( !scene->initFailed())
139 break; // -->
140 delete scene;
141 scene = NULL;
142 break; // do not fall back to XRender for now, maybe in the future
143 #endif
144 #ifdef KWIN_HAVE_XRENDER_COMPOSITING
145 case XRenderCompositing:
146 kDebug( 1212 ) << "XRender compositing";
147 scene = new SceneXrender( this );
148 break;
149 #endif
150 default:
151 #ifndef KWIN_HAVE_COMPOSITING
152 kDebug( 1212 ) << "Compositing was not available at compile time";
153 #else
154 kDebug( 1212 ) << "No compositing";
155 #endif
156 delete cm_selection;
157 return;
159 if( scene == NULL || scene->initFailed())
161 kError( 1212 ) << "Failed to initialize compositing, compositing disabled";
162 kError( 1212 ) << "Consult http://techbase.kde.org/Projects/KWin/4.0-release-notes#Setting_up";
163 delete scene;
164 scene = NULL;
165 delete cm_selection;
166 return;
168 int rate = 0;
169 if( options->refreshRate > 0 )
170 { // use manually configured refresh rate
171 rate = options->refreshRate;
173 #ifdef HAVE_XRANDR
174 else
175 { // autoconfigure refresh rate based on XRandR info
176 if( Extensions::randrAvailable() )
178 XRRScreenConfiguration *config;
180 config = XRRGetScreenInfo( display(), rootWindow() );
181 rate = XRRConfigCurrentRate( config );
182 XRRFreeScreenConfigInfo( config );
185 #endif
186 // 0Hz or less is invalid, so we fallback to a default rate
187 if( rate <= 0 )
188 rate = 50;
189 // QTimer gives us 1msec (1000Hz) at best, so we ignore anything higher;
190 // however, additional throttling prevents very high rates from taking place anyway
191 else if( rate > 1000 )
192 rate = 1000;
193 kDebug( 1212 ) << "Refresh rate " << rate << "Hz";
194 compositeRate = 1000 / rate;
195 lastCompositePaint.start();
196 // fake a previous paint, so that the next starts right now
197 nextPaintReference = QTime::currentTime().addMSecs( -compositeRate );
198 compositeTimer.setSingleShot( true );
199 checkCompositeTimer();
200 composite_paint_times.clear();
201 XCompositeRedirectSubwindows( display(), rootWindow(), CompositeRedirectManual );
202 new EffectsHandlerImpl( scene->compositingType() ); // sets also the 'effects' pointer
203 addRepaintFull();
204 foreach( Client* c, clients )
205 c->setupCompositing();
206 foreach( Client* c, desktops )
207 c->setupCompositing();
208 foreach( Unmanaged* c, unmanaged )
209 c->setupCompositing();
210 foreach( Client* c, clients )
211 scene->windowAdded( c );
212 foreach( Client* c, desktops )
213 scene->windowAdded( c );
214 foreach( Unmanaged* c, unmanaged )
215 scene->windowAdded( c );
216 discardPopup(); // force re-creation of the Alt+F3 popup (opacity option)
217 #else
218 kDebug( 1212 ) << "Compositing was not available at compile time";
219 #endif
222 void Workspace::finishCompositing()
224 #ifdef KWIN_HAVE_COMPOSITING
225 if( scene == NULL )
226 return;
227 delete cm_selection;
228 foreach( Client* c, clients )
229 scene->windowClosed( c, NULL );
230 foreach( Client* c, desktops )
231 scene->windowClosed( c, NULL );
232 foreach( Unmanaged* c, unmanaged )
233 scene->windowClosed( c, NULL );
234 foreach( Deleted* c, deleted )
235 scene->windowDeleted( c );
236 foreach( Client* c, clients )
237 c->finishCompositing();
238 foreach( Client* c, desktops )
239 c->finishCompositing();
240 foreach( Unmanaged* c, unmanaged )
241 c->finishCompositing();
242 foreach( Deleted* c, deleted )
243 c->finishCompositing();
244 XCompositeUnredirectSubwindows( display(), rootWindow(), CompositeRedirectManual );
245 delete effects;
246 effects = NULL;
247 delete scene;
248 scene = NULL;
249 compositeTimer.stop();
250 repaints_region = QRegion();
251 for( ClientList::ConstIterator it = clients.constBegin();
252 it != clients.constEnd();
253 ++it )
254 { // forward all opacity values to the frame in case there'll be other CM running
255 if( (*it)->opacity() != 1.0 )
257 NETWinInfo2 i( display(), (*it)->frameId(), rootWindow(), 0 );
258 i.setOpacity( static_cast< unsigned long >((*it)->opacity() * 0xffffffff ));
261 discardPopup(); // force re-creation of the Alt+F3 popup (opacity option)
262 // discard all Deleted windows (#152914)
263 while( !deleted.isEmpty())
264 deleted.first()->discard( Allowed );
265 #endif
268 void Workspace::lostCMSelection()
270 kDebug( 1212 ) << "Lost compositing manager selection";
271 finishCompositing();
274 // for the shortcut
275 void Workspace::slotToggleCompositing()
277 suspendCompositing( !compositingSuspended );
280 void Workspace::suspendCompositing()
282 suspendCompositing( true );
285 void Workspace::suspendCompositing( bool suspend )
287 compositingSuspended = suspend;
288 finishCompositing();
289 setupCompositing(); // will do nothing if suspended
292 void Workspace::addRepaint( int x, int y, int w, int h )
294 if( !compositing())
295 return;
296 repaints_region += QRegion( x, y, w, h );
297 checkCompositeTimer();
300 void Workspace::addRepaint( const QRect& r )
302 if( !compositing())
303 return;
304 repaints_region += r;
305 checkCompositeTimer();
308 void Workspace::addRepaint( const QRegion& r )
310 if( !compositing())
311 return;
312 repaints_region += r;
313 checkCompositeTimer();
316 void Workspace::addRepaintFull()
318 if( !compositing())
319 return;
320 repaints_region = QRegion( 0, 0, displayWidth(), displayHeight());
321 checkCompositeTimer();
324 void Workspace::performCompositing()
326 #ifdef KWIN_HAVE_COMPOSITING
327 // The event loop apparently tries to fire a QTimer as often as possible, even
328 // at the expense of not processing many X events. This means that the composite
329 // repaints can seriously impact performance of everything else, therefore throttle
330 // them - leave at least 1msec time after one repaint is finished and next one
331 // is started.
332 if( lastCompositePaint.elapsed() < 1 )
334 compositeTimer.start( 1 );
335 return;
337 if( !scene->waitSyncAvailable())
338 nextPaintReference = QTime::currentTime();
339 checkCursorPos();
340 if((( repaints_region.isEmpty() && !windowRepaintsPending()) // no damage
341 || !overlay_visible ) // nothing is visible anyway
342 // HACK: don't idle during active full screen effect so that mouse events are not dropped (bug #177226)
343 && !static_cast< EffectsHandlerImpl* >( effects )->activeFullScreenEffect() )
345 scene->idle();
346 // Note: It would seem here we should undo suspended unredirect, but when scenes need
347 // it for some reason, e.g. transformations or translucency, the next pass that does not
348 // need this anymore and paints normally will also reset the suspended unredirect.
349 // Otherwise the window would not be painted normally anyway.
350 return;
352 // create a list of all windows in the stacking order
353 ToplevelList windows = xStackingOrder();
354 foreach( EffectWindow* c, static_cast< EffectsHandlerImpl* >( effects )->elevatedWindows())
356 Toplevel* t = static_cast< EffectWindowImpl* >( c )->window();
357 windows.removeAll( t );
358 windows.append( t );
360 // skip windows that are not yet ready for being painted
361 ToplevelList tmp = windows;
362 windows.clear();
363 #if 0
364 // There is a bug somewhere that prevents this from working properly (#160393), but additionally
365 // this cannot be used so carelessly - needs protections against broken clients, the window
366 // should not get focus before it's displayed, handle unredirected windows properly and so on.
367 foreach( Toplevel* c, tmp )
368 if( c->readyForPainting())
369 windows.append( c );
370 #else
371 foreach( Toplevel* c, tmp )
372 windows.append( c );
373 #endif
374 foreach( Toplevel* c, windows )
375 { // This could be possibly optimized WRT obscuring, but that'd need being already
376 // past prePaint() phase - probably not worth it.
377 // TODO I think effects->transformWindowDamage() doesn't need to be called here,
378 // pre-paint will extend painted window areas as necessary.
379 repaints_region |= c->repaints().translated( c->pos());
380 c->resetRepaints( c->rect());
382 QRegion repaints = repaints_region;
383 // clear all repaints, so that post-pass can add repaints for the next repaint
384 repaints_region = QRegion();
385 QTime t = QTime::currentTime();
386 scene->paint( repaints, windows );
387 if( scene->waitSyncAvailable())
389 // If vsync is used, schedule the next repaint slightly in advance of the next sync,
390 // so that there is still time for the drawing to take place. We have just synced, and
391 // nextPaintReference is time from which multiples of compositeRate should be added,
392 // so set it 10ms back (meaning next paint will be in 'compositeRate - 10').
393 // However, make sure the reserve is smaller than the composite rate.
394 int reserve = compositeRate <= 10 ? compositeRate - 1 : 10;
395 nextPaintReference = QTime::currentTime().addMSecs( -reserve );
397 // Trigger at least one more pass even if there would be nothing to paint, so that scene->idle()
398 // is called the next time. If there would be nothing pending, it will not restart the timer and
399 // checkCompositeTime() would restart it again somewhen later, called from functions that
400 // would again add something pending.
401 checkCompositeTimer();
402 checkCompositePaintTime( t.elapsed());
403 lastCompositePaint.start();
404 #endif
407 bool Workspace::windowRepaintsPending() const
409 foreach( Toplevel* c, clients )
410 if( !c->repaints().isEmpty())
411 return true;
412 foreach( Toplevel* c, desktops )
413 if( !c->repaints().isEmpty())
414 return true;
415 foreach( Toplevel* c, unmanaged )
416 if( !c->repaints().isEmpty())
417 return true;
418 foreach( Toplevel* c, deleted )
419 if( !c->repaints().isEmpty())
420 return true;
421 return false;
424 void Workspace::setCompositeTimer()
426 if( !compositing()) // should not really happen, but there may be e.g. some damage events still pending
427 return;
428 // The last paint set nextPaintReference as a reference time to which multiples of compositeRate
429 // should be added for the next paint. qBound() for protection; system time can change without notice.
430 compositeTimer.start( qBound( 0, nextPaintReference.msecsTo( QTime::currentTime() ), 250 ) % compositeRate );
433 bool Workspace::createOverlay()
435 assert( overlay == None );
436 if( !Extensions::compositeOverlayAvailable())
437 return false;
438 if( !Extensions::shapeInputAvailable()) // needed in setupOverlay()
439 return false;
440 #ifdef HAVE_XCOMPOSITE_OVERLAY
441 overlay = XCompositeGetOverlayWindow( display(), rootWindow());
442 if( overlay == None )
443 return false;
444 return true;
445 #else
446 return false;
447 #endif
450 void Workspace::checkCompositePaintTime( int msec )
452 if( options->disableCompositingChecks )
453 return;
454 composite_paint_times.prepend( msec );
455 bool tooslow = false;
456 // If last 3 paints were way too slow, disable and warn.
457 // 1 second seems reasonable, it's not that difficult to get relatively high times
458 // with high system load.
459 const int MAX_LONG_PAINT = 1000;
460 if( composite_paint_times.count() >= 3 && composite_paint_times[ 0 ] > MAX_LONG_PAINT
461 && composite_paint_times[ 1 ] > MAX_LONG_PAINT && composite_paint_times[ 2 ] > MAX_LONG_PAINT )
463 kDebug( 1212 ) << "Too long paint times, suspending";
464 tooslow = true;
466 // If last 15 seconds all paints (all of them) were quite slow, disable and warn too. Quite slow being 0,1s
467 // should be reasonable, that's 10fps and having constant 10fps is bad.
468 // This may possibly trigger also when activating an expensive effect, so this may need tweaking.
469 const int MAX_SHORT_PAINT = 100;
470 const int SHORT_TIME = 15000; // 15 sec
471 int time = 0;
472 foreach( int t, composite_paint_times )
474 if( t < MAX_SHORT_PAINT )
475 break;
476 time += t;
477 if( time > SHORT_TIME ) // all paints in the given time were long
479 kDebug( 1212 ) << "Long paint times for long time, suspending";
480 tooslow = true;
481 break;
484 if( composite_paint_times.count() > 1000 )
485 composite_paint_times.removeLast();
486 if( tooslow )
488 QTimer::singleShot( 0, this, SLOT( suspendCompositing()));
489 QString shortcut, message;
490 if( KAction* action = qobject_cast<KAction*>( keys->action("Suspend Compositing")))
491 shortcut = action->globalShortcut().primary().toString(QKeySequence::NativeText);
492 if (shortcut.isEmpty())
493 message = i18n( "Compositing was too slow and has been suspended.\n"
494 "You can disable functionality checks in advanced compositing settings." );
495 else
496 message = i18n( "Compositing was too slow and has been suspended.\n"
497 "If this was only a temporary problem, you can resume using the '%1' shortcut.\n"
498 "You can also disable functionality checks in advanced compositing settings.", shortcut );
499 Notify::raise( Notify::CompositingSlow, message );
500 compositeTimer.start( 1000 ); // so that it doesn't trigger sooner than suspendCompositing()
504 void Workspace::setupOverlay( Window w )
506 assert( overlay != None );
507 assert( Extensions::shapeInputAvailable());
508 XSetWindowBackgroundPixmap( display(), overlay, None );
509 overlay_shape = QRegion();
510 setOverlayShape( QRect( 0, 0, displayWidth(), displayHeight()));
511 if( w != None )
513 XSetWindowBackgroundPixmap( display(), w, None );
514 XShapeCombineRectangles( display(), w, ShapeInput, 0, 0, NULL, 0, ShapeSet, Unsorted );
516 XSelectInput( display(), overlay, VisibilityChangeMask );
519 void Workspace::showOverlay()
521 assert( overlay != None );
522 if( overlay_shown )
523 return;
524 XMapSubwindows( display(), overlay );
525 XMapWindow( display(), overlay );
526 overlay_shown = true;
529 void Workspace::hideOverlay()
531 assert( overlay != None );
532 XUnmapWindow( display(), overlay );
533 overlay_shown = false;
534 setOverlayShape( QRect( 0, 0, displayWidth(), displayHeight()));
537 void Workspace::setOverlayShape( const QRegion& reg )
539 // Avoid setting the same shape again, it causes flicker (apparently it is not a no-op
540 // and triggers something).
541 if( reg == overlay_shape )
542 return;
543 QVector< QRect > rects = reg.rects();
544 XRectangle* xrects = new XRectangle[ rects.count() ];
545 for( int i = 0;
546 i < rects.count();
547 ++i )
549 xrects[ i ].x = rects[ i ].x();
550 xrects[ i ].y = rects[ i ].y();
551 xrects[ i ].width = rects[ i ].width();
552 xrects[ i ].height = rects[ i ].height();
554 XShapeCombineRectangles( display(), overlay, ShapeBounding, 0, 0,
555 xrects, rects.count(), ShapeSet, Unsorted );
556 delete[] xrects;
557 XShapeCombineRectangles( display(), overlay, ShapeInput, 0, 0, NULL, 0, ShapeSet, Unsorted );
558 overlay_shape = reg;
561 void Workspace::destroyOverlay()
563 if( overlay == None )
564 return;
565 // reset the overlay shape
566 XRectangle rec = { 0, 0, displayWidth(), displayHeight() };
567 XShapeCombineRectangles( display(), overlay, ShapeBounding, 0, 0, &rec, 1, ShapeSet, Unsorted );
568 XShapeCombineRectangles( display(), overlay, ShapeInput, 0, 0, &rec, 1, ShapeSet, Unsorted );
569 #ifdef HAVE_XCOMPOSITE_OVERLAY
570 XCompositeReleaseOverlayWindow( display(), overlay );
571 #endif
572 overlay = None;
573 overlay_shown = false;
576 bool Workspace::compositingActive()
578 return compositing();
581 // force is needed when the list of windows changes (e.g. a window goes away)
582 void Workspace::checkUnredirect( bool force )
584 if( !compositing() || overlay == None || !options->unredirectFullscreen )
585 return;
586 if( force )
587 forceUnredirectCheck = true;
588 if( !unredirectTimer.isActive())
589 unredirectTimer.start( 0 );
592 void Workspace::delayedCheckUnredirect()
594 if( !compositing() || overlay == None || !options->unredirectFullscreen )
595 return;
596 ToplevelList list;
597 bool changed = forceUnredirectCheck;
598 foreach( Client* c, clients )
599 list.append( c );
600 foreach( Unmanaged* c, unmanaged )
601 list.append( c );
602 foreach( Toplevel* c, list )
604 if( c->updateUnredirectedState())
605 changed = true;
607 // no desktops, no Deleted ones
608 if( !changed )
609 return;
610 forceUnredirectCheck = false;
611 // Cut out parts from the overlay window where unredirected windows are,
612 // so that they are actually visible.
613 QRegion reg( 0, 0, displayWidth(), displayHeight());
614 foreach( Toplevel* c, list )
616 if( c->unredirected())
617 reg -= c->geometry();
619 setOverlayShape( reg );
622 //****************************************
623 // Toplevel
624 //****************************************
626 void Toplevel::setupCompositing()
628 #ifdef KWIN_HAVE_COMPOSITING
629 if( !compositing())
630 return;
631 if( damage_handle != None )
632 return;
633 damage_handle = XDamageCreate( display(), frameId(), XDamageReportRawRectangles );
634 damage_region = QRegion( 0, 0, width(), height());
635 effect_window = new EffectWindowImpl();
636 effect_window->setWindow( this );
637 unredirect = false;
638 workspace()->checkUnredirect( true );
639 #endif
642 void Toplevel::finishCompositing()
644 #ifdef KWIN_HAVE_COMPOSITING
645 if( damage_handle == None )
646 return;
647 workspace()->checkUnredirect( true );
648 if( effect_window->window() == this ) // otherwise it's already passed to Deleted, don't free data
650 discardWindowPixmap();
651 delete effect_window;
653 XDamageDestroy( display(), damage_handle );
654 damage_handle = None;
655 damage_region = QRegion();
656 repaints_region = QRegion();
657 effect_window = NULL;
658 #endif
661 void Toplevel::discardWindowPixmap()
663 addDamageFull();
664 if( window_pix == None )
665 return;
666 XFreePixmap( display(), window_pix );
667 window_pix = None;
668 if( effectWindow() != NULL && effectWindow()->sceneWindow() != NULL )
669 effectWindow()->sceneWindow()->pixmapDiscarded();
672 Pixmap Toplevel::createWindowPixmap()
674 #ifdef KWIN_HAVE_COMPOSITING
675 assert( compositing());
676 if( unredirected())
677 return None;
678 grabXServer();
679 KXErrorHandler err;
680 Pixmap pix = XCompositeNameWindowPixmap( display(), frameId());
681 // check that the received pixmap is valid and actually matches what we
682 // know about the window (i.e. size)
683 XWindowAttributes attrs;
684 if( !XGetWindowAttributes( display(), frameId(), &attrs )
685 || err.error( false )
686 || attrs.width != width() || attrs.height != height() || attrs.map_state != IsViewable )
688 kDebug( 1212 ) << "Creating window pixmap failed: " << this;
689 XFreePixmap( display(), pix );
690 pix = None;
692 ungrabXServer();
693 return pix;
694 #else
695 return None;
696 #endif
699 #ifdef HAVE_XDAMAGE
700 void Toplevel::damageNotifyEvent( XDamageNotifyEvent* e )
702 QRegion damage( e->area.x, e->area.y, e->area.width, e->area.height );
703 // compress
704 int cnt = 1;
705 while( XPending( display()))
707 XEvent e2;
708 if( XPeekEvent( display(), &e2 ) && e2.type == Extensions::damageNotifyEvent()
709 && e2.xany.window == frameId())
711 XNextEvent( display(), &e2 );
712 if( cnt > 200 )
714 // If there are way too many damage events in the queue, just discard them
715 // and damage the whole window. Otherwise the X server can just overload
716 // us with a flood of damage events. Should be probably optimized
717 // in the X server, as this is rather lame.
718 damage = rect();
719 continue;
721 XDamageNotifyEvent* e = reinterpret_cast< XDamageNotifyEvent* >( &e2 );
722 QRect r( e->area.x, e->area.y, e->area.width, e->area.height );
723 ++cnt;
724 // If there are too many damaged rectangles, increase them
725 // to be multiples of 100x100 px grid, since QRegion get quite
726 // slow with many rectangles, and there is little to gain by using
727 // many small rectangles (rather the opposite, several large should
728 // be often faster).
729 if( cnt > 50 )
731 r.setLeft( r.left() / 100 * 100 );
732 r.setRight(( r.right() + 99 ) / 100 * 100 );
733 r.setTop( r.top() / 100 * 100 );
734 r.setBottom(( r.bottom() + 99 ) / 100 * 100 );
736 damage += r;
737 continue;
739 break;
741 foreach( const QRect& r, damage.rects())
742 addDamage( r );
745 void Client::damageNotifyEvent( XDamageNotifyEvent* e )
747 Toplevel::damageNotifyEvent( e );
748 #ifdef HAVE_XSYNC
749 if( sync_counter == None ) // cannot detect complete redraw, consider done now
750 ready_for_painting = true;
751 #else
752 ready_for_painting = true; // no sync at all, consider done now
753 #endif
755 #endif
757 void Toplevel::addDamage( const QRect& r )
759 addDamage( r.x(), r.y(), r.width(), r.height());
762 void Toplevel::addDamage( int x, int y, int w, int h )
764 if( !compositing())
765 return;
766 QRect r( x, y, w, h );
767 // resizing the decoration may lag behind a bit and when shrinking there
768 // may be a damage event coming with size larger than the current window size
769 r &= rect();
770 damage_region += r;
771 repaints_region += r;
772 static_cast<EffectsHandlerImpl*>(effects)->windowDamaged( effectWindow(), r );
773 workspace()->checkCompositeTimer();
776 void Toplevel::addDamageFull()
778 if( !compositing())
779 return;
780 damage_region = rect();
781 repaints_region = rect();
782 static_cast<EffectsHandlerImpl*>(effects)->windowDamaged( effectWindow(), rect());
783 workspace()->checkCompositeTimer();
786 void Toplevel::resetDamage( const QRect& r )
788 damage_region -= r;
791 void Toplevel::addRepaint( const QRect& r )
793 addRepaint( r.x(), r.y(), r.width(), r.height());
796 void Toplevel::addRepaint( int x, int y, int w, int h )
798 if( !compositing())
799 return;
800 QRect r( x, y, w, h );
801 r &= rect();
802 repaints_region += r;
803 workspace()->checkCompositeTimer();
806 void Toplevel::addRepaintFull()
808 repaints_region = rect();
809 workspace()->checkCompositeTimer();
812 void Toplevel::resetRepaints( const QRect& r )
814 repaints_region -= r;
817 void Toplevel::addWorkspaceRepaint( int x, int y, int w, int h )
819 addWorkspaceRepaint( QRect( x, y, w, h ));
822 void Toplevel::addWorkspaceRepaint( const QRect& r2 )
824 if( !compositing())
825 return;
826 if( effectWindow() == NULL ) // TODO - this can happen during window destruction
827 return workspace()->addRepaint( r2 );
828 QRect r = effects->transformWindowDamage( effectWindow(), r2 );
829 workspace()->addRepaint( r );
832 bool Toplevel::updateUnredirectedState()
834 assert( compositing());
835 bool should = shouldUnredirect() && !unredirectSuspend && !shape() && !hasAlpha() && opacity() == 1.0 &&
836 !static_cast<EffectsHandlerImpl*>( effects )->activeFullScreenEffect();
837 if( should && !unredirect )
839 unredirect = true;
840 kDebug( 1212 ) << "Unredirecting:" << this;
841 #ifdef HAVE_XCOMPOSITE
842 XCompositeUnredirectWindow( display(), frameId(), CompositeRedirectManual );
843 #endif
844 return true;
846 else if( !should && unredirect )
848 unredirect = false;
849 kDebug( 1212 ) << "Redirecting:" << this;
850 #ifdef HAVE_XCOMPOSITE
851 XCompositeRedirectWindow( display(), frameId(), CompositeRedirectManual );
852 #endif
853 discardWindowPixmap();
854 return true;
856 return false;
859 void Toplevel::suspendUnredirect( bool suspend )
861 if( unredirectSuspend == suspend )
862 return;
863 unredirectSuspend = suspend;
864 workspace()->checkUnredirect();
867 //****************************************
868 // Client
869 //****************************************
871 void Client::setupCompositing()
873 Toplevel::setupCompositing();
874 updateVisibility(); // for internalKeep()
877 void Client::finishCompositing()
879 Toplevel::finishCompositing();
880 updateVisibility();
883 bool Client::shouldUnredirect() const
885 if( isActiveFullScreen())
887 ToplevelList stacking = workspace()->xStackingOrder();
888 for( int pos = stacking.count() - 1;
889 pos >= 0;
890 --pos )
892 Toplevel* c = stacking.at( pos );
893 if( c == this ) // is not covered by any other window, ok to unredirect
894 return true;
895 if( c->geometry().intersects( geometry()))
896 return false;
898 abort();
900 return false;
903 //****************************************
904 // Unmanaged
905 //****************************************
907 bool Unmanaged::shouldUnredirect() const
909 // the pixmap is needed for the login effect, a nicer solution would be the login effect increasing
910 // refcount for the window pixmap (which would prevent unredirect), avoiding this hack
911 if( resourceClass() == "ksplashx" || resourceClass() == "ksplashsimple" )
912 return false;
913 // it must cover whole display or one xinerama screen, and be the topmost there
914 if( geometry() == workspace()->clientArea( FullArea, geometry().center(), workspace()->currentDesktop())
915 || geometry() == workspace()->clientArea( ScreenArea, geometry().center(), workspace()->currentDesktop()))
917 ToplevelList stacking = workspace()->xStackingOrder();
918 for( int pos = stacking.count() - 1;
919 pos >= 0;
920 --pos )
922 Toplevel* c = stacking.at( pos );
923 if( c == this ) // is not covered by any other window, ok to unredirect
924 return true;
925 if( c->geometry().intersects( geometry()))
926 return false;
928 abort();
930 return false;
933 //****************************************
934 // Deleted
935 //****************************************
937 bool Deleted::shouldUnredirect() const
939 return false;
942 } // namespace