kwin/effects/coverswitch/coverswitch.cpp
Martin Gräßlin cf5de22586 Introduce a build option KWIN_BUILD_OPENGL_1_COMPOSITING
If the build option is enabled KWIN_HAVE_OPENGL_1 is passed as a compile
flag when build against OpenGL.

This compile flag is meant to replace the KWIN_HAVE_OPENGLES. So far code
has been ifdefed for special behavior of OpenGL ES 2.0 and to remove
fixed functionality calls which are not available in OpenGL ES 2.0.

With this build flag the fixed functionality calls which are only used in
the OpenGL1 Compositor can be removed and keeping the KWIN_HAVE_OPENGLES
for the real differences between OpenGL 2.x and OpenGL ES 2.0.

E.g. a call like glColor4f should be in an
glColor4f(1.0, 1.0, 1.0, 1.0);

while a call like glPolygonMode should be in an
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

Building for OpenGL ES 2.0 of course implies that KWIN_HAVE_OPENGL_1 is
not defined.
2012-10-25 11:34:12 +02:00

1065 lines
44 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 <QMouseEvent>
#include <klocale.h>
#include <kapplication.h>
#include <kcolorscheme.h>
#include <kconfiggroup.h>
#include <kglobal.h>
#include <kstandarddirs.h>
#include <KDE/KIcon>
#include <kwinglutils.h>
#include <math.h>
#include <kdebug.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(NULL)
, primaryTabBox(false)
, secondaryTabBox(false)
{
reconfigure(ReconfigureAll);
// Caption frame
captionFont.setBold(true);
captionFont.setPointSize(captionFont.pointSize() * 2);
const QString fragmentshader = KGlobal::dirs()->findResource("data", "kwin/coverswitch-reflection.glsl");
m_reflectionShader = ShaderManager::instance()->loadFragmentShader(ShaderManager::GenericShader, fragmentshader);
connect(effects, SIGNAL(windowClosed(KWin::EffectWindow*)), this, SLOT(slotWindowClosed(KWin::EffectWindow*)));
connect(effects, SIGNAL(tabBoxAdded(int)), this, SLOT(slotTabBoxAdded(int)));
connect(effects, SIGNAL(tabBoxClosed()), this, SLOT(slotTabBoxClosed()));
connect(effects, SIGNAL(tabBoxUpdated()), this, SLOT(slotTabBoxUpdated()));
connect(effects, SIGNAL(tabBoxKeyEvent(QKeyEvent*)), this, SLOT(slotTabBoxKeyEvent(QKeyEvent*)));
}
CoverSwitchEffect::~CoverSwitchEffect()
{
delete captionFrame;
delete m_reflectionShader;
}
bool CoverSwitchEffect::supported()
{
return effects->isOpenGLCompositing();
}
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(QTimeLine::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.setCurrentTime(timeLine.currentTime() + 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 {
#ifdef KWIN_HAVE_OPENGL_1
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) {
// no reflections during start and stop animation
// except when using a shader
if ((!start && !stop) || effects->compositingType() == OpenGL2Compositing)
paintScene(frontWindow, leftWindows, rightWindows, true);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
#ifndef KWIN_HAVE_OPENGLES
glPolygonMode(GL_FRONT, GL_FILL);
#endif
// 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.currentValue();
} else if (stop) {
mirrorColor[0][3] = 1.0 - timeLine.currentValue();
} 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 {
#ifdef KWIN_HAVE_OPENGL_1
glPushMatrix();
QRect fullRect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop());
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 {
#ifdef KWIN_HAVE_OPENGL_1
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.currentValue();
else if (stop)
opacity = 1.0 - timeLine.currentValue();
if (animation)
captionFrame->setCrossFadeProgress(timeLine.currentValue());
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.currentValue() == 1.0) {
timeLine.setCurrentTime(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();
// 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.currentValue() < 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.currentValue() < 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.setOpacity(1.0 - timeLine.currentValue());
if (stop)
data.setOpacity(timeLine.currentValue());
} else
return;
}
}
if ((start || stop) && (!w->isOnCurrentDesktop() || w->isMinimized())) {
if (stop) // Fade out windows not on the current desktop
data.setOpacity((1.0 - timeLine.currentValue()));
else // Fade in Windows from other desktops when animation is started
data.setOpacity(timeLine.currentValue());
}
effects->paintWindow(w, mask, region, data);
}
void CoverSwitchEffect::slotTabBoxAdded(int mode)
{
if (effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this)
return;
if (!mActivated) {
// only for windows mode
if (((mode == TabBoxWindowsMode && primaryTabBox) ||
(mode == TabBoxWindowsAlternativeMode && secondaryTabBox) ||
(mode == TabBoxCurrentAppWindowsMode && primaryTabBox) ||
(mode == TabBoxCurrentAppWindowsAlternativeMode && 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());
if (!captionFrame) {
captionFrame = effects->effectFrame(EffectFrameStyled);
captionFrame->setFont(captionFont);
captionFrame->enableCrossFade(true);
}
captionFrame->setGeometry(frameRect);
captionFrame->setIconSize(QSize(frameRect.height(), frameRect.height()));
// And initial contents
updateCaption();
}
effects->addRepaintFull();
} else {
startRequested = true;
}
}
}
}
void CoverSwitchEffect::slotTabBoxClosed()
{
if (mActivated) {
if (animateStop) {
if (!animation && !start) {
stop = true;
} else if (start && scheduled_directions.isEmpty()) {
start = false;
stop = true;
timeLine.setCurrentTime(timeLine.duration() - timeLine.currentValue());
} else {
stopRequested = true;
}
} else
effects->setActiveFullScreenEffect(0);
mActivated = false;
effects->unrefTabBox();
effects->destroyInputWindow(input);
effects->addRepaintFull();
}
}
void CoverSwitchEffect::slotTabBoxUpdated()
{
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();
updateCaption();
}
}
effects->addRepaintFull();
}
}
void CoverSwitchEffect::paintWindowCover(EffectWindow* w, bool reflectedWindow, WindowPaintData& data)
{
QRect windowRect = w->geometry();
data.setYTranslation(area.height() - windowRect.y() - windowRect.height());
data.setZTranslation(-zPosition);
if (start) {
if (w->isMinimized()) {
data.multiplyOpacity(timeLine.currentValue());
} else {
const QVector3D translation = data.translation() * timeLine.currentValue();
data.setXTranslation(translation.x());
data.setYTranslation(translation.y());
data.setZTranslation(translation.z());
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.translate(- clientRect.x() * (1.0f - timeLine.currentValue()));
}
if (clientRect.height() != fullRect.height() && clientRect.y() != fullRect.y()) {
data.translate(0.0, - clientRect.y() * (1.0f - timeLine.currentValue()));
}
} else {
if (clientRect.width() != fullRect.width() && clientRect.x() < area.x()) {
data.translate(- clientRect.width() * (1.0f - timeLine.currentValue()));
}
if (clientRect.height() != fullRect.height() && clientRect.y() < area.y()) {
data.translate(0.0, - clientRect.height() * (1.0f - timeLine.currentValue()));
}
}
}
data.setRotationAngle(data.rotationAngle() * timeLine.currentValue());
}
}
if (stop) {
if (w->isMinimized() && w != effects->activeWindow()) {
data.multiplyOpacity((1.0 - timeLine.currentValue()));
} else {
const QVector3D translation = data.translation() * (1.0 - timeLine.currentValue());
data.setXTranslation(translation.x());
data.setYTranslation(translation.y());
data.setZTranslation(translation.z());
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.translate(- clientRect.x() * timeLine.currentValue());
}
if (clientRect.height() != fullRect.height() && clientRect.y() != fullRect.y()) {
data.translate(0.0, - clientRect.y() * timeLine.currentValue());
}
} else {
if (clientRect.width() != fullRect.width() && clientRect.x() < rect.x()) {
data.translate(- clientRect.width() * timeLine.currentValue());
}
if (clientRect.height() != fullRect.height() && clientRect.y() < area.y()) {
data.translate(0.0, - clientRect.height() * timeLine.currentValue());
}
}
}
data.setRotationAngle(data.rotationAngle() * (1.0 - timeLine.currentValue()));
}
}
if (reflectedWindow) {
if (effects->compositingType() == OpenGL2Compositing) {
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.setYTranslation(- area.height() - windowRect.y() - windowRect.height());
if (start) {
data.multiplyOpacity(timeLine.currentValue());
} else if (stop) {
data.multiplyOpacity(1.0 - timeLine.currentValue());
}
effects->drawWindow(w,
PAINT_WINDOW_TRANSFORMED,
infiniteRegion(), data);
shader->setUniform("screenTransformation", origMatrix);
ShaderManager::instance()->popShader();
} else {
#ifdef KWIN_HAVE_OPENGL_1
glPushMatrix();
glScalef(1.0, -1.0, 1.0);
data.setYTranslation(- 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;
bool specialHandlingForward = false;
WindowPaintData data(frontWindow);
data.setXTranslation(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) {
float distance = 0.0;
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.translate(distance * timeLine.currentValue());
data.setRotationAxis(Qt::YAxis);
data.setRotationAngle(-angle * timeLine.currentValue());
data.setRotationOrigin(QVector3D(frontWindow->geometry().width(), 0.0, 0.0));
} 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.translate(distance * timeLine.currentValue() * factor);
data.setRotationAxis(Qt::YAxis);
data.setRotationAngle(angle * timeLine.currentValue());
}
}
if (specialHandlingForward) {
data.multiplyOpacity((1.0 - timeLine.currentValue() * 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.currentValue() >= 0.5 && additionalWindow != NULL) {
WindowPaintData data(additionalWindow);
data.setRotationAxis(Qt::YAxis);
data.setRotationAngle(angle * rotateFactor);
if (left) {
data.translate(-xTranslate - additionalWindow->geometry().x());
}
else {
data.translate(xTranslate + area.width() -
additionalWindow->geometry().x() - additionalWindow->geometry().width());
data.setRotationOrigin(QVector3D(additionalWindow->geometry().width(), 0.0, 0.0));
}
data.multiplyOpacity((timeLine.currentValue() - 0.5) * 2.0);
paintWindowCover(additionalWindow, reflectedWindows, data);
}
// normal behaviour
for (int i = 0; i < windows.count(); i++) {
window = windows.at(i);
if (window == NULL || window->isDeleted()) {
continue;
}
WindowPaintData data(window);
data.setRotationAxis(Qt::YAxis);
data.setRotationAngle(angle);
if (left)
data.translate(-xTranslate + xTranslate * i / windowCount - window->geometry().x());
else
data.translate(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.translate((xTranslate / windowCount + (width - window->geometry().width()) * 0.5f) * timeLine.currentValue());
data.setRotationAngle(angle - angle * timeLine.currentValue());
}
// 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.translate(xTranslate / windowCount * timeLine.currentValue());
}
} else {
if ((i == windowCount - 1) && !left) {
// left most window on right side -> move to front
data.translate(- (xTranslate / windowCount + (width - window->geometry().width()) * 0.5f) * timeLine.currentValue());
data.setRotationAngle(angle - angle * timeLine.currentValue());
}
// 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.translate(- xTranslate / windowCount * timeLine.currentValue());
}
}
}
if (!left)
data.setRotationOrigin(QVector3D(window->geometry().width(), 0.0, 0.0));
data.setRotationAngle(data.rotationAngle() * rotateFactor);
// 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.currentValue() < 0.5) {
data.multiplyOpacity((1.0 - timeLine.currentValue() * 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;
QMouseEvent* event = static_cast< QMouseEvent* >(e);
switch (event->button()) {
case Qt::XButton1: // wheel up
selectPreviousWindow();
break;
case Qt::XButton2: // wheel down
selectNextWindow();
break;
case Qt::LeftButton:
case Qt::RightButton:
case Qt::MidButton:
default:
QPoint pos = event->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;
}
}
break;
}
}
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::slotWindowClosed(EffectWindow* c)
{
if (c == selected_window)
selected_window = 0;
// 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);
}
}
bool CoverSwitchEffect::isActive() const
{
return mActivated || stop || stopRequested;
}
void CoverSwitchEffect::updateCaption()
{
if (!selected_window || !windowTitle) {
return;
}
if (selected_window->isDesktop()) {
captionFrame->setText(i18nc("Special entry in alt+tab list for minimizing all windows",
"Show Desktop"));
static QPixmap pix = KIcon("user-desktop").pixmap(captionFrame->iconSize());
captionFrame->setIcon(pix);
} else {
captionFrame->setText(selected_window->caption());
captionFrame->setIcon(selected_window->icon());
}
}
void CoverSwitchEffect::slotTabBoxKeyEvent(QKeyEvent *event)
{
if (event->type() == QEvent::KeyPress) {
switch (event->key()) {
case Qt::Key_Left:
selectPreviousWindow();
break;
case Qt::Key_Right:
selectNextWindow();
break;
default:
// nothing
break;
}
}
}
void CoverSwitchEffect::selectNextOrPreviousWindow(bool forward)
{
if (!mActivated || !selected_window) {
return;
}
const int index = effects->currentTabBoxWindowList().indexOf(selected_window);
int newIndex = index;
if (forward) {
++newIndex;
} else {
--newIndex;
}
if (newIndex == effects->currentTabBoxWindowList().size()) {
newIndex = 0;
} else if (newIndex < 0) {
newIndex = effects->currentTabBoxWindowList().size() -1;
}
if (index == newIndex) {
return;
}
effects->setTabBoxWindow(effects->currentTabBoxWindowList().at(newIndex));
}
} // namespace