/******************************************************************** 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 "cube_inside.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 ) , mousePolling( false ) , cube_painting( false ) , keyboard_grab( false ) , schedule_close( false ) , painting_desktop( 1 ) , frontDesktop( 0 ) , cubeOpacity( 1.0 ) , opacityDesktopOnly( true ) , displayDesktopName( false ) , desktopNameFrame( EffectFrame::Styled ) , 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 ) , activeScreen( 0 ) , bottomCap( false ) , closeOnMouseRelease( false ) , zoom( 0.0 ) , zPosition( 0.0 ) , useForTabBox( false ) , tabBoxMode( false ) , shortcutsRegistered( false ) , mode( Cube ) , useShaders( false ) , cylinderShader( 0 ) , sphereShader( 0 ) , zOrderingFactor( 0.0f ) , mAddedHeightCoeff1( 0.0f ) , mAddedHeightCoeff2( 0.0f ) , capListCreated( false ) , recompileList( true ) , glList( 0 ) , m_proxy( this ) { desktopNameFont.setBold( true ); desktopNameFont.setPointSize( 14 ); desktopNameFrame.setFont( desktopNameFont ); reconfigure( ReconfigureAll ); } bool CubeEffect::supported() { return effects->compositingType() == OpenGLCompositing; } void CubeEffect::reconfigure( ReconfigureFlags ) { loadConfig( "Cube" ); } void CubeEffect::loadConfig( QString config ) { KConfigGroup conf = effects->effectConfig( config ); foreach( ElectricBorder border, borderActivate ) { effects->unreserveElectricBorder( border ); } foreach( ElectricBorder border, borderActivateCylinder ) { effects->unreserveElectricBorder( border ); } foreach( ElectricBorder border, borderActivateSphere ) { effects->unreserveElectricBorder( border ); } borderActivate.clear(); borderActivateCylinder.clear(); borderActivateSphere.clear(); QList borderList = QList(); borderList.append( int( ElectricNone ) ); borderList = conf.readEntry( "BorderActivate", borderList ); foreach( int i, borderList ) { borderActivate.append( ElectricBorder( i ) ); effects->reserveElectricBorder( ElectricBorder( i ) ); } borderList.clear(); borderList.append( int( ElectricNone ) ); borderList = conf.readEntry( "BorderActivateCylinder", borderList ); foreach( int i, borderList ) { borderActivateCylinder.append( ElectricBorder( i ) ); effects->reserveElectricBorder( ElectricBorder( i ) ); } borderList.clear(); borderList.append( int( ElectricNone ) ); borderList = conf.readEntry( "BorderActivateSphere", borderList ); foreach( int i, borderList ) { borderActivateSphere.append( ElectricBorder( i ) ); effects->reserveElectricBorder( ElectricBorder( i ) ); } cubeOpacity = (float)conf.readEntry( "Opacity", 80 )/100.0f; opacityDesktopOnly = conf.readEntry( "OpacityDesktopOnly", false ); displayDesktopName = conf.readEntry( "DisplayDesktopName", true ); reflection = conf.readEntry( "Reflection", true ); rotationDuration = animationTime( conf, "RotationDuration", 500 ); backgroundColor = conf.readEntry( "BackgroundColor", QColor( Qt::black ) ); capColor = conf.readEntry( "CapColor", KColorScheme( QPalette::Active, KColorScheme::Window ).background().color() ); paintCaps = conf.readEntry( "Caps", true ); closeOnMouseRelease = conf.readEntry( "CloseOnMouseRelease", false ); float defaultZPosition = 100.0f; if( config == "Sphere" ) defaultZPosition = 450.0f; zPosition = conf.readEntry( "ZPosition", defaultZPosition ); useForTabBox = conf.readEntry( "TabBox", false ); invertKeys = conf.readEntry( "InvertKeys", false ); invertMouse = conf.readEntry( "InvertMouse", false ); capDeformationFactor = conf.readEntry( "CapDeformation", 0 )/100.0f; useZOrdering = conf.readEntry( "ZOrdering", false ); QString file = conf.readEntry( "Wallpaper", QString("") ); if( wallpaper ) wallpaper->discard(); 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 ) { QString capPath = conf.readEntry( "CapPath", KGlobal::dirs()->findResource( "appdata", "cubecap.png" ) ); QImage img = QImage( capPath ); if( !img.isNull() ) { capTexture = new GLTexture( img ); capTexture->setFilter( GL_LINEAR ); capTexture->setWrapMode( GL_CLAMP_TO_BORDER ); } } timeLine.setCurveShape( TimeLine::EaseInOutCurve ); timeLine.setDuration( rotationDuration ); verticalTimeLine.setCurveShape( TimeLine::EaseInOutCurve ); verticalTimeLine.setDuration( rotationDuration ); // do not connect the shortcut if we use cylinder or sphere if( !shortcutsRegistered ) { KActionCollection* actionCollection = new KActionCollection( this ); KAction* cubeAction = static_cast< KAction* >( actionCollection->addAction( "Cube" )); cubeAction->setText( i18n("Desktop Cube" )); cubeAction->setGlobalShortcut( KShortcut( Qt::CTRL + Qt::Key_F11 )); cubeShortcut = cubeAction->globalShortcut(); KAction* cylinderAction = static_cast< KAction* >( actionCollection->addAction( "Cylinder" )); cylinderAction->setText( i18n("Desktop Cylinder" )); cylinderAction->setGlobalShortcut( KShortcut(), KAction::ActiveShortcut); cylinderShortcut = cylinderAction->globalShortcut(); KAction* sphereAction = static_cast< KAction* >( actionCollection->addAction( "Sphere" )); sphereAction->setText( i18n("Desktop Sphere" )); sphereAction->setGlobalShortcut( KShortcut(), KAction::ActiveShortcut); sphereShortcut = sphereAction->globalShortcut(); connect( cubeAction, SIGNAL( triggered( bool )), this, SLOT( toggleCube())); connect( cylinderAction, SIGNAL( triggered( bool )), this, SLOT( toggleCylinder())); connect( sphereAction, SIGNAL( triggered( bool )), this, SLOT( toggleSphere())); connect( cubeAction, SIGNAL( globalShortcutChanged( QKeySequence )), this, SLOT( cubeShortcutChanged(QKeySequence))); connect( cylinderAction, SIGNAL( globalShortcutChanged( QKeySequence )), this, SLOT( cylinderShortcutChanged(QKeySequence))); connect( sphereAction, SIGNAL( globalShortcutChanged( QKeySequence )), this, SLOT( sphereShortcutChanged(QKeySequence))); shortcutsRegistered = true; } } CubeEffect::~CubeEffect() { foreach( ElectricBorder border, borderActivate ) { effects->unreserveElectricBorder( border ); } foreach( ElectricBorder border, borderActivateCylinder ) { effects->unreserveElectricBorder( border ); } foreach( ElectricBorder border, borderActivateSphere ) { effects->unreserveElectricBorder( border ); } delete wallpaper; delete capTexture; delete cylinderShader; delete sphereShader; } bool CubeEffect::loadShader() { if( !(GLShader::fragmentShaderSupported() && (effects->compositingType() == OpenGLCompositing))) return false; QString fragmentshader = KGlobal::dirs()->findResource( "data", "kwin/cylinder.frag" ); QString cylinderVertexshader = KGlobal::dirs()->findResource( "data", "kwin/cylinder.vert" ); QString sphereVertexshader = KGlobal::dirs()->findResource( "data", "kwin/sphere.vert" ); if( fragmentshader.isEmpty() || cylinderVertexshader.isEmpty() || sphereVertexshader.isEmpty() ) { kError(1212) << "Couldn't locate shader files" << endl; return false; } cylinderShader = new GLShader(cylinderVertexshader, fragmentshader); if( !cylinderShader->isValid() ) { kError(1212) << "The cylinder shader failed to load!" << endl; return false; } else { cylinderShader->bind(); cylinderShader->setUniform( "winTexture", 0 ); QRect rect = effects->clientArea( FullArea, activeScreen, effects->currentDesktop() ); cylinderShader->setUniform( "width", (float)rect.width() ); cylinderShader->unbind(); } sphereShader = new GLShader( sphereVertexshader, fragmentshader ); if( !sphereShader->isValid() ) { kError(1212) << "The sphere shader failed to load!" << endl; return false; } else { sphereShader->bind(); sphereShader->setUniform( "winTexture", 0 ); QRect rect = effects->clientArea( FullArea, activeScreen, effects->currentDesktop() ); sphereShader->setUniform( "width", (float)rect.width() ); sphereShader->setUniform( "height", (float)rect.height() ); sphereShader->unbind(); } return true; } 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 ); recompileList = true; } if( verticalRotating ) { verticalTimeLine.addTime( time ); recompileList = true; } } effects->prePaintScreen( data, time ); } void CubeEffect::paintScreen( int mask, QRegion region, ScreenPaintData& data ) { if( activated ) { if( recompileList ) { recompileList = false; glPushMatrix(); glNewList( glList, GL_COMPILE ); rotateCube(); glEndList(); glPopMatrix(); } // compile List for cube if( useList ) { glNewList( glList + 1, GL_COMPILE ); glPushMatrix(); paintCube( mask, region, data ); glPopMatrix(); glEndList(); } QRect rect = 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 ) { 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 ); // some veriables needed for painting the caps 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 zTranslate = zPosition + zoom; if( start ) zTranslate *= timeLine.value(); if( stop ) zTranslate *= ( 1.0 - timeLine.value() ); // reflection if( reflection && mode != Sphere ) { // 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 ); // TODO reflection is not correct when mixing manual (mouse) rotating with rotation by cursor keys // there's also a small bug when zooming float addedHeight1 = -sin( asin( float( rect.height() ) / mAddedHeightCoeff1 ) + fabs( manualVerticalAngle ) * M_PI / 180.0f ) * mAddedHeightCoeff1; float addedHeight2 = -sin( asin( float( rect.height() ) / mAddedHeightCoeff2 ) + fabs( manualVerticalAngle ) * M_PI / 180.0f ) * mAddedHeightCoeff2 - addedHeight1; if( manualVerticalAngle > 0.0f && effects->numberOfDesktops() & 1 ) glTranslatef( 0.0, cos( fabs( manualAngle ) * M_PI / 360.0f * float( effects->numberOfDesktops() ) ) * addedHeight2 + addedHeight1 - float( rect.height() ), 0.0 ); else glTranslatef( 0.0, sin( fabs( manualAngle ) * M_PI / 360.0f * float( effects->numberOfDesktops() ) ) * addedHeight2 + addedHeight1 - float( rect.height() ), 0.0 ); glEnable( GL_CLIP_PLANE0 ); reflectionPainting = true; glEnable( GL_CULL_FACE ); // caps if( paintCaps && ( effects->numberOfDesktops() >= 2 ) ) { glPushMatrix(); glCallList( glList ); glTranslatef( rect.width()/2, 0.0, -point-zTranslate ); glRotatef( (1-frontDesktop)*360.0f / effects->numberOfDesktops(), 0.0, 1.0, 0.0 ); glTranslatef( 0.0, rect.height(), 0.0 ); glCullFace( GL_FRONT ); // bottom texture has to be mirrored glMatrixMode( GL_TEXTURE ); glPushMatrix(); glLoadIdentity(); glScalef( 1.0f, -1.0f, 1.0f ); glTranslatef( 0.0f, -1.0f, 0.0f ); glMatrixMode( GL_MODELVIEW ); glCallList( glList + 2 ); glMatrixMode( GL_TEXTURE ); glPopMatrix(); glMatrixMode( GL_MODELVIEW ); glTranslatef( 0.0, -rect.height(), 0.0 ); glCullFace( GL_BACK ); glCallList( glList + 2 ); glPopMatrix(); } // cube glCullFace( GL_FRONT ); if( mode == Cylinder ) { cylinderShader->bind(); cylinderShader->setUniform( "front", 1.0f ); cylinderShader->unbind(); } glPushMatrix(); glCallList( glList ); if( useList ) glCallList( glList + 1 ); else { glPushMatrix(); paintCube( mask, region, data ); glPopMatrix(); } glPopMatrix(); // call the inside cube effects foreach( CubeInsideEffect* inside, m_cubeInsideEffects ) { glPushMatrix(); glCallList( glList ); glTranslatef( rect.width()/2, rect.height()/2, -point-zTranslate ); glRotatef( (1-frontDesktop)*360.0f / effects->numberOfDesktops(), 0.0, 1.0, 0.0 ); inside->paint(); glPopMatrix(); } glCullFace( GL_BACK ); if( mode == Cylinder ) { cylinderShader->bind(); cylinderShader->setUniform( "front", -1.0f ); cylinderShader->unbind(); } glPushMatrix(); glCallList( glList ); if( useList ) glCallList( glList + 1 ); else { glPushMatrix(); paintCube( mask, region, data ); glPopMatrix(); } glPopMatrix(); // cap if( paintCaps && ( effects->numberOfDesktops() >= 2 ) ) { glPushMatrix(); glCallList( glList ); glTranslatef( rect.width()/2, 0.0, -point-zTranslate ); glRotatef( (1-frontDesktop)*360.0f / effects->numberOfDesktops(), 0.0, 1.0, 0.0 ); glTranslatef( 0.0, rect.height(), 0.0 ); glCullFace( GL_BACK ); // bottom texture has to be mirrored glMatrixMode( GL_TEXTURE ); glPushMatrix(); glLoadIdentity(); glScalef( 1.0f, -1.0f, 1.0f ); glTranslatef( 0.0f, -1.0f, 0.0f ); glMatrixMode( GL_MODELVIEW ); glCallList( glList + 2 ); glMatrixMode( GL_TEXTURE ); glPopMatrix(); glMatrixMode( GL_MODELVIEW ); glTranslatef( 0.0, -rect.height(), 0.0 ); glCullFace( GL_FRONT ); glCallList( glList + 2 ); glPopMatrix(); } glDisable( GL_CULL_FACE ); reflectionPainting = false; glDisable( GL_CLIP_PLANE0 ); glPopMatrix(); glPushMatrix(); 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 )); } glEnable( GL_CULL_FACE ); // caps if( paintCaps && ( effects->numberOfDesktops() >= 2 )) { glPushMatrix(); glCallList( glList ); glTranslatef( rect.width()/2, 0.0, -point-zTranslate ); glRotatef( (1-frontDesktop)*360.0f / effects->numberOfDesktops(), 0.0, 1.0, 0.0 ); glTranslatef( 0.0, rect.height(), 0.0 ); glCullFace( GL_BACK ); if( mode == Sphere ) { glPushMatrix(); glScalef( 1.0, -1.0, 1.0 ); } // bottom texture has to be mirrored glMatrixMode( GL_TEXTURE ); glPushMatrix(); glLoadIdentity(); glScalef( 1.0f, -1.0f, 1.0f ); glTranslatef( 0.0f, -1.0f, 0.0f ); glMatrixMode( GL_MODELVIEW ); glCallList( glList + 2 ); glMatrixMode( GL_TEXTURE ); glPopMatrix(); glMatrixMode( GL_MODELVIEW ); if( mode == Sphere ) glPopMatrix(); glTranslatef( 0.0, -rect.height(), 0.0 ); glCullFace( GL_FRONT ); glCallList( glList + 2 ); glPopMatrix(); } // cube glCullFace( GL_BACK ); if( mode == Cylinder ) { cylinderShader->bind(); cylinderShader->setUniform( "front", -1.0f ); cylinderShader->unbind(); } if( mode == Sphere ) { sphereShader->bind(); sphereShader->setUniform( "front", -1.0f ); sphereShader->unbind(); } glPushMatrix(); glCallList( glList ); if( useList ) glCallList( glList + 1 ); else { glPushMatrix(); paintCube( mask, region, data ); glPopMatrix(); } glPopMatrix(); // call the inside cube effects foreach( CubeInsideEffect* inside, m_cubeInsideEffects ) { glPushMatrix(); glCallList( glList ); glTranslatef( rect.width()/2, rect.height()/2, -point-zTranslate ); glRotatef( (1-frontDesktop)*360.0f / effects->numberOfDesktops(), 0.0, 1.0, 0.0 ); inside->paint(); glPopMatrix(); } glCullFace( GL_FRONT ); if( mode == Cylinder ) { cylinderShader->bind(); cylinderShader->setUniform( "front", 1.0f ); cylinderShader->unbind(); } if( mode == Sphere ) { sphereShader->bind(); sphereShader->setUniform( "front", 1.0f ); sphereShader->unbind(); } glPushMatrix(); glCallList( glList ); if( useList ) glCallList( glList + 1 ); else { glPushMatrix(); paintCube( mask, region, data ); glPopMatrix(); } glPopMatrix(); // we painted once without glList, now it's safe to paint using lists useList = true; // cap if( paintCaps && ( effects->numberOfDesktops() >= 2 )) { glPushMatrix(); glCallList( glList ); glTranslatef( rect.width()/2, 0.0, -point-zTranslate ); glRotatef( (1-frontDesktop)*360.0f / effects->numberOfDesktops(), 0.0, 1.0, 0.0 ); glTranslatef( 0.0, rect.height(), 0.0 ); glCullFace( GL_FRONT ); if( mode == Sphere ) { glPushMatrix(); glScalef( 1.0, -1.0, 1.0 ); } // bottom texture has to be mirrored glMatrixMode( GL_TEXTURE ); glPushMatrix(); glLoadIdentity(); glScalef( 1.0f, -1.0f, 1.0f ); glTranslatef( 0.0f, -1.0f, 0.0f ); glMatrixMode( GL_MODELVIEW ); glCallList( glList + 2 ); glMatrixMode( GL_TEXTURE ); glPopMatrix(); glMatrixMode( GL_MODELVIEW ); if( mode == Sphere ) glPopMatrix(); glTranslatef( 0.0, -rect.height(), 0.0 ); glCullFace( GL_BACK ); glCallList( glList + 2 ); glPopMatrix(); } glDisable( GL_CULL_FACE ); glDisable( GL_BLEND ); glPopAttrib(); // desktop name box - inspired from coverswitch if( displayDesktopName ) { double opacity = 1.0; if( start ) opacity = timeLine.value(); if( stop ) opacity = 1.0 - timeLine.value(); QRect screenRect = effects->clientArea( ScreenArea, activeScreen, frontDesktop ); QRect frameRect = QRect( screenRect.width() * 0.33f + screenRect.x(), screenRect.height() * 0.95f + screenRect.y(), screenRect.width() * 0.34f, QFontMetrics( desktopNameFont ).height() ); desktopNameFrame.setGeometry( frameRect ); desktopNameFrame.setText( effects->desktopName( frontDesktop ) ); desktopNameFrame.render( region, opacity ); } } else { effects->paintScreen( mask, region, data ); } } void CubeEffect::rotateCube() { QRect rect = effects->clientArea( FullArea, activeScreen, effects->currentDesktop() ); float internalCubeAngle = 360.0f / effects->numberOfDesktops(); float zTranslate = zPosition + zoom; if( start ) zTranslate *= timeLine.value(); if( stop ) zTranslate *= ( 1.0 - timeLine.value() ); // 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); 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 ); } 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 ); } } void CubeEffect::paintCube( int mask, QRegion region, ScreenPaintData& data ) { QRect rect = effects->clientArea( FullArea, activeScreen, effects->currentDesktop() ); float internalCubeAngle = 360.0f / effects->numberOfDesktops(); cube_painting = true; float zTranslate = zPosition + zoom; if( start ) zTranslate *= timeLine.value(); if( stop ) zTranslate *= ( 1.0 - timeLine.value() ); // 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); for( int i=0; inumberOfDesktops(); i++ ) { // start painting the cube painting_desktop = (i + frontDesktop )%effects->numberOfDesktops(); if( painting_desktop == 0 ) { painting_desktop = effects->numberOfDesktops(); } ScreenPaintData newData = data; RotationData rot = RotationData(); rot.axis = RotationData::YAxis; rot.angle = internalCubeAngle * i; rot.xRotationPoint = rect.width()/2; rot.zRotationPoint = -point; newData.rotation = &rot; newData.zTranslate = -zTranslate; effects->paintScreen( mask, region, newData ); } cube_painting = false; painting_desktop = effects->currentDesktop(); } void CubeEffect::paintCap() { if( ( !paintCaps ) || effects->numberOfDesktops() <= 2 ) return; if( !capListCreated ) { capListCreated = true; glNewList( glList + 2, GL_COMPILE ); glColor4f( capColor.redF(), capColor.greenF(), capColor.blueF(), cubeOpacity ); if( texturedCaps && effects->numberOfDesktops() > 3 && capTexture ) { // modulate the cap texture: cap color should be background for translucent pixels // cube opacity should be used for all pixels // blend with cap color float color[4] = { capColor.redF(), capColor.greenF(), capColor.blueF(), cubeOpacity }; glActiveTexture( GL_TEXTURE0 ); capTexture->bind(); glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL ); glColor4fv( color ); // set Opacity to cube opacity // TODO: change opacity during start/stop animation glActiveTexture( GL_TEXTURE1 ); capTexture->bind(); glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE ); glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS ); glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE ); glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_CONSTANT ); glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color ); glActiveTexture( GL_TEXTURE0 ); glTexParameterfv( GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, color ); } glPushMatrix(); switch( mode ) { case Cube: paintCubeCap(); break; case Cylinder: paintCylinderCap(); break; case Sphere: paintSphereCap(); break; default: // impossible break; } glPopMatrix(); if( texturedCaps && effects->numberOfDesktops() > 3 && capTexture ) { glActiveTexture( GL_TEXTURE1 ); glDisable( capTexture->target() ); glActiveTexture( GL_TEXTURE0 ); glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); glColor4f( 0.0f, 0.0f, 0.0f, 0.0f ); capTexture->unbind(); } glEndList(); } } void CubeEffect::paintCubeCap() { QRect rect = effects->clientArea( FullArea, activeScreen, effects->currentDesktop() ); float cubeAngle = (float)((float)(effects->numberOfDesktops() - 2 )/(float)effects->numberOfDesktops() * 180.0f); float z = rect.width()/2*tan(cubeAngle*0.5f*M_PI/180.0f); float zTexture = rect.width()/2*tan(45.0f*M_PI/180.0f); float angle = 360.0f/effects->numberOfDesktops(); bool texture = texturedCaps && effects->numberOfDesktops() > 3 && capTexture; 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; jclientArea( FullArea, activeScreen, effects->currentDesktop() ); float cubeAngle = (float)((float)(effects->numberOfDesktops() - 2 )/(float)effects->numberOfDesktops() * 180.0f); float radian = (cubeAngle*0.5)*M_PI/180; float radius = (rect.width()*0.5)*tan(radian); float segment = radius/30.0f; bool texture = texturedCaps && effects->numberOfDesktops() > 3 && capTexture; for( int i=1; i<=30; i++ ) { glBegin( GL_TRIANGLE_STRIP ); int steps = 72; for( int j=0; j<=steps; j++ ) { float azimuthAngle = (j*(360.0f/steps))*M_PI/180.0f; float x1 = segment*(i-1) * sin( azimuthAngle ); float x2 = segment*i * sin( azimuthAngle ); float z1 = segment*(i-1) * cos( azimuthAngle ); float z2 = segment*i * cos( azimuthAngle ); if( texture ) glTexCoord2f( (radius+x1)/(radius*2.0f), 1.0f - (z1+radius)/(radius*2.0f) ); glVertex3f( x1, 0.0, z1 ); if( texture ) glTexCoord2f( (radius+x2)/(radius*2.0f), 1.0f - (z2+radius)/(radius*2.0f) ); glVertex3f( x2, 0.0, z2 ); } glEnd(); } } void CubeEffect::paintSphereCap() { QRect rect = effects->clientArea( FullArea, activeScreen, effects->currentDesktop() ); float cubeAngle = (float)((float)(effects->numberOfDesktops() - 2 )/(float)effects->numberOfDesktops() * 180.0f); float zTexture = rect.width()/2*tan(45.0f*M_PI/180.0f); float radius = (rect.width()*0.5)/cos(cubeAngle*0.5*M_PI/180.0); float angle = acos( (rect.height()*0.5)/radius )*180.0/M_PI; angle /= 30; bool texture = texturedCaps && effects->numberOfDesktops() > 3 && capTexture; glPushMatrix(); glTranslatef( 0.0, -rect.height()*0.5, 0.0 ); glBegin( GL_QUADS ); for( int i=0; i<30; i++ ) { float topAngle = angle*i*M_PI/180.0; float bottomAngle = angle*(i+1)*M_PI/180.0; float yTop = rect.height() - radius * cos( topAngle ); yTop -= (yTop-rect.height()*0.5)*capDeformationFactor; float yBottom = rect.height() -radius * cos( bottomAngle ); yBottom -= (yBottom-rect.height()*0.5)*capDeformationFactor; for( int j=0; j<36; j++ ) { float x = radius * sin( topAngle ) * sin( (90.0+j*10.0)*M_PI/180.0 ); float z = radius * sin( topAngle ) * cos( (90.0+j*10.0)*M_PI/180.0 ); if( texture ) glTexCoord2f( x/(rect.width())+0.5, 0.5 - z/zTexture * 0.5 ); glVertex3f( x, yTop, z ); x = radius * sin( bottomAngle ) * sin( (90.0+j*10.0)*M_PI/180.00 ); z = radius * sin( bottomAngle ) * cos( (90.0+j*10.0)*M_PI/180.0 ); if( texture ) glTexCoord2f( x/(rect.width())+0.5, 0.5 - z/zTexture * 0.5 ); glVertex3f( x, yBottom, z ); x = radius * sin( bottomAngle ) * sin( (90.0+(j+1)*10.0)*M_PI/180.0 ); z = radius * sin( bottomAngle ) * cos( (90.0+(j+1)*10.0)*M_PI/180.0 ); if( texture ) glTexCoord2f( x/(rect.width())+0.5, 0.5 - z/zTexture * 0.5 ); glVertex3f( x, yBottom, z ); x = radius * sin( topAngle ) * sin( (90.0+(j+1)*10.0)*M_PI/180.0 ); z = radius * sin( topAngle ) * cos( (90.0+(j+1)*10.0)*M_PI/180.0 ); if( texture ) glTexCoord2f( x/(rect.width())+0.5, 0.5 - z/zTexture * 0.5 ); glVertex3f( x, yTop, z ); } } glEnd(); glPopMatrix(); } 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( glList, 3 ); desktopNameFrame.free(); } 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; stop = true; effects->addRepaintFull(); } } } void CubeEffect::prePaintWindow( EffectWindow* w, WindowPrePaintData& data, int time ) { if( activated ) { if( cube_painting ) { if( mode == Cylinder || mode == Sphere ) { int leftDesktop = frontDesktop -1; int rightDesktop = frontDesktop + 1; if( leftDesktop == 0 ) leftDesktop = effects->numberOfDesktops(); if( rightDesktop > effects->numberOfDesktops() ) rightDesktop = 1; if( painting_desktop == frontDesktop ) data.quads = data.quads.makeGrid( 40 ); else if( painting_desktop == leftDesktop || painting_desktop == rightDesktop ) data.quads = data.quads.makeGrid( 100 ); else data.quads = data.quads.makeGrid( 250 ); } 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() ); } if( useZOrdering && !w->isDesktop() && !w->isDock() ) data.setTransformed(); 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 ) && mode == Cube ) { 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 ) && mode == Cube ) { 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 ) { if( mode == Cylinder ) { cylinderShader->bind(); cylinderShader->setUniform( "xCoord", (float)w->x() ); cylinderShader->setUniform( "cubeAngle", (effects->numberOfDesktops() - 2 )/(float)effects->numberOfDesktops() * 180.0f ); cylinderShader->setUniform( "useTexture", 1.0f ); float factor = 0.0f; if( start ) factor = 1.0f - timeLine.value(); if( stop ) factor = timeLine.value(); cylinderShader->setUniform( "timeLine", factor ); data.shader = cylinderShader; } if( mode == Sphere ) { sphereShader->bind(); sphereShader->setUniform( "xCoord", (float)w->x() ); sphereShader->setUniform( "yCoord", (float)w->y() ); sphereShader->setUniform( "cubeAngle", (effects->numberOfDesktops() - 2 )/(float)effects->numberOfDesktops() * 180.0f ); sphereShader->setUniform( "useTexture", 1.0f ); float factor = 0.0f; if( start ) factor = 1.0f - timeLine.value(); if( stop ) factor = timeLine.value(); sphereShader->setUniform( "timeLine", factor ); data.shader = sphereShader; } if( data.shader ) { int texw = w->width(); int texh = w->height(); if( !GLTexture::NPOTTextureSupported() ) { kWarning( 1212 ) << "NPOT textures not supported, wasting some memory" ; texw = nearestPowerOfTwo(texw); texh = nearestPowerOfTwo(texh); } data.shader->setTextureWidth( texw ); data.shader->setTextureHeight( texh ); } //kDebug(1212) << w->caption(); float opacity = cubeOpacity; 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()); } // z-Ordering if( !w->isDesktop() && !w->isDock() && useZOrdering ) { float zOrdering = (effects->stackingOrder().indexOf( w )+1)*zOrderingFactor; if( start ) zOrdering *= timeLine.value(); if( stop ) zOrdering *= (1.0 - timeLine.value()); data.zTranslate += zOrdering; } // 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; glPushMatrix(); if( w->isOnDesktop( prev_desktop ) && ( mask & PAINT_WINDOW_TRANSFORMED ) ) { QRect rect = effects->clientArea( FullArea, activeScreen, prev_desktop); WindowQuadList new_quads; foreach( const WindowQuad &quad, data.quads ) { if( quad.right() > rect.width() - w->x() ) { new_quads.append( quad ); } } data.quads = new_quads; RotationData rot = RotationData(); rot.axis = RotationData::YAxis; rot.xRotationPoint = rect.width() - w->x(); rot.angle = 360.0f / effects->numberOfDesktops(); data.rotation = &rot; 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); glTranslatef( rect.width()/2, 0.0, -point ); glRotatef( -360.0f / effects->numberOfDesktops(), 0.0, 1.0, 0.0 ); glTranslatef( -rect.width()/2, 0.0, point ); } if( w->isOnDesktop( next_desktop ) && ( mask & PAINT_WINDOW_TRANSFORMED ) ) { QRect rect = effects->clientArea( FullArea, activeScreen, next_desktop); WindowQuadList new_quads; foreach( const WindowQuad &quad, data.quads ) { if( w->x() + quad.right() <= rect.x() ) { new_quads.append( quad ); } } data.quads = new_quads; RotationData rot = RotationData(); rot.axis = RotationData::YAxis; rot.xRotationPoint = -w->x(); rot.angle = -360.0f / effects->numberOfDesktops(); data.rotation = &rot; 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); glTranslatef( rect.width()/2, 0.0, -point ); glRotatef( 360.0f / effects->numberOfDesktops(), 0.0, 1.0, 0.0 ); glTranslatef( -rect.width()/2, 0.0, point ); } 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()); } } // 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; if( opacityDesktopOnly && !w->isDesktop() ) 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 ); if( activated && cube_painting ) { if( w->isDesktop() && effects->numScreens() > 1 && paintCaps ) { QRect rect = effects->clientArea( FullArea, activeScreen, painting_desktop ); QRegion paint = QRegion( rect ); for( int i=0; inumScreens(); i++ ) { if( i == w->screen() ) continue; paint = paint.subtracted( QRegion( effects->clientArea( ScreenArea, i, painting_desktop ))); } paint = paint.subtracted( QRegion( w->geometry())); // in case of free area in multiscreen setup fill it with cap color if( !paint.isEmpty() ) { if( mode == Cylinder ) { cylinderShader->setUniform( "useTexture", -1.0f ); cylinderShader->setUniform( "xCoord", 0.0f ); } if( mode == Sphere ) { sphereShader->setUniform( "useTexture", -1.0f ); sphereShader->setUniform( "xCoord", 0.0f ); sphereShader->setUniform( "yCoord", 0.0f ); } glColor4f( capColor.redF(), capColor.greenF(), capColor.blueF(), cubeOpacity ); glPushAttrib( GL_CURRENT_BIT | GL_ENABLE_BIT ); glEnable( GL_BLEND ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); glBegin( GL_QUADS ); float quadSize = 0.0f; int leftDesktop = frontDesktop -1; int rightDesktop = frontDesktop + 1; if( leftDesktop == 0 ) leftDesktop = effects->numberOfDesktops(); if( rightDesktop > effects->numberOfDesktops() ) rightDesktop = 1; if( painting_desktop == frontDesktop ) quadSize = 100.0f; else if( painting_desktop == leftDesktop || painting_desktop == rightDesktop ) quadSize = 150.0f; else quadSize = 250.0f; foreach( const QRect &paintRect, paint.rects() ) { for( int i=0; i<=(paintRect.height()/quadSize); i++ ) { for( int j=0; j<=(paintRect.width()/quadSize); j++ ) { glVertex2f( paintRect.x()+j*quadSize, paintRect.y()+i*quadSize ); glVertex2f( qMin( paintRect.x()+(j+1)*quadSize, (float)paintRect.x() + paintRect.width() ), paintRect.y()+i*quadSize ); glVertex2f( qMin( paintRect.x()+(j+1)*quadSize, (float)paintRect.x() + paintRect.width() ), qMin( paintRect.y() + (i+1)*quadSize, (float)paintRect.y() + paintRect.height() ) ); glVertex2f( paintRect.x()+j*quadSize, qMin( paintRect.y() + (i+1)*quadSize, (float)paintRect.y() + paintRect.height() ) ); } } } glEnd(); glDisable( GL_BLEND ); glPopAttrib(); } } glPopMatrix(); if( mode == Cylinder ) cylinderShader->unbind(); if( mode == Sphere ) sphereShader->unbind(); } } bool CubeEffect::borderActivated( ElectricBorder border ) { if( !borderActivate.contains( border ) && !borderActivateCylinder.contains( border ) && !borderActivateSphere.contains( border ) ) return false; if( effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this ) return false; if( borderActivate.contains( border ) ) { if( !activated || ( activated && mode == Cube ) ) toggleCube(); else return false; } if( borderActivateCylinder.contains( border ) ) { if( !activated || ( activated && mode == Cylinder ) ) toggleCylinder(); else return false; } if( borderActivateSphere.contains( border ) ) { if( !activated || ( activated && mode == Sphere ) ) toggleSphere(); else return false; } return true; } void CubeEffect::toggleCube() { kDebug(1212) << "toggle cube"; toggle( Cube ); } void CubeEffect::toggleCylinder() { kDebug(1212) << "toggle cylinder"; if( !useShaders ) useShaders = loadShader(); if( useShaders ) toggle( Cylinder ); else kError( 1212 ) << "Sorry shaders are not available - cannot activate Cylinder"; } void CubeEffect::toggleSphere() { kDebug(1212) << "toggle sphere"; if( !useShaders ) useShaders = loadShader(); if( useShaders ) toggle( Sphere ); else kError( 1212 ) << "Sorry shaders are not available - cannot activate Sphere"; } void CubeEffect::toggle( CubeMode newMode ) { if( ( effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this ) || effects->numberOfDesktops() < 2 ) return; if( !activated ) { mode = newMode; setActive( true ); } else { setActive( false ); } } void CubeEffect::grabbedKeyboardEvent( QKeyEvent* e ) { if( stop ) return; // taken from desktopgrid.cpp if( e->type() == QEvent::KeyPress ) { // check for global shortcuts // HACK: keyboard grab disables the global shortcuts so we have to check for global shortcut (bug 156155) if( mode == Cube && cubeShortcut.contains( e->key() + e->modifiers() ) ) { toggleCube(); return; } if( mode == Cylinder && cylinderShortcut.contains( e->key() + e->modifiers() ) ) { toggleCylinder(); return; } if( mode == Sphere && sphereShortcut.contains( e->key() + e->modifiers() ) ) { toggleSphere(); return; } 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; if( invertKeys ) rotationDirection = Right; else rotationDirection = Left; } else { if( rotations.count() < effects->numberOfDesktops() ) { if( invertKeys ) rotations.enqueue( Right ); else rotations.enqueue( Left ); } } break; case Qt::Key_Right: // rotate to next desktop kDebug(1212) << "right"; if( !rotating && !start ) { rotating = true; if( invertKeys ) rotationDirection = Left; else rotationDirection = Right; } else { if( rotations.count() < effects->numberOfDesktops() ) { if( invertKeys ) rotations.enqueue( Left ); else rotations.enqueue( Right ); } } break; case Qt::Key_Up: kDebug(1212) << "up"; if( invertKeys ) { 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; } } else { 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( invertKeys ) { 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; } } else { 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 ); recompileList = true; break; case Qt::Key_Minus: zoom += 10.0f; recompileList = true; 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; isetActive( true ); } if( active ) { if( !mousePolling ) { effects->startMousePolling(); mousePolling = true; } activated = true; activeScreen = effects->activeScreen(); keyboard_grab = effects->grabKeyboard( this ); input = effects->createInputWindow( this, 0, 0, displayWidth(), displayHeight(), Qt::OpenHandCursor ); frontDesktop = effects->currentDesktop(); zoom = 0.0; zOrderingFactor = zPosition / ( effects->stackingOrder().count() - 1 ); start = true; effects->setActiveFullScreenEffect( this ); kDebug(1212) << "Cube is activated"; verticalPosition = Normal; verticalRotating = false; manualAngle = 0.0; manualVerticalAngle = 0.0; if( reflection ) { // clip parts above the reflection area double eqn[4] = {0.0, 1.0, 0.0, 0.0}; glPushMatrix(); QRect rect = effects->clientArea( FullArea, activeScreen, effects->currentDesktop()); glTranslatef( 0.0, rect.height(), 0.0 ); glClipPlane( GL_CLIP_PLANE0, eqn ); glPopMatrix(); float temporaryCoeff = float( rect.width() ) / tan( M_PI / float( effects->numberOfDesktops() ) ); mAddedHeightCoeff1 = sqrt( float( rect.height() ) * float( rect.height() ) + temporaryCoeff * temporaryCoeff ); mAddedHeightCoeff2 = sqrt( float( rect.height() ) * float( rect.height() ) + float( rect.width() ) * float( rect.width() ) + temporaryCoeff * temporaryCoeff ); } // create the needed GL lists glList = glGenLists(3); capListCreated = false; recompileList = true; useList = false; // create the capList if( paintCaps ) paintCap(); effects->addRepaintFull(); } else { if( mousePolling ) { effects->stopMousePolling(); mousePolling = false; } 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, Qt::KeyboardModifiers ) { if( !activated ) return; if( tabBoxMode ) return; if( stop ) return; QRect 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; if( invertMouse ) manualVerticalAngle += deltaVerticalDegrees; else 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; if( deltaX == 0 ) { if( pos.x() == 0 ) deltaDegrees = 5.0f; if( pos.x() == displayWidth() -1 ) deltaDegrees = -5.0f; } if( invertMouse ) manualAngle += deltaDegrees; else manualAngle -= deltaDegrees; if( deltaDegrees != 0.0 ) repaint = true; } if( repaint ) { recompileList = true; 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::windowInputMouseEvent( Window w, QEvent* e ) { assert( w == input ); Q_UNUSED( w ); QMouseEvent *mouse = dynamic_cast< QMouseEvent* >( e ); if( mouse && mouse->type() == QEvent::MouseButtonRelease ) { if( mouse->button() == Qt::XButton1 ) { if( !rotating && !start ) { rotating = true; if( invertMouse ) rotationDirection = Right; else rotationDirection = Left; } else { if( rotations.count() < effects->numberOfDesktops() ) { if( invertMouse ) rotations.enqueue( Right ); else rotations.enqueue( Left ); } } effects->addRepaintFull(); } if( mouse->button() == Qt::XButton2 ) { if( !rotating && !start ) { rotating = true; if( invertMouse ) rotationDirection = Left; else rotationDirection = Right; } else { if( rotations.count() < effects->numberOfDesktops() ) { if( invertMouse ) rotations.enqueue( Left ); else rotations.enqueue( Right ); } } effects->addRepaintFull(); } } } void CubeEffect::tabBoxAdded( int mode ) { if( activated ) return; if( effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this ) return; if( useForTabBox && mode == TabBoxDesktopListMode ) { effects->refTabBox(); tabBoxMode = true; setActive( true ); rotateToDesktop( effects->currentTabBoxDesktop() ); } } void CubeEffect::tabBoxUpdated() { if( activated ) { rotateToDesktop( effects->currentTabBoxDesktop() ); effects->addRepaintFull(); } } void CubeEffect::tabBoxClosed() { if( activated ) { effects->unrefTabBox(); tabBoxMode = false; setActive( false ); } } void CubeEffect::windowAdded( EffectWindow* ) { // when there is a new window we have to paint it once without using GL List // to prevent that the window is black if( activated ) useList = false; } void CubeEffect::cubeShortcutChanged( const QKeySequence& seq ) { cubeShortcut = KShortcut( seq ); } void CubeEffect::cylinderShortcutChanged( const QKeySequence& seq ) { cylinderShortcut = KShortcut( seq ); } void CubeEffect::sphereShortcutChanged( const QKeySequence& seq ) { sphereShortcut = KShortcut( seq ); } void* CubeEffect::proxy() { return &m_proxy; } void CubeEffect::registerCubeInsideEffect(CubeInsideEffect* effect) { m_cubeInsideEffects.append( effect ); } void CubeEffect::unregisterCubeInsideEffect(CubeInsideEffect* effect) { m_cubeInsideEffects.removeAll( effect ); } } // namespace