kwin/effects/cube/cubeslide.cpp
Martin Gräßlin 82b8faadab Welcome back Cube effect
Currently working:
* cube slide
* basic cube
* rotations
* reflections

Not working:
* Cube Inside effects
* Reflection plane (needs to be done in a shader)
* cube caps
* sphere/cylinder
* filled in areas in multi desktop
2011-01-30 14:12:01 +01:00

712 lines
26 KiB
C++

/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2009 Martin Gräßlin <kde@martin-graesslin.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "cubeslide.h"
#include <kwinconfig.h>
#include <kconfiggroup.h>
#include <math.h>
#include <kwinglutils.h>
namespace KWin
{
KWIN_EFFECT( cubeslide, CubeSlideEffect )
KWIN_EFFECT_SUPPORTED( cubeslide, CubeSlideEffect::supported() )
CubeSlideEffect::CubeSlideEffect()
: windowMoving( false )
, desktopChangedWhileMoving( false )
, progressRestriction( 0.0f )
{
reconfigure( ReconfigureAll );
}
CubeSlideEffect::~CubeSlideEffect()
{
}
bool CubeSlideEffect::supported()
{
return effects->compositingType() == OpenGLCompositing;
}
void CubeSlideEffect::reconfigure( ReconfigureFlags )
{
KConfigGroup conf = effects->effectConfig( "CubeSlide" );
rotationDuration = animationTime( conf, "RotationDuration", 500 );
timeLine.setCurveShape( TimeLine::EaseInOutCurve );
timeLine.setDuration( rotationDuration );
dontSlidePanels = conf.readEntry( "DontSlidePanels", true );
dontSlideStickyWindows = conf.readEntry( "DontSlideStickyWindows", false );
usePagerLayout = conf.readEntry( "UsePagerLayout", true );
useWindowMoving = conf.readEntry( "UseWindowMoving", false );
}
void CubeSlideEffect::prePaintScreen( ScreenPrePaintData& data, int time)
{
if( !slideRotations.empty() )
{
data.mask |= PAINT_SCREEN_TRANSFORMED | Effect::PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS | PAINT_SCREEN_BACKGROUND_FIRST;
timeLine.addTime( time );
if( windowMoving && timeLine.progress() > progressRestriction )
timeLine.setProgress( progressRestriction );
if( dontSlidePanels )
panels.clear();
stickyWindows.clear();
}
effects->prePaintScreen( data, time );
}
void CubeSlideEffect::paintScreen( int mask, QRegion region, ScreenPaintData& data)
{
if( !slideRotations.empty() )
{
#ifdef KWIN_HAVE_OPENGLES
glEnable( GL_CULL_FACE );
glCullFace( GL_FRONT );
paintSlideCube( mask, region, data );
glCullFace( GL_BACK );
paintSlideCube( mask, region, data );
glDisable( GL_CULL_FACE );
#else
glPushAttrib( GL_CURRENT_BIT | GL_ENABLE_BIT );
glEnable( GL_CULL_FACE );
glCullFace( GL_FRONT );
glPushMatrix();
paintSlideCube( mask, region, data );
glPopMatrix();
glCullFace( GL_BACK );
glPushMatrix();
paintSlideCube( mask, region, data );
glPopMatrix();
glDisable( GL_CULL_FACE );
glPopAttrib();
#endif
if( dontSlidePanels )
{
foreach( EffectWindow* w, panels )
{
WindowPaintData wData( w );
effects->paintWindow( w, 0, QRegion( w->x(), w->y(), w->width(), w->height() ), wData );
}
}
foreach( EffectWindow* w, stickyWindows )
{
WindowPaintData wData( w );
effects->paintWindow( w, 0, QRegion( w->x(), w->y(), w->width(), w->height() ), wData );
}
}
else
effects->paintScreen( mask, region, data );
}
void CubeSlideEffect::paintSlideCube(int mask, QRegion region, ScreenPaintData& data)
{
// slide cube only paints to desktops at a time
// first the horizontal rotations followed by vertical rotations
QRect rect = effects->clientArea( FullArea, effects->activeScreen(), effects->currentDesktop() );
float point = rect.width()/2*tan(45.0f*M_PI/180.0f);
cube_painting = true;
painting_desktop = front_desktop;
ScreenPaintData firstFaceData = data;
ScreenPaintData secondFaceData = data;
RotationData firstFaceRot = RotationData();
RotationData secondFaceRot = RotationData();
RotationDirection direction = slideRotations.head();
int secondDesktop;
switch ( direction )
{
case Left:
firstFaceRot.axis = RotationData::YAxis;
secondFaceRot.axis = RotationData::YAxis;
if( usePagerLayout )
secondDesktop = effects->desktopToLeft( front_desktop, true );
else
{
secondDesktop = front_desktop - 1;
if( secondDesktop == 0 )
secondDesktop = effects->numberOfDesktops();
}
firstFaceRot.angle = 90.0f*timeLine.value();
secondFaceRot.angle = -90.0f*(1.0f - timeLine.value());
break;
case Right:
firstFaceRot.axis = RotationData::YAxis;
secondFaceRot.axis = RotationData::YAxis;
if( usePagerLayout )
secondDesktop = effects->desktopToRight( front_desktop, true );
else
{
secondDesktop = front_desktop + 1;
if( secondDesktop > effects->numberOfDesktops() )
secondDesktop = 1;
}
firstFaceRot.angle = -90.0f*timeLine.value();
secondFaceRot.angle = 90.0f*(1.0f - timeLine.value());
break;
case Upwards:
firstFaceRot.axis = RotationData::XAxis;
secondFaceRot.axis = RotationData::XAxis;
secondDesktop = effects->desktopAbove( front_desktop, true);
firstFaceRot.angle = -90.0f*timeLine.value();
secondFaceRot.angle = 90.0f*(1.0f - timeLine.value());
point = rect.height()/2*tan(45.0f*M_PI/180.0f);
break;
case Downwards:
firstFaceRot.axis = RotationData::XAxis;
secondFaceRot.axis = RotationData::XAxis;
secondDesktop = effects->desktopBelow( front_desktop, true );
firstFaceRot.angle = 90.0f*timeLine.value();
secondFaceRot.angle = -90.0f*(1.0f - timeLine.value());
point = rect.height()/2*tan(45.0f*M_PI/180.0f);
break;
default:
// totally impossible
return;
}
// front desktop
firstFaceRot.xRotationPoint = rect.width()/2;
firstFaceRot.yRotationPoint = rect.height()/2;
firstFaceRot.zRotationPoint = -point;
firstFaceData.rotation = &firstFaceRot;
other_desktop = secondDesktop;
firstDesktop = true;
effects->paintScreen( mask, region, firstFaceData );
// second desktop
other_desktop = painting_desktop;
painting_desktop = secondDesktop;
firstDesktop = false;
secondFaceRot.xRotationPoint = rect.width()/2;
secondFaceRot.yRotationPoint = rect.height()/2;
secondFaceRot.zRotationPoint = -point;
secondFaceData.rotation = &secondFaceRot;
effects->paintScreen( mask, region, secondFaceData );
cube_painting = false;
painting_desktop = effects->currentDesktop();
}
void CubeSlideEffect::prePaintWindow( EffectWindow* w, WindowPrePaintData& data, int time)
{
if( !slideRotations.empty() && cube_painting )
{
QRect rect = effects->clientArea( FullArea, effects->activeScreen(), painting_desktop );
if( dontSlidePanels && w->isDock())
{
w->setData( WindowForceBlurRole, QVariant( true ) );
panels.insert( w );
}
if( !w->isManaged() )
{
w->setData( WindowForceBlurRole, QVariant( true ) );
stickyWindows.insert( w );
}
else if( dontSlideStickyWindows && !w->isDock() &&
!w->isDesktop() && w->isOnAllDesktops())
{
w->setData( WindowForceBlurRole, QVariant( true ) );
stickyWindows.insert( w );
}
if( w->isOnDesktop( painting_desktop ) )
{
if( w->x() < rect.x() )
{
data.quads = data.quads.splitAtX( -w->x() );
}
if( w->x() + w->width() > rect.x() + rect.width() )
{
data.quads = data.quads.splitAtX( rect.width() - w->x() );
}
if( w->y() < rect.y() )
{
data.quads = data.quads.splitAtY( -w->y() );
}
if( w->y() + w->height() > rect.y() + rect.height() )
{
data.quads = data.quads.splitAtY( rect.height() - w->y() );
}
w->enablePainting( EffectWindow::PAINT_DISABLED_BY_DESKTOP );
}
else if( w->isOnDesktop( other_desktop ) )
{
RotationDirection direction = slideRotations.head();
bool enable = false;
if( w->x() < rect.x() &&
( direction == Left || direction == Right ) )
{
data.quads = data.quads.splitAtX( -w->x() );
enable = true;
}
if( w->x() + w->width() > rect.x() + rect.width() &&
( direction == Left || direction == Right ) )
{
data.quads = data.quads.splitAtX( rect.width() - w->x() );
enable = true;
}
if( w->y() < rect.y() &&
( direction == Upwards || direction == Downwards ) )
{
data.quads = data.quads.splitAtY( -w->y() );
enable = true;
}
if( w->y() + w->height() > rect.y() + rect.height() &&
( direction == Upwards || direction == Downwards ) )
{
data.quads = data.quads.splitAtY( rect.height() - w->y() );
enable = true;
}
if( enable )
{
data.setTransformed();
data.setTranslucent();
w->enablePainting( EffectWindow::PAINT_DISABLED_BY_DESKTOP );
}
else
w->disablePainting( EffectWindow::PAINT_DISABLED_BY_DESKTOP );
}
else
w->disablePainting( EffectWindow::PAINT_DISABLED_BY_DESKTOP );
}
effects->prePaintWindow( w, data, time );
}
void CubeSlideEffect::paintWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data)
{
if( !slideRotations.empty() && cube_painting )
{
if( dontSlidePanels && w->isDock() )
return;
if( stickyWindows.contains( w ) )
return;
// filter out quads overlapping the edges
QRect rect = effects->clientArea( FullArea, effects->activeScreen(), painting_desktop );
if( w->isOnDesktop( painting_desktop ) )
{
if( w->x() < rect.x() )
{
WindowQuadList new_quads;
foreach( const WindowQuad &quad, data.quads )
{
if( quad.right() > -w->x() )
{
new_quads.append( quad );
}
}
data.quads = new_quads;
}
if( w->x() + w->width() > rect.x() + rect.width() )
{
WindowQuadList new_quads;
foreach( const WindowQuad &quad, data.quads )
{
if( quad.right() <= rect.width() - w->x() )
{
new_quads.append( quad );
}
}
data.quads = new_quads;
}
if( w->y() < rect.y() )
{
WindowQuadList new_quads;
foreach( const WindowQuad &quad, data.quads )
{
if( quad.bottom() > -w->y() )
{
new_quads.append( quad );
}
}
data.quads = new_quads;
}
if( w->y() + w->height() > rect.y() + rect.height() )
{
WindowQuadList new_quads;
foreach( const WindowQuad &quad, data.quads )
{
if( quad.bottom() <= rect.height() - w->y() )
{
new_quads.append( quad );
}
}
data.quads = new_quads;
}
}
// paint windows overlapping edges from other desktop
if( w->isOnDesktop( other_desktop ) && ( mask & PAINT_WINDOW_TRANSFORMED ) )
{
RotationDirection direction = slideRotations.head();
if( w->x() < rect.x() &&
( direction == Left || direction == Right ) )
{
WindowQuadList new_quads;
data.xTranslate = rect.width();
foreach( const WindowQuad &quad, data.quads )
{
if( quad.right() <= -w->x() )
{
new_quads.append( quad );
}
}
data.quads = new_quads;
}
if( w->x() + w->width() > rect.x() + rect.width() &&
( direction == Left || direction == Right ) )
{
WindowQuadList new_quads;
data.xTranslate = -rect.width();
foreach( const WindowQuad &quad, data.quads )
{
if( quad.right() > rect.width() - w->x() )
{
new_quads.append( quad );
}
}
data.quads = new_quads;
}
if( w->y() < rect.y() &&
( direction == Upwards || direction == Downwards ) )
{
WindowQuadList new_quads;
data.yTranslate = rect.height();
foreach( const WindowQuad &quad, data.quads )
{
if( quad.bottom() <= -w->y() )
{
new_quads.append( quad );
}
}
data.quads = new_quads;
}
if( w->y() + w->height() > rect.y() + rect.height() &&
( direction == Upwards || direction == Downwards ) )
{
WindowQuadList new_quads;
data.yTranslate = -rect.height();
foreach( const WindowQuad &quad, data.quads )
{
if( quad.bottom() > rect.height() - w->y() )
{
new_quads.append( quad );
}
}
data.quads = new_quads;
}
if( firstDesktop )
data.opacity *= timeLine.value();
else
data.opacity *= ( 1.0 - timeLine.value() );
}
}
effects->paintWindow( w, mask, region, data );
}
void CubeSlideEffect::postPaintScreen()
{
effects->postPaintScreen();
if( !slideRotations.empty() )
{
if( timeLine.value() == 1.0 )
{
RotationDirection direction = slideRotations.dequeue();
switch (direction)
{
case Left:
if( usePagerLayout )
front_desktop = effects->desktopToLeft( front_desktop, true );
else
{
front_desktop--;
if( front_desktop == 0 )
front_desktop = effects->numberOfDesktops();
}
break;
case Right:
if( usePagerLayout )
front_desktop = effects->desktopToRight( front_desktop, true );
else
{
front_desktop++;
if( front_desktop > effects->numberOfDesktops() )
front_desktop = 1;
}
break;
case Upwards:
front_desktop = effects->desktopAbove( front_desktop, true );
break;
case Downwards:
front_desktop = effects->desktopBelow( front_desktop, true );
break;
}
timeLine.setProgress( 0.0 );
if( slideRotations.count() == 1 )
timeLine.setCurveShape( TimeLine::EaseOutCurve );
else
timeLine.setCurveShape( TimeLine::LinearCurve );
if( slideRotations.empty() )
{
foreach( EffectWindow* w, panels )
w->setData( WindowForceBlurRole, QVariant( false ) );
foreach( EffectWindow* w, stickyWindows )
w->setData( WindowForceBlurRole, QVariant( false ) );
stickyWindows.clear();
panels.clear();
effects->setActiveFullScreenEffect( 0 );
}
}
effects->addRepaintFull();
}
}
void CubeSlideEffect::desktopChanged( int old )
{
if( effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this )
return;
if( old > effects->numberOfDesktops() )
{
// number of desktops has been reduced -> no animation
return;
}
if( windowMoving )
{
desktopChangedWhileMoving = true;
progressRestriction = 1.0 - progressRestriction;
effects->addRepaintFull();
return;
}
bool activate = true;
if( !slideRotations.empty() )
{
// last slide still in progress
activate = false;
RotationDirection direction = slideRotations.dequeue();
slideRotations.clear();
slideRotations.enqueue( direction );
switch (direction)
{
case Left:
if( usePagerLayout )
old = effects->desktopToLeft( front_desktop, true );
else
{
old = front_desktop - 1;
if( old == 0 )
old = effects->numberOfDesktops();
}
break;
case Right:
if( usePagerLayout )
old = effects->desktopToRight( front_desktop, true );
else
{
old = front_desktop + 1;
if( old > effects->numberOfDesktops() )
old = 1;
}
break;
case Upwards:
old = effects->desktopAbove( front_desktop, true );
break;
case Downwards:
old = effects->desktopBelow( front_desktop, true );
break;
}
}
if( usePagerLayout )
{
// calculate distance in respect to pager
QPoint diff = effects->desktopGridCoords( effects->currentDesktop() ) - effects->desktopGridCoords( old );
if( qAbs( diff.x() ) > effects->desktopGridWidth()/2 )
{
int sign = -1 * (diff.x()/qAbs( diff.x() ));
diff.setX( sign * ( effects->desktopGridWidth() - qAbs( diff.x() )));
}
if( diff.x() > 0 )
{
for( int i=0; i<diff.x(); i++ )
{
slideRotations.enqueue( Right );
}
}
else if( diff.x() < 0 )
{
diff.setX( -diff.x() );
for( int i=0; i<diff.x(); i++ )
{
slideRotations.enqueue( Left );
}
}
if( qAbs( diff.y() ) > effects->desktopGridHeight()/2 )
{
int sign = -1 * (diff.y()/qAbs( diff.y() ));
diff.setY( sign * ( effects->desktopGridHeight() - qAbs( diff.y() )));
}
if( diff.y() > 0 )
{
for( int i=0; i<diff.y(); i++ )
{
slideRotations.enqueue( Downwards );
}
}
if( diff.y() < 0 )
{
diff.setY( -diff.y() );
for( int i=0; i<diff.y(); i++ )
{
slideRotations.enqueue( Upwards );
}
}
}
else
{
// ignore pager layout
int left = old - effects->currentDesktop();
if( left < 0 )
left = effects->numberOfDesktops() + left;
int right = effects->currentDesktop() - old;
if( right < 0 )
right = effects->numberOfDesktops() + right;
if( left < right )
{
for( int i=0; i<left; i++ )
{
slideRotations.enqueue( Left );
}
}
else
{
for( int i=0; i<right; i++ )
{
slideRotations.enqueue( Right );
}
}
}
timeLine.setDuration( (float)rotationDuration / (float)slideRotations.count() );
if( activate )
{
if( slideRotations.count() == 1 )
timeLine.setCurveShape( TimeLine::EaseInOutCurve );
else
timeLine.setCurveShape( TimeLine::EaseInCurve );
effects->setActiveFullScreenEffect( this );
timeLine.setProgress( 0.0 );
front_desktop = old;
effects->addRepaintFull();
}
}
void CubeSlideEffect::windowUserMovedResized( EffectWindow* c, bool first, bool last )
{
if( !useWindowMoving )
return;
if( (first && last) || c->isUserResize() )
return;
if( last )
{
if( !desktopChangedWhileMoving )
{
if( slideRotations.isEmpty() )
return;
const RotationDirection direction = slideRotations.dequeue();
switch( direction )
{
case Left:
slideRotations.enqueue( Right );
break;
case Right:
slideRotations.enqueue( Left );
break;
case Upwards:
slideRotations.enqueue( Downwards );
break;
case Downwards:
slideRotations.enqueue( Upwards );
break;
default:
break; // impossible
}
timeLine.setProgress( 1.0 - timeLine.progress() );
}
desktopChangedWhileMoving = false;
windowMoving = false;
effects->addRepaintFull();
return;
}
const QPoint cursor = effects->cursorPos();
const int horizontal = displayWidth()*0.1;
const int vertical = displayHeight()*0.1;
const QRect leftRect( 0, displayHeight()*0.1, horizontal, displayHeight()*0.8 );
const QRect rightRect( displayWidth() - horizontal, displayHeight()*0.1, horizontal, displayHeight()*0.8 );
const QRect topRect( horizontal, 0, displayWidth()*0.8, vertical );
const QRect bottomRect( horizontal, displayHeight() - vertical, displayWidth() - horizontal*2, vertical );
if( leftRect.contains( cursor ) )
{
if( effects->desktopToLeft(effects->currentDesktop()) != effects->currentDesktop() )
windowMovingChanged( 0.3 * (float)(horizontal - cursor.x())/(float)horizontal, Left );
}
else if( rightRect.contains( cursor ) )
{
if( effects->desktopToRight(effects->currentDesktop()) != effects->currentDesktop() )
windowMovingChanged( 0.3 * (float)(cursor.x() - displayWidth() + horizontal)/(float)horizontal, Right );
}
else if( topRect.contains( cursor ) )
{
if( effects->desktopAbove(effects->currentDesktop()) != effects->currentDesktop() )
windowMovingChanged( 0.3 * (float)(vertical - cursor.y())/(float)vertical, Upwards);
}
else if( bottomRect.contains( cursor ) )
{
if( effects->desktopBelow(effects->currentDesktop()) != effects->currentDesktop() )
windowMovingChanged( 0.3 * (float)(cursor.y() - displayHeight() + vertical)/(float)vertical, Downwards );
}
else
{
// not in one of the areas
windowMoving = false;
desktopChangedWhileMoving = false;
timeLine.setProgress( 0.0 );
if( !slideRotations.isEmpty() )
slideRotations.clear();
effects->setActiveFullScreenEffect( 0 );
effects->addRepaintFull();
}
}
void CubeSlideEffect::windowMovingChanged( float progress, RotationDirection direction )
{
if( desktopChangedWhileMoving )
progressRestriction = 1.0 - progress;
else
progressRestriction = progress;
front_desktop = effects->currentDesktop();
if( slideRotations.isEmpty() )
{
slideRotations.enqueue( direction );
timeLine.setCurveShape( TimeLine::EaseInOutCurve );
windowMoving = true;
effects->setActiveFullScreenEffect( this );
}
effects->addRepaintFull();
}
} // namespace