diff --git a/effects/cube.cpp b/effects/cube.cpp new file mode 100644 index 0000000000..55d3c62052 --- /dev/null +++ b/effects/cube.cpp @@ -0,0 +1,1245 @@ +/******************************************************************** + 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 + +namespace KWin +{ + +KWIN_EFFECT( cube, CubeEffect ) + +CubeEffect::CubeEffect() + : activated( false ) + , cube_painting( false ) + , keyboard_grab( false ) + , schedule_close( false ) + , frontDesktop( 0 ) + , cubeOpacity( 1.0 ) + , displayDesktopName( true ) + , reflection( true ) + , rotating( false ) + , desktopChangedWhileRotating( false ) + , paintCaps( true ) + , rotationDirection( Left ) + , verticalRotationDirection( Upwards ) + , verticalPosition( Normal ) + , wallpaper( NULL ) + , manualAngle( 0.0 ) + , manualVerticalAngle( 0.0 ) + , currentShape( TimeLine::EaseInOutCurve ) + , start( false ) + , stop( false ) + , reflectionPainting( false ) + , slide( false ) + { + KConfigGroup conf = effects->effectConfig( "Cube" ); + 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 ); + int rotationDuration = conf.readEntry( "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 ); + QString file = conf.readEntry( "Wallpaper", QString("") ); + if( !file.isEmpty() ) + { + QImage img = QImage( file ); + if( !img.isNull() ) + { + wallpaper = new GLTexture( img ); + } + } + + timeLine.setCurveShape( TimeLine::EaseInOutCurve ); + timeLine.setDuration( rotationDuration ); + + verticalTimeLine.setCurveShape( TimeLine::EaseInOutCurve ); + verticalTimeLine.setDuration( rotationDuration ); + + 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())); + + // calculate values for projection matrix + // we use the projection matrix as in compiz (see Compiz: screen.c) + // with following values: + // fovy = 60.0 + // acpect = 1.0 + // zNear = 0.1 + // zFar = 100.0 + fovy = 60.0f; + aspect = 1.0f; + zNear = 0.1f; + zFar = 100.0f; + ymax = zNear * tan( fovy * M_PI / 360.0f ); + ymin = -ymax; + xmin = ymin * aspect; + xmax = ymax * aspect; + } + +CubeEffect::~CubeEffect() + { + effects->unreserveElectricBorder( borderActivate ); + delete wallpaper; + } + +void CubeEffect::prePaintScreen( ScreenPrePaintData& data, int time ) + { + if( activated ) + { + //kDebug(); + 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 ) + { + //kDebug(); + QRect rect = effects->clientArea( FullArea, effects->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(); + } + + glMatrixMode( GL_PROJECTION ); + glPushMatrix(); + glPushAttrib( GL_CURRENT_BIT | GL_ENABLE_BIT ); + glLoadIdentity(); + + glFrustum( xmin, xmax, ymin, ymax, zNear, zFar ); + + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + + glPushMatrix(); + float zTranslate = -zNear*2.0-2.0; + if( start ) + zTranslate = -zNear - (zNear + 2.0)*timeLine.value(); + if( stop ) + zTranslate = -zNear - (zNear + 2.0)*( 1.0 - timeLine.value() ); + if( slide ) + zTranslate = -zNear*2; + glTranslatef( 0.0, 0.0, zTranslate ); + glEnable( GL_BLEND ); + glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + + // reflection + if( reflection && (!slide) ) + { + glPushMatrix(); + float zValue = -1.0f; + float scaleFactor = ( zNear*2+1 - zValue * 0.8f ) * tan( fovy * M_PI / 360.0f )/ymax; + float scaleFactor2 = zFar*0.3* tan( fovy * M_PI / 360.0f )/ymax; + if( start ) + { + zValue = zValue * timeLine.value(); + scaleFactor = ( zNear + (zNear+1.0)*timeLine.value() - zValue * 0.8f) * tan( fovy * M_PI / 360.0f )/ymax; + } + if( stop ) + { + zValue = zValue * ( 1.0 - timeLine.value() ); + scaleFactor = ( zNear + (zNear+1.0)*( 1.0 - timeLine.value() ) - zValue * 0.8f) * tan( fovy * M_PI / 360.0f )/ymax; + } + glScalef( 1.0, -1.0, 1.0 ); + glTranslatef( 0.0, ymax*scaleFactor*2, 0.0 ); + reflectionPainting = true; + paintScene( mask, region, data ); + reflectionPainting = false; + glPopMatrix(); + glPushMatrix(); + if( start || stop ) + { + zValue = -1.0f; + scaleFactor = ( zNear*2+1 - zValue * 0.8f ) * tan( fovy * M_PI / 360.0f )/ymax; + glPushMatrix(); + glTranslatef( 0.0, 0.0, -zTranslate + (-zNear*2.0-2.0) ); + } + glTranslatef( 0.0, ymin*scaleFactor, 0.0 ); + float vertices[] = { + xmin*scaleFactor, 0.0, zNear*2+2, + xmax*scaleFactor, 0.0, zNear*2+2, + xmax*scaleFactor2, 0.0, -zFar*0.3+zNear*2+2, + xmin*scaleFactor2, 0.0, -zFar*0.3+zNear*2+2 }; + // foreground + float alpha = 0.9; + if( start ) + alpha = 0.5 + 0.4 * timeLine.value(); + if( stop ) + alpha = 0.5 + 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(); + if( start || stop ) + { + glPopMatrix(); + } + glPopMatrix(); + } + paintScene( mask, region, data ); + + glPopMatrix(); + glDisable( GL_BLEND ); + glPopAttrib(); + glMatrixMode( GL_PROJECTION ); + glPopMatrix(); + glMatrixMode( GL_MODELVIEW ); + + // 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( stop && timeLine.value() == 1.0 ) + { + effects->paintScreen( mask, region, data ); + } + } + else + { + effects->paintScreen( mask, region, data ); + } + } + +void CubeEffect::paintScene( int mask, QRegion region, ScreenPaintData& data ) + { + QRect rect = effects->clientArea( FullArea, effects->activeScreen(), effects->currentDesktop()); + float zValue = -1.0f; + float scaleFactor = ( zNear*2+1 - zValue * 0.8f ) * tan( fovy * M_PI / 360.0f )/ymax; + if( start ) + { + zValue = zValue * timeLine.value(); + scaleFactor = ( zNear + (zNear+1)*timeLine.value() - zValue * 0.8f) * tan( fovy * M_PI / 360.0f )/ymax; + } + if( stop ) + { + zValue = zValue * ( 1.0 - timeLine.value() ); + scaleFactor = ( zNear + (zNear+1)*( 1.0 - timeLine.value() ) - zValue * 0.8f) * tan( fovy * M_PI / 360.0f )/ymax; + } + if( slide ) + { + scaleFactor = zNear * 2 * tan( fovy * M_PI / 360.0f )/ymax; + } + int rightSteps = effects->numberOfDesktops()/2; + int leftSteps = rightSteps+1; + int rightSideCounter = 0; + int leftSideCounter = 0; + float internalCubeAngle = 360.0f / effects->numberOfDesktops(); + cube_painting = true; + float xTranslate = xmin*scaleFactor; + float xRight = (xmax-xmin)*scaleFactor; + float yTranslate = ymax*scaleFactor; + float yTop = (ymax-ymin)*scaleFactor; + int desktopIndex = 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 = xmax*scaleFactor*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()); + } + glTranslatef( 0.0, 0.0, -point); + glRotatef( angle, 1.0, 0.0, 0.0 ); + glTranslatef( 0.0, 0.0, point); + + // 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]*xTranslate + M[4]*yTranslate + M[8]*0 + M[12]*1; + float Y = M[1]*xTranslate + M[5]*yTranslate + M[9]*0 + M[13]*1; + float Z = M[2]*xTranslate + M[6]*yTranslate + M[10]*0 + M[14]*1; + float W = M[3]*xTranslate + M[7]*yTranslate + M[11]*0 + 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]*xTranslate + M[4]*(-yTranslate) + M[8]*0 + M[12]*1; + Y = M[1]*xTranslate + M[5]*(-yTranslate) + M[9]*0 + M[13]*1; + Z = M[2]*xTranslate + M[6]*(-yTranslate) + M[10]*0 + M[14]*1; + W = M[3]*xTranslate + M[7]*(-yTranslate) + M[11]*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 yFrontBottom = (V[3]/2)*normY+(V[3]-V[1])/2; + + // change matrix to a rear position + glPushMatrix(); + glTranslatef( 0.0, 0.0, -point); + float desktops = (effects->numberOfDesktops()/2.0); + glRotatef( desktops*internalCubeAngle, 1.0, 0.0, 0.0 ); + glTranslatef( xTranslate, -yTranslate, 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, yTranslate*2, 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) && !stop ) ) + { + if( manualAngle > internalCubeAngle * 0.5f ) + { + manualAngle -= internalCubeAngle; + frontDesktop--; + if( frontDesktop == 0 ) + frontDesktop = effects->numberOfDesktops(); + } + if( manualAngle < -internalCubeAngle * 0.5f ) + { + manualAngle += internalCubeAngle; + frontDesktop++; + if( frontDesktop > effects->numberOfDesktops() ) + frontDesktop = 1; + } + float rotationAngle = internalCubeAngle * timeLine.value(); + if( rotationAngle > internalCubeAngle * 0.5f ) + { + rotationAngle -= internalCubeAngle; + if( !desktopChangedWhileRotating ) + { + desktopChangedWhileRotating = true; + if( rotationDirection == Left ) + { + frontDesktop++; + } + else if( rotationDirection == Right ) + { + frontDesktop--; + } + if( frontDesktop > effects->numberOfDesktops() ) + frontDesktop = 1; + else if( frontDesktop == 0 ) + frontDesktop = effects->numberOfDesktops(); + } + } + if( rotationDirection == Left ) + { + rotationAngle *= -1; + } + rotationAngle += manualAngle * (1.0 - timeLine.value()); + glTranslatef( 0.0, 0.0, -point); + glRotatef( rotationAngle, 0.0, 1.0, 0.0 ); + glTranslatef( 0.0, 0.0, point); + } + + if( topCapBefore || topCapAfter ) + { + if( (topCapAfter && !reflectionPainting) || (topCapBefore && reflectionPainting) ) + { + // paint the bottom cap + glTranslatef( 0.0, -yTranslate, 0.0 ); + paintCap( xmin*scaleFactor, xmax*scaleFactor, point ); + glTranslatef( 0.0, yTranslate, 0.0 ); + } + if( (topCapBefore && !reflectionPainting) || (topCapAfter && reflectionPainting) ) + { + // paint the top cap + glTranslatef( 0.0, yTranslate, 0.0 ); + paintCap( xmin*scaleFactor, xmax*scaleFactor, point ); + glTranslatef( 0.0, -yTranslate, 0.0 ); + } + } + + for( int i=0; inumberOfDesktops(); i++ ) + { + if( !topCapAfter && !topCapBefore && i == effects->numberOfDesktops()/2 -1 && !slide ) + { + // paint the bottom cap + glTranslatef( 0.0, -yTranslate, 0.0 ); + paintCap( xmin*scaleFactor, xmax*scaleFactor, point ); + glTranslatef( 0.0, yTranslate, 0.0 ); + // paint the top cap + glTranslatef( 0.0, yTranslate, 0.0 ); + paintCap( xmin*scaleFactor, xmax*scaleFactor, point ); + glTranslatef( 0.0, -yTranslate, 0.0 ); + } + 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++; + } + glPushMatrix(); + glTranslatef( 0.0, 0.0, -point ); + glRotatef( internalCubeAngle * desktopIndex, 0.0, 1.0, 0.0 ); + glTranslatef( xTranslate, yTranslate, point ); + glScalef( xRight/rect.width(), -yTop/rect.height(), 1.0 ); + + // start painting the cube + painting_desktop = (desktopIndex + frontDesktop )%effects->numberOfDesktops(); + if( painting_desktop == 0 ) + { + painting_desktop = effects->numberOfDesktops(); + } + effects->paintScreen( mask, region, data ); + glPopMatrix(); + } + if( topCapBefore || topCapAfter ) + { + if( (topCapAfter && !reflectionPainting) || (topCapBefore && reflectionPainting) ) + { + // paint the top cap + glTranslatef( 0.0, yTranslate, 0.0 ); + paintCap( xmin*scaleFactor, xmax*scaleFactor, point ); + glTranslatef( 0.0, -yTranslate, 0.0 ); + } + if( (topCapBefore && !reflectionPainting) || (topCapAfter && reflectionPainting) ) + { + // paint the bottom cap + glTranslatef( 0.0, -yTranslate, 0.0 ); + paintCap( xmin*scaleFactor, xmax*scaleFactor, point ); + glTranslatef( 0.0, yTranslate, 0.0 ); + } + } + cube_painting = false; + painting_desktop = effects->currentDesktop(); + } + +void CubeEffect::paintCap( float left, float right, float z ) + { + 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(); + glTranslatef( 0.0, 0.0, -z ); + for( int i=0; inumberOfDesktops(); i++ ) + { + glPushMatrix(); + glRotatef( i*angle, 0.0, 1.0, 0.0 ); + glBegin( GL_POLYGON ); + glVertex3f( left, 0.0, z ); + glVertex3f(right, 0.0, z ); + glVertex3f( 0.0, 0.0, 0.0 ); + glEnd(); + glPopMatrix(); + } + glPopMatrix(); + } + +void CubeEffect::postPaintScreen() + { + effects->postPaintScreen(); + if( activated ) + { + //kDebug(); + if( start ) + { + if( timeLine.value() == 1.0 ) + { + start = false; + timeLine.setProgress(0.0); + } + effects->addRepaintFull(); + } + 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 ); + } + 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; + effects->setActiveFullScreenEffect( 0 ); + } + effects->addRepaintFull(); + } + } + } + +void CubeEffect::prePaintWindow( EffectWindow* w, WindowPrePaintData& data, int time ) + { + if( activated ) + { + //kDebug(); + if( cube_painting ) + { + if( w->isOnDesktop( painting_desktop )) + { + QRect rect = effects->clientArea( FullArea, effects->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, effects->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, effects->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() << 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(); + } + if( stop ) + { + opacity = 1.0 - (1.0 - opacity)*( 1.0 - timeLine.value() ); + if( reflectionPainting ) + opacity = 0.5 + ( cubeOpacity - 0.5 )*( 1.0 - timeLine.value() ); + } + if( !slide || (slide && !w->isDesktop()) ) + data.opacity *= opacity; + // 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, effects->activeScreen(), prev_desktop); + data.xTranslate = -rect.width(); + WindowQuadList new_quads; + foreach( 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, effects->activeScreen(), next_desktop); + data.xTranslate = rect.width(); + WindowQuadList new_quads; + foreach( WindowQuad quad, data.quads ) + { + if( w->x() + quad.right() <= rect.x() ) + { + new_quads.append( quad ); + } + } + data.quads = new_quads; + } + QRect rect = effects->clientArea( FullArea, effects->activeScreen(), painting_desktop ); + if( w->isOnDesktop(painting_desktop) && w->x() < rect.x() ) + { + WindowQuadList new_quads; + foreach( 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( 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( 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( 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 ); + } + +void CubeEffect::postPaintWindow( EffectWindow* w ) + { + if( activated && !cube_painting ) + { + // a window was updated which has not been handled by cube_painting. + // trigger full repaint, so that this window will be updated + //effects->addRepaintFull(); + } + effects->postPaintWindow( w ); + } + +bool CubeEffect::borderActivated( ElectricBorder border ) + { + if( effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this ) + return false; + if( border == borderActivate && !activated ) + { + kDebug() << "border activated"; + toggle(); + return true; + } + return false; + } + +void CubeEffect::toggle() + { + if( effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this ) + return; + if( !activated ) + { + setActive( true ); + } + else + { + setActive( false ); + } + } + +void CubeEffect::grabbedKeyboardEvent( QKeyEvent* e ) + { + if( start || 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() << "left"; + if( !rotating ) + { + rotating = true; + rotationDirection = Left; + } + else + { + rotations.enqueue( Left ); + } + break; + case Qt::Key_Right: + // rotate to next desktop + kDebug() << "right"; + if( !rotating ) + { + rotating = true; + rotationDirection = Right; + } + else + { + rotations.enqueue( Right ); + } + break; + case Qt::Key_Up: + kDebug() << "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() << "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; + 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; igrabKeyboard( this ); + input = effects->createInputWindow( this, 0, 0, displayWidth(), displayHeight(), + Qt::OpenHandCursor ); + frontDesktop = effects->currentDesktop(); + start = true; + } + effects->setActiveFullScreenEffect( this ); + kDebug() << "Cube is activated"; + verticalPosition = Normal; + verticalRotating = false; + manualAngle = 0.0; + manualVerticalAngle = 0.0; + 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( stop || slide ) + return; + QRect rect = effects->clientArea( FullArea, effects->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 = oldpos.y() - pos.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( 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; + slide = true; + setActive( true ); + frontDesktop = old; + rotateToDesktop( effects->currentDesktop() ); + setActive( false ); + } + +} // namespace diff --git a/effects/cube.desktop b/effects/cube.desktop new file mode 100644 index 0000000000..56154f9b2c --- /dev/null +++ b/effects/cube.desktop @@ -0,0 +1,17 @@ +[Desktop Entry] +Name=Desktop Cube +Icon=preferences-system-windows-effect-cube +Comment=Map virtual desktops on a cube + +Type=Service +X-KDE-ServiceTypes=KWin/Effect +X-KDE-PluginInfo-Author=Martin Gräßlin +X-KDE-PluginInfo-Email=ubuntu@martin-graesslin.com +X-KDE-PluginInfo-Name=kwin4_effect_cube +X-KDE-PluginInfo-Version=0.1.0 +X-KDE-PluginInfo-Category=Window Management +X-KDE-PluginInfo-Depends= +X-KDE-PluginInfo-License=GPL +X-KDE-PluginInfo-EnabledByDefault=false +X-KDE-Library=kwin4_effect_builtins +X-KDE-Ordering=50 diff --git a/effects/cube.h b/effects/cube.h new file mode 100644 index 0000000000..7ad8d99cb9 --- /dev/null +++ b/effects/cube.h @@ -0,0 +1,116 @@ +/******************************************************************** + 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 . +*********************************************************************/ + +#ifndef KWIN_CUBE_H +#define KWIN_CUBE_H + +#include +#include +#include +#include + +namespace KWin +{ + +class CubeEffect + : public QObject, public Effect + { + Q_OBJECT + public: + CubeEffect(); + ~CubeEffect(); + virtual void prePaintScreen( ScreenPrePaintData& data, int time ); + virtual void paintScreen( int mask, QRegion region, ScreenPaintData& data ); + virtual void postPaintScreen(); + virtual void prePaintWindow( EffectWindow* w, WindowPrePaintData& data, int time ); + virtual void paintWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data ); + virtual void postPaintWindow( EffectWindow* w); + virtual bool borderActivated( ElectricBorder border ); + virtual void grabbedKeyboardEvent( QKeyEvent* e ); + virtual void mouseChanged( const QPoint& pos, const QPoint& oldpos, Qt::MouseButtons buttons, + Qt::MouseButtons oldbuttons, Qt::KeyboardModifiers modifiers, Qt::KeyboardModifiers oldmodifiers ); + virtual void desktopChanged( int old ); + protected slots: + void toggle(); + protected: + enum RotationDirection + { + Left, + Right, + Upwards, + Downwards + }; + enum VerticalRotationPosition + { + Up, + Normal, + Down + }; + void paintScene( int mask, QRegion region, ScreenPaintData& data ); + void paintCap( float left, float right, float z ); + void rotateToDesktop( int desktop ); + void setActive( bool active ); + bool activated; + bool cube_painting; + bool keyboard_grab; + bool schedule_close; + ElectricBorder borderActivate; + int painting_desktop; + Window input; + int frontDesktop; + float cubeOpacity; + bool displayDesktopName; + bool reflection; + bool rotating; + bool verticalRotating; + bool desktopChangedWhileRotating; + bool paintCaps; + TimeLine timeLine; + TimeLine verticalTimeLine; + RotationDirection rotationDirection; + RotationDirection verticalRotationDirection; + VerticalRotationPosition verticalPosition; + QQueue rotations; + QQueue verticalRotations; + QColor backgroundColor; + QColor capColor; + GLTexture* wallpaper; + float manualAngle; + float manualVerticalAngle; + TimeLine::CurveShape currentShape; + bool start; + bool stop; + bool reflectionPainting; + bool slide; + + // variables for defining the projection matrix + float fovy; + float aspect; + float ymax; + float ymin; + float xmax; + float xmin; + float zNear; + float zFar; + }; + +} // namespace + +#endif diff --git a/effects/cube_config.cpp b/effects/cube_config.cpp new file mode 100644 index 0000000000..3dab4390ec --- /dev/null +++ b/effects/cube_config.cpp @@ -0,0 +1,197 @@ +/******************************************************************** + 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_config.h" +#include + +#include +#include +#include +#include +#include + +#include +#include +#ifndef KDE_USE_FINAL +KWIN_EFFECT_CONFIG_FACTORY +#endif + +namespace KWin +{ + +CubeEffectConfigForm::CubeEffectConfigForm(QWidget* parent) : QWidget(parent) +{ + setupUi(this); +} + +CubeEffectConfig::CubeEffectConfig(QWidget* parent, const QVariantList& args) : + KCModule(EffectFactory::componentData(), parent, args) + { + m_ui = new CubeEffectConfigForm(this); + + QGridLayout* layout = new QGridLayout(this); + + layout->addWidget(m_ui, 0, 0); + + m_ui->screenEdgeCombo->addItem(i18n("Top")); + m_ui->screenEdgeCombo->addItem(i18n("Top-right")); + m_ui->screenEdgeCombo->addItem(i18n("Right")); + m_ui->screenEdgeCombo->addItem(i18n("Bottom-right")); + m_ui->screenEdgeCombo->addItem(i18n("Bottom")); + m_ui->screenEdgeCombo->addItem(i18n("Bottom-left")); + m_ui->screenEdgeCombo->addItem(i18n("Left")); + m_ui->screenEdgeCombo->addItem(i18n("Top-left")); + m_ui->screenEdgeCombo->addItem(i18n("None")); + + m_actionCollection = new KActionCollection( this, componentData() ); + m_actionCollection->setConfigGroup( "Cube" ); + m_actionCollection->setConfigGlobal(true); + + KAction* a = (KAction*) m_actionCollection->addAction( "Cube" ); + a->setText( i18n("Desktop Cube" )); + a->setProperty("isConfigurationAction", true); + a->setGlobalShortcut( KShortcut( Qt::CTRL + Qt::Key_F11 )); + + m_ui->editor->addCollection(m_actionCollection); + + connect(m_ui->screenEdgeCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(changed())); + connect(m_ui->rotationDurationSpin, SIGNAL(valueChanged(int)), this, SLOT(changed())); + connect(m_ui->cubeOpacitySlider, SIGNAL(valueChanged(int)), this, SLOT(changed())); + connect(m_ui->cubeOpacitySpin, SIGNAL(valueChanged(int)), this, SLOT(changed())); + connect(m_ui->displayDesktopNameBox, SIGNAL(stateChanged(int)), this, SLOT(changed())); + connect(m_ui->reflectionBox, SIGNAL(stateChanged(int)), this, SLOT(changed())); + connect(m_ui->backgroundColorButton, SIGNAL(changed(QColor)), this, SLOT(changed())); + connect(m_ui->cubeCapsBox, SIGNAL(stateChanged(int)), this, SLOT(changed())); + connect(m_ui->cubeCapsBox, SIGNAL(stateChanged(int)), this, SLOT(capsSelectionChanged())); + connect(m_ui->capColorButton, SIGNAL(changed(QColor)), this, SLOT(changed())); + + load(); + } + +void CubeEffectConfig::load() + { + KCModule::load(); + + KConfigGroup conf = EffectsHandler::effectConfig( "Cube" ); + + int duration = conf.readEntry( "RotationDuration", 500 ); + float opacity = conf.readEntry( "Opacity", 80 ); + bool desktopName = conf.readEntry( "DisplayDesktopName", true ); + bool reflection = conf.readEntry( "Reflection", true ); + int activateBorder = conf.readEntry( "BorderActivate", (int)ElectricNone ); + QColor background = conf.readEntry( "BackgroundColor", QColor( Qt::black ) ); + QColor capColor = conf.readEntry( "CapColor", KColorScheme( QPalette::Active, KColorScheme::Window ).background().color() ); + bool caps = conf.readEntry( "Caps", true ); + if( activateBorder == (int)ElectricNone ) + activateBorder--; + m_ui->screenEdgeCombo->setCurrentIndex( activateBorder ); + + m_ui->rotationDurationSpin->setValue( duration ); + m_ui->cubeOpacitySlider->setValue( opacity ); + m_ui->cubeOpacitySpin->setValue( opacity ); + if( desktopName ) + { + m_ui->displayDesktopNameBox->setCheckState( Qt::Checked ); + } + else + { + m_ui->displayDesktopNameBox->setCheckState( Qt::Unchecked ); + } + if( reflection ) + { + m_ui->reflectionBox->setCheckState( Qt::Checked ); + } + else + { + m_ui->reflectionBox->setCheckState( Qt::Unchecked ); + } + if( caps ) + { + m_ui->cubeCapsBox->setCheckState( Qt::Checked ); + } + else + { + m_ui->cubeCapsBox->setCheckState( Qt::Unchecked ); + } + m_ui->backgroundColorButton->setColor( background ); + m_ui->capColorButton->setColor( capColor ); + capsSelectionChanged(); + + emit changed(false); + } + +void CubeEffectConfig::save() + { + KConfigGroup conf = EffectsHandler::effectConfig( "Cube" ); + + conf.writeEntry( "RotationDuration", m_ui->rotationDurationSpin->value() ); + conf.writeEntry( "DisplayDesktopName", m_ui->displayDesktopNameBox->checkState() == Qt::Checked ? true : false ); + conf.writeEntry( "Reflection", m_ui->reflectionBox->checkState() == Qt::Checked ? true : false ); + conf.writeEntry( "Opacity", m_ui->cubeOpacitySpin->value() ); + conf.writeEntry( "BackgroundColor", m_ui->backgroundColorButton->color() ); + conf.writeEntry( "Caps", m_ui->cubeCapsBox->checkState() == Qt::Checked ? true : false ); + conf.writeEntry( "CapColor", m_ui->capColorButton->color() ); + + int activateBorder = m_ui->screenEdgeCombo->currentIndex(); + if( activateBorder == (int)ELECTRIC_COUNT ) + activateBorder = (int)ElectricNone; + conf.writeEntry( "BorderActivate", activateBorder ); + + m_ui->editor->save(); + + conf.sync(); + + emit changed(false); + EffectsHandler::sendReloadMessage( "cube" ); + } + +void CubeEffectConfig::defaults() + { + m_ui->rotationDurationSpin->setValue( 500 ); + m_ui->displayDesktopNameBox->setCheckState( Qt::Checked ); + m_ui->reflectionBox->setCheckState( Qt::Checked ); + m_ui->cubeOpacitySpin->setValue( 80 ); + m_ui->cubeOpacitySlider->setValue( 80 ); + m_ui->screenEdgeCombo->setCurrentIndex( (int)ElectricNone -1 ); + m_ui->backgroundColorButton->setColor( QColor( Qt::black ) ); + m_ui->cubeCapsBox->setCheckState( Qt::Checked ); + m_ui->capColorButton->setColor( KColorScheme( QPalette::Active, KColorScheme::Window ).background().color() ); + m_ui->editor->allDefault(); + emit changed(true); + } + +void CubeEffectConfig::capsSelectionChanged() + { + if( m_ui->cubeCapsBox->checkState() == Qt::Checked ) + { + // activate cap color + m_ui->capColorButton->setEnabled( true ); + m_ui->capColorLabel->setEnabled( true ); + } + else + { + // deactivate cap color + m_ui->capColorButton->setEnabled( false ); + m_ui->capColorLabel->setEnabled( false ); + } + } + +} // namespace + +#include "cube_config.moc" diff --git a/effects/cube_config.desktop b/effects/cube_config.desktop new file mode 100644 index 0000000000..041c1ecaf6 --- /dev/null +++ b/effects/cube_config.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Type=Service +X-KDE-ServiceTypes=KCModule + +X-KDE-Library=kcm_kwin4_effect_builtins +X-KDE-ParentComponents=kwin4_effect_cube +X-KDE-PluginKeyword=cube + +Name=Desktop Cube diff --git a/effects/cube_config.h b/effects/cube_config.h new file mode 100644 index 0000000000..c61400a811 --- /dev/null +++ b/effects/cube_config.h @@ -0,0 +1,60 @@ +/******************************************************************** + 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 . +*********************************************************************/ + +#ifndef KWIN_CUBE_CONFIG_H +#define KWIN_CUBE_CONFIG_H + +#include + +#include "ui_cube_config.h" + + +namespace KWin +{ + +class CubeEffectConfigForm : public QWidget, public Ui::CubeEffectConfigForm +{ + Q_OBJECT + public: + explicit CubeEffectConfigForm(QWidget* parent); +}; + +class CubeEffectConfig : public KCModule + { + Q_OBJECT + public: + explicit CubeEffectConfig(QWidget* parent = 0, const QVariantList& args = QVariantList()); + + public slots: + virtual void save(); + virtual void load(); + virtual void defaults(); + + private slots: + void capsSelectionChanged(); + + private: + CubeEffectConfigForm* m_ui; + KActionCollection* m_actionCollection; + }; + +} // namespace + +#endif diff --git a/effects/cube_config.ui b/effects/cube_config.ui new file mode 100644 index 0000000000..a99e7e94ca --- /dev/null +++ b/effects/cube_config.ui @@ -0,0 +1,249 @@ + + KWin::CubeEffectConfigForm + + + + 0 + 0 + 400 + 396 + + + + + + + Appearance + + + + + + Rotation &duration: + + + rotationDurationSpin + + + + + + + msec + + + 5000 + + + 500 + + + + + + + Cube &opacity: + + + cubeOpacitySpin + + + + + + + + + + Screen &edge: + + + screenEdgeCombo + + + + + + + + + 100 + + + 1 + + + 100 + + + Qt::Horizontal + + + QSlider::TicksBelow + + + 10 + + + + + + + + + Fully transparent + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Fully opaque + + + + + + + + + + + % + + + 100 + + + 100 + + + + + + + Display desktop &name + + + + + + + &Reflection + + + + + + + &Background Color: + + + backgroundColorButton + + + + + + + + + + Cube &Caps + + + + + + + C&ap Color: + + + capColorButton + + + + + + + + + + + + + + + + + KColorButton + QPushButton +
kcolorbutton.h
+
+ + KWin::GlobalShortcutsEditor + QWidget +
kwineffects.h
+ 1 +
+
+ + screenEdgeCombo + rotationDurationSpin + cubeOpacitySpin + cubeOpacitySlider + displayDesktopNameBox + reflectionBox + backgroundColorButton + cubeCapsBox + capColorButton + + + + + cubeOpacitySpin + valueChanged(int) + cubeOpacitySlider + setValue(int) + + + 386 + 175 + + + 235 + 162 + + + + + cubeOpacitySlider + valueChanged(int) + cubeOpacitySpin + setValue(int) + + + 133 + 162 + + + 386 + 175 + + + + +
diff --git a/effects/cylinder.cpp b/effects/cylinder.cpp new file mode 100644 index 0000000000..c1998534c2 --- /dev/null +++ b/effects/cylinder.cpp @@ -0,0 +1,123 @@ +/******************************************************************** + 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 "cylinder.h" + +#include +#include + +#include + +#include + +namespace KWin +{ + +KWIN_EFFECT( cylinder, CylinderEffect ) + +CylinderEffect::CylinderEffect() + : CubeEffect() + , mInited( false ) + , mValid( true ) + , mShader( 0 ) + { + } + +CylinderEffect::~CylinderEffect() + { + delete mShader; + } + +bool CylinderEffect::loadData() + { + mInited = true; + QString fragmentshader = KGlobal::dirs()->findResource("data", "kwin/cylinder.frag"); + QString vertexshader = KGlobal::dirs()->findResource("data", "kwin/cylinder.vert"); + if(fragmentshader.isEmpty() || vertexshader.isEmpty()) + { + kError() << "Couldn't locate shader files" << endl; + return false; + } + + mShader = new GLShader(vertexshader, fragmentshader); + if(!mShader->isValid()) + { + kError() << "The shader failed to load!" << endl; + return false; + } + else + { + mShader->bind(); + mShader->setUniform( "winTexture", 0 ); + mShader->setUniform( "opacity", cubeOpacity ); + QRect rect = effects->clientArea( FullArea, effects->activeScreen(), effects->currentDesktop()); + mShader->setUniform( "width", (float)rect.width() ); + mShader->setUniform( "origWidth", (float) (2.0 * xmax * ( zNear*2+1 + 1.0f * 0.8f ) * tan( fovy * M_PI / 360.0f )/ymax) ); + mShader->unbind(); + } + return true; + } +void CylinderEffect::prePaintWindow( EffectWindow* w, WindowPrePaintData& data, int time ) + { + if( activated ) + { + if( cube_painting ) + { + if( w->isOnDesktop( painting_desktop )) + { + data.quads = data.quads.makeGrid( 40 ); + w->enablePainting( EffectWindow::PAINT_DISABLED_BY_DESKTOP ); + } + else + { + w->disablePainting( EffectWindow::PAINT_DISABLED_BY_DESKTOP ); + } + } + } + effects->prePaintWindow( w, data, time ); + } + +void CylinderEffect::paintWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data ) + { + if( activated && cube_painting ) + { + if( mValid && !mInited ) + mValid = loadData(); + bool useShader = mValid; + if( useShader ) + { + mShader->bind(); + mShader->setUniform( "windowWidth", (float)w->width() ); + mShader->setUniform( "windowHeight", (float)w->height() ); + mShader->setUniform( "xCoord", (float)w->x() ); + mShader->setUniform( "cubeAngle", (effects->numberOfDesktops() - 2 )/(float)effects->numberOfDesktops() * 180.0f ); + data.shader = mShader; + } + CubeEffect::paintWindow( w, mask, region, data ); + if( useShader ) + { + mShader->unbind(); + } + } + else + effects->paintWindow( w, mask, region, data ); + } + +} // namespace diff --git a/effects/cylinder.desktop b/effects/cylinder.desktop new file mode 100644 index 0000000000..168def242a --- /dev/null +++ b/effects/cylinder.desktop @@ -0,0 +1,17 @@ +[Desktop Entry] +Name=Desktop Cylinder +Icon=preferences-system-windows-effect-cylinder +Comment=Map virtual desktops on a cylinder + +Type=Service +X-KDE-ServiceTypes=KWin/Effect +X-KDE-PluginInfo-Author=Martin Gräßlin +X-KDE-PluginInfo-Email=ubuntu@martin-graesslin.com +X-KDE-PluginInfo-Name=kwin4_effect_cylinder +X-KDE-PluginInfo-Version=0.1.0 +X-KDE-PluginInfo-Category=Window Management +X-KDE-PluginInfo-Depends= +X-KDE-PluginInfo-License=GPL +X-KDE-PluginInfo-EnabledByDefault=false +X-KDE-Library=kwin4_effect_builtins +X-KDE-Ordering=50 diff --git a/effects/cylinder.h b/effects/cylinder.h new file mode 100644 index 0000000000..db7755ff7f --- /dev/null +++ b/effects/cylinder.h @@ -0,0 +1,47 @@ +/******************************************************************** + 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 . +*********************************************************************/ + +#ifndef KWIN_CYLINDER_H +#define KWIN_CYLINDER_H + +#include +#include + +namespace KWin +{ + +class CylinderEffect + : public CubeEffect + { + public: + CylinderEffect(); + ~CylinderEffect(); + virtual void prePaintWindow( EffectWindow* w, WindowPrePaintData& data, int time ); + virtual void paintWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data ); + private: + bool loadData(); + bool mInited; + bool mValid; + GLShader* mShader; + }; + +} // namespace + +#endif diff --git a/effects/data/cylinder.frag b/effects/data/cylinder.frag new file mode 100644 index 0000000000..aa0d119cd7 --- /dev/null +++ b/effects/data/cylinder.frag @@ -0,0 +1,15 @@ +uniform sampler2D winTexture; +uniform float windowWidth; +uniform float windowHeight; +uniform float opacity; + +vec2 pix2tex(vec2 pix) +{ + return vec2(pix.x / windowWidth, pix.y / windowHeight); +} + +void main() +{ + gl_FragColor.rgba = texture2D(winTexture, pix2tex(gl_TexCoord[0].xy)).rgba; + gl_FragColor.a *= opacity; +} diff --git a/effects/data/cylinder.vert b/effects/data/cylinder.vert new file mode 100644 index 0000000000..228e8c4310 --- /dev/null +++ b/effects/data/cylinder.vert @@ -0,0 +1,59 @@ +/******************************************************************** + 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 . +*********************************************************************/ +uniform float width; +uniform float cubeAngle; +uniform float xCoord; +uniform float origWidth; + +void main() +{ + gl_TexCoord[0].xy = gl_Vertex.xy; + vec4 vertex = gl_Vertex.xyzw; + float radian = radians(cubeAngle*0.5); + // height of the triangle compound of one side of the cube and the two bisecting lines + float midpoint = origWidth*0.5*tan(radian); + // radius of the circle + float radius = (origWidth*0.5)/cos(radian); + + // the calculation does the following: + // At every x position we move on to the circle und create a new circular segment + // The distance between the new chord and the desktop (z=0) is the wanted z value + // The length of the new chord is 2*distance(x,middle of desktop(width*0.5)) + // Now we calculate the angle between chord and radius as acos(distance/radius) + // With this angle we can can calculate the height of the new triangle (radius-distance*2-radius) + // The height is the opposite leg of the triangle compound of distance-*height*-radius + // New height minus old height (midpoint) is the looked for z-value + + // distance from midpoint of desktop to x coord + float distance = width*0.5 - (vertex.x+xCoord); + if( (vertex.x+xCoord) > width*0.5 ) + { + distance = (vertex.x+xCoord) - width*0.5; + } + // distance in correct format + distance = distance/(width*0.5)*origWidth*0.5; + float angle = acos( distance/radius ); + float h = radius; + // if distance == 0 -> angle=90 -> tan(90) singularity + if( distance != 0.0 ) + h = tan( angle ) * distance; + vertex.z = h - midpoint; + gl_Position = gl_ModelViewProjectionMatrix * vertex; +}