ad070909a3
CoverSwitch uses new concept of combining a custom fragment shader with the built-in generic vertex shader. This shader is used to render the reflection plane on top of the reflected windows. The fragment shader uses two uniform colors for front and back and interpolates between them based on the texcoords (which are not used for texture lookup). The reflection plane vertices could be buffered.
1117 lines
43 KiB
C++
1117 lines
43 KiB
C++
/********************************************************************
|
|
KWin - the KDE window manager
|
|
This file is part of the KDE project.
|
|
|
|
Copyright (C) 2008 Martin Gräßlin <ubuntu@martin-graesslin.com>
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*********************************************************************/
|
|
#include "coverswitch.h"
|
|
|
|
#include <kwinconfig.h>
|
|
#include <QFont>
|
|
#include <QMatrix4x4>
|
|
#include <klocale.h>
|
|
#include <kapplication.h>
|
|
#include <kcolorscheme.h>
|
|
#include <kconfiggroup.h>
|
|
#include <kglobal.h>
|
|
#include <kstandarddirs.h>
|
|
|
|
#include <kwinglutils.h>
|
|
|
|
#include <math.h>
|
|
|
|
#include <kdebug.h>
|
|
|
|
#include <kwinglutils.h>
|
|
#include "../boxswitch/boxswitch_proxy.h"
|
|
|
|
namespace KWin
|
|
{
|
|
|
|
KWIN_EFFECT( coverswitch, CoverSwitchEffect )
|
|
KWIN_EFFECT_SUPPORTED( coverswitch, CoverSwitchEffect::supported() )
|
|
|
|
CoverSwitchEffect::CoverSwitchEffect()
|
|
: mActivated( 0 )
|
|
, angle( 60.0 )
|
|
, animation( false )
|
|
, start( false )
|
|
, stop( false )
|
|
, stopRequested( false )
|
|
, startRequested( false )
|
|
, zPosition( 900.0 )
|
|
, scaleFactor( 0.0 )
|
|
, direction( Left )
|
|
, selected_window( 0 )
|
|
, captionFrame( effects->effectFrame( EffectFrameStyled ) )
|
|
, primaryTabBox( false )
|
|
, secondaryTabBox( false )
|
|
{
|
|
reconfigure( ReconfigureAll );
|
|
|
|
// Caption frame
|
|
captionFont.setBold( true );
|
|
captionFont.setPointSize( captionFont.pointSize() * 2 );
|
|
captionFrame->setFont( captionFont );
|
|
captionFrame->enableCrossFade( true );
|
|
|
|
const QString fragmentshader = KGlobal::dirs()->findResource("data", "kwin/coverswitch-reflection.glsl");
|
|
m_reflectionShader = ShaderManager::instance()->loadFragmentShader(ShaderManager::GenericShader, fragmentshader);
|
|
}
|
|
|
|
CoverSwitchEffect::~CoverSwitchEffect()
|
|
{
|
|
delete captionFrame;
|
|
delete m_reflectionShader;
|
|
}
|
|
|
|
bool CoverSwitchEffect::supported()
|
|
{
|
|
return effects->compositingType() == OpenGLCompositing;
|
|
}
|
|
|
|
void CoverSwitchEffect::reconfigure( ReconfigureFlags )
|
|
{
|
|
KConfigGroup conf = effects->effectConfig( "CoverSwitch" );
|
|
animationDuration = animationTime( conf, "Duration", 200 );
|
|
animateSwitch = conf.readEntry( "AnimateSwitch", true );
|
|
animateStart = conf.readEntry( "AnimateStart", true );
|
|
animateStop = conf.readEntry( "AnimateStop", true );
|
|
reflection = conf.readEntry( "Reflection", true );
|
|
windowTitle = conf.readEntry( "WindowTitle", true );
|
|
zPosition = conf.readEntry( "ZPosition", 900.0 );
|
|
thumbnails = conf.readEntry( "Thumbnails", true );
|
|
dynamicThumbnails = conf.readEntry( "DynamicThumbnails", true );
|
|
thumbnailWindows = conf.readEntry( "ThumbnailWindows", 8 );
|
|
timeLine.setCurveShape( TimeLine::EaseInOutCurve );
|
|
timeLine.setDuration( animationDuration );
|
|
primaryTabBox = conf.readEntry( "TabBox", false );
|
|
secondaryTabBox = conf.readEntry( "TabBoxAlternative", false );
|
|
QColor tmp = conf.readEntry( "MirrorFrontColor", QColor(0,0,0) );
|
|
mirrorColor[0][0] = tmp.redF();
|
|
mirrorColor[0][1] = tmp.greenF();
|
|
mirrorColor[0][2] = tmp.blueF();
|
|
mirrorColor[0][3] = 1.0;
|
|
tmp = conf.readEntry( "MirrorRearColor", QColor(0,0,0) );
|
|
mirrorColor[1][0] = tmp.redF();
|
|
mirrorColor[1][1] = tmp.greenF();
|
|
mirrorColor[1][2] = tmp.blueF();
|
|
mirrorColor[1][3] = -1.0;
|
|
|
|
}
|
|
|
|
void CoverSwitchEffect::prePaintScreen( ScreenPrePaintData& data, int time )
|
|
{
|
|
if( mActivated || stop || stopRequested )
|
|
{
|
|
data.mask |= Effect::PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS;
|
|
if( animation || start || stop )
|
|
{
|
|
timeLine.addTime( (double)time );
|
|
}
|
|
if( selected_window == NULL )
|
|
abort();
|
|
}
|
|
effects->prePaintScreen(data, time);
|
|
}
|
|
|
|
void CoverSwitchEffect::paintScreen( int mask, QRegion region, ScreenPaintData& data )
|
|
{
|
|
effects->paintScreen( mask, region, data );
|
|
|
|
if( mActivated || stop || stopRequested )
|
|
{
|
|
if( effects->numScreens() > 1 )
|
|
{
|
|
#ifndef KWIN_HAVE_OPENGLES
|
|
// unfortunatelly we have to change the projection matrix in dual screen mode
|
|
QRect fullRect = effects->clientArea( FullArea, activeScreen, effects->currentDesktop() );
|
|
glMatrixMode( GL_PROJECTION );
|
|
glPushMatrix();
|
|
glLoadIdentity();
|
|
float fovy = 60.0f;
|
|
float aspect = 1.0f;
|
|
float zNear = 0.1f;
|
|
float zFar = 100.0f;
|
|
float ymax = zNear * tan( fovy * M_PI / 360.0f );
|
|
float ymin = -ymax;
|
|
float xmin = ymin * aspect;
|
|
float xmax = ymax * aspect;
|
|
float xTranslate = 0.0;
|
|
float yTranslate = 0.0;
|
|
float xminFactor = 1.0;
|
|
float xmaxFactor = 1.0;
|
|
float yminFactor = 1.0;
|
|
float ymaxFactor = 1.0;
|
|
if( area.x() == 0 && area.width() != fullRect.width() )
|
|
{
|
|
// horizontal layout: left screen
|
|
xminFactor = (float)area.width()/(float)fullRect.width();
|
|
xmaxFactor = ((float)fullRect.width()-(float)area.width()*0.5f)/((float)fullRect.width()*0.5f);
|
|
xTranslate = (float)fullRect.width()*0.5f-(float)area.width()*0.5f;
|
|
}
|
|
if( area.x() != 0 && area.width() != fullRect.width() )
|
|
{
|
|
// horizontal layout: right screen
|
|
xminFactor = ((float)fullRect.width()-(float)area.width()*0.5f)/((float)fullRect.width()*0.5f);
|
|
xmaxFactor = (float)area.width()/(float)fullRect.width();
|
|
xTranslate = (float)fullRect.width()*0.5f-(float)area.width()*0.5f;
|
|
}
|
|
if( area.y() == 0 && area.height() != fullRect.height() )
|
|
{
|
|
// vertical layout: top screen
|
|
yminFactor = ((float)fullRect.height()-(float)area.height()*0.5f)/((float)fullRect.height()*0.5f);
|
|
ymaxFactor = (float)area.height()/(float)fullRect.height();
|
|
yTranslate = (float)fullRect.height()*0.5f-(float)area.height()*0.5f;
|
|
}
|
|
if( area.y() != 0 && area.height() != fullRect.height() )
|
|
{
|
|
// vertical layout: bottom screen
|
|
yminFactor = (float)area.height()/(float)fullRect.height();
|
|
ymaxFactor = ((float)fullRect.height()-(float)area.height()*0.5f)/((float)fullRect.height()*0.5f);
|
|
yTranslate = (float)fullRect.height()*0.5f-(float)area.height()*0.5f;
|
|
}
|
|
glFrustum( xmin*xminFactor, xmax*xmaxFactor, ymin*yminFactor, ymax*ymaxFactor, zNear, zFar );
|
|
glMatrixMode( GL_MODELVIEW );
|
|
glPushMatrix();
|
|
glTranslatef( xTranslate, yTranslate, 0.0 );
|
|
#endif
|
|
}
|
|
|
|
QList< EffectWindow* > tempList = currentWindowList;
|
|
int index = tempList.indexOf( selected_window );
|
|
if( animation || start || stop )
|
|
{
|
|
if( !start && !stop )
|
|
{
|
|
if( direction == Right )
|
|
index++;
|
|
else
|
|
index--;
|
|
if( index < 0 )
|
|
index = tempList.count() + index;
|
|
if( index >= tempList.count() )
|
|
index = index % tempList.count();
|
|
}
|
|
foreach( Direction direction, scheduled_directions )
|
|
{
|
|
if( direction == Right )
|
|
index++;
|
|
else
|
|
index--;
|
|
if( index < 0 )
|
|
index = tempList.count() + index;
|
|
if( index >= tempList.count() )
|
|
index = index % tempList.count();
|
|
}
|
|
}
|
|
int leftIndex = index -1;
|
|
if( leftIndex < 0 )
|
|
leftIndex = tempList.count() -1;
|
|
int rightIndex = index +1;
|
|
if( rightIndex == tempList.count() )
|
|
rightIndex = 0;
|
|
|
|
EffectWindow* frontWindow = tempList[ index ];
|
|
leftWindows.clear();
|
|
rightWindows.clear();
|
|
|
|
bool evenWindows = ( tempList.count() % 2 == 0 ) ? true : false;
|
|
int leftWindowCount = 0;
|
|
if( evenWindows )
|
|
leftWindowCount = tempList.count()/2 - 1;
|
|
else
|
|
leftWindowCount = ( tempList.count() - 1 )/2;
|
|
for( int i=0; i < leftWindowCount; i++ )
|
|
{
|
|
int tempIndex = ( leftIndex - i );
|
|
if( tempIndex < 0 )
|
|
tempIndex = tempList.count() + tempIndex;
|
|
leftWindows.prepend( tempList[ tempIndex ] );
|
|
}
|
|
int rightWindowCount = 0;
|
|
if( evenWindows )
|
|
rightWindowCount = tempList.count()/2;
|
|
else
|
|
rightWindowCount = ( tempList.count() - 1 )/2;
|
|
for( int i=0; i < rightWindowCount; i++ )
|
|
{
|
|
int tempIndex = ( rightIndex + i ) % tempList.count();
|
|
rightWindows.prepend( tempList[ tempIndex ] );
|
|
}
|
|
|
|
if( reflection )
|
|
{
|
|
// restrict painting the reflections to the current screen
|
|
QRegion clip = QRegion( area );
|
|
PaintClipper::push( clip );
|
|
// no reflections during start and stop animation
|
|
// except when using a shader
|
|
if( (!start && !stop) || ShaderManager::instance()->isValid() )
|
|
paintScene( frontWindow, leftWindows, rightWindows, true );
|
|
PaintClipper::pop( clip );
|
|
glEnable( GL_BLEND );
|
|
glBlendFunc( GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA );
|
|
#ifndef KWIN_HAVE_OPENGLES
|
|
glPolygonMode( GL_FRONT, GL_FILL );
|
|
#endif
|
|
QRect fullRect = effects->clientArea( FullArea, activeScreen, effects->currentDesktop() );
|
|
// 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 reflectionScaleFactor = 100000 * tan( 60.0 * M_PI / 360.0f )/area.width();
|
|
float vertices[] = {
|
|
-area.width()*0.5f, area.height(), 0.0,
|
|
area.width()*0.5f, area.height(), 0.0,
|
|
(float)area.width()*reflectionScaleFactor, area.height(), -5000,
|
|
-(float)area.width()*reflectionScaleFactor, area.height(), -5000 };
|
|
// foreground
|
|
if (start) {
|
|
mirrorColor[0][3] = timeLine.value();
|
|
} else if (stop) {
|
|
mirrorColor[0][3] = 1.0 - timeLine.value();
|
|
} else {
|
|
mirrorColor[0][3] = 1.0;
|
|
}
|
|
|
|
int y = 0;
|
|
// have to adjust the y values to fit OpenGL
|
|
// in OpenGL y==0 is at bottom, in Qt at top
|
|
if( effects->numScreens() > 1 )
|
|
{
|
|
QRect fullArea = effects->clientArea( FullArea, 0, 1 );
|
|
if( fullArea.height() != area.height() )
|
|
{
|
|
if( area.y() == 0 )
|
|
y = fullArea.height() - area.height();
|
|
else
|
|
y = fullArea.height() - area.y() - area.height();
|
|
}
|
|
}
|
|
// use scissor to restrict painting of the reflection plane to current screen
|
|
glScissor( area.x(), y, area.width(), area.height() );
|
|
glEnable( GL_SCISSOR_TEST );
|
|
|
|
ShaderManager *shaderManager = ShaderManager::instance();
|
|
if (shaderManager->isValid() && m_reflectionShader->isValid()) {
|
|
shaderManager->pushShader(m_reflectionShader);
|
|
QMatrix4x4 windowTransformation;
|
|
if (effects->numScreens() > 1 && area.x() != fullRect.x()) {
|
|
// have to change the reflection area in horizontal layout and right screen
|
|
windowTransformation.translate(-area.x(), 0.0, 0.0);
|
|
}
|
|
windowTransformation.translate(area.x() + area.width()*0.5f, 0.0, 0.0);
|
|
m_reflectionShader->setUniform("windowTransformation", windowTransformation);
|
|
m_reflectionShader->setUniform("u_frontColor", QVector4D(mirrorColor[0][0], mirrorColor[0][1], mirrorColor[0][2], mirrorColor[0][3]));
|
|
m_reflectionShader->setUniform("u_backColor", QVector4D(mirrorColor[1][0], mirrorColor[1][1], mirrorColor[1][2], mirrorColor[1][3]));
|
|
// TODO: make this one properly
|
|
QVector<float> verts;
|
|
QVector<float> texcoords;
|
|
verts.reserve(18);
|
|
texcoords.reserve(12);
|
|
texcoords << 1.0 << 0.0;
|
|
verts << vertices[6] << vertices[7] << vertices[8];
|
|
texcoords << 1.0 << 0.0;
|
|
verts << vertices[9] << vertices[10] << vertices[11];
|
|
texcoords << 0.0 << 0.0;
|
|
verts << vertices[0] << vertices[1] << vertices[2];
|
|
texcoords << 0.0 << 0.0;
|
|
verts << vertices[0] << vertices[1] << vertices[2];
|
|
texcoords << 0.0 << 0.0;
|
|
verts << vertices[3] << vertices[4] << vertices[5];
|
|
texcoords << 1.0 << 0.0;
|
|
verts << vertices[6] << vertices[7] << vertices[8];
|
|
GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer();
|
|
vbo->reset();
|
|
vbo->setUseShader(true);
|
|
vbo->setData(6, 3, verts.data(), texcoords.data());
|
|
vbo->render(GL_TRIANGLES);
|
|
|
|
shaderManager->popShader();
|
|
} else {
|
|
#ifndef KWIN_HAVE_OPENGLES
|
|
glPushMatrix();
|
|
if( effects->numScreens() > 1 && area.x() != fullRect.x() )
|
|
{
|
|
// have to change the reflection area in horizontal layout and right screen
|
|
glTranslatef( -area.x(), 0.0, 0.0 );
|
|
}
|
|
glTranslatef( area.x() + area.width()*0.5f, 0.0, 0.0 );
|
|
glColor4fv( mirrorColor[0] );
|
|
glBegin( GL_POLYGON );
|
|
glVertex3f( vertices[0], vertices[1], vertices[2] );
|
|
glVertex3f( vertices[3], vertices[4], vertices[5] );
|
|
// rearground
|
|
glColor4fv( mirrorColor[1] );
|
|
glVertex3f( vertices[6], vertices[7], vertices[8] );
|
|
glVertex3f( vertices[9], vertices[10], vertices[11] );
|
|
glEnd();
|
|
|
|
glPopMatrix();
|
|
#endif
|
|
}
|
|
glDisable( GL_SCISSOR_TEST );
|
|
glDisable( GL_BLEND );
|
|
}
|
|
paintScene( frontWindow, leftWindows, rightWindows );
|
|
|
|
if( effects->numScreens() > 1 )
|
|
{
|
|
#ifndef KWIN_HAVE_OPENGLES
|
|
glPopMatrix();
|
|
// revert change of projection matrix
|
|
glMatrixMode( GL_PROJECTION );
|
|
glPopMatrix();
|
|
glMatrixMode( GL_MODELVIEW );
|
|
#endif
|
|
}
|
|
|
|
// Render the caption frame
|
|
if (windowTitle)
|
|
{
|
|
double opacity = 1.0;
|
|
if( start )
|
|
opacity = timeLine.value();
|
|
else if( stop )
|
|
opacity = 1.0 - timeLine.value();
|
|
if( animation )
|
|
captionFrame->setCrossFadeProgress( timeLine.value() );
|
|
captionFrame->render( region, opacity );
|
|
}
|
|
|
|
if( ( thumbnails && (!dynamicThumbnails ||
|
|
(dynamicThumbnails && currentWindowList.size() >= thumbnailWindows)) )
|
|
&& !( start || stop ) )
|
|
{
|
|
BoxSwitchEffectProxy *proxy =
|
|
static_cast<BoxSwitchEffectProxy*>( effects->getProxy( "boxswitch" ) );
|
|
if( proxy )
|
|
proxy->paintWindowsBox( region );
|
|
}
|
|
}
|
|
}
|
|
|
|
void CoverSwitchEffect::postPaintScreen()
|
|
{
|
|
if( ( mActivated && ( animation || start ) ) || stop || stopRequested )
|
|
{
|
|
if( timeLine.value() == 1.0 )
|
|
{
|
|
timeLine.setProgress(0.0);
|
|
if( stop )
|
|
{
|
|
stop = false;
|
|
effects->setActiveFullScreenEffect( 0 );
|
|
foreach( EffectWindow* window, referrencedWindows )
|
|
{
|
|
window->unrefWindow();
|
|
}
|
|
referrencedWindows.clear();
|
|
currentWindowList.clear();
|
|
if( startRequested )
|
|
{
|
|
startRequested = false;
|
|
mActivated = true;
|
|
effects->refTabBox();
|
|
currentWindowList = effects->currentTabBoxWindowList();
|
|
if( animateStart )
|
|
{
|
|
start = true;
|
|
}
|
|
}
|
|
}
|
|
else if( !scheduled_directions.isEmpty() )
|
|
{
|
|
direction = scheduled_directions.dequeue();
|
|
if( start )
|
|
{
|
|
animation = true;
|
|
start = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
animation = false;
|
|
start = false;
|
|
if( stopRequested )
|
|
{
|
|
stopRequested = false;
|
|
stop = true;
|
|
}
|
|
}
|
|
}
|
|
effects->addRepaintFull();
|
|
}
|
|
effects->postPaintScreen();
|
|
}
|
|
|
|
void CoverSwitchEffect::paintScene( EffectWindow* frontWindow, const EffectWindowList& leftWindows,
|
|
const EffectWindowList& rightWindows, bool reflectedWindows )
|
|
{
|
|
// LAYOUT
|
|
// one window in the front. Other windows left and right rotated
|
|
// for odd number of windows: left: (n-1)/2; front: 1; right: (n-1)/2
|
|
// for even number of windows: left: n/2; front: 1; right: n/2 -1
|
|
//
|
|
// ANIMATION
|
|
// forward (alt+tab)
|
|
// all left windows are moved to next position
|
|
// top most left window is rotated and moved to front window position
|
|
// front window is rotated and moved to next right window position
|
|
// right windows are moved to next position
|
|
// last right window becomes totally transparent in half the time
|
|
// appears transparent on left side and becomes totally opaque again
|
|
// backward (alt+shift+tab) same as forward but opposite direction
|
|
int width = area.width();
|
|
int leftWindowCount = leftWindows.count();
|
|
int rightWindowCount = rightWindows.count();
|
|
RotationData rot;
|
|
rot.axis = RotationData::YAxis;
|
|
|
|
|
|
// Problem during animation: a window which is painted after another window
|
|
// appears in front of the other
|
|
// so during animation the painting order has to be rearreanged
|
|
// paint sequence no animation: left, right, front
|
|
// paint sequence forward animation: right, front, left
|
|
|
|
if( !animation )
|
|
{
|
|
paintWindows( leftWindows, true, reflectedWindows );
|
|
paintWindows( rightWindows, false, reflectedWindows );
|
|
paintFrontWindow( frontWindow, width, leftWindowCount, rightWindowCount, reflectedWindows );
|
|
}
|
|
else
|
|
{
|
|
if( direction == Right )
|
|
{
|
|
if( timeLine.value() < 0.5 )
|
|
{
|
|
// paint in normal way
|
|
paintWindows( leftWindows, true, reflectedWindows );
|
|
paintWindows( rightWindows, false, reflectedWindows );
|
|
paintFrontWindow( frontWindow, width, leftWindowCount, rightWindowCount, reflectedWindows );
|
|
}
|
|
else
|
|
{
|
|
paintWindows( rightWindows, false, reflectedWindows );
|
|
paintFrontWindow( frontWindow, width, leftWindowCount, rightWindowCount, reflectedWindows );
|
|
paintWindows( leftWindows, true, reflectedWindows, rightWindows.at( 0 ) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
paintWindows( leftWindows, true, reflectedWindows );
|
|
if( timeLine.value() < 0.5 )
|
|
{
|
|
paintWindows( rightWindows, false, reflectedWindows );
|
|
paintFrontWindow( frontWindow, width, leftWindowCount, rightWindowCount, reflectedWindows );
|
|
}
|
|
else
|
|
{
|
|
EffectWindow* leftWindow;
|
|
if( leftWindowCount > 0)
|
|
{
|
|
leftWindow = leftWindows.at( 0 );
|
|
paintFrontWindow( frontWindow, width, leftWindowCount, rightWindowCount, reflectedWindows );
|
|
}
|
|
else
|
|
leftWindow = frontWindow;
|
|
paintWindows( rightWindows, false, reflectedWindows, leftWindow );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CoverSwitchEffect::paintWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data )
|
|
{
|
|
if( mActivated || stop || stopRequested )
|
|
{
|
|
if( !( mask & PAINT_WINDOW_TRANSFORMED ) && !w->isDesktop() )
|
|
{
|
|
if( ( start || stop ) && w->isDock() )
|
|
{
|
|
data.opacity = 1.0 - timeLine.value();
|
|
if( stop )
|
|
data.opacity = timeLine.value();
|
|
}
|
|
else
|
|
return;
|
|
}
|
|
}
|
|
if ( ( start || stop ) && (!w->isOnCurrentDesktop() || w->isMinimized() ) )
|
|
{
|
|
if (stop) // Fade out windows not on the current desktop
|
|
data.opacity = (1.0 - timeLine.value());
|
|
else // Fade in Windows from other desktops when animation is started
|
|
data.opacity = timeLine.value();
|
|
}
|
|
effects->paintWindow( w, mask, region, data );
|
|
}
|
|
|
|
void CoverSwitchEffect::tabBoxAdded( int mode )
|
|
{
|
|
if( effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this )
|
|
return;
|
|
if( !mActivated )
|
|
{
|
|
// only for windows mode
|
|
if( (( mode == TabBoxWindowsMode && primaryTabBox ) ||
|
|
( mode == TabBoxWindowsAlternativeMode && secondaryTabBox ))
|
|
&& effects->currentTabBoxWindowList().count() > 0 )
|
|
{
|
|
input = effects->createFullScreenInputWindow( this, Qt::ArrowCursor );
|
|
activeScreen = effects->activeScreen();
|
|
BoxSwitchEffectProxy *proxy =
|
|
static_cast<BoxSwitchEffectProxy*>( effects->getProxy( "boxswitch" ) );
|
|
if( proxy )
|
|
proxy->activate( mode, true, false, 0.05f );
|
|
if( !stop && !stopRequested )
|
|
{
|
|
effects->refTabBox();
|
|
effects->setActiveFullScreenEffect( this );
|
|
scheduled_directions.clear();
|
|
selected_window = effects->currentTabBoxWindow();
|
|
currentWindowList = effects->currentTabBoxWindowList();
|
|
direction = Left;
|
|
mActivated = true;
|
|
if( animateStart )
|
|
{
|
|
start = true;
|
|
}
|
|
|
|
// Calculation of correct area
|
|
area = effects->clientArea( FullScreenArea, activeScreen, effects->currentDesktop());
|
|
scaleFactor = (zPosition+1100) * 2.0 * tan( 60.0 * M_PI / 360.0f )/displayWidth();
|
|
if( displayWidth()-area.width() != 0 )
|
|
{
|
|
// one of the screens is smaller than the other (horizontal)
|
|
if( area.width() < displayWidth() - area.width() )
|
|
scaleFactor *= (float)area.width()/(float)(displayWidth()-area.width());
|
|
else if( area.width() != displayWidth() - area.width() )
|
|
{
|
|
// vertical layout with different width
|
|
// but we don't want to catch screens with same width and different height
|
|
if( displayHeight() != area.height() )
|
|
scaleFactor *= (float)area.width()/(float)(displayWidth());
|
|
}
|
|
}
|
|
|
|
// Setup caption frame geometry
|
|
if (windowTitle)
|
|
{
|
|
QRect frameRect = QRect( area.width() * 0.25f + area.x(),
|
|
area.height() * 0.9f + area.y(),
|
|
area.width() * 0.5f,
|
|
QFontMetrics( captionFont ).height() );
|
|
captionFrame->setGeometry( frameRect );
|
|
captionFrame->setIconSize( QSize( frameRect.height(), frameRect.height() ));
|
|
// And initial contents
|
|
captionFrame->setText( selected_window->caption() );
|
|
captionFrame->setIcon( selected_window->icon() );
|
|
}
|
|
|
|
effects->addRepaintFull();
|
|
}
|
|
else
|
|
{
|
|
startRequested = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CoverSwitchEffect::tabBoxClosed()
|
|
{
|
|
if( mActivated )
|
|
{
|
|
if( animateStop )
|
|
{
|
|
if( !animation && !start )
|
|
{
|
|
stop = true;
|
|
}
|
|
else if( start && scheduled_directions.isEmpty() )
|
|
{
|
|
start = false;
|
|
stop = true;
|
|
timeLine.setProgress( 1.0 - timeLine.value() );
|
|
}
|
|
else
|
|
{
|
|
stopRequested = true;
|
|
}
|
|
}
|
|
else
|
|
effects->setActiveFullScreenEffect( 0 );
|
|
mActivated = false;
|
|
effects->unrefTabBox();
|
|
effects->destroyInputWindow( input );
|
|
effects->addRepaintFull();
|
|
}
|
|
}
|
|
|
|
void CoverSwitchEffect::tabBoxUpdated()
|
|
{
|
|
if( mActivated )
|
|
{
|
|
if( animateSwitch && currentWindowList.count() > 1)
|
|
{
|
|
// determine the switch direction
|
|
if( selected_window != effects->currentTabBoxWindow() )
|
|
{
|
|
if( selected_window != NULL )
|
|
{
|
|
int old_index = currentWindowList.indexOf( selected_window );
|
|
int new_index = effects->currentTabBoxWindowList().indexOf( effects->currentTabBoxWindow() );
|
|
Direction new_direction;
|
|
int distance = new_index - old_index;
|
|
if( distance > 0 )
|
|
new_direction = Left;
|
|
if( distance < 0 )
|
|
new_direction = Right;
|
|
if( effects->currentTabBoxWindowList().count() == 2 )
|
|
{
|
|
new_direction = Left;
|
|
distance = 1;
|
|
}
|
|
if( distance != 0 )
|
|
{
|
|
distance = abs( distance );
|
|
int tempDistance = effects->currentTabBoxWindowList().count() - distance;
|
|
if( tempDistance < abs( distance ) )
|
|
{
|
|
distance = tempDistance;
|
|
if( new_direction == Left )
|
|
new_direction = Right;
|
|
else
|
|
new_direction = Left;
|
|
}
|
|
if( !animation && !start )
|
|
{
|
|
animation = true;
|
|
direction = new_direction;
|
|
distance--;
|
|
}
|
|
for( int i=0; i<distance; i++ )
|
|
{
|
|
if( !scheduled_directions.isEmpty() && scheduled_directions.last() != new_direction )
|
|
scheduled_directions.pop_back();
|
|
else
|
|
scheduled_directions.enqueue( new_direction );
|
|
if( scheduled_directions.count() == effects->currentTabBoxWindowList().count() )
|
|
scheduled_directions.clear();
|
|
}
|
|
}
|
|
}
|
|
selected_window = effects->currentTabBoxWindow();
|
|
currentWindowList = effects->currentTabBoxWindowList();
|
|
captionFrame->setText( selected_window->caption() );
|
|
captionFrame->setIcon( selected_window->icon() );
|
|
}
|
|
}
|
|
effects->addRepaintFull();
|
|
}
|
|
}
|
|
|
|
void CoverSwitchEffect::paintWindowCover( EffectWindow* w, bool reflectedWindow, WindowPaintData& data )
|
|
{
|
|
QRect windowRect = w->geometry();
|
|
data.yTranslate = area.height() - windowRect.y() - windowRect.height();
|
|
data.zTranslate = -zPosition;
|
|
if( start )
|
|
{
|
|
if( w->isMinimized() )
|
|
{
|
|
data.opacity *= timeLine.value();
|
|
}
|
|
else
|
|
{
|
|
data.xTranslate *= timeLine.value();
|
|
data.yTranslate *= timeLine.value();
|
|
if( effects->numScreens() > 1)
|
|
{
|
|
QRect clientRect = effects->clientArea( FullScreenArea, w->screen(), effects->currentDesktop() );
|
|
QRect fullRect = effects->clientArea( FullArea, activeScreen, effects->currentDesktop() );
|
|
if( w->screen() == activeScreen )
|
|
{
|
|
if( clientRect.width() != fullRect.width() && clientRect.x() != fullRect.x() )
|
|
{
|
|
data.xTranslate -= clientRect.x()*(1.0f-timeLine.value());
|
|
}
|
|
if( clientRect.height() != fullRect.height() && clientRect.y() != fullRect.y() )
|
|
{
|
|
data.yTranslate -= clientRect.y()*(1.0f-timeLine.value());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( clientRect.width() != fullRect.width() && clientRect.x() < area.x())
|
|
{
|
|
data.xTranslate -= clientRect.width()*(1.0f-timeLine.value());
|
|
}
|
|
if( clientRect.height() != fullRect.height() && clientRect.y() < area.y() )
|
|
{
|
|
data.yTranslate -= clientRect.height()*(1.0f-timeLine.value());
|
|
}
|
|
}
|
|
}
|
|
data.zTranslate *= timeLine.value();
|
|
if( data.rotation )
|
|
data.rotation->angle *= timeLine.value();
|
|
}
|
|
}
|
|
if( stop )
|
|
{
|
|
if( w->isMinimized() && w != effects->activeWindow() )
|
|
{
|
|
data.opacity *= (1.0 - timeLine.value());
|
|
}
|
|
else
|
|
{
|
|
data.xTranslate *= (1.0 - timeLine.value());
|
|
data.yTranslate *= (1.0 - timeLine.value());
|
|
if( effects->numScreens() > 1)
|
|
{
|
|
QRect clientRect = effects->clientArea( FullScreenArea, w->screen(), effects->currentDesktop() );
|
|
QRect rect = effects->clientArea( FullScreenArea, activeScreen, effects->currentDesktop() );
|
|
QRect fullRect = effects->clientArea( FullArea, activeScreen, effects->currentDesktop() );
|
|
if( w->screen() == activeScreen )
|
|
{
|
|
if( clientRect.width() != fullRect.width() && clientRect.x() != fullRect.x() )
|
|
{
|
|
data.xTranslate -= clientRect.x()*timeLine.value();
|
|
}
|
|
if( clientRect.height() != fullRect.height() && clientRect.y() != fullRect.y() )
|
|
{
|
|
data.yTranslate -= clientRect.y()*timeLine.value();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( clientRect.width() != fullRect.width() && clientRect.x() < rect.x())
|
|
{
|
|
data.xTranslate -= clientRect.width()*timeLine.value();
|
|
}
|
|
if( clientRect.height() != fullRect.height() && clientRect.y() < area.y() )
|
|
{
|
|
data.yTranslate -= clientRect.height()*timeLine.value();
|
|
}
|
|
}
|
|
}
|
|
data.zTranslate *= (1.0 - timeLine.value());
|
|
if( data.rotation )
|
|
data.rotation->angle *= (1.0 - timeLine.value());
|
|
}
|
|
}
|
|
|
|
QRect thumbnail = infiniteRegion();
|
|
|
|
if( reflectedWindow )
|
|
{
|
|
if (ShaderManager::instance()->isValid()) {
|
|
GLShader *shader = ShaderManager::instance()->pushShader(ShaderManager::GenericShader);
|
|
QMatrix4x4 origMatrix = shader->getUniformMatrix4x4("screenTransformation");
|
|
QMatrix4x4 reflectionMatrix;
|
|
reflectionMatrix.scale(1.0, -1.0, 1.0);
|
|
shader->setUniform("screenTransformation", origMatrix*reflectionMatrix);
|
|
data.yTranslate = - area.height() - windowRect.y() - windowRect.height();
|
|
if (start) {
|
|
data.opacity *= timeLine.value();
|
|
} else if (stop) {
|
|
data.opacity *= 1.0 - timeLine.value();
|
|
}
|
|
effects->paintWindow( w,
|
|
PAINT_WINDOW_TRANSFORMED,
|
|
infiniteRegion(), data );
|
|
shader->setUniform("screenTransformation", origMatrix);
|
|
ShaderManager::instance()->popShader();
|
|
} else {
|
|
#ifndef KWIN_HAVE_OPENGLES
|
|
glPushMatrix();
|
|
glScalef( 1.0, -1.0, 1.0 );
|
|
data.yTranslate = - area.height() - windowRect.y() - windowRect.height();
|
|
effects->paintWindow( w,
|
|
PAINT_WINDOW_TRANSFORMED,
|
|
infiniteRegion(), data );
|
|
glPopMatrix();
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
effects->paintWindow( w,
|
|
PAINT_WINDOW_TRANSFORMED,
|
|
infiniteRegion(), data );
|
|
}
|
|
}
|
|
|
|
void CoverSwitchEffect::paintFrontWindow( EffectWindow* frontWindow, int width, int leftWindows, int rightWindows, bool reflectedWindow )
|
|
{
|
|
if( frontWindow == NULL )
|
|
return;
|
|
float distance = 0.0;
|
|
bool specialHandlingForward = false;
|
|
WindowPaintData data( frontWindow );
|
|
data.xTranslate = area.width()*0.5 - frontWindow->geometry().x() - frontWindow->geometry().width()*0.5;
|
|
if( leftWindows == 0 )
|
|
{
|
|
leftWindows = 1;
|
|
if( !start && !stop )
|
|
specialHandlingForward = true;
|
|
}
|
|
if( rightWindows == 0 )
|
|
{
|
|
rightWindows = 1;
|
|
}
|
|
if( animation )
|
|
{
|
|
if( direction == Right )
|
|
{
|
|
// move to right
|
|
distance = -frontWindow->geometry().width()*0.5f + area.width()*0.5f +
|
|
(((float)displayWidth()*0.5*scaleFactor)-(float)area.width()*0.5f)/rightWindows;
|
|
data.xTranslate += distance * timeLine.value();
|
|
RotationData rot;
|
|
rot.axis = RotationData::YAxis;
|
|
rot.angle = -angle*timeLine.value();
|
|
rot.xRotationPoint = frontWindow->geometry().width();
|
|
data.rotation = &rot;
|
|
}
|
|
else
|
|
{
|
|
// move to left
|
|
distance = frontWindow->geometry().width()*0.5f - area.width()*0.5f +
|
|
((float)width*0.5f-((float)displayWidth()*0.5*scaleFactor))/leftWindows;
|
|
float factor = 1.0;
|
|
if( specialHandlingForward )
|
|
factor = 2.0;
|
|
data.xTranslate += distance * timeLine.value() * factor;
|
|
RotationData rot;
|
|
rot.axis = RotationData::YAxis;
|
|
rot.angle = angle*timeLine.value();
|
|
data.rotation = &rot;
|
|
}
|
|
}
|
|
if( specialHandlingForward )
|
|
{
|
|
data.opacity *= (1.0 - timeLine.value() * 2.0);
|
|
paintWindowCover( frontWindow, reflectedWindow, data );
|
|
}
|
|
else
|
|
paintWindowCover( frontWindow, reflectedWindow, data );
|
|
}
|
|
|
|
void CoverSwitchEffect::paintWindows( const EffectWindowList& windows, bool left, bool reflectedWindows, EffectWindow* additionalWindow )
|
|
{
|
|
int width = area.width();
|
|
int windowCount = windows.count();
|
|
EffectWindow* window;
|
|
|
|
int rotateFactor = 1;
|
|
if( !left )
|
|
{
|
|
rotateFactor = -1;
|
|
}
|
|
|
|
float xTranslate = -((float)(width)*0.5f-((float)displayWidth()*0.5*scaleFactor));
|
|
if( !left )
|
|
xTranslate = ((float)displayWidth()*0.5*scaleFactor)-(float)width*0.5f;
|
|
// handling for additional window from other side
|
|
// has to appear on this side after half of the time
|
|
if( animation && timeLine.value() >= 0.5 && additionalWindow != NULL )
|
|
{
|
|
RotationData rot;
|
|
rot.axis = RotationData::YAxis;
|
|
rot.angle = angle;
|
|
rot.angle = angle*rotateFactor;
|
|
WindowPaintData data( additionalWindow );
|
|
if( left )
|
|
data.xTranslate += -xTranslate - additionalWindow->geometry().x();
|
|
else
|
|
{
|
|
data.xTranslate += xTranslate + area.width() -
|
|
additionalWindow->geometry().x() - additionalWindow->geometry().width();
|
|
rot.xRotationPoint = additionalWindow->geometry().width();
|
|
}
|
|
data.rotation = &rot;
|
|
data.opacity *= ( timeLine.value() - 0.5 ) * 2.0;
|
|
paintWindowCover( additionalWindow, reflectedWindows, data );
|
|
}
|
|
RotationData rot;
|
|
rot.axis = RotationData::YAxis;
|
|
// normal behaviour
|
|
for( int i=0; i < windows.count(); i++)
|
|
{
|
|
window = windows.at( i );
|
|
if( window == NULL || window->isDeleted() )
|
|
{
|
|
continue;
|
|
}
|
|
WindowPaintData data( window );
|
|
rot.angle = angle;
|
|
if( left )
|
|
data.xTranslate += -xTranslate + xTranslate*i/windowCount - window->geometry().x();
|
|
else
|
|
data.xTranslate += xTranslate + width - xTranslate*i/windowCount - window->geometry().x() - window->geometry().width();
|
|
if( animation )
|
|
{
|
|
if( direction == Right )
|
|
{
|
|
if( ( i == windowCount - 1 ) && left )
|
|
{
|
|
// right most window on left side -> move to front
|
|
// have to move one window distance plus half the difference between the window and the desktop size
|
|
data.xTranslate += (xTranslate/windowCount + (width - window->geometry().width())*0.5f)*timeLine.value();
|
|
rot.angle = ( angle - angle * timeLine.value() );
|
|
}
|
|
// right most window does not have to be moved
|
|
else if( !left && ( i == 0 ) ); // do nothing
|
|
else
|
|
{
|
|
// all other windows - move to next position
|
|
data.xTranslate += xTranslate/windowCount * timeLine.value();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( ( i == windowCount - 1 ) && !left )
|
|
{
|
|
// left most window on right side -> move to front
|
|
data.xTranslate -= (xTranslate/windowCount + (width - window->geometry().width())*0.5f)*timeLine.value();
|
|
rot.angle = ( angle - angle * timeLine.value() );
|
|
}
|
|
// left most window does not have to be moved
|
|
else if( i==0 && left); // do nothing
|
|
else
|
|
{
|
|
// all other windows - move to next position
|
|
data.xTranslate -= xTranslate/windowCount * timeLine.value();
|
|
}
|
|
}
|
|
}
|
|
if( left )
|
|
rot.xRotationPoint = 0.0;
|
|
else
|
|
rot.xRotationPoint = window->geometry().width();
|
|
rot.angle *= rotateFactor;
|
|
data.rotation = &rot;
|
|
// make window most to edge transparent if animation
|
|
if( animation && i == 0 && ( ( direction == Left && left ) || ( direction == Right && !left ) ) )
|
|
{
|
|
// only for the first half of the animation
|
|
if( timeLine.value() < 0.5 )
|
|
{
|
|
data.opacity *= (1.0 - timeLine.value() * 2.0);
|
|
paintWindowCover( window, reflectedWindows, data );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
paintWindowCover( window, reflectedWindows, data );
|
|
}
|
|
}
|
|
}
|
|
|
|
void CoverSwitchEffect::windowInputMouseEvent( Window w, QEvent* e )
|
|
{
|
|
assert( w == input );
|
|
Q_UNUSED( w );
|
|
if( e->type() != QEvent::MouseButtonPress )
|
|
return;
|
|
// we don't want click events during animations
|
|
if( animation )
|
|
return;
|
|
QPoint pos = static_cast< QMouseEvent* >( e )->pos();
|
|
|
|
// determine if a window has been clicked
|
|
// not interested in events above a fullscreen window (ignoring panel size)
|
|
if( pos.y() < (area.height()*scaleFactor - area.height())*0.5f*(1.0f/scaleFactor) )
|
|
return;
|
|
|
|
// if there is no selected window (that is no window at all) we cannot click it
|
|
if( !selected_window )
|
|
return;
|
|
|
|
if( pos.x() < (area.width()*scaleFactor - selected_window->width())*0.5f*(1.0f/scaleFactor) )
|
|
{
|
|
float availableSize = (area.width()*scaleFactor - area.width())*0.5f*(1.0f/scaleFactor);
|
|
for( int i=0;i<leftWindows.count();i++ )
|
|
{
|
|
int windowPos = availableSize/leftWindows.count()*i;
|
|
if( pos.x() < windowPos )
|
|
continue;
|
|
if( i+1 < leftWindows.count() )
|
|
{
|
|
if( pos.x() > availableSize/leftWindows.count()*(i+1) )
|
|
continue;
|
|
}
|
|
|
|
effects->setTabBoxWindow( leftWindows[i] );
|
|
return;
|
|
}
|
|
}
|
|
|
|
if( pos.x() > area.width() - (area.width()*scaleFactor - selected_window->width())*0.5f*(1.0f/scaleFactor) )
|
|
{
|
|
float availableSize = (area.width()*scaleFactor - area.width())*0.5f*(1.0f/scaleFactor);
|
|
for( int i=0;i<rightWindows.count();i++ )
|
|
{
|
|
int windowPos = area.width() - availableSize/rightWindows.count()*i;
|
|
if( pos.x() > windowPos )
|
|
continue;
|
|
if( i+1 < rightWindows.count() )
|
|
{
|
|
if( pos.x() < area.width() - availableSize/rightWindows.count()*(i+1) )
|
|
continue;
|
|
}
|
|
|
|
effects->setTabBoxWindow( rightWindows[i] );
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CoverSwitchEffect::abort()
|
|
{
|
|
// it's possible that abort is called after tabbox has been closed
|
|
// in this case the cleanup is already done (see bug 207554)
|
|
if( mActivated )
|
|
{
|
|
effects->unrefTabBox();
|
|
effects->destroyInputWindow( input );
|
|
}
|
|
effects->setActiveFullScreenEffect( 0 );
|
|
mActivated = false;
|
|
stop = false;
|
|
stopRequested = false;
|
|
effects->addRepaintFull();
|
|
captionFrame->free();
|
|
}
|
|
|
|
void CoverSwitchEffect::windowClosed( EffectWindow* c )
|
|
{
|
|
// if the list is not empty, the effect is active
|
|
if( !currentWindowList.isEmpty() )
|
|
{
|
|
c->refWindow();
|
|
referrencedWindows.append( c );
|
|
currentWindowList.removeAll( c );
|
|
leftWindows.removeAll( c );
|
|
rightWindows.removeAll( c );
|
|
}
|
|
}
|
|
|
|
} // namespace
|