not quite so much needs to be delayed to the init() function
[personal-kdebase.git] / workspace / kwin / lib / kwineffects.cpp
blob9e16e280585c8d2b9eb64febccc09113704cd100
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 *********************************************************************/
21 #include "kwineffects.h"
23 #include "kwinglutils.h"
24 #include "kwinxrenderutils.h"
26 #include <QtDBus/QtDBus>
27 #include <QVariant>
28 #include <QList>
29 #include <QtCore/QTimeLine>
30 #include <QtGui/QFontMetrics>
31 #include <QtGui/QPainter>
32 #include <QtGui/QPixmap>
34 #include <kdebug.h>
35 #include <ksharedconfig.h>
36 #include <kconfiggroup.h>
38 #include <assert.h>
40 #ifdef KWIN_HAVE_XRENDER_COMPOSITING
41 #include <X11/extensions/Xrender.h>
42 #include <X11/extensions/Xfixes.h>
43 #endif
45 namespace KWin
48 void WindowPrePaintData::setTranslucent()
50 mask |= Effect::PAINT_WINDOW_TRANSLUCENT;
51 mask &= ~Effect::PAINT_WINDOW_OPAQUE;
52 clip = QRegion(); // cannot clip, will be transparent
55 void WindowPrePaintData::setTransformed()
57 mask |= Effect::PAINT_WINDOW_TRANSFORMED;
61 WindowPaintData::WindowPaintData( EffectWindow* w )
62 : opacity( w->opacity())
63 , contents_opacity( 1.0 )
64 , decoration_opacity( 1.0 )
65 , xScale( 1 )
66 , yScale( 1 )
67 , zScale( 1 )
68 , xTranslate( 0 )
69 , yTranslate( 0 )
70 , zTranslate( 0 )
71 , saturation( 1 )
72 , brightness( 1 )
73 , shader( NULL )
74 , rotation( NULL )
76 quads = w->buildQuads();
79 ScreenPaintData::ScreenPaintData()
80 : xScale( 1 )
81 , yScale( 1 )
82 , zScale( 1 )
83 , xTranslate( 0 )
84 , yTranslate( 0 )
85 , zTranslate( 0 )
86 , rotation( NULL )
90 RotationData::RotationData()
91 : axis( ZAxis )
92 , angle( 0.0 )
93 , xRotationPoint( 0.0 )
94 , yRotationPoint( 0.0 )
95 , zRotationPoint( 0.0 )
99 //****************************************
100 // Effect
101 //****************************************
103 Effect::Effect()
107 Effect::~Effect()
111 void Effect::reconfigure( ReconfigureFlags )
115 void Effect::windowUserMovedResized( EffectWindow* , bool, bool )
119 void Effect::windowOpacityChanged( EffectWindow*, double )
123 void Effect::windowAdded( EffectWindow* )
127 void Effect::windowClosed( EffectWindow* )
131 void Effect::windowDeleted( EffectWindow* )
135 void Effect::windowActivated( EffectWindow* )
139 void Effect::windowMinimized( EffectWindow* )
143 void Effect::windowUnminimized( EffectWindow* )
147 void Effect::windowInputMouseEvent( Window, QEvent* )
151 void Effect::grabbedKeyboardEvent( QKeyEvent* )
155 void Effect::propertyNotify( EffectWindow* , long )
159 void Effect::desktopChanged( int )
163 void Effect::windowDamaged( EffectWindow*, const QRect& )
167 void Effect::windowGeometryShapeChanged( EffectWindow*, const QRect& )
171 void Effect::tabBoxAdded( int )
175 void Effect::tabBoxClosed()
179 void Effect::tabBoxUpdated()
182 bool Effect::borderActivated( ElectricBorder )
184 return false;
187 void Effect::mouseChanged( const QPoint&, const QPoint&, Qt::MouseButtons,
188 Qt::MouseButtons, Qt::KeyboardModifiers, Qt::KeyboardModifiers )
192 void Effect::prePaintScreen( ScreenPrePaintData& data, int time )
194 effects->prePaintScreen( data, time );
197 void Effect::paintScreen( int mask, QRegion region, ScreenPaintData& data )
199 effects->paintScreen( mask, region, data );
202 void Effect::postPaintScreen()
204 effects->postPaintScreen();
207 void Effect::prePaintWindow( EffectWindow* w, WindowPrePaintData& data, int time )
209 effects->prePaintWindow( w, data, time );
212 void Effect::paintWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data )
214 effects->paintWindow( w, mask, region, data );
217 void Effect::postPaintWindow( EffectWindow* w )
219 effects->postPaintWindow( w );
222 void Effect::drawWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data )
224 effects->drawWindow( w, mask, region, data );
227 void Effect::buildQuads( EffectWindow* w, WindowQuadList& quadList )
229 effects->buildQuads( w, quadList );
232 QRect Effect::transformWindowDamage( EffectWindow* w, const QRect& r )
234 return effects->transformWindowDamage( w, r );
237 void Effect::setPositionTransformations( WindowPaintData& data, QRect& region, EffectWindow* w,
238 const QRect& r, Qt::AspectRatioMode aspect )
240 QSize size = w->size();
241 size.scale( r.size(), aspect );
242 data.xScale = size.width() / double( w->width());
243 data.yScale = size.height() / double( w->height());
244 int width = int( w->width() * data.xScale );
245 int height = int( w->height() * data.yScale );
246 int x = r.x() + ( r.width() - width ) / 2;
247 int y = r.y() + ( r.height() - height ) / 2;
248 region = QRect( x, y, width, height );
249 data.xTranslate = x - w->x();
250 data.yTranslate = y - w->y();
253 int Effect::displayWidth()
255 return KWin::displayWidth();
258 int Effect::displayHeight()
260 return KWin::displayHeight();
263 QPoint Effect::cursorPos()
265 return effects->cursorPos();
268 double Effect::animationTime( const KConfigGroup& cfg, const QString& key, int defaultTime )
270 int time = cfg.readEntry( key, 0 );
271 return time != 0 ? time : qMax( defaultTime * effects->animationTimeFactor(), 1. );
274 double Effect::animationTime( int defaultTime )
275 { // at least 1ms, otherwise 0ms times can break some things
276 return qMax( defaultTime * effects->animationTimeFactor(), 1. );
279 //****************************************
280 // EffectsHandler
281 //****************************************
283 EffectsHandler::EffectsHandler(CompositingType type)
284 : current_paint_screen( 0 )
285 , current_paint_window( 0 )
286 , current_draw_window( 0 )
287 , current_build_quads( 0 )
288 , current_transform( 0 )
289 , compositing_type( type )
291 if( compositing_type == NoCompositing )
292 return;
293 KWin::effects = this;
296 EffectsHandler::~EffectsHandler()
298 // All effects should already be unloaded by Impl dtor
299 assert( loaded_effects.count() == 0 );
302 QRect EffectsHandler::transformWindowDamage( EffectWindow* w, const QRect& r )
304 if( current_transform < loaded_effects.size())
306 QRect rr = loaded_effects[current_transform++].second->transformWindowDamage( w, r );
307 --current_transform;
308 return rr;
310 else
311 return r;
314 Window EffectsHandler::createInputWindow( Effect* e, const QRect& r, const QCursor& cursor )
316 return createInputWindow( e, r.x(), r.y(), r.width(), r.height(), cursor );
319 Window EffectsHandler::createFullScreenInputWindow( Effect* e, const QCursor& cursor )
321 return createInputWindow( e, 0, 0, displayWidth(), displayHeight(), cursor );
324 CompositingType EffectsHandler::compositingType() const
326 return compositing_type;
329 bool EffectsHandler::saturationSupported() const
331 switch( compositing_type )
333 #ifdef KWIN_HAVE_OPENGL_COMPOSITING
334 case OpenGLCompositing:
335 return GLTexture::saturationSupported();
336 #endif
337 case XRenderCompositing:
338 return false; // never
339 default:
340 abort();
344 void EffectsHandler::sendReloadMessage( const QString& effectname )
346 QDBusMessage message = QDBusMessage::createMethodCall("org.kde.kwin", "/KWin", "org.kde.KWin", "reconfigureEffect");
347 message << QString("kwin4_effect_" + effectname);
348 QDBusConnection::sessionBus().send(message);
351 KConfigGroup EffectsHandler::effectConfig( const QString& effectname )
353 KSharedConfig::Ptr kwinconfig = KSharedConfig::openConfig( "kwinrc", KConfig::NoGlobals );
354 return kwinconfig->group( "Effect-" + effectname );
357 bool EffectsHandler::paintText( const QString& text, const QRect& rect, const QColor& color,
358 const QFont& font, const Qt::Alignment& alignment )
360 QPainter p;
361 // Calculate size of the text
362 QFontMetrics fm( font );
363 QString painttext = fm.elidedText( text, Qt::ElideRight, rect.width() );
364 QRect textrect = fm.boundingRect( painttext );
366 // Create temporary QImage where the text will be drawn onto
367 QImage textImage( textrect.width(), textrect.height(), QImage::Format_ARGB32 );
368 textImage.fill( Qt::transparent );
370 // Draw the text
371 p.begin( &textImage );
372 p.setFont( font );
373 p.setRenderHint( QPainter::TextAntialiasing );
374 p.setPen( color );
375 p.drawText( -textrect.topLeft(), painttext );
376 p.end();
378 // Area covered by text
379 int rectX, rectY;
380 if( alignment & Qt::AlignLeft )
381 rectX = rect.x();
382 else if( alignment & Qt::AlignRight )
383 rectX = rect.right() - textrect.width();
384 else
385 rectX = rect.center().x() - textrect.width() / 2;
386 if( alignment & Qt::AlignTop )
387 rectY = rect.y();
388 else if( alignment & Qt::AlignBottom )
389 rectY = rect.bottom() - textrect.height();
390 else
391 rectY = rect.center().y() - textrect.height() / 2;
392 QRect area( rectX, rectY, textrect.width(), textrect.height() );
394 #ifdef KWIN_HAVE_OPENGL_COMPOSITING
395 if( effects->compositingType() == OpenGLCompositing )
397 GLTexture textTexture( textImage, GL_TEXTURE_RECTANGLE_ARB );
398 glPushAttrib( GL_CURRENT_BIT | GL_ENABLE_BIT | GL_TEXTURE_BIT );
399 glEnable( GL_BLEND );
400 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
401 textTexture.bind();
402 textTexture.render( infiniteRegion(), area );
403 textTexture.unbind();
404 glPopAttrib();
405 return true;
407 #endif
408 #ifdef KWIN_HAVE_XRENDER_COMPOSITING
409 if( effects->compositingType() == XRenderCompositing )
411 XRenderPicture textPicture( QPixmap::fromImage( textImage ));
412 XRenderComposite( display(), textImage.depth() == 32 ? PictOpOver : PictOpSrc,
413 textPicture, None, effects->xrenderBufferPicture(),
414 0, 0, 0, 0, area.x(), area.y(), area.width(), area.height());
415 return true;
417 #endif
418 return false;
421 bool EffectsHandler::paintTextWithBackground( const QString& text, const QRect& rect, const QColor& color,
422 const QColor& bgcolor, const QFont& font, const Qt::Alignment& alignment )
424 // Calculate size of the text
425 QFontMetrics fm( font );
426 QString painttext = fm.elidedText( text, Qt::ElideRight, rect.width() );
427 QRect textrect = fm.boundingRect( painttext );
429 // Area covered by text
430 int rectX, rectY;
431 if( alignment & Qt::AlignLeft )
432 rectX = rect.x();
433 else if( alignment & Qt::AlignRight )
434 rectX = rect.right() - textrect.width();
435 else
436 rectX = rect.center().x() - textrect.width() / 2;
437 if( alignment & Qt::AlignTop )
438 rectY = rect.y();
439 else if( alignment & Qt::AlignBottom )
440 rectY = rect.bottom() - textrect.height();
441 else
442 rectY = rect.center().y() - textrect.height() / 2;
443 QRect area( rectX, rectY, textrect.width(), textrect.height() );
445 #ifdef KWIN_HAVE_OPENGL_COMPOSITING
446 if( effects->compositingType() == OpenGLCompositing )
448 glColor4f( bgcolor.redF(), bgcolor.greenF(), bgcolor.blueF(), bgcolor.alphaF() );
449 renderRoundBox( area.adjusted( -8, -3, 8, 3 ), 5 );
451 return paintText( text, rect, color, font, alignment );
453 #endif
454 #ifdef KWIN_HAVE_XRENDER_COMPOSITING
455 if( effects->compositingType() == XRenderCompositing )
457 xRenderRoundBox( effects->xrenderBufferPicture(), area.adjusted( -8, -3, 8, 3 ), 5, bgcolor );
458 return paintText( text, rect, color, font, alignment );
460 #endif
461 return false;
465 EffectsHandler* effects = 0;
468 //****************************************
469 // EffectWindow
470 //****************************************
472 EffectWindow::EffectWindow()
476 EffectWindow::~EffectWindow()
480 bool EffectWindow::isOnCurrentDesktop() const
482 return isOnDesktop( effects->currentDesktop());
485 bool EffectWindow::isOnDesktop( int d ) const
487 return desktop() == d || isOnAllDesktops();
490 bool EffectWindow::hasDecoration() const
492 return contentsRect() != QRect( 0, 0, width(), height());
496 //****************************************
497 // EffectWindowGroup
498 //****************************************
500 EffectWindowGroup::~EffectWindowGroup()
504 //****************************************
505 // GlobalShortcutsEditor
506 //****************************************
508 GlobalShortcutsEditor::GlobalShortcutsEditor( QWidget *parent ) :
509 KShortcutsEditor( parent, GlobalAction )
513 /***************************************************************
514 WindowQuad
515 ***************************************************************/
517 WindowQuad WindowQuad::makeSubQuad( double x1, double y1, double x2, double y2 ) const
519 assert( x1 < x2 && y1 < y2 && x1 >= left() && x2 <= right() && y1 >= top() && y2 <= bottom());
520 #ifndef NDEBUG
521 if( isTransformed())
522 kFatal( 1212 ) << "Splitting quads is allowed only in pre-paint calls!" ;
523 #endif
524 WindowQuad ret( *this );
525 // vertices are clockwise starting from topleft
526 ret.verts[ 0 ].px = x1;
527 ret.verts[ 3 ].px = x1;
528 ret.verts[ 1 ].px = x2;
529 ret.verts[ 2 ].px = x2;
530 ret.verts[ 0 ].py = y1;
531 ret.verts[ 1 ].py = y1;
532 ret.verts[ 2 ].py = y2;
533 ret.verts[ 3 ].py = y2;
534 // original x/y are supposed to be the same, no transforming is done here
535 ret.verts[ 0 ].ox = x1;
536 ret.verts[ 3 ].ox = x1;
537 ret.verts[ 1 ].ox = x2;
538 ret.verts[ 2 ].ox = x2;
539 ret.verts[ 0 ].oy = y1;
540 ret.verts[ 1 ].oy = y1;
541 ret.verts[ 2 ].oy = y2;
542 ret.verts[ 3 ].oy = y2;
543 double my_tleft = verts[ 0 ].tx;
544 double my_tright = verts[ 2 ].tx;
545 double my_ttop = verts[ 0 ].ty;
546 double my_tbottom = verts[ 2 ].ty;
547 double tleft = ( x1 - left()) / ( right() - left()) * ( my_tright - my_tleft ) + my_tleft;
548 double tright = ( x2 - left()) / ( right() - left()) * ( my_tright - my_tleft ) + my_tleft;
549 double ttop = ( y1 - top()) / ( bottom() - top()) * ( my_tbottom - my_ttop ) + my_ttop;
550 double tbottom = ( y2 - top()) / ( bottom() - top()) * ( my_tbottom - my_ttop ) + my_ttop;
551 ret.verts[ 0 ].tx = tleft;
552 ret.verts[ 3 ].tx = tleft;
553 ret.verts[ 1 ].tx = tright;
554 ret.verts[ 2 ].tx = tright;
555 ret.verts[ 0 ].ty = ttop;
556 ret.verts[ 1 ].ty = ttop;
557 ret.verts[ 2 ].ty = tbottom;
558 ret.verts[ 3 ].ty = tbottom;
559 return ret;
562 bool WindowQuad::smoothNeeded() const
564 // smoothing is needed if the width or height of the quad does not match the original size
565 double width = verts[ 1 ].ox - verts[ 0 ].ox;
566 double height = verts[ 2 ].oy - verts[ 1 ].oy;
567 return( verts[ 1 ].px - verts[ 0 ].px != width || verts[ 2 ].px - verts[ 3 ].px != width
568 || verts[ 2 ].py - verts[ 1 ].py != height || verts[ 3 ].py - verts[ 0 ].py != height );
571 /***************************************************************
572 WindowQuadList
573 ***************************************************************/
575 WindowQuadList WindowQuadList::splitAtX( double x ) const
577 WindowQuadList ret;
578 foreach( const WindowQuad &quad, *this )
580 #ifndef NDEBUG
581 if( quad.isTransformed())
582 kFatal( 1212 ) << "Splitting quads is allowed only in pre-paint calls!" ;
583 #endif
584 bool wholeleft = true;
585 bool wholeright = true;
586 for( int i = 0;
587 i < 4;
588 ++i )
590 if( quad[ i ].x() < x )
591 wholeright = false;
592 if( quad[ i ].x() > x )
593 wholeleft = false;
595 if( wholeleft || wholeright ) // is whole in one split part
597 ret.append( quad );
598 continue;
600 if( quad.left() == quad.right() ) // quad has no size
602 ret.append( quad );
603 continue;
605 ret.append( quad.makeSubQuad( quad.left(), quad.top(), x, quad.bottom()));
606 ret.append( quad.makeSubQuad( x, quad.top(), quad.right(), quad.bottom()));
608 return ret;
611 WindowQuadList WindowQuadList::splitAtY( double y ) const
613 WindowQuadList ret;
614 foreach( const WindowQuad &quad, *this )
616 #ifndef NDEBUG
617 if( quad.isTransformed())
618 kFatal( 1212 ) << "Splitting quads is allowed only in pre-paint calls!" ;
619 #endif
620 bool wholetop = true;
621 bool wholebottom = true;
622 for( int i = 0;
623 i < 4;
624 ++i )
626 if( quad[ i ].y() < y )
627 wholebottom = false;
628 if( quad[ i ].y() > y )
629 wholetop = false;
631 if( wholetop || wholebottom ) // is whole in one split part
633 ret.append( quad );
634 continue;
636 if( quad.top() == quad.bottom() ) // quad has no size
638 ret.append( quad );
639 continue;
641 ret.append( quad.makeSubQuad( quad.left(), quad.top(), quad.right(), y ));
642 ret.append( quad.makeSubQuad( quad.left(), y, quad.right(), quad.bottom()));
644 return ret;
647 WindowQuadList WindowQuadList::makeGrid( int maxquadsize ) const
649 if( empty())
650 return *this;
651 // find the bounding rectangle
652 double left = first().left();
653 double right = first().right();
654 double top = first().top();
655 double bottom = first().bottom();
656 foreach( const WindowQuad &quad, *this )
658 #ifndef NDEBUG
659 if( quad.isTransformed())
660 kFatal( 1212 ) << "Splitting quads is allowed only in pre-paint calls!" ;
661 #endif
662 left = qMin( left, quad.left());
663 right = qMax( right, quad.right());
664 top = qMin( top, quad.top());
665 bottom = qMax( bottom, quad.bottom());
667 WindowQuadList ret;
668 for( double x = left;
669 x < right;
670 x += maxquadsize )
672 for( double y = top;
673 y < bottom;
674 y += maxquadsize )
676 foreach( const WindowQuad &quad, *this )
678 if( QRectF( QPointF( quad.left(), quad.top()), QPointF( quad.right(), quad.bottom()))
679 .intersects( QRectF( x, y, maxquadsize, maxquadsize )))
681 ret.append( quad.makeSubQuad( qMax( x, quad.left()), qMax( y, quad.top()),
682 qMin( quad.right(), x + maxquadsize ), qMin( quad.bottom(), y + maxquadsize )));
687 return ret;
690 WindowQuadList WindowQuadList::makeRegularGrid( int xSubdivisions, int ySubdivisions ) const
692 if( empty())
693 return *this;
694 // find the bounding rectangle
695 double left = first().left();
696 double right = first().right();
697 double top = first().top();
698 double bottom = first().bottom();
699 foreach( const WindowQuad &quad, *this )
701 #ifndef NDEBUG
702 if( quad.isTransformed())
703 kFatal( 1212 ) << "Splitting quads is allowed only in pre-paint calls!" ;
704 #endif
705 left = qMin( left, quad.left());
706 right = qMax( right, quad.right());
707 top = qMin( top, quad.top());
708 bottom = qMax( bottom, quad.bottom());
711 double xincrement = (right - left) / xSubdivisions;
712 double yincrement = (bottom - top) / ySubdivisions;
713 WindowQuadList ret;
714 for( double y = top;
715 y < bottom;
716 y += yincrement )
718 for( double x = left;
719 x < right;
720 x += xincrement)
722 foreach( const WindowQuad &quad, *this )
724 if( QRectF( QPointF( quad.left(), quad.top()), QPointF( quad.right(), quad.bottom()))
725 .intersects( QRectF( x, y, xincrement, yincrement )))
727 ret.append( quad.makeSubQuad( qMax( x, quad.left()), qMax( y, quad.top()),
728 qMin( quad.right(), x + xincrement ), qMin( quad.bottom(), y + yincrement )));
733 return ret;
736 void WindowQuadList::makeArrays( float** vertices, float** texcoords ) const
738 *vertices = new float[ count() * 4 * 2 ];
739 *texcoords = new float[ count() * 4 * 2 ];
740 float* vpos = *vertices;
741 float* tpos = *texcoords;
742 for( int i = 0;
743 i < count();
744 ++i )
745 for( int j = 0;
746 j < 4;
747 ++j )
749 *vpos++ = at( i )[ j ].x();
750 *vpos++ = at( i )[ j ].y();
751 *tpos++ = at( i )[ j ].tx;
752 *tpos++ = at( i )[ j ].ty;
756 WindowQuadList WindowQuadList::select( WindowQuadType type ) const
758 foreach( const WindowQuad &q, *this )
760 if( q.type() != type ) // something else than ones to select, make a copy and filter
762 WindowQuadList ret;
763 foreach( const WindowQuad &q, *this )
765 if( q.type() == type )
766 ret.append( q );
768 return ret;
771 return *this; // nothing to filter out
774 WindowQuadList WindowQuadList::filterOut( WindowQuadType type ) const
776 foreach( const WindowQuad &q, *this )
778 if( q.type() == type ) // something to filter out, make a copy and filter
780 WindowQuadList ret;
781 foreach( const WindowQuad &q, *this )
783 if( q.type() != type )
784 ret.append( q );
786 return ret;
789 return *this; // nothing to filter out
792 bool WindowQuadList::smoothNeeded() const
794 foreach( const WindowQuad &q, *this )
795 if( q.smoothNeeded())
796 return true;
797 return false;
800 bool WindowQuadList::isTransformed() const
802 foreach( const WindowQuad &q, *this )
803 if( q.isTransformed())
804 return true;
805 return false;
808 /***************************************************************
809 PaintClipper
810 ***************************************************************/
812 QStack< QRegion >* PaintClipper::areas = NULL;
814 PaintClipper::PaintClipper( const QRegion& allowed_area )
815 : area( allowed_area )
817 push( area );
820 PaintClipper::~PaintClipper()
822 pop( area );
825 void PaintClipper::push( const QRegion& allowed_area )
827 if( allowed_area == infiniteRegion()) // don't push these
828 return;
829 if( areas == NULL )
830 areas = new QStack< QRegion >;
831 areas->push( allowed_area );
834 void PaintClipper::pop( const QRegion& allowed_area )
836 if( allowed_area == infiniteRegion())
837 return;
838 Q_ASSERT( areas != NULL );
839 Q_ASSERT( areas->top() == allowed_area );
840 areas->pop();
841 if( areas->isEmpty())
843 delete areas;
844 areas = NULL;
848 bool PaintClipper::clip()
850 return areas != NULL;
853 QRegion PaintClipper::paintArea()
855 assert( areas != NULL ); // can be called only with clip() == true
856 QRegion ret = QRegion( 0, 0, displayWidth(), displayHeight());
857 foreach( const QRegion &r, *areas )
858 ret &= r;
859 return ret;
862 struct PaintClipper::Iterator::Data
864 Data() : index( 0 ) {}
865 int index;
866 #ifdef KWIN_HAVE_OPENGL_COMPOSITING
867 QVector< QRect > rects;
868 #endif
871 PaintClipper::Iterator::Iterator()
872 : data( new Data )
874 #ifdef KWIN_HAVE_OPENGL_COMPOSITING
875 if( clip() && effects->compositingType() == OpenGLCompositing )
877 glPushAttrib( GL_SCISSOR_BIT );
878 glEnable( GL_SCISSOR_TEST );
879 data->rects = paintArea().rects();
880 data->index = -1;
881 next(); // move to the first one
883 #endif
884 #ifdef KWIN_HAVE_XRENDER_COMPOSITING
885 if( clip() && effects->compositingType() == XRenderCompositing )
887 XserverRegion region = toXserverRegion( paintArea());
888 XFixesSetPictureClipRegion( display(), effects->xrenderBufferPicture(), 0, 0, region );
889 XFixesDestroyRegion( display(), region ); // it's ref-counted
891 #endif
894 PaintClipper::Iterator::~Iterator()
896 #ifdef KWIN_HAVE_OPENGL_COMPOSITING
897 if( clip() && effects->compositingType() == OpenGLCompositing )
898 glPopAttrib();
899 #endif
900 #ifdef KWIN_HAVE_XRENDER_COMPOSITING
901 if( clip() && effects->compositingType() == XRenderCompositing )
902 XFixesSetPictureClipRegion( display(), effects->xrenderBufferPicture(), 0, 0, None );
903 #endif
904 delete data;
907 bool PaintClipper::Iterator::isDone()
909 if( !clip())
910 return data->index == 1; // run once
911 #ifdef KWIN_HAVE_OPENGL_COMPOSITING
912 if( effects->compositingType() == OpenGLCompositing )
913 return data->index >= data->rects.count(); // run once per each area
914 #endif
915 #ifdef KWIN_HAVE_XRENDER_COMPOSITING
916 if( effects->compositingType() == XRenderCompositing )
917 return data->index == 1; // run once
918 #endif
919 abort();
922 void PaintClipper::Iterator::next()
924 data->index++;
925 #ifdef KWIN_HAVE_OPENGL_COMPOSITING
926 if( clip() && effects->compositingType() == OpenGLCompositing && data->index < data->rects.count())
928 const QRect& r = data->rects[ data->index ];
929 // Scissor rect has to be given in OpenGL coords
930 glScissor( r.x(), displayHeight() - r.y() - r.height(), r.width(), r.height());
932 #endif
935 QRect PaintClipper::Iterator::boundingRect() const
937 if( !clip())
938 return infiniteRegion();
939 #ifdef KWIN_HAVE_OPENGL_COMPOSITING
940 if( effects->compositingType() == OpenGLCompositing )
941 return data->rects[ data->index ];
942 #endif
943 #ifdef KWIN_HAVE_XRENDER_COMPOSITING
944 if( effects->compositingType() == XRenderCompositing )
945 return paintArea().boundingRect();
946 #endif
947 abort();
948 return infiniteRegion();
952 /***************************************************************
953 TimeLine
954 ***************************************************************/
956 TimeLine::TimeLine(const int duration)
958 m_Time = 0;
959 m_Duration = duration;
960 m_TimeLine = new QTimeLine(m_Duration ? m_Duration : 1); // (avoid QTimeLine warning)
961 m_TimeLine->setFrameRange(0, m_Duration);
962 m_TimeLine->setCurveShape(QTimeLine::EaseInCurve);
965 TimeLine::TimeLine(const TimeLine &other)
967 m_Time = other.m_Time;
968 m_Duration = other.m_Duration;
969 m_TimeLine = new QTimeLine(m_Duration ? m_Duration : 1);
970 m_TimeLine->setFrameRange(0, m_Duration);
971 setCurveShape(m_CurveShape);
972 if( m_Duration != 0 )
973 setProgress(m_Progress);
976 TimeLine::~TimeLine()
978 delete m_TimeLine;
981 int TimeLine::duration() const
983 return m_Duration;
986 void TimeLine::setDuration(const int msec)
988 m_Duration = msec;
989 m_TimeLine->setDuration(m_Duration);
990 m_TimeLine->setFrameRange(0, m_Duration);
993 double TimeLine::value() const
995 Q_ASSERT( m_Duration != 0 );
996 return valueForTime(m_Time);
999 double TimeLine::valueForTime(const int msec) const
1001 Q_ASSERT( m_Duration != 0 );
1002 // Catch non QTimeLine CurveShapes here, (but there are none right now)
1005 // else use QTimeLine ...
1006 return m_TimeLine->valueForTime(msec);
1009 void TimeLine::addTime(const int msec)
1011 Q_ASSERT( m_Duration != 0 );
1012 m_Time = qMin(m_Duration, m_Time + msec);
1013 m_Progress = (double)m_Time / m_Duration;
1016 void TimeLine::removeTime(const int msec)
1018 Q_ASSERT( m_Duration != 0 );
1019 m_Time = qMax(0, m_Time - msec);
1020 m_Progress = (double)m_Time / m_Duration;
1023 void TimeLine::setProgress(const double progress)
1025 Q_ASSERT( m_Duration != 0 );
1026 m_Progress = progress;
1027 m_Time = qRound(m_Duration * progress);
1030 double TimeLine::progress() const
1032 Q_ASSERT( m_Duration != 0 );
1033 return m_Progress;
1036 int TimeLine::time() const
1038 Q_ASSERT( m_Duration != 0 );
1039 return m_Time;
1042 void TimeLine::addProgress(const double progress)
1044 Q_ASSERT( m_Duration != 0 );
1045 m_Progress += progress;
1046 m_Time = (int)(m_Duration * m_Progress);
1049 void TimeLine::setCurveShape(CurveShape curveShape)
1051 switch (curveShape)
1053 case EaseInCurve:
1054 m_TimeLine->setCurveShape(QTimeLine::EaseInCurve);
1055 break;
1056 case EaseOutCurve:
1057 m_TimeLine->setCurveShape(QTimeLine::EaseOutCurve);
1058 break;
1059 case EaseInOutCurve:
1060 m_TimeLine->setCurveShape(QTimeLine::EaseInOutCurve);
1061 break;
1062 case LinearCurve:
1063 m_TimeLine->setCurveShape(QTimeLine::LinearCurve);
1064 break;
1065 case SineCurve:
1066 m_TimeLine->setCurveShape(QTimeLine::SineCurve);
1067 break;
1069 m_CurveShape = curveShape;
1072 /***************************************************************
1073 WindowMotionManager
1074 ***************************************************************/
1076 WindowMotionManager::WindowMotionManager( bool useGlobalAnimationModifier )
1077 : m_useGlobalAnimationModifier( useGlobalAnimationModifier )
1078 , m_movingWindows( 0 )
1079 { // TODO: Allow developer to modify motion attributes
1080 } // TODO: What happens when the window moves by an external force?
1082 WindowMotionManager::~WindowMotionManager()
1086 void WindowMotionManager::manage( EffectWindow *w )
1088 if( m_managedWindows.contains( w ))
1089 return;
1091 double strength = 0.9;
1092 double decay = 0.75;
1093 if( m_useGlobalAnimationModifier )
1095 if( effects->animationTimeFactor() ) // If == 0 we just skip the calculation
1096 strength = 0.9 / effects->animationTimeFactor();
1097 decay = effects->animationTimeFactor() / ( effects->animationTimeFactor() + 0.3333333 );
1100 m_managedWindows[ w ] = WindowMotion();
1101 m_managedWindows[ w ].translation.setStrengthDecay( strength, decay );
1102 m_managedWindows[ w ].scale.setStrengthDecay( strength, decay / 2.0 );
1104 m_managedWindows[ w ].translation.setValue( w->pos() );
1105 m_managedWindows[ w ].scale.setValue( QPointF( 1.0, 1.0 ));
1108 void WindowMotionManager::unmanage( EffectWindow *w )
1110 if( !m_managedWindows.contains( w ))
1111 return;
1113 QPointF diffT = m_managedWindows[ w ].translation.distance();
1114 QPointF diffS = m_managedWindows[ w ].scale.distance();
1115 if( diffT.x() != 0.0 || diffT.y() != 0.0 || diffS.x() != 0.0 || diffS.y() != 0.0 )
1116 m_movingWindows--; // Window was moving
1118 m_managedWindows.remove( w );
1121 void WindowMotionManager::unmanageAll()
1123 m_managedWindows.clear();
1124 m_movingWindows = 0;
1127 void WindowMotionManager::calculate( int time )
1129 if( !effects->animationTimeFactor() )
1130 { // Just skip it completely if the user wants no animation
1131 m_movingWindows = 0;
1132 QHash<EffectWindow*, WindowMotion>::iterator it = m_managedWindows.begin();
1133 for(; it != m_managedWindows.end(); it++ )
1135 WindowMotion *motion = &it.value();
1136 motion->translation.finish();
1137 motion->scale.finish();
1141 QHash<EffectWindow*, WindowMotion>::iterator it = m_managedWindows.begin();
1142 for(; it != m_managedWindows.end(); it++ )
1144 WindowMotion *motion = &it.value();
1145 bool stopped = false;
1147 // TODO: What happens when distance() == 0 but we are still moving fast?
1148 // TODO: Motion needs to be calculated from the window's center
1150 QPointF diffT = motion->translation.distance();
1151 if( diffT != QPoint( 0.0, 0.0 ))
1152 { // Still moving
1153 motion->translation.calculate( time );
1154 diffT = motion->translation.distance();
1155 if( qAbs( diffT.x() ) < 0.5 && qAbs( motion->translation.velocity().x() ) < 0.2 &&
1156 qAbs( diffT.y() ) < 0.5 && qAbs( motion->translation.velocity().y() ) < 0.2 )
1157 { // Hide tiny oscillations
1158 motion->translation.finish();
1159 diffT = QPoint( 0.0, 0.0 );
1160 stopped = true;
1164 QPointF diffS = motion->scale.distance();
1165 if( diffS != QPoint( 0.0, 0.0 ))
1166 { // Still scaling
1167 motion->scale.calculate( time );
1168 diffS = motion->scale.distance();
1169 if( qAbs( diffS.x() ) < 0.002 && qAbs( motion->scale.velocity().x() ) < 0.05 &&
1170 qAbs( diffS.y() ) < 0.002 && qAbs( motion->scale.velocity().y() ) < 0.05 )
1171 { // Hide tiny oscillations
1172 motion->scale.finish();
1173 diffS = QPoint( 0.0, 0.0 );
1174 stopped = true;
1178 // We just finished this window's motion
1179 if( stopped && diffT == QPoint( 0.0, 0.0 ) && diffS == QPoint( 0.0, 0.0 ))
1180 m_movingWindows--;
1184 void WindowMotionManager::reset()
1186 if( !m_managedWindows.count() )
1187 return;
1189 EffectWindowList windows = m_managedWindows.keys();
1191 for( int i = 0; i < windows.size(); i++ )
1193 EffectWindow *w = windows.at( i );
1194 m_managedWindows[ w ].translation.setTarget( w->pos() );
1195 m_managedWindows[ w ].translation.finish();
1196 m_managedWindows[ w ].scale.setTarget( QPointF( 1.0, 1.0 ));
1197 m_managedWindows[ w ].scale.finish();
1201 void WindowMotionManager::reset( EffectWindow *w )
1203 if( !m_managedWindows.contains( w ))
1204 return;
1206 m_managedWindows[ w ].translation.setTarget( w->pos() );
1207 m_managedWindows[ w ].translation.finish();
1208 m_managedWindows[ w ].scale.setTarget( QPointF( 1.0, 1.0 ));
1209 m_managedWindows[ w ].scale.finish();
1212 void WindowMotionManager::apply( EffectWindow *w, WindowPaintData &data )
1214 if( !m_managedWindows.contains( w ))
1215 return;
1217 // TODO: Take into account existing scale so that we can work with multiple managers (E.g. Present windows + grid)
1218 data.xTranslate += m_managedWindows[ w ].translation.value().x() - w->x();
1219 data.yTranslate += m_managedWindows[ w ].translation.value().y() - w->y();
1220 data.xScale *= m_managedWindows[ w ].scale.value().x();
1221 data.yScale *= m_managedWindows[ w ].scale.value().y();
1224 void WindowMotionManager::moveWindow( EffectWindow *w, QPoint target, double scale, double yScale )
1226 if( !m_managedWindows.contains( w ))
1227 abort(); // Notify the effect author that they did something wrong
1229 if( yScale == 0.0 )
1230 yScale = scale;
1231 QPointF scalePoint( scale, yScale );
1233 if( m_managedWindows[ w ].translation.value() == target &&
1234 m_managedWindows[ w ].scale.value() == scalePoint )
1235 return; // Window already at that position
1237 m_managedWindows[ w ].translation.setTarget( target );
1238 m_managedWindows[ w ].scale.setTarget( scalePoint );
1240 if( m_managedWindows[ w ].translation.velocity() == QPointF( 0.0, 0.0 ) &&
1241 m_managedWindows[ w ].scale.velocity() == QPointF( 0.0, 0.0 ))
1242 m_movingWindows++;
1245 QRectF WindowMotionManager::transformedGeometry( EffectWindow *w ) const
1247 QRectF geometry( w->geometry() );
1249 // TODO: Take into account existing scale so that we can work with multiple managers (E.g. Present windows + grid)
1250 geometry.moveTo( m_managedWindows[ w ].translation.value() );
1251 geometry.setWidth( geometry.width() * m_managedWindows[ w ].scale.value().x() );
1252 geometry.setHeight( geometry.height() * m_managedWindows[ w ].scale.value().y() );
1254 return geometry;
1257 EffectWindow* WindowMotionManager::windowAtPoint( QPoint point, bool useStackingOrder ) const
1259 // TODO: Stacking order uses EffectsHandler::stackingOrder() then filters by m_managedWindows
1260 EffectWindowList windows = m_managedWindows.keys();
1262 for( int i = 0; i < windows.size(); i++ )
1263 if( transformedGeometry( windows.at( i )).contains( point ))
1264 return windows.at( i );
1266 return NULL;
1269 } // namespace