kwin/effects/coverswitch/coverswitch.cpp
2011-01-31 20:07:03 +01:00

996 lines
42 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) {
QMatrix4x4 origProjection;
QMatrix4x4 origModelview;
ShaderManager *shaderManager = ShaderManager::instance();
if (effects->numScreens() > 1) {
// unfortunatelly we have to change the projection matrix in dual screen mode
QRect fullRect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop());
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;
}
QMatrix4x4 projection;
projection.frustum(xmin * xminFactor, xmax * xmaxFactor, ymin * yminFactor, ymax * ymaxFactor, zNear, zFar);
QMatrix4x4 modelview;
modelview.translate(xTranslate, yTranslate, 0.0);
if (shaderManager->isShaderBound()) {
GLShader *shader = shaderManager->pushShader(ShaderManager::GenericShader);
origProjection = shader->getUniformMatrix4x4("projection");
origModelview = shader->getUniformMatrix4x4("modelview");
shader->setUniform("projection", projection);
shader->setUniform("modelview", origModelview * modelview);
shaderManager->popShader();
} else {
#ifndef KWIN_HAVE_OPENGLES
glMatrixMode(GL_PROJECTION);
pushMatrix();
loadMatrix(projection);
glMatrixMode(GL_MODELVIEW);
pushMatrix(modelview);
#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);
if (shaderManager->isValid() && m_reflectionShader->isValid()) {
shaderManager->pushShader(m_reflectionShader);
QMatrix4x4 windowTransformation;
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->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) {
if (shaderManager->isShaderBound()) {
GLShader *shader = shaderManager->pushShader(ShaderManager::GenericShader);
shader->setUniform("projection", origProjection);
shader->setUniform("modelview", origModelview);
shaderManager->popShader();
} else {
#ifndef KWIN_HAVE_OPENGLES
popMatrix();
// revert change of projection matrix
glMatrixMode(GL_PROJECTION);
popMatrix();
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