/******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2008 Martin Gräßlin 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 . *********************************************************************/ #include "cube.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace KWin { KWIN_EFFECT( cube, CubeEffect ) KWIN_EFFECT_SUPPORTED( cube, CubeEffect::supported() ) CubeEffect::CubeEffect() : activated( false ) , cube_painting( false ) , keyboard_grab( false ) , schedule_close( false ) , borderActivate( ElectricNone ) , frontDesktop( 0 ) , cubeOpacity( 1.0 ) , displayDesktopName( true ) , reflection( true ) , rotating( false ) , desktopChangedWhileRotating( false ) , paintCaps( true ) , rotationDirection( Left ) , verticalRotationDirection( Upwards ) , verticalPosition( Normal ) , wallpaper( NULL ) , texturedCaps( true ) , capTexture( NULL ) , manualAngle( 0.0 ) , manualVerticalAngle( 0.0 ) , currentShape( TimeLine::EaseInOutCurve ) , start( false ) , stop( false ) , reflectionPainting( false ) , slide( false ) , oldDesktop( 0 ) , activeScreen( 0 ) , animateDesktopChange( false ) , bigCube( false ) , bottomCap( false ) , closeOnMouseRelease( false ) , zoom( 0.0 ) , zPosition( 0.0 ) , useForTabBox( false ) , tabBoxMode( false ) , capListCreated( false ) , capList( 0 ) { reconfigure( ReconfigureAll ); KActionCollection* actionCollection = new KActionCollection( this ); KAction* a = static_cast< KAction* >( actionCollection->addAction( "Cube" )); a->setText( i18n("Desktop Cube" )); a->setGlobalShortcut( KShortcut( Qt::CTRL + Qt::Key_F11 )); connect( a, SIGNAL( triggered( bool )), this, SLOT( toggle())); } bool CubeEffect::supported() { return effects->compositingType() == OpenGLCompositing; } void CubeEffect::reconfigure( ReconfigureFlags ) { loadConfig( "Cube" ); } void CubeEffect::loadConfig( QString config ) { KConfigGroup conf = effects->effectConfig( config ); effects->unreserveElectricBorder( borderActivate ); borderActivate = (ElectricBorder)conf.readEntry( "BorderActivate", (int)ElectricNone ); effects->reserveElectricBorder( borderActivate ); cubeOpacity = (float)conf.readEntry( "Opacity", 80 )/100.0f; displayDesktopName = conf.readEntry( "DisplayDesktopName", true ); reflection = conf.readEntry( "Reflection", true ); rotationDuration = animationTime( conf, "RotationDuration", 500 ); backgroundColor = conf.readEntry( "BackgroundColor", QColor( Qt::black ) ); animateDesktopChange = conf.readEntry( "AnimateDesktopChange", false ); bigCube = conf.readEntry( "BigCube", false ); capColor = conf.readEntry( "CapColor", KColorScheme( QPalette::Active, KColorScheme::Window ).background().color() ); paintCaps = conf.readEntry( "Caps", true ); closeOnMouseRelease = conf.readEntry( "CloseOnMouseRelease", false ); zPosition = conf.readEntry( "ZPosition", 100.0 ); useForTabBox = conf.readEntry( "TabBox", false ); QString file = conf.readEntry( "Wallpaper", QString("") ); delete wallpaper; wallpaper = NULL; if( !file.isEmpty() ) { QImage img = QImage( file ); if( !img.isNull() ) { wallpaper = new GLTexture( img ); } } delete capTexture; capTexture = NULL; texturedCaps = conf.readEntry( "TexturedCaps", true ); if( texturedCaps ) { QImage img = QImage( KGlobal::dirs()->findResource( "appdata", "cubecap.png" ) ); if( !img.isNull() ) { // change the alpha value of each pixel for( int x=0; xsetFilter( GL_LINEAR ); capTexture->setWrapMode( GL_CLAMP_TO_EDGE ); } } timeLine.setCurveShape( TimeLine::EaseInOutCurve ); timeLine.setDuration( rotationDuration ); verticalTimeLine.setCurveShape( TimeLine::EaseInOutCurve ); verticalTimeLine.setDuration( rotationDuration ); } CubeEffect::~CubeEffect() { effects->unreserveElectricBorder( borderActivate ); delete wallpaper; delete capTexture; } void CubeEffect::prePaintScreen( ScreenPrePaintData& data, int time ) { if( activated ) { data.mask |= PAINT_SCREEN_TRANSFORMED | Effect::PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS | PAINT_SCREEN_BACKGROUND_FIRST; if( rotating || start || stop ) { timeLine.addTime( time ); } if( verticalRotating ) { verticalTimeLine.addTime( time ); } } effects->prePaintScreen( data, time ); } void CubeEffect::paintScreen( int mask, QRegion region, ScreenPaintData& data ) { if( activated ) { QRect rect = effects->clientArea( FullScreenArea, activeScreen, effects->currentDesktop()); if( effects->numScreens() > 1 && (slide || bigCube ) ) rect = effects->clientArea( FullArea, activeScreen, effects->currentDesktop() ); QRect fullRect = effects->clientArea( FullArea, activeScreen, effects->currentDesktop() ); // background float clearColor[4]; glGetFloatv( GL_COLOR_CLEAR_VALUE, clearColor ); glClearColor( backgroundColor.redF(), backgroundColor.greenF(), backgroundColor.blueF(), 1.0 ); glClear( GL_COLOR_BUFFER_BIT ); glClearColor( clearColor[0], clearColor[1], clearColor[2], clearColor[3] ); // wallpaper if( wallpaper && !slide ) { wallpaper->bind(); wallpaper->render( region, rect ); wallpaper->unbind(); } glPushAttrib( GL_CURRENT_BIT | GL_ENABLE_BIT ); glEnable( GL_BLEND ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); if( effects->numScreens() > 1 && !slide && !bigCube ) { windowsOnOtherScreens.clear(); // unfortunatelly we have to change the projection matrix in dual screen mode glMatrixMode( GL_PROJECTION ); glPushMatrix(); glLoadIdentity(); float fovy = 60.0f; float aspect = 1.0f; float zNear = 0.1f; float zFar = 100.0f; float ymax = zNear * tan( fovy * M_PI / 360.0f ); float ymin = -ymax; float xmin = ymin * aspect; float xmax = ymax * aspect; float xTranslate = 0.0; float yTranslate = 0.0; float xminFactor = 1.0; float xmaxFactor = 1.0; float yminFactor = 1.0; float ymaxFactor = 1.0; if( rect.x() == 0 && rect.width() != fullRect.width() ) { // horizontal layout: left screen xminFactor = (float)rect.width()/(float)fullRect.width(); xmaxFactor = ((float)fullRect.width()-(float)rect.width()*0.5f)/((float)fullRect.width()*0.5f); xTranslate = (float)fullRect.width()*0.5f-(float)rect.width()*0.5f; } if( rect.x() != 0 && rect.width() != fullRect.width() ) { // horizontal layout: right screen xminFactor = ((float)fullRect.width()-(float)rect.width()*0.5f)/((float)fullRect.width()*0.5f); xmaxFactor = (float)rect.width()/(float)fullRect.width(); xTranslate = (float)fullRect.width()*0.5f-(float)rect.width()*0.5f; } if( rect.y() == 0 && rect.height() != fullRect.height() ) { // vertical layout: top screen yminFactor = ((float)fullRect.height()-(float)rect.height()*0.5f)/((float)fullRect.height()*0.5f); ymaxFactor = (float)rect.height()/(float)fullRect.height(); yTranslate = (float)fullRect.height()*0.5f-(float)rect.height()*0.5f; } if( rect.y() != 0 && rect.height() != fullRect.height() ) { // vertical layout: bottom screen yminFactor = (float)rect.height()/(float)fullRect.height(); ymaxFactor = ((float)fullRect.height()-(float)rect.height()*0.5f)/((float)fullRect.height()*0.5f); yTranslate = (float)fullRect.height()*0.5f-(float)rect.height()*0.5f; } glFrustum( xmin*xminFactor, xmax*xmaxFactor, ymin*yminFactor, ymax*ymaxFactor, zNear, zFar ); glMatrixMode( GL_MODELVIEW ); glPushMatrix(); glTranslatef( xTranslate, yTranslate, 0.0 ); } // reflection if( reflection && (!slide) ) { // restrict painting the reflections to the current screen PaintClipper::push( QRegion( rect )); glPushMatrix(); // we can use a huge scale factor (needed to calculate the rearground vertices) // as we restrict with a PaintClipper painting on the current screen float scaleFactor = 1000000 * tan( 60.0 * M_PI / 360.0f )/rect.height(); glScalef( 1.0, -1.0, 1.0 ); glTranslatef( 0.0, -rect.height()*2, 0.0 ); glEnable( GL_CLIP_PLANE0 ); reflectionPainting = true; paintScene( mask, region, data ); reflectionPainting = false; glDisable( GL_CLIP_PLANE0 ); glPopMatrix(); glPushMatrix(); if( effects->numScreens() > 1 && rect.x() != fullRect.x() && !slide && !bigCube ) { // have to change the reflection area in horizontal layout and right screen glTranslatef( -rect.x(), 0.0, 0.0 ); } glTranslatef( rect.x() + rect.width()*0.5f, 0.0, 0.0 ); float vertices[] = { -rect.width()*0.5f, rect.height(), 0.0, rect.width()*0.5f, rect.height(), 0.0, (float)rect.width()*scaleFactor, rect.height(), -5000, -(float)rect.width()*scaleFactor, rect.height(), -5000 }; // foreground float alpha = 0.7; if( start ) alpha = 0.3 + 0.4 * timeLine.value(); if( stop ) alpha = 0.3 + 0.4 * ( 1.0 - timeLine.value() ); glColor4f( 0.0, 0.0, 0.0, alpha ); glBegin( GL_POLYGON ); glVertex3f( vertices[0], vertices[1], vertices[2] ); glVertex3f( vertices[3], vertices[4], vertices[5] ); // rearground alpha = -1.0; glColor4f( 0.0, 0.0, 0.0, alpha ); glVertex3f( vertices[6], vertices[7], vertices[8] ); glVertex3f( vertices[9], vertices[10], vertices[11] ); glEnd(); glPopMatrix(); PaintClipper::pop( QRegion( rect )); } glPushMatrix(); paintScene( mask, region, data ); glPopMatrix(); if( effects->numScreens() > 1 && !slide && !bigCube ) { glPopMatrix(); // revert change of projection matrix glMatrixMode( GL_PROJECTION ); glPopMatrix(); glMatrixMode( GL_MODELVIEW ); } glDisable( GL_BLEND ); glPopAttrib(); // desktop name box - inspired from coverswitch if( displayDesktopName && (!slide) ) { QColor color_frame; QColor color_text; color_frame = KColorScheme( QPalette::Active, KColorScheme::Window ).background().color(); color_frame.setAlphaF( 0.5 ); color_text = KColorScheme( QPalette::Active, KColorScheme::Window ).foreground().color(); if( start ) { color_frame.setAlphaF( 0.5 * timeLine.value() ); color_text.setAlphaF( timeLine.value() ); } if( stop ) { color_frame.setAlphaF( 0.5 - 0.5 * timeLine.value() ); color_text.setAlphaF( 1.0 - timeLine.value() ); } QFont text_font; text_font.setBold( true ); text_font.setPointSize( 14 ); glPushAttrib( GL_CURRENT_BIT ); glColor4f( color_frame.redF(), color_frame.greenF(), color_frame.blueF(), color_frame.alphaF()); QRect frameRect = QRect( rect.width()*0.33f + rect.x(), rect.height() + rect.y() - QFontMetrics( text_font ).height() * 1.2f, rect.width()*0.33f, QFontMetrics( text_font ).height() * 1.2f ); renderRoundBoxWithEdge( frameRect ); effects->paintText( effects->desktopName( frontDesktop ), frameRect.center(), frameRect.width(), color_text, text_font ); glPopAttrib(); } if( effects->numScreens() > 1 && !slide && !bigCube ) { foreach( EffectWindow* w, windowsOnOtherScreens ) { WindowPaintData wData( w ); if( start && !w->isDesktop() && !w->isDock() ) wData.opacity *= (1.0 - timeLine.value()); if( stop && !w->isDesktop() && !w->isDock() ) wData.opacity *= timeLine.value(); effects->paintWindow( w, 0, QRegion( w->x(), w->y(), w->width(), w->height() ), wData ); } } } else { effects->paintScreen( mask, region, data ); } } void CubeEffect::paintScene( int mask, QRegion region, ScreenPaintData& data ) { QRect rect = effects->clientArea( FullScreenArea, activeScreen, effects->currentDesktop()); if( effects->numScreens() > 1 && (slide || bigCube ) ) rect = effects->clientArea( FullArea, activeScreen, effects->currentDesktop() ); float xScale = 1.0; float yScale = 1.0; if( effects->numScreens() > 1 && !slide && !bigCube ) { QRect fullRect = effects->clientArea( FullArea, activeScreen, effects->currentDesktop() ); xScale = (float)rect.width()/(float)fullRect.width(); yScale = (float)rect.height()/(float)fullRect.height(); if( start ) { xScale = xScale + (1.0 - xScale) * (1.0 - timeLine.value()); yScale = yScale + (1.0 - yScale) * (1.0 - timeLine.value()); if( rect.x() > 0 ) glTranslatef( -rect.x()*(1.0 - timeLine.value()), 0.0, 0.0 ); if( rect.y() > 0 ) glTranslatef( 0.0, -rect.y()*(1.0 - timeLine.value()), 0.0 ); } if( stop ) { xScale = xScale + (1.0 - xScale) * timeLine.value(); yScale = yScale + (1.0 - yScale) * timeLine.value(); if( rect.x() > 0 ) glTranslatef( -rect.x()*timeLine.value(), 0.0, 0.0 ); if( rect.y() > 0 ) glTranslatef( 0.0, -rect.y()*timeLine.value(), 0.0 ); } glScalef( xScale, yScale, 1.0 ); rect = fullRect; } int rightSteps = effects->numberOfDesktops()/2; int leftSteps = rightSteps+1; int rightSideCounter = 0; int leftSideCounter = 0; float internalCubeAngle = 360.0f / effects->numberOfDesktops(); cube_painting = true; int desktopIndex = 0; float zTranslate = zPosition + zoom; if( start ) zTranslate *= timeLine.value(); if( stop ) zTranslate *= ( 1.0 - timeLine.value() ); if( slide ) zTranslate = 0.0; bool topCapAfter = false; bool topCapBefore = false; // Rotation of the cube float cubeAngle = (float)((float)(effects->numberOfDesktops() - 2 )/(float)effects->numberOfDesktops() * 180.0f); float point = rect.width()/2*tan(cubeAngle*0.5f*M_PI/180.0f); float zTexture = rect.width()/2*tan(45.0f*M_PI/180.0f); if( verticalRotating || verticalPosition != Normal || manualVerticalAngle != 0.0 ) { // change the verticalPosition if manualVerticalAngle > 90 or < -90 degrees if( manualVerticalAngle <= -90.0 ) { manualVerticalAngle += 90.0; if( verticalPosition == Normal ) verticalPosition = Down; if( verticalPosition == Up ) verticalPosition = Normal; } if( manualVerticalAngle >= 90.0 ) { manualVerticalAngle -= 90.0; if( verticalPosition == Normal ) verticalPosition = Up; if( verticalPosition == Down ) verticalPosition = Normal; } float angle = 0.0; if( verticalPosition == Up ) { angle = 90.0; if( !verticalRotating) { if( manualVerticalAngle < 0.0 ) angle += manualVerticalAngle; else manualVerticalAngle = 0.0; } } else if( verticalPosition == Down ) { angle = -90.0; if( !verticalRotating) { if( manualVerticalAngle > 0.0 ) angle += manualVerticalAngle; else manualVerticalAngle = 0.0; } } else { angle = manualVerticalAngle; } if( verticalRotating ) { angle *= verticalTimeLine.value(); if( verticalPosition == Normal && verticalRotationDirection == Upwards ) angle = -90.0 + 90*verticalTimeLine.value(); if( verticalPosition == Normal && verticalRotationDirection == Downwards ) angle = 90.0 - 90*verticalTimeLine.value(); angle += manualVerticalAngle * (1.0-verticalTimeLine.value()); } if( stop ) angle *= (1.0 - timeLine.value()); glTranslatef( rect.width()/2, rect.height()/2, -point-zTranslate ); glRotatef( angle, 1.0, 0.0, 0.0 ); glTranslatef( -rect.width()/2, -rect.height()/2, point+zTranslate ); // calculate if the caps have to be painted before/after or during desktop painting if( paintCaps ) { float M[16]; float P[16]; float V[4]; glGetFloatv( GL_PROJECTION_MATRIX, P ); glGetFloatv( GL_MODELVIEW_MATRIX, M ); glGetFloatv( GL_VIEWPORT, V ); // calculate y coordinate of the top of front desktop float X = M[0]*0.0 + M[4]*0.0 + M[8]*(-zTranslate) + M[12]*1; float Y = M[1]*0.0 + M[5]*0.0 + M[9]*(-zTranslate) + M[13]*1; float Z = M[2]*0.0 + M[6]*0.0 + M[10]*(-zTranslate) + M[14]*1; float W = M[3]*0.0 + M[7]*0.0 + M[11]*(-zTranslate) + M[15]*1; float clipY = P[1]*X + P[5]*Y + P[9]*Z + P[13]*W; float clipW = P[3]*X + P[7]*Y + P[11]*Z + P[15]*W; float normY = clipY/clipW; float yFront = (V[3]/2)*normY+(V[3]-V[1])/2; // calculate y coordinate of the bottom of front desktop X = M[0]*0.0 + M[4]*rect.height() + M[8]*(-zTranslate) + M[12]*1; Y = M[1]*0.0 + M[5]*rect.height() + M[9]*(-zTranslate) + M[13]*1; Z = M[2]*0.0 + M[6]*rect.height() + M[10]*(-zTranslate) + M[14]*1; W = M[3]*0.0 + M[7]*rect.height() + M[11]*(-zTranslate) + M[15]*1; clipY = P[1]*X + P[5]*Y + P[9]*Z + P[13]*W; clipW = P[3]*X + P[7]*Y + P[11]*Z + P[15]*W; normY = clipY/clipW; float yFrontBottom = (V[3]/2)*normY+(V[3]-V[1])/2; // change matrix to a rear position glPushMatrix(); glTranslatef( 0.0, 0.0, -point-zTranslate ); float desktops = (effects->numberOfDesktops()/2.0); glRotatef( desktops*internalCubeAngle, 1.0, 0.0, 0.0 ); glTranslatef( 0.0, 0.0, point ); glGetFloatv(GL_MODELVIEW_MATRIX, M); // calculate y coordinate of the top of rear desktop X = M[0]*0.0 + M[4]*0.0 + M[8]*0.0 + M[12]*1; Y = M[1]*0.0 + M[5]*0.0 + M[9]*0.0 + M[13]*1; Z = M[2]*0.0 + M[6]*0.0 + M[10]*0.0 + M[14]*1; W = M[3]*0.0 + M[7]*0.0 + M[11]*0.0 + M[15]*1; clipY = P[1]*X + P[5]*Y + P[9]*Z + P[13]*W; clipW = P[3]*X + P[7]*Y + P[11]*Z + P[15]*W; normY = clipY/clipW; float yBack = (V[3]/2)*normY+(V[3]-V[1])/2; // calculate y coordniate of the bottom of rear desktop glTranslatef( 0.0, -rect.height(), 0.0 ); glGetFloatv(GL_MODELVIEW_MATRIX, M); X = M[0]*0.0 + M[4]*0.0 + M[8]*0.0 + M[12]*1; Y = M[1]*0.0 + M[5]*0.0 + M[9]*0.0 + M[13]*1; Z = M[2]*0.0 + M[6]*0.0 + M[10]*0.0 + M[14]*1; W = M[3]*0.0 + M[7]*0.0 + M[11]*0.0 + M[15]*1; clipY = P[1]*X + P[5]*Y + P[9]*Z + P[13]*W; clipW = P[3]*X + P[7]*Y + P[11]*Z + P[15]*W; normY = clipY/clipW; float yBackBottom = (V[3]/2)*normY+(V[3]-V[1])/2; glPopMatrix(); if( yBack >= yFront ) topCapAfter = true; if( yBackBottom <= yFrontBottom ) topCapBefore = true; } } if( rotating || (manualAngle != 0.0) ) { int tempFrontDesktop = frontDesktop; if( manualAngle > internalCubeAngle * 0.5f ) { manualAngle -= internalCubeAngle; tempFrontDesktop--; if( tempFrontDesktop == 0 ) tempFrontDesktop = effects->numberOfDesktops(); } if( manualAngle < -internalCubeAngle * 0.5f ) { manualAngle += internalCubeAngle; tempFrontDesktop++; if( tempFrontDesktop > effects->numberOfDesktops() ) tempFrontDesktop = 1; } float rotationAngle = internalCubeAngle * timeLine.value(); if( rotationAngle > internalCubeAngle * 0.5f ) { rotationAngle -= internalCubeAngle; if( !desktopChangedWhileRotating ) { desktopChangedWhileRotating = true; if( rotationDirection == Left ) { tempFrontDesktop++; } else if( rotationDirection == Right ) { tempFrontDesktop--; } if( tempFrontDesktop > effects->numberOfDesktops() ) tempFrontDesktop = 1; else if( tempFrontDesktop == 0 ) tempFrontDesktop = effects->numberOfDesktops(); } } // don't change front desktop during stop animation as this would break some logic if( !stop ) frontDesktop = tempFrontDesktop; if( rotationDirection == Left ) { rotationAngle *= -1; } if( stop ) rotationAngle = manualAngle * (1.0 - timeLine.value()); else rotationAngle += manualAngle * (1.0 - timeLine.value()); glTranslatef( rect.width()/2, rect.height()/2, -point-zTranslate ); glRotatef( rotationAngle, 0.0, 1.0, 0.0 ); glTranslatef( -rect.width()/2, -rect.height()/2, point+zTranslate ); } if( topCapBefore || topCapAfter ) { if( (topCapAfter && !reflectionPainting) || (topCapBefore && reflectionPainting) ) { // paint the bottom cap bottomCap = true; glTranslatef( 0.0, rect.height(), 0.0 ); paintCap( point, zTexture ); glTranslatef( 0.0, -rect.height(), 0.0 ); bottomCap = false; } if( (topCapBefore && !reflectionPainting) || (topCapAfter && reflectionPainting) ) { // paint the top cap paintCap( point, zTexture ); } } for( int i=0; inumberOfDesktops(); i++ ) { if( !topCapAfter && !topCapBefore && i == effects->numberOfDesktops()/2 -1 && !slide ) { // paint the bottom cap bottomCap = true; glTranslatef( 0.0, rect.height(), 0.0 ); paintCap( point, zTexture ); glTranslatef( 0.0, -rect.height(), 0.0 ); bottomCap = false; // paint the top cap paintCap( point, zTexture ); } if( i%2 == 0 && i != effects->numberOfDesktops() -1) { // desktops on the right (including back) desktopIndex = rightSteps - rightSideCounter; rightSideCounter++; } else { // desktops on the left (including front) desktopIndex = leftSteps + leftSideCounter; leftSideCounter++; } // start painting the cube painting_desktop = (desktopIndex + frontDesktop )%effects->numberOfDesktops(); if( painting_desktop == 0 ) { painting_desktop = effects->numberOfDesktops(); } if( slide ) { // only paint required desktops during slide phase if( painting_desktop != frontDesktop ) { int leftDesktop = frontDesktop - 1; int rightDesktop = frontDesktop + 1; if( leftDesktop == 0 ) leftDesktop = effects->numberOfDesktops(); if( rightDesktop > effects->numberOfDesktops() ) rightDesktop = 1; if( !desktopChangedWhileRotating && rotationDirection == Left && painting_desktop != rightDesktop ) { continue; } if( desktopChangedWhileRotating && rotationDirection == Left && painting_desktop != leftDesktop ) { continue; } if( !desktopChangedWhileRotating && rotationDirection == Right && painting_desktop != leftDesktop ) { continue; } if( desktopChangedWhileRotating && rotationDirection == Right && painting_desktop != rightDesktop ) { continue; } } } ScreenPaintData newData = data; RotationData rot = RotationData(); rot.axis = RotationData::YAxis; rot.angle = internalCubeAngle * desktopIndex; rot.xRotationPoint = rect.width()/2; rot.zRotationPoint = -point; newData.rotation = &rot; newData.zTranslate = -zTranslate; effects->paintScreen( mask, region, newData ); } if( topCapBefore || topCapAfter ) { if( (topCapAfter && !reflectionPainting) || (topCapBefore && reflectionPainting) ) { // paint the top cap paintCap( point, zTexture ); } if( (topCapBefore && !reflectionPainting) || (topCapAfter && reflectionPainting) ) { // paint the bottom cap bottomCap = true; glTranslatef( 0.0, rect.height(), 0.0 ); paintCap( point, zTexture ); glTranslatef( 0.0, -rect.height(), 0.0 ); bottomCap = false; } } cube_painting = false; painting_desktop = effects->currentDesktop(); } void CubeEffect::paintCap( float z, float zTexture ) { QRect rect = effects->clientArea( FullArea, activeScreen, effects->currentDesktop()); if( ( !paintCaps ) || effects->numberOfDesktops() <= 2 ) return; float opacity = cubeOpacity; if( start ) opacity = 1.0 - (1.0 - opacity)*timeLine.value(); if( stop ) opacity = 1.0 - (1.0 - opacity)*( 1.0 - timeLine.value() ); glColor4f( capColor.redF(), capColor.greenF(), capColor.blueF(), opacity ); float angle = 360.0f/effects->numberOfDesktops(); glPushMatrix(); float zTranslate = zPosition + zoom; if( start ) zTranslate *= timeLine.value(); if( stop ) zTranslate *= ( 1.0 - timeLine.value() ); glTranslatef( rect.width()/2, 0.0, -z-zTranslate ); glRotatef( (1-frontDesktop)*angle, 0.0, 1.0, 0.0 ); if( !capListCreated ) { capListCreated = true; glNewList( capList, GL_COMPILE_AND_EXECUTE ); bool texture = false; if( texturedCaps && effects->numberOfDesktops() > 3 && capTexture ) { texture = true; paintCapStep( z, zTexture, true ); } else paintCapStep( z, zTexture, false ); glEndList(); } else glCallList( capList ); glPopMatrix(); } void CubeEffect::paintCapStep( float z, float zTexture, bool texture ) { QRect rect = effects->clientArea( FullArea, activeScreen, effects->currentDesktop()); float angle = 360.0f/effects->numberOfDesktops(); if( texture ) capTexture->bind(); for( int i=0; inumberOfDesktops(); i++ ) { int triangleRows = effects->numberOfDesktops()*5; float zTriangleDistance = z/(float)triangleRows; float widthTriangle = tan( angle*0.5 * M_PI/180.0 ) * zTriangleDistance; float currentWidth = 0.0; glBegin( GL_TRIANGLES ); float cosValue = cos( i*angle * M_PI/180.0 ); float sinValue = sin( i*angle * M_PI/180.0 ); for( int j=0; junbind(); } } void CubeEffect::postPaintScreen() { effects->postPaintScreen(); if( activated ) { if( start ) { if( timeLine.value() == 1.0 ) { start = false; timeLine.setProgress(0.0); // more rotations? if( !rotations.empty() ) { rotationDirection = rotations.dequeue(); rotating = true; // change the curve shape if current shape is not easeInOut if( currentShape != TimeLine::EaseInOutCurve ) { // more rotations follow -> linear curve if( !rotations.empty() ) { currentShape = TimeLine::LinearCurve; } // last rotation step -> easeOut curve else { currentShape = TimeLine::EaseOutCurve; } timeLine.setCurveShape( currentShape ); } else { // if there is at least one more rotation, we can change to easeIn if( !rotations.empty() ) { currentShape = TimeLine::EaseInCurve; timeLine.setCurveShape( currentShape ); } } } } effects->addRepaintFull(); return; // schedule_close could have been called, start has to finish first } if( stop ) { if( timeLine.value() == 1.0 ) { effects->setCurrentDesktop( frontDesktop ); stop = false; timeLine.setProgress(0.0); activated = false; // set the new desktop if( keyboard_grab ) effects->ungrabKeyboard(); keyboard_grab = false; effects->destroyInputWindow( input ); effects->setActiveFullScreenEffect( 0 ); // delete the GL lists glDeleteLists( capList, 1 ); } effects->addRepaintFull(); } if( rotating || verticalRotating ) { if( rotating && timeLine.value() == 1.0 ) { timeLine.setProgress(0.0); rotating = false; desktopChangedWhileRotating = false; manualAngle = 0.0; // more rotations? if( !rotations.empty() ) { rotationDirection = rotations.dequeue(); rotating = true; // change the curve shape if current shape is not easeInOut if( currentShape != TimeLine::EaseInOutCurve ) { // more rotations follow -> linear curve if( !rotations.empty() ) { currentShape = TimeLine::LinearCurve; } // last rotation step -> easeOut curve else { currentShape = TimeLine::EaseOutCurve; } timeLine.setCurveShape( currentShape ); } else { // if there is at least one more rotation, we can change to easeIn if( !rotations.empty() ) { currentShape = TimeLine::EaseInCurve; timeLine.setCurveShape( currentShape ); } } } else { // reset curve shape if there are no more rotations if( currentShape != TimeLine::EaseInOutCurve ) { currentShape = TimeLine::EaseInOutCurve; timeLine.setCurveShape( currentShape ); } } } if( verticalRotating && verticalTimeLine.value() == 1.0 ) { verticalTimeLine.setProgress(0.0); verticalRotating = false; manualVerticalAngle = 0.0; // more rotations? if( !verticalRotations.empty() ) { verticalRotationDirection = verticalRotations.dequeue(); verticalRotating = true; if( verticalRotationDirection == Upwards ) { if( verticalPosition == Normal ) verticalPosition = Up; if( verticalPosition == Down ) verticalPosition = Normal; } if( verticalRotationDirection == Downwards ) { if( verticalPosition == Normal ) verticalPosition = Down; if( verticalPosition == Up ) verticalPosition = Normal; } } } effects->addRepaintFull(); return; // rotation has to end before cube is closed } if( schedule_close ) { schedule_close = false; if( !slide ) { stop = true; } else { activated = false; slide = false; timeLine.setDuration( rotationDuration ); effects->setActiveFullScreenEffect( 0 ); } effects->addRepaintFull(); } } } void CubeEffect::prePaintWindow( EffectWindow* w, WindowPrePaintData& data, int time ) { if( activated ) { if( cube_painting ) { if( ( w->isDesktop() || w->isDock() ) && w->screen() != activeScreen && w->isOnDesktop( effects->currentDesktop() ) ) { windowsOnOtherScreens.append( w ); } if( (start || stop) && w->screen() != activeScreen && w->isOnDesktop( effects->currentDesktop() ) && !w->isDesktop() && !w->isDock() ) { windowsOnOtherScreens.append( w ); } if( w->isOnDesktop( painting_desktop )) { QRect rect = effects->clientArea( FullArea, activeScreen, 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 { // check for windows belonging to the previous desktop int prev_desktop = painting_desktop -1; if( prev_desktop == 0 ) prev_desktop = effects->numberOfDesktops(); if( w->isOnDesktop( prev_desktop ) ) { QRect rect = effects->clientArea( FullArea, activeScreen, prev_desktop); if( w->x()+w->width() > rect.x() + rect.width() ) { w->enablePainting( EffectWindow::PAINT_DISABLED_BY_DESKTOP ); 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() ); } data.setTransformed(); effects->prePaintWindow( w, data, time ); return; } } // check for windows belonging to the next desktop int next_desktop = painting_desktop +1; if( next_desktop > effects->numberOfDesktops() ) next_desktop = 1; if( w->isOnDesktop( next_desktop ) ) { QRect rect = effects->clientArea( FullArea, activeScreen, next_desktop); if( w->x() < rect.x() ) { w->enablePainting( EffectWindow::PAINT_DISABLED_BY_DESKTOP ); data.quads = data.quads.splitAtX( -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() ); } data.setTransformed(); effects->prePaintWindow( w, data, time ); return; } } w->disablePainting( EffectWindow::PAINT_DISABLED_BY_DESKTOP ); } } } effects->prePaintWindow( w, data, time ); } void CubeEffect::paintWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data ) { if( activated && cube_painting ) { //kDebug(1212) << w->caption(); float opacity = cubeOpacity; if( slide ) opacity = 1.0f; if( start ) { opacity = 1.0 - (1.0 - opacity)*timeLine.value(); if( reflectionPainting ) opacity = 0.5 + ( cubeOpacity - 0.5 )*timeLine.value(); // fade in windows belonging to different desktops if( painting_desktop == effects->currentDesktop() && (!w->isOnDesktop( painting_desktop )) ) opacity = timeLine.value() * cubeOpacity; } if( stop ) { opacity = 1.0 - (1.0 - opacity)*( 1.0 - timeLine.value() ); if( reflectionPainting ) opacity = 0.5 + ( cubeOpacity - 0.5 )*( 1.0 - timeLine.value() ); // fade out windows belonging to different desktops if( painting_desktop == effects->currentDesktop() && (!w->isOnDesktop( painting_desktop )) ) opacity = cubeOpacity * (1.0 - timeLine.value()); } bool slideOpacity = false; // fade in windows from different desktops in first slide step if( slide && painting_desktop == oldDesktop && (!w->isOnDesktop( painting_desktop )) ) { opacity = timeLine.value(); slideOpacity = true; } // fade out windows from different desktops in last slide step if( slide && rotations.empty() && painting_desktop == effects->currentDesktop() && (!w->isOnDesktop( painting_desktop )) ) { opacity = 1.0 - timeLine.value(); slideOpacity = true; } // check for windows belonging to the previous desktop int prev_desktop = painting_desktop -1; if( prev_desktop == 0 ) prev_desktop = effects->numberOfDesktops(); int next_desktop = painting_desktop +1; if( next_desktop > effects->numberOfDesktops() ) next_desktop = 1; if( w->isOnDesktop( prev_desktop ) && ( mask & PAINT_WINDOW_TRANSFORMED ) ) { QRect rect = effects->clientArea( FullArea, activeScreen, prev_desktop); data.xTranslate = -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->isOnDesktop( next_desktop ) && ( mask & PAINT_WINDOW_TRANSFORMED ) ) { QRect rect = effects->clientArea( FullArea, activeScreen, next_desktop); data.xTranslate = rect.width(); WindowQuadList new_quads; foreach( const WindowQuad &quad, data.quads ) { if( w->x() + quad.right() <= rect.x() ) { new_quads.append( quad ); } } data.quads = new_quads; } QRect rect = effects->clientArea( FullArea, activeScreen, painting_desktop ); if( start || stop ) { // we have to change opacity values for fade in/out of windows which are shown on front-desktop if( prev_desktop == effects->currentDesktop() && w->x() < rect.x() ) { if( start ) opacity = timeLine.value() * cubeOpacity; if( stop ) opacity = cubeOpacity * (1.0 - timeLine.value()); } if( next_desktop == effects->currentDesktop() && w->x() + w->width() > rect.x() + rect.width() ) { if( start ) opacity = timeLine.value() * cubeOpacity; if( stop ) opacity = cubeOpacity * (1.0 - timeLine.value()); } } if( !slide || (slide && !w->isDesktop()) || slideOpacity ) { // HACK set opacity to 0.99 in case of fully opaque to ensure that windows are painted in correct sequence // bug #173214 if( opacity > 0.99f ) opacity = 0.99f; data.opacity *= opacity; } if( w->isOnDesktop(painting_desktop) && 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->isOnDesktop(painting_desktop) && 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; } } effects->paintWindow( w, mask, region, data ); } bool CubeEffect::borderActivated( ElectricBorder border ) { if( effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this ) return false; if( border == borderActivate && !activated ) { kDebug(1212) << "border activated"; toggle(); return true; } return false; } void CubeEffect::toggle() { if( effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this || effects->numberOfDesktops() < 2 ) return; if( !activated ) { setActive( true ); } else { setActive( false ); } } void CubeEffect::grabbedKeyboardEvent( QKeyEvent* e ) { if( stop ) return; // taken from desktopgrid.cpp if( e->type() == QEvent::KeyPress ) { int desktop = -1; // switch by F or just if( e->key() >= Qt::Key_F1 && e->key() <= Qt::Key_F35 ) desktop = e->key() - Qt::Key_F1 + 1; else if( e->key() >= Qt::Key_0 && e->key() <= Qt::Key_9 ) desktop = e->key() == Qt::Key_0 ? 10 : e->key() - Qt::Key_0; if( desktop != -1 ) { if( desktop <= effects->numberOfDesktops()) { // we have to rotate to chosen desktop // and end effect when rotation finished rotateToDesktop( desktop ); setActive( false ); } return; } switch( e->key()) { // wrap only on autorepeat case Qt::Key_Left: // rotate to previous desktop kDebug(1212) << "left"; if( !rotating && !start ) { rotating = true; rotationDirection = Left; } else { if( rotations.count() < effects->numberOfDesktops() ) rotations.enqueue( Left ); } break; case Qt::Key_Right: // rotate to next desktop kDebug(1212) << "right"; if( !rotating && !start ) { rotating = true; rotationDirection = Right; } else { if( rotations.count() < effects->numberOfDesktops() ) rotations.enqueue( Right ); } break; case Qt::Key_Up: kDebug(1212) << "up"; if( verticalPosition != Up ) { if( !verticalRotating ) { verticalRotating = true; verticalRotationDirection = Upwards; if( verticalPosition == Normal ) verticalPosition = Up; if( verticalPosition == Down ) verticalPosition = Normal; } else { verticalRotations.enqueue( Upwards ); } } else if( manualVerticalAngle < 0.0 && !verticalRotating ) { // rotate to up position from the manual position verticalRotating = true; verticalRotationDirection = Upwards; verticalPosition = Up; manualVerticalAngle += 90.0; } break; case Qt::Key_Down: kDebug(1212) << "down"; if( verticalPosition != Down ) { if( !verticalRotating ) { verticalRotating = true; verticalRotationDirection = Downwards; if( verticalPosition == Normal ) verticalPosition = Down; if( verticalPosition == Up ) verticalPosition = Normal; } else { verticalRotations.enqueue( Downwards ); } } else if( manualVerticalAngle > 0.0 && !verticalRotating ) { // rotate to down position from the manual position verticalRotating = true; verticalRotationDirection = Downwards; verticalPosition = Down; manualVerticalAngle -= 90.0; } break; case Qt::Key_Escape: rotateToDesktop( effects->currentDesktop() ); setActive( false ); return; case Qt::Key_Enter: case Qt::Key_Return: case Qt::Key_Space: setActive( false ); return; case Qt::Key_Plus: zoom -= 10.0; zoom = qMax( -zPosition, zoom ); break; case Qt::Key_Minus: zoom += 10.0f; break; default: break; } effects->addRepaintFull(); } } void CubeEffect::rotateToDesktop( int desktop ) { int tempFrontDesktop = frontDesktop; if( !rotations.empty() ) { // all scheduled rotations will be removed as a speed up rotations.clear(); } if( rotating && !desktopChangedWhileRotating ) { // front desktop will change during the actual rotation - this has to be considered if( rotationDirection == Left ) { tempFrontDesktop++; } else if( rotationDirection == Right ) { tempFrontDesktop--; } if( tempFrontDesktop > effects->numberOfDesktops() ) tempFrontDesktop = 1; else if( tempFrontDesktop == 0 ) tempFrontDesktop = effects->numberOfDesktops(); } // find the fastest rotation path from tempFrontDesktop to desktop int rightRotations = tempFrontDesktop - desktop; if( rightRotations < 0 ) rightRotations += effects->numberOfDesktops(); int leftRotations = desktop - tempFrontDesktop; if( leftRotations < 0 ) leftRotations += effects->numberOfDesktops(); if( leftRotations <= rightRotations ) { for( int i=0; iactiveScreen(); if( !slide ) { keyboard_grab = effects->grabKeyboard( this ); input = effects->createInputWindow( this, 0, 0, displayWidth(), displayHeight(), Qt::OpenHandCursor ); frontDesktop = effects->currentDesktop(); zoom = 0.0; start = true; } effects->setActiveFullScreenEffect( this ); kDebug(1212) << "Cube is activated"; verticalPosition = Normal; verticalRotating = false; manualAngle = 0.0; manualVerticalAngle = 0.0; if( reflection && !slide ) { // clip parts above the reflection area double eqn[4] = {0.0, 1.0, 0.0, 0.0}; glPushMatrix(); QRect rect = effects->clientArea( FullScreenArea, activeScreen, effects->currentDesktop()); if( effects->numScreens() > 1 && bigCube ) rect = effects->clientArea( FullArea, activeScreen, effects->currentDesktop() ); glTranslatef( 0.0, rect.height(), 0.0 ); glClipPlane( GL_CLIP_PLANE0, eqn ); glPopMatrix(); } // create the needed GL lists capList = glGenLists(1); capListCreated = false; effects->addRepaintFull(); } else { schedule_close = true; // we have to add a repaint, to start the deactivating effects->addRepaintFull(); } } void CubeEffect::mouseChanged( const QPoint& pos, const QPoint& oldpos, Qt::MouseButtons buttons, Qt::MouseButtons oldbuttons, Qt::KeyboardModifiers modifiers, Qt::KeyboardModifiers oldmodifiers ) { if( !activated ) return; if( tabBoxMode ) return; if( stop || slide ) return; QRect rect = effects->clientArea( FullScreenArea, activeScreen, effects->currentDesktop()); if( effects->numScreens() > 1 && (slide || bigCube ) ) rect = effects->clientArea( FullArea, activeScreen, effects->currentDesktop() ); if( buttons.testFlag( Qt::LeftButton ) ) { bool repaint = false; // vertical movement only if there is not a rotation if( !verticalRotating ) { // display height corresponds to 180* int deltaY = pos.y() - oldpos.y(); float deltaVerticalDegrees = (float)deltaY/rect.height()*180.0f; manualVerticalAngle -= deltaVerticalDegrees; if( deltaVerticalDegrees != 0.0 ) repaint = true; } // horizontal movement only if there is not a rotation if( !rotating ) { // display width corresponds to sum of angles of the polyhedron int deltaX = oldpos.x() - pos.x(); float deltaDegrees = (float)deltaX/rect.width() * 360.0f; manualAngle -= deltaDegrees; if( deltaDegrees != 0.0 ) repaint = true; } if( repaint ) effects->addRepaintFull(); } if( !oldbuttons.testFlag( Qt::LeftButton ) && buttons.testFlag( Qt::LeftButton ) ) { XDefineCursor( display(), input, QCursor( Qt::ClosedHandCursor).handle() ); } if( oldbuttons.testFlag( Qt::LeftButton) && !buttons.testFlag( Qt::LeftButton ) ) { XDefineCursor( display(), input, QCursor( Qt::OpenHandCursor).handle() ); if( closeOnMouseRelease ) setActive( false ); } if( oldbuttons.testFlag( Qt::RightButton) && !buttons.testFlag( Qt::RightButton ) ) { // end effect on right mouse button setActive( false ); } } void CubeEffect::desktopChanged( int old ) { if( effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this ) return; if( activated ) return; if( !animateDesktopChange ) return; slide = true; oldDesktop = old; setActive( true ); frontDesktop = old; rotateToDesktop( effects->currentDesktop() ); setActive( false ); } void CubeEffect::tabBoxAdded( int mode ) { if( activated ) return; if( effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this ) return; if( useForTabBox && mode != TabBoxWindowsMode ) { effects->refTabBox(); tabBoxMode = true; setActive( true ); rotateToDesktop( effects->currentTabBoxDesktop() ); } } void CubeEffect::tabBoxUpdated() { if( activated ) rotateToDesktop( effects->currentTabBoxDesktop() ); } void CubeEffect::tabBoxClosed() { if( activated ) { effects->unrefTabBox(); tabBoxMode = false; setActive( false ); } } } // namespace