kwin/effects/cube/cube.cpp

1990 lines
82 KiB
C++
Raw Normal View History

/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2008 Martin Gräßlin <mgraesslin@kde.org>
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 "cube.h"
// KConfigSkeleton
#include "cubeconfig.h"
#include "cube_inside.h"
#include <QAction>
#include <KGlobalAccel>
#include <KLocalizedString>
#include <kwinconfig.h>
#include <QApplication>
#include <QColor>
#include <QElapsedTimer>
#include <QRect>
#include <QEvent>
#include <QFutureWatcher>
#include <QKeyEvent>
#include <QtConcurrentRun>
#include <QVector2D>
#include <QVector3D>
#include <math.h>
#include <kwinglutils.h>
#include <kwinglplatform.h>
namespace KWin
{
CubeEffect::CubeEffect()
2011-01-30 14:34:42 +00:00
: activated(false)
, cube_painting(false)
, keyboard_grab(false)
, schedule_close(false)
, painting_desktop(1)
, frontDesktop(0)
, cubeOpacity(1.0)
, opacityDesktopOnly(true)
, displayDesktopName(false)
, desktopNameFrame(NULL)
2011-01-30 14:34:42 +00:00
, reflection(true)
, rotating(false)
, desktopChangedWhileRotating(false)
, paintCaps(true)
, rotationDirection(Left)
, verticalRotationDirection(Upwards)
, verticalPosition(Normal)
, wallpaper(NULL)
, texturedCaps(true)
, capTexture(NULL)
, manualAngle(0.0)
, manualVerticalAngle(0.0)
, currentShape(QTimeLine::EaseInOutCurve)
2011-01-30 14:34:42 +00:00
, start(false)
, stop(false)
, reflectionPainting(false)
, activeScreen(0)
, bottomCap(false)
, closeOnMouseRelease(false)
, zoom(0.0)
, zPosition(0.0)
, useForTabBox(false)
, tabBoxMode(false)
, shortcutsRegistered(false)
, mode(Cube)
, useShaders(false)
, cylinderShader(0)
, sphereShader(0)
, zOrderingFactor(0.0f)
, mAddedHeightCoeff1(0.0f)
, mAddedHeightCoeff2(0.0f)
, m_shadersDir(QStringLiteral("kwin/shaders/1.10/"))
2011-01-30 14:34:42 +00:00
, m_cubeCapBuffer(NULL)
, m_proxy(this)
{
desktopNameFont.setBold(true);
desktopNameFont.setPointSize(14);
#ifdef KWIN_HAVE_OPENGLES
const qint64 coreVersionNumber = kVersionNumber(3, 0);
#else
const qint64 coreVersionNumber = kVersionNumber(1, 40);
#endif
if (GLPlatform::instance()->glslVersion() >= coreVersionNumber)
m_shadersDir = QStringLiteral("kwin/shaders/1.40/");
if (effects->compositingType() == OpenGL2Compositing) {
const QString fragmentshader = QStandardPaths::locate(QStandardPaths::GenericDataLocation, m_shadersDir + QStringLiteral("cube-reflection.glsl"));
m_reflectionShader = ShaderManager::instance()->loadFragmentShader(ShaderManager::GenericShader, fragmentshader);
const QString capshader = QStandardPaths::locate(QStandardPaths::GenericDataLocation, m_shadersDir + QStringLiteral("cube-cap.glsl"));
m_capShader = ShaderManager::instance()->loadFragmentShader(ShaderManager::GenericShader, capshader);
} else {
m_reflectionShader = NULL;
m_capShader = NULL;
}
m_textureMirrorMatrix.scale(1.0, -1.0, 1.0);
m_textureMirrorMatrix.translate(0.0, -1.0, 0.0);
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(screenGeometryChanged(const QSize&)), this, SLOT(slotResetShaders()));
2011-01-30 14:34:42 +00:00
reconfigure(ReconfigureAll);
}
bool CubeEffect::supported()
2011-01-30 14:34:42 +00:00
{
return effects->isOpenGLCompositing();
2011-01-30 14:34:42 +00:00
}
2011-01-30 14:34:42 +00:00
void CubeEffect::reconfigure(ReconfigureFlags)
{
CubeConfig::self()->read();
2011-01-30 14:34:42 +00:00
foreach (ElectricBorder border, borderActivate) {
effects->unreserveElectricBorder(border, this);
2011-01-30 14:34:42 +00:00
}
foreach (ElectricBorder border, borderActivateCylinder) {
effects->unreserveElectricBorder(border, this);
2011-01-30 14:34:42 +00:00
}
foreach (ElectricBorder border, borderActivateSphere) {
effects->unreserveElectricBorder(border, this);
2011-01-30 14:34:42 +00:00
}
borderActivate.clear();
borderActivateCylinder.clear();
borderActivateSphere.clear();
QList<int> borderList = QList<int>();
2011-01-30 14:34:42 +00:00
borderList.append(int(ElectricNone));
borderList = CubeConfig::borderActivate();
2011-01-30 14:34:42 +00:00
foreach (int i, borderList) {
borderActivate.append(ElectricBorder(i));
effects->reserveElectricBorder(ElectricBorder(i), this);
2011-01-30 14:34:42 +00:00
}
borderList.clear();
2011-01-30 14:34:42 +00:00
borderList.append(int(ElectricNone));
borderList = CubeConfig::borderActivateCylinder();
2011-01-30 14:34:42 +00:00
foreach (int i, borderList) {
borderActivateCylinder.append(ElectricBorder(i));
effects->reserveElectricBorder(ElectricBorder(i), this);
2011-01-30 14:34:42 +00:00
}
borderList.clear();
2011-01-30 14:34:42 +00:00
borderList.append(int(ElectricNone));
borderList = CubeConfig::borderActivateSphere();
2011-01-30 14:34:42 +00:00
foreach (int i, borderList) {
borderActivateSphere.append(ElectricBorder(i));
effects->reserveElectricBorder(ElectricBorder(i), this);
2011-01-30 14:34:42 +00:00
}
cubeOpacity = (float)CubeConfig::opacity() / 100.0f;
opacityDesktopOnly = CubeConfig::opacityDesktopOnly();
displayDesktopName = CubeConfig::displayDesktopName();
reflection = CubeConfig::reflection();
// TODO: rename rotationDuration to duration
rotationDuration = animationTime(CubeConfig::rotationDuration() != 0 ? CubeConfig::rotationDuration() : 500);
backgroundColor = CubeConfig::backgroundColor();
capColor = CubeConfig::capColor();
paintCaps = CubeConfig::caps();
closeOnMouseRelease = CubeConfig::closeOnMouseRelease();
zPosition = CubeConfig::zPosition();
useForTabBox = CubeConfig::tabBox();
invertKeys = CubeConfig::invertKeys();
invertMouse = CubeConfig::invertMouse();
capDeformationFactor = (float)CubeConfig::capDeformation() / 100.0f;
useZOrdering = CubeConfig::zOrdering();
delete wallpaper;
wallpaper = NULL;
delete capTexture;
capTexture = NULL;
texturedCaps = CubeConfig::texturedCaps();
timeLine.setCurveShape(QTimeLine::EaseInOutCurve);
2011-01-30 14:34:42 +00:00
timeLine.setDuration(rotationDuration);
verticalTimeLine.setCurveShape(QTimeLine::EaseInOutCurve);
2011-01-30 14:34:42 +00:00
verticalTimeLine.setDuration(rotationDuration);
// do not connect the shortcut if we use cylinder or sphere
2011-01-30 14:34:42 +00:00
if (!shortcutsRegistered) {
QAction* cubeAction = new QAction(this);
cubeAction->setObjectName(QStringLiteral("Cube"));
2011-01-30 14:34:42 +00:00
cubeAction->setText(i18n("Desktop Cube"));
KGlobalAccel::self()->setDefaultShortcut(cubeAction, QList<QKeySequence>() << Qt::CTRL + Qt::Key_F11);
KGlobalAccel::self()->setShortcut(cubeAction, QList<QKeySequence>() << Qt::CTRL + Qt::Key_F11);
effects->registerGlobalShortcut(Qt::CTRL + Qt::Key_F11, cubeAction);
effects->registerPointerShortcut(Qt::ControlModifier | Qt::AltModifier, Qt::LeftButton, cubeAction);
cubeShortcut = KGlobalAccel::self()->shortcut(cubeAction);
QAction* cylinderAction = new QAction(this);
cylinderAction->setObjectName(QStringLiteral("Cylinder"));
2011-01-30 14:34:42 +00:00
cylinderAction->setText(i18n("Desktop Cylinder"));
KGlobalAccel::self()->setShortcut(cylinderAction, QList<QKeySequence>());
effects->registerGlobalShortcut(QKeySequence(), cylinderAction);
cylinderShortcut = KGlobalAccel::self()->shortcut(cylinderAction);
QAction* sphereAction = new QAction(this);
sphereAction->setObjectName(QStringLiteral("Sphere"));
2011-01-30 14:34:42 +00:00
sphereAction->setText(i18n("Desktop Sphere"));
KGlobalAccel::self()->setShortcut(sphereAction, QList<QKeySequence>());
sphereShortcut = KGlobalAccel::self()->shortcut(sphereAction);
effects->registerGlobalShortcut(QKeySequence(), sphereAction);
2011-01-30 14:34:42 +00:00
connect(cubeAction, SIGNAL(triggered(bool)), this, SLOT(toggleCube()));
connect(cylinderAction, SIGNAL(triggered(bool)), this, SLOT(toggleCylinder()));
connect(sphereAction, SIGNAL(triggered(bool)), this, SLOT(toggleSphere()));
connect(KGlobalAccel::self(), &KGlobalAccel::globalShortcutChanged, this, &CubeEffect::globalShortcutChanged);
shortcutsRegistered = true;
2011-01-30 14:34:42 +00:00
}
// set the cap color on the shader
if (m_capShader && m_capShader->isValid()) {
ShaderBinder binder(m_capShader);
m_capShader->setUniform("u_capColor", capColor);
}
2011-01-30 14:34:42 +00:00
}
CubeEffect::~CubeEffect()
2011-01-30 14:34:42 +00:00
{
delete wallpaper;
delete capTexture;
delete cylinderShader;
delete sphereShader;
delete desktopNameFrame;
delete m_reflectionShader;
delete m_capShader;
delete m_cubeCapBuffer;
2011-01-30 14:34:42 +00:00
}
QImage CubeEffect::loadCubeCap(const QString &capPath)
{
if (!texturedCaps) {
return QImage();
}
return QImage(capPath);
}
void CubeEffect::slotCubeCapLoaded()
{
QFutureWatcher<QImage> *watcher = dynamic_cast<QFutureWatcher<QImage>*>(sender());
if (!watcher) {
// not invoked from future watcher
return;
}
QImage img = watcher->result();
if (!img.isNull()) {
Better handling for making the compositing OpenGL context current With QtQuick2 it's possible that the scene graph rendering context either lives in an own thread or uses the main GUI thread. In the latter case it's the same thread as our compositing OpenGL context lives in. This means our basic assumption that between two rendering passes the context stays current does not hold. The code already ensured that before we start a rendering pass the context is made current, but there are many more possible cases. If we use OpenGL in areas not triggered by the rendering loop but in response to other events the context needs to be made current. This includes the loading and unloading of effects (some effects use OpenGL in the static effect check, in the ctor and dtor), background loading of texture data, lazy loading after first usage invoked by shortcut, etc. etc. To properly handle these cases new methods are added to EffectsHandler to make the compositing OpenGL context current. These calls delegate down into the scene. On non-OpenGL scenes they are noop, but on OpenGL they go into the backend and make the context current. In addition they ensure that Qt doesn't think that it's QOpenGLContext is current by calling doneCurrent() on the QOpenGLContext::currentContext(). This unfortunately causes an additional call to makeCurrent with a null context, but there is no other way to tell Qt - it doesn't notice when a different context is made current with low level API calls. In the multi-threaded architecture this doesn't matter as ::currentContext() returns null. A short evaluation showed that a transition to QOpenGLContext doesn't seem feasible. Qt only supports either GLX or EGL while KWin supports both and when entering the transition phase for Wayland, it would become extremely tricky if our native platform is X11, but we want a Wayland EGL context. A future solution might be to have a "KWin-QPA plugin" which uses either xcb or Wayland and hides everything from Qt. The API documentation is extended to describe when the effects-framework ensures that an OpenGL context is current. The effects are changed to make the context current in cases where it's not guaranteed. This has been done by looking for creation or deletion of GLTextures and Shaders. If there are other OpenGL usages outside the rendering loop, ctor/dtor this needs to be changed, too.
2013-11-22 14:05:36 +00:00
effects->makeOpenGLContextCurrent();
capTexture = new GLTexture(img);
capTexture->setFilter(GL_LINEAR);
#ifndef KWIN_HAVE_OPENGLES
capTexture->setWrapMode(GL_CLAMP_TO_BORDER);
#endif
// need to recreate the VBO for the cube cap
delete m_cubeCapBuffer;
m_cubeCapBuffer = NULL;
effects->addRepaintFull();
}
watcher->deleteLater();
}
QImage CubeEffect::loadWallPaper(const QString &file)
{
return QImage(file);
}
void CubeEffect::slotWallPaperLoaded()
{
QFutureWatcher<QImage> *watcher = dynamic_cast<QFutureWatcher<QImage>*>(sender());
if (!watcher) {
// not invoked from future watcher
return;
}
QImage img = watcher->result();
if (!img.isNull()) {
Better handling for making the compositing OpenGL context current With QtQuick2 it's possible that the scene graph rendering context either lives in an own thread or uses the main GUI thread. In the latter case it's the same thread as our compositing OpenGL context lives in. This means our basic assumption that between two rendering passes the context stays current does not hold. The code already ensured that before we start a rendering pass the context is made current, but there are many more possible cases. If we use OpenGL in areas not triggered by the rendering loop but in response to other events the context needs to be made current. This includes the loading and unloading of effects (some effects use OpenGL in the static effect check, in the ctor and dtor), background loading of texture data, lazy loading after first usage invoked by shortcut, etc. etc. To properly handle these cases new methods are added to EffectsHandler to make the compositing OpenGL context current. These calls delegate down into the scene. On non-OpenGL scenes they are noop, but on OpenGL they go into the backend and make the context current. In addition they ensure that Qt doesn't think that it's QOpenGLContext is current by calling doneCurrent() on the QOpenGLContext::currentContext(). This unfortunately causes an additional call to makeCurrent with a null context, but there is no other way to tell Qt - it doesn't notice when a different context is made current with low level API calls. In the multi-threaded architecture this doesn't matter as ::currentContext() returns null. A short evaluation showed that a transition to QOpenGLContext doesn't seem feasible. Qt only supports either GLX or EGL while KWin supports both and when entering the transition phase for Wayland, it would become extremely tricky if our native platform is X11, but we want a Wayland EGL context. A future solution might be to have a "KWin-QPA plugin" which uses either xcb or Wayland and hides everything from Qt. The API documentation is extended to describe when the effects-framework ensures that an OpenGL context is current. The effects are changed to make the context current in cases where it's not guaranteed. This has been done by looking for creation or deletion of GLTextures and Shaders. If there are other OpenGL usages outside the rendering loop, ctor/dtor this needs to be changed, too.
2013-11-22 14:05:36 +00:00
effects->makeOpenGLContextCurrent();
wallpaper = new GLTexture(img);
effects->addRepaintFull();
}
watcher->deleteLater();
}
void CubeEffect::slotResetShaders()
{
ShaderManager::instance()->resetShader(m_capShader, ShaderManager::GenericShader);
ShaderManager::instance()->resetShader(m_reflectionShader, ShaderManager::GenericShader);
ShaderManager::instance()->resetShader(cylinderShader, ShaderManager::GenericShader);
ShaderManager::instance()->resetShader(sphereShader, ShaderManager::GenericShader);
}
bool CubeEffect::loadShader()
2011-01-30 14:34:42 +00:00
{
Better handling for making the compositing OpenGL context current With QtQuick2 it's possible that the scene graph rendering context either lives in an own thread or uses the main GUI thread. In the latter case it's the same thread as our compositing OpenGL context lives in. This means our basic assumption that between two rendering passes the context stays current does not hold. The code already ensured that before we start a rendering pass the context is made current, but there are many more possible cases. If we use OpenGL in areas not triggered by the rendering loop but in response to other events the context needs to be made current. This includes the loading and unloading of effects (some effects use OpenGL in the static effect check, in the ctor and dtor), background loading of texture data, lazy loading after first usage invoked by shortcut, etc. etc. To properly handle these cases new methods are added to EffectsHandler to make the compositing OpenGL context current. These calls delegate down into the scene. On non-OpenGL scenes they are noop, but on OpenGL they go into the backend and make the context current. In addition they ensure that Qt doesn't think that it's QOpenGLContext is current by calling doneCurrent() on the QOpenGLContext::currentContext(). This unfortunately causes an additional call to makeCurrent with a null context, but there is no other way to tell Qt - it doesn't notice when a different context is made current with low level API calls. In the multi-threaded architecture this doesn't matter as ::currentContext() returns null. A short evaluation showed that a transition to QOpenGLContext doesn't seem feasible. Qt only supports either GLX or EGL while KWin supports both and when entering the transition phase for Wayland, it would become extremely tricky if our native platform is X11, but we want a Wayland EGL context. A future solution might be to have a "KWin-QPA plugin" which uses either xcb or Wayland and hides everything from Qt. The API documentation is extended to describe when the effects-framework ensures that an OpenGL context is current. The effects are changed to make the context current in cases where it's not guaranteed. This has been done by looking for creation or deletion of GLTextures and Shaders. If there are other OpenGL usages outside the rendering loop, ctor/dtor this needs to be changed, too.
2013-11-22 14:05:36 +00:00
effects->makeOpenGLContextCurrent();
if (!(GLPlatform::instance()->supports(GLSL) &&
(effects->compositingType() == OpenGL2Compositing)))
return false;
QString cylinderVertexshader = QStandardPaths::locate(QStandardPaths::GenericDataLocation, m_shadersDir + QStringLiteral("cylinder.vert"));
QString sphereVertexshader = QStandardPaths::locate(QStandardPaths::GenericDataLocation, m_shadersDir + QStringLiteral("sphere.vert"));
if (cylinderVertexshader.isEmpty() || sphereVertexshader.isEmpty()) {
qCritical() << "Couldn't locate shader files" << endl;
return false;
2011-01-30 14:34:42 +00:00
}
cylinderShader = ShaderManager::instance()->loadVertexShader(ShaderManager::GenericShader, cylinderVertexshader);
2011-01-30 14:34:42 +00:00
if (!cylinderShader->isValid()) {
qCritical() << "The cylinder shader failed to load!" << endl;
return false;
2011-01-30 14:34:42 +00:00
} else {
ShaderBinder binder(cylinderShader);
cylinderShader->setUniform("sampler", 0);
QMatrix4x4 projection;
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;
projection.frustum(xmin, xmax, ymin, ymax, zNear, zFar);
cylinderShader->setUniform(GLShader::ProjectionMatrix, projection);
QMatrix4x4 modelview;
2011-01-30 14:34:42 +00:00
float scaleFactor = 1.1 * tan(fovy * M_PI / 360.0f) / ymax;
modelview.translate(xmin * scaleFactor, ymax * scaleFactor, -1.1);
const QSize screenSize = effects->virtualScreenSize();
modelview.scale((xmax - xmin)*scaleFactor / screenSize.width(), -(ymax - ymin)*scaleFactor / screenSize.height(), 0.001);
cylinderShader->setUniform(GLShader::ModelViewMatrix, modelview);
const QMatrix4x4 identity;
cylinderShader->setUniform(GLShader::ScreenTransformation, identity);
cylinderShader->setUniform(GLShader::WindowTransformation, identity);
2011-01-30 14:34:42 +00:00
QRect rect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop());
cylinderShader->setUniform("width", (float)rect.width() * 0.5f);
}
sphereShader = ShaderManager::instance()->loadVertexShader(ShaderManager::GenericShader, sphereVertexshader);
2011-01-30 14:34:42 +00:00
if (!sphereShader->isValid()) {
qCritical() << "The sphere shader failed to load!" << endl;
return false;
2011-01-30 14:34:42 +00:00
} else {
ShaderBinder binder(sphereShader);
sphereShader->setUniform("sampler", 0);
QMatrix4x4 projection;
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;
projection.frustum(xmin, xmax, ymin, ymax, zNear, zFar);
sphereShader->setUniform(GLShader::ProjectionMatrix, projection);
QMatrix4x4 modelview;
2011-01-30 14:34:42 +00:00
float scaleFactor = 1.1 * tan(fovy * M_PI / 360.0f) / ymax;
modelview.translate(xmin * scaleFactor, ymax * scaleFactor, -1.1);
const QSize screenSize = effects->virtualScreenSize();
modelview.scale((xmax - xmin)*scaleFactor / screenSize.width(), -(ymax - ymin)*scaleFactor / screenSize.height(), 0.001);
sphereShader->setUniform(GLShader::ModelViewMatrix, modelview);
const QMatrix4x4 identity;
sphereShader->setUniform(GLShader::ScreenTransformation, identity);
sphereShader->setUniform(GLShader::WindowTransformation, identity);
2011-01-30 14:34:42 +00:00
QRect rect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop());
sphereShader->setUniform("width", (float)rect.width() * 0.5f);
sphereShader->setUniform("height", (float)rect.height() * 0.5f);
sphereShader->setUniform("u_offset", QVector2D(0, 0));
}
2011-01-30 14:34:42 +00:00
return true;
}
2011-01-30 14:34:42 +00:00
void CubeEffect::prePaintScreen(ScreenPrePaintData& data, int time)
{
if (activated) {
data.mask |= PAINT_SCREEN_TRANSFORMED | Effect::PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS | PAINT_SCREEN_BACKGROUND_FIRST;
2011-01-30 14:34:42 +00:00
if (rotating || start || stop) {
timeLine.setCurrentTime(timeLine.currentTime() + time);
rotateCube();
2011-01-30 14:34:42 +00:00
}
if (verticalRotating) {
verticalTimeLine.setCurrentTime(verticalTimeLine.currentTime() + time);
rotateCube();
}
}
2011-01-30 14:34:42 +00:00
effects->prePaintScreen(data, time);
}
2011-01-30 14:34:42 +00:00
void CubeEffect::paintScreen(int mask, QRegion region, ScreenPaintData& data)
{
if (activated) {
QRect rect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop());
// background
float clearColor[4];
2011-01-30 14:34:42 +00:00
glGetFloatv(GL_COLOR_CLEAR_VALUE, clearColor);
glClearColor(backgroundColor.redF(), backgroundColor.greenF(), backgroundColor.blueF(), 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]);
// wallpaper
2011-01-30 14:34:42 +00:00
if (wallpaper) {
ShaderBinder binder(ShaderManager::SimpleShader);
wallpaper->bind();
2011-01-30 14:34:42 +00:00
wallpaper->render(region, rect);
wallpaper->unbind();
2011-01-30 14:34:42 +00:00
}
2011-01-30 14:34:42 +00:00
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// some veriables needed for painting the caps
2011-01-30 14:34:42 +00:00
float cubeAngle = (float)((float)(effects->numberOfDesktops() - 2) / (float)effects->numberOfDesktops() * 180.0f);
float point = rect.width() / 2 * tan(cubeAngle * 0.5f * M_PI / 180.0f);
float zTranslate = zPosition + zoom;
2011-01-30 14:34:42 +00:00
if (start)
zTranslate *= timeLine.currentValue();
2011-01-30 14:34:42 +00:00
if (stop)
zTranslate *= (1.0 - timeLine.currentValue());
// reflection
2011-01-30 14:34:42 +00:00
if (reflection && mode != Sphere) {
// we can use a huge scale factor (needed to calculate the rearground vertices)
2011-01-30 14:34:42 +00:00
float scaleFactor = 1000000 * tan(60.0 * M_PI / 360.0f) / rect.height();
m_reflectionMatrix.setToIdentity();
m_reflectionMatrix.scale(1.0, -1.0, 1.0);
// TODO reflection is not correct when mixing manual (mouse) rotating with rotation by cursor keys
// there's also a small bug when zooming
2011-01-30 14:34:42 +00:00
float addedHeight1 = -sin(asin(float(rect.height()) / mAddedHeightCoeff1) + fabs(manualVerticalAngle) * M_PI / 180.0f) * mAddedHeightCoeff1;
float addedHeight2 = -sin(asin(float(rect.height()) / mAddedHeightCoeff2) + fabs(manualVerticalAngle) * M_PI / 180.0f) * mAddedHeightCoeff2 - addedHeight1;
if (manualVerticalAngle > 0.0f && effects->numberOfDesktops() & 1) {
m_reflectionMatrix.translate(0.0, cos(fabs(manualAngle) * M_PI / 360.0f * float(effects->numberOfDesktops())) * addedHeight2 + addedHeight1 - float(rect.height()), 0.0);
} else {
m_reflectionMatrix.translate(0.0, sin(fabs(manualAngle) * M_PI / 360.0f * float(effects->numberOfDesktops())) * addedHeight2 + addedHeight1 - float(rect.height()), 0.0);
}
#ifndef KWIN_HAVE_OPENGLES
// TODO: find a solution for GLES
2011-01-30 14:34:42 +00:00
glEnable(GL_CLIP_PLANE0);
#endif
reflectionPainting = true;
2011-01-30 14:34:42 +00:00
glEnable(GL_CULL_FACE);
paintCap(true, -point - zTranslate);
// cube
2011-01-30 14:34:42 +00:00
glCullFace(GL_BACK);
paintCube(mask, region, data);
2011-01-30 14:34:42 +00:00
glCullFace(GL_FRONT);
paintCube(mask, region, data);
2011-01-30 14:34:42 +00:00
paintCap(false, -point - zTranslate);
glDisable(GL_CULL_FACE);
reflectionPainting = false;
#ifndef KWIN_HAVE_OPENGLES
// TODO: find a solution for GLES
2011-01-30 14:34:42 +00:00
glDisable(GL_CLIP_PLANE0);
#endif
const float width = rect.width();
const float height = rect.height();
float vertices[] = {
-width * 0.5f, height, 0.0,
width * 0.5f, height, 0.0,
width * scaleFactor, height, -5000,
-width * scaleFactor, height, -5000
2011-01-30 14:34:42 +00:00
};
// foreground
float alpha = 0.7;
2011-01-30 14:34:42 +00:00
if (start)
alpha = 0.3 + 0.4 * timeLine.currentValue();
2011-01-30 14:34:42 +00:00
if (stop)
alpha = 0.3 + 0.4 * (1.0 - timeLine.currentValue());
2011-01-30 14:34:42 +00:00
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
if (m_reflectionShader && m_reflectionShader->isValid()) {
// ensure blending is enabled - no attribute stack
ShaderBinder binder(m_reflectionShader);
QMatrix4x4 windowTransformation;
2011-01-30 14:34:42 +00:00
windowTransformation.translate(rect.x() + rect.width() * 0.5f, 0.0, 0.0);
m_reflectionShader->setUniform("windowTransformation", windowTransformation);
m_reflectionShader->setUniform("u_alpha", alpha);
QVector<float> verts;
QVector<float> texcoords;
verts.reserve(18);
texcoords.reserve(12);
texcoords << 0.0 << 0.0;
verts << vertices[6] << vertices[7] << vertices[8];
texcoords << 0.0 << 0.0;
verts << vertices[9] << vertices[10] << vertices[11];
texcoords << 1.0 << 0.0;
verts << vertices[0] << vertices[1] << vertices[2];
texcoords << 1.0 << 0.0;
verts << vertices[0] << vertices[1] << vertices[2];
texcoords << 1.0 << 0.0;
verts << vertices[3] << vertices[4] << vertices[5];
texcoords << 0.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);
}
2011-01-30 14:34:42 +00:00
glDisable(GL_BLEND);
}
glEnable(GL_CULL_FACE);
// caps
2011-01-30 14:34:42 +00:00
paintCap(false, -point - zTranslate);
// cube
2011-01-30 14:34:42 +00:00
glCullFace(GL_FRONT);
paintCube(mask, region, data);
2011-01-30 14:34:42 +00:00
glCullFace(GL_BACK);
paintCube(mask, region, data);
// cap
2011-01-30 14:34:42 +00:00
paintCap(true, -point - zTranslate);
glDisable(GL_CULL_FACE);
2011-01-30 14:34:42 +00:00
glDisable(GL_BLEND);
// desktop name box - inspired from coverswitch
2011-01-30 14:34:42 +00:00
if (displayDesktopName) {
double opacity = 1.0;
2011-01-30 14:34:42 +00:00
if (start)
opacity = timeLine.currentValue();
2011-01-30 14:34:42 +00:00
if (stop)
opacity = 1.0 - timeLine.currentValue();
2011-01-30 14:34:42 +00:00
QRect screenRect = effects->clientArea(ScreenArea, activeScreen, frontDesktop);
QRect frameRect = QRect(screenRect.width() * 0.33f + screenRect.x(), screenRect.height() * 0.95f + screenRect.y(),
screenRect.width() * 0.34f, QFontMetrics(desktopNameFont).height());
if (!desktopNameFrame) {
desktopNameFrame = effects->effectFrame(EffectFrameStyled);
desktopNameFrame->setFont(desktopNameFont);
}
2011-01-30 14:34:42 +00:00
desktopNameFrame->setGeometry(frameRect);
desktopNameFrame->setText(effects->desktopName(frontDesktop));
desktopNameFrame->render(region, opacity);
}
// restore the ScreenTransformation after all desktops are painted
// if not done GenericShader keeps the rotation data and transforms windows incorrectly in other rendering calls
if (effects->compositingType() == OpenGL2Compositing) {
GLShader *shader = ShaderManager::instance()->pushShader(KWin::ShaderManager::GenericShader);
shader->setUniform(GLShader::ScreenTransformation, QMatrix4x4());
ShaderManager::instance()->popShader();
}
2011-01-30 14:34:42 +00:00
} else {
effects->paintScreen(mask, region, data);
}
2011-01-30 14:34:42 +00:00
}
void CubeEffect::rotateCube()
2011-01-30 14:34:42 +00:00
{
QRect rect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop());
m_rotationMatrix.setToIdentity();
float internalCubeAngle = 360.0f / effects->numberOfDesktops();
float zTranslate = zPosition + zoom;
2011-01-30 14:34:42 +00:00
if (start)
zTranslate *= timeLine.currentValue();
2011-01-30 14:34:42 +00:00
if (stop)
zTranslate *= (1.0 - timeLine.currentValue());
// Rotation of the cube
2011-01-30 14:34:42 +00:00
float cubeAngle = (float)((float)(effects->numberOfDesktops() - 2) / (float)effects->numberOfDesktops() * 180.0f);
float point = rect.width() / 2 * tan(cubeAngle * 0.5f * M_PI / 180.0f);
if (verticalRotating || verticalPosition != Normal || manualVerticalAngle != 0.0) {
// change the verticalPosition if manualVerticalAngle > 90 or < -90 degrees
2011-01-30 14:34:42 +00:00
if (manualVerticalAngle <= -90.0) {
manualVerticalAngle += 90.0;
2011-01-30 14:34:42 +00:00
if (verticalPosition == Normal)
verticalPosition = Down;
2011-01-30 14:34:42 +00:00
if (verticalPosition == Up)
verticalPosition = Normal;
2011-01-30 14:34:42 +00:00
}
if (manualVerticalAngle >= 90.0) {
manualVerticalAngle -= 90.0;
2011-01-30 14:34:42 +00:00
if (verticalPosition == Normal)
verticalPosition = Up;
2011-01-30 14:34:42 +00:00
if (verticalPosition == Down)
verticalPosition = Normal;
2011-01-30 14:34:42 +00:00
}
float angle = 0.0;
2011-01-30 14:34:42 +00:00
if (verticalPosition == Up) {
angle = 90.0;
2011-01-30 14:34:42 +00:00
if (!verticalRotating) {
if (manualVerticalAngle < 0.0)
angle += manualVerticalAngle;
else
manualVerticalAngle = 0.0;
}
2011-01-30 14:34:42 +00:00
} else if (verticalPosition == Down) {
angle = -90.0;
2011-01-30 14:34:42 +00:00
if (!verticalRotating) {
if (manualVerticalAngle > 0.0)
angle += manualVerticalAngle;
else
manualVerticalAngle = 0.0;
}
2011-01-30 14:34:42 +00:00
} else {
angle = manualVerticalAngle;
2011-01-30 14:34:42 +00:00
}
if (verticalRotating) {
angle *= verticalTimeLine.currentValue();
2011-01-30 14:34:42 +00:00
if (verticalPosition == Normal && verticalRotationDirection == Upwards)
angle = -90.0 + 90 * verticalTimeLine.currentValue();
2011-01-30 14:34:42 +00:00
if (verticalPosition == Normal && verticalRotationDirection == Downwards)
angle = 90.0 - 90 * verticalTimeLine.currentValue();
angle += manualVerticalAngle * (1.0 - verticalTimeLine.currentValue());
2011-01-30 14:34:42 +00:00
}
if (stop)
angle *= (1.0 - timeLine.currentValue());
2011-01-30 14:34:42 +00:00
m_rotationMatrix.translate(rect.width() / 2, rect.height() / 2, -point - zTranslate);
m_rotationMatrix.rotate(angle, 1.0, 0.0, 0.0);
2011-01-30 14:34:42 +00:00
m_rotationMatrix.translate(-rect.width() / 2, -rect.height() / 2, point + zTranslate);
}
if (rotating || (manualAngle != 0.0)) {
int tempFrontDesktop = frontDesktop;
2011-01-30 14:34:42 +00:00
if (manualAngle > internalCubeAngle * 0.5f) {
manualAngle -= internalCubeAngle;
tempFrontDesktop--;
2011-01-30 14:34:42 +00:00
if (tempFrontDesktop == 0)
tempFrontDesktop = effects->numberOfDesktops();
2011-01-30 14:34:42 +00:00
}
if (manualAngle < -internalCubeAngle * 0.5f) {
manualAngle += internalCubeAngle;
tempFrontDesktop++;
2011-01-30 14:34:42 +00:00
if (tempFrontDesktop > effects->numberOfDesktops())
tempFrontDesktop = 1;
2011-01-30 14:34:42 +00:00
}
float rotationAngle = internalCubeAngle * timeLine.currentValue();
2011-01-30 14:34:42 +00:00
if (rotationAngle > internalCubeAngle * 0.5f) {
rotationAngle -= internalCubeAngle;
2011-01-30 14:34:42 +00:00
if (!desktopChangedWhileRotating) {
desktopChangedWhileRotating = true;
2011-01-30 14:34:42 +00:00
if (rotationDirection == Left) {
tempFrontDesktop++;
2011-01-30 14:34:42 +00:00
} else if (rotationDirection == Right) {
tempFrontDesktop--;
2011-01-30 14:34:42 +00:00
}
if (tempFrontDesktop > effects->numberOfDesktops())
tempFrontDesktop = 1;
2011-01-30 14:34:42 +00:00
else if (tempFrontDesktop == 0)
tempFrontDesktop = effects->numberOfDesktops();
}
2011-01-30 14:34:42 +00:00
}
// don't change front desktop during stop animation as this would break some logic
2011-01-30 14:34:42 +00:00
if (!stop)
frontDesktop = tempFrontDesktop;
2011-01-30 14:34:42 +00:00
if (rotationDirection == Left) {
rotationAngle *= -1;
2011-01-30 14:34:42 +00:00
}
if (stop)
rotationAngle = manualAngle * (1.0 - timeLine.currentValue());
else
rotationAngle += manualAngle * (1.0 - timeLine.currentValue());
2011-01-30 14:34:42 +00:00
m_rotationMatrix.translate(rect.width() / 2, rect.height() / 2, -point - zTranslate);
m_rotationMatrix.rotate(rotationAngle, 0.0, 1.0, 0.0);
2011-01-30 14:34:42 +00:00
m_rotationMatrix.translate(-rect.width() / 2, -rect.height() / 2, point + zTranslate);
}
2011-01-30 14:34:42 +00:00
}
2011-01-30 14:34:42 +00:00
void CubeEffect::paintCube(int mask, QRegion region, ScreenPaintData& data)
{
QRect rect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop());
float internalCubeAngle = 360.0f / effects->numberOfDesktops();
cube_painting = true;
float zTranslate = zPosition + zoom;
2011-01-30 14:34:42 +00:00
if (start)
zTranslate *= timeLine.currentValue();
2011-01-30 14:34:42 +00:00
if (stop)
zTranslate *= (1.0 - timeLine.currentValue());
// Rotation of the cube
2011-01-30 14:34:42 +00:00
float cubeAngle = (float)((float)(effects->numberOfDesktops() - 2) / (float)effects->numberOfDesktops() * 180.0f);
float point = rect.width() / 2 * tan(cubeAngle * 0.5f * M_PI / 180.0f);
2011-01-30 14:34:42 +00:00
for (int i = 0; i < effects->numberOfDesktops(); i++) {
// start painting the cube
2011-01-30 14:34:42 +00:00
painting_desktop = (i + frontDesktop) % effects->numberOfDesktops();
if (painting_desktop == 0) {
painting_desktop = effects->numberOfDesktops();
2011-01-30 14:34:42 +00:00
}
ScreenPaintData newData = data;
newData.setRotationAxis(Qt::YAxis);
newData.setRotationAngle(internalCubeAngle * i);
newData.setRotationOrigin(QVector3D(rect.width() / 2, 0.0, -point));
newData.setZTranslation(-zTranslate);
2011-01-30 14:34:42 +00:00
effects->paintScreen(mask, region, newData);
}
cube_painting = false;
painting_desktop = effects->currentDesktop();
2011-01-30 14:34:42 +00:00
}
void CubeEffect::paintCap(bool frontFirst, float zOffset)
2011-01-30 14:34:42 +00:00
{
if ((!paintCaps) || effects->numberOfDesktops() <= 2)
return;
GLenum firstCull = frontFirst ? GL_FRONT : GL_BACK;
GLenum secondCull = frontFirst ? GL_BACK : GL_FRONT;
const QRect rect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop());
// create the VBO if not yet created
if (!m_cubeCapBuffer) {
2011-01-30 14:34:42 +00:00
switch(mode) {
case Cube:
paintCubeCap();
break;
case Cylinder:
paintCylinderCap();
break;
case Sphere:
paintSphereCap();
break;
default:
// impossible
break;
}
}
QMatrix4x4 capMatrix;
2011-01-30 14:34:42 +00:00
capMatrix.translate(rect.width() / 2, 0.0, zOffset);
capMatrix.rotate((1 - frontDesktop) * 360.0f / effects->numberOfDesktops(), 0.0, 1.0, 0.0);
capMatrix.translate(0.0, rect.height(), 0.0);
if (mode == Sphere) {
capMatrix.scale(1.0, -1.0, 1.0);
}
bool capShader = false;
if (effects->compositingType() == OpenGL2Compositing && m_capShader && m_capShader->isValid()) {
capShader = true;
ShaderManager::instance()->pushShader(m_capShader);
float opacity = cubeOpacity;
if (start) {
opacity *= timeLine.currentValue();
} else if (stop) {
opacity *= (1.0 - timeLine.currentValue());
}
m_capShader->setUniform("u_opacity", opacity);
2010-12-31 09:53:12 +00:00
m_capShader->setUniform("u_mirror", 1);
if (reflectionPainting) {
m_capShader->setUniform(GLShader::ScreenTransformation, m_reflectionMatrix * m_rotationMatrix);
} else {
m_capShader->setUniform(GLShader::ScreenTransformation, m_rotationMatrix);
}
m_capShader->setUniform(GLShader::WindowTransformation, capMatrix);
2011-05-01 09:18:02 +00:00
m_capShader->setUniform("u_untextured", texturedCaps ? 0 : 1);
if (texturedCaps && effects->numberOfDesktops() > 3 && capTexture) {
capTexture->bind();
}
}
glEnable(GL_BLEND);
glCullFace(firstCull);
m_cubeCapBuffer->render(GL_TRIANGLES);
if (mode == Sphere) {
capMatrix.scale(1.0, -1.0, 1.0);
}
capMatrix.translate(0.0, -rect.height(), 0.0);
if (capShader) {
m_capShader->setUniform("windowTransformation", capMatrix);
2010-12-31 09:53:12 +00:00
m_capShader->setUniform("u_mirror", 0);
}
glCullFace(secondCull);
m_cubeCapBuffer->render(GL_TRIANGLES);
glDisable(GL_BLEND);
if (capShader) {
ShaderManager::instance()->popShader();
if (texturedCaps && effects->numberOfDesktops() > 3 && capTexture) {
capTexture->unbind();
}
}
2011-01-30 14:34:42 +00:00
}
void CubeEffect::paintCubeCap()
2011-01-30 14:34:42 +00:00
{
QRect rect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop());
float cubeAngle = (float)((float)(effects->numberOfDesktops() - 2) / (float)effects->numberOfDesktops() * 180.0f);
float z = rect.width() / 2 * tan(cubeAngle * 0.5f * M_PI / 180.0f);
float zTexture = rect.width() / 2 * tan(45.0f * M_PI / 180.0f);
float angle = 360.0f / effects->numberOfDesktops();
bool texture = texturedCaps && effects->numberOfDesktops() > 3 && capTexture;
QVector<float> verts;
QVector<float> texCoords;
2011-01-30 14:34:42 +00:00
for (int i = 0; i < effects->numberOfDesktops(); i++) {
int triangleRows = effects->numberOfDesktops() * 5;
float zTriangleDistance = z / (float)triangleRows;
float widthTriangle = tan(angle * 0.5 * M_PI / 180.0) * zTriangleDistance;
float currentWidth = 0.0;
2011-01-30 14:34:42 +00:00
float cosValue = cos(i * angle * M_PI / 180.0);
float sinValue = sin(i * angle * M_PI / 180.0);
for (int j = 0; j < triangleRows; j++) {
float previousWidth = currentWidth;
2011-01-30 14:34:42 +00:00
currentWidth = tan(angle * 0.5 * M_PI / 180.0) * zTriangleDistance * (j + 1);
int evenTriangles = 0;
int oddTriangles = 0;
2011-01-30 14:34:42 +00:00
for (int k = 0; k < floor(currentWidth / widthTriangle * 2 - 1 + 0.5f); k++) {
float x1 = -previousWidth;
float x2 = -currentWidth;
float x3 = 0.0;
float z1 = 0.0;
float z2 = 0.0;
float z3 = 0.0;
2011-01-30 14:34:42 +00:00
if (k % 2 == 0) {
x1 += evenTriangles * widthTriangle * 2;
x2 += evenTriangles * widthTriangle * 2;
x3 = x2 + widthTriangle * 2;
z1 = j * zTriangleDistance;
z2 = (j + 1) * zTriangleDistance;
z3 = (j + 1) * zTriangleDistance;
float xRot = cosValue * x1 - sinValue * z1;
float zRot = sinValue * x1 + cosValue * z1;
x1 = xRot;
z1 = zRot;
xRot = cosValue * x2 - sinValue * z2;
zRot = sinValue * x2 + cosValue * z2;
x2 = xRot;
z2 = zRot;
xRot = cosValue * x3 - sinValue * z3;
zRot = sinValue * x3 + cosValue * z3;
x3 = xRot;
z3 = zRot;
evenTriangles++;
2011-01-30 14:34:42 +00:00
} else {
x1 += oddTriangles * widthTriangle * 2;
x2 += (oddTriangles + 1) * widthTriangle * 2;
x3 = x1 + widthTriangle * 2;
z1 = j * zTriangleDistance;
z2 = (j + 1) * zTriangleDistance;
z3 = j * zTriangleDistance;
float xRot = cosValue * x1 - sinValue * z1;
float zRot = sinValue * x1 + cosValue * z1;
x1 = xRot;
z1 = zRot;
xRot = cosValue * x2 - sinValue * z2;
zRot = sinValue * x2 + cosValue * z2;
x2 = xRot;
z2 = zRot;
xRot = cosValue * x3 - sinValue * z3;
zRot = sinValue * x3 + cosValue * z3;
x3 = xRot;
z3 = zRot;
oddTriangles++;
2011-01-30 14:34:42 +00:00
}
float texX1 = 0.0;
float texX2 = 0.0;
float texX3 = 0.0;
float texY1 = 0.0;
float texY2 = 0.0;
float texY3 = 0.0;
2011-01-30 14:34:42 +00:00
if (texture) {
if (capTexture->isYInverted()) {
texX1 = x1 / (rect.width()) + 0.5;
texY1 = 0.5 + z1 / zTexture * 0.5;
texX2 = x2 / (rect.width()) + 0.5;
texY2 = 0.5 + z2 / zTexture * 0.5;
texX3 = x3 / (rect.width()) + 0.5;
texY3 = 0.5 + z3 / zTexture * 0.5;
texCoords << texX1 << texY1;
} else {
texX1 = x1 / (rect.width()) + 0.5;
texY1 = 0.5 - z1 / zTexture * 0.5;
texX2 = x2 / (rect.width()) + 0.5;
texY2 = 0.5 - z2 / zTexture * 0.5;
texX3 = x3 / (rect.width()) + 0.5;
texY3 = 0.5 - z3 / zTexture * 0.5;
texCoords << texX1 << texY1;
}
2011-01-30 14:34:42 +00:00
}
verts << x1 << 0.0 << z1;
2011-01-30 14:34:42 +00:00
if (texture) {
texCoords << texX2 << texY2;
2011-01-30 14:34:42 +00:00
}
verts << x2 << 0.0 << z2;
2011-01-30 14:34:42 +00:00
if (texture) {
texCoords << texX3 << texY3;
}
2011-01-30 14:34:42 +00:00
verts << x3 << 0.0 << z3;
}
}
2011-01-30 14:34:42 +00:00
}
delete m_cubeCapBuffer;
m_cubeCapBuffer = new GLVertexBuffer(GLVertexBuffer::Static);
2011-01-30 14:34:42 +00:00
m_cubeCapBuffer->setData(verts.count() / 3, 3, verts.constData(), texture ? texCoords.constData() : NULL);
}
void CubeEffect::paintCylinderCap()
2011-01-30 14:34:42 +00:00
{
QRect rect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop());
float cubeAngle = (float)((float)(effects->numberOfDesktops() - 2) / (float)effects->numberOfDesktops() * 180.0f);
2011-01-30 14:34:42 +00:00
float radian = (cubeAngle * 0.5) * M_PI / 180;
float radius = (rect.width() * 0.5) * tan(radian);
float segment = radius / 30.0f;
bool texture = texturedCaps && effects->numberOfDesktops() > 3 && capTexture;
QVector<float> verts;
QVector<float> texCoords;
2011-01-30 14:34:42 +00:00
for (int i = 1; i <= 30; i++) {
int steps = 72;
2011-01-30 14:34:42 +00:00
for (int j = 0; j <= steps; j++) {
const float azimuthAngle = (j * (360.0f / steps)) * M_PI / 180.0f;
const float azimuthAngle2 = ((j + 1) * (360.0f / steps)) * M_PI / 180.0f;
const float x1 = segment * (i - 1) * sin(azimuthAngle);
const float x2 = segment * i * sin(azimuthAngle);
const float x3 = segment * (i - 1) * sin(azimuthAngle2);
const float x4 = segment * i * sin(azimuthAngle2);
const float z1 = segment * (i - 1) * cos(azimuthAngle);
const float z2 = segment * i * cos(azimuthAngle);
const float z3 = segment * (i - 1) * cos(azimuthAngle2);
const float z4 = segment * i * cos(azimuthAngle2);
if (texture) {
if (capTexture->isYInverted()) {
texCoords << (radius + x1) / (radius * 2.0f) << (z1 + radius) / (radius * 2.0f);
texCoords << (radius + x2) / (radius * 2.0f) << (z2 + radius) / (radius * 2.0f);
texCoords << (radius + x3) / (radius * 2.0f) << (z3 + radius) / (radius * 2.0f);
texCoords << (radius + x4) / (radius * 2.0f) << (z4 + radius) / (radius * 2.0f);
texCoords << (radius + x3) / (radius * 2.0f) << (z3 + radius) / (radius * 2.0f);
texCoords << (radius + x2) / (radius * 2.0f) << (z2 + radius) / (radius * 2.0f);
} else {
texCoords << (radius + x1) / (radius * 2.0f) << 1.0f - (z1 + radius) / (radius * 2.0f);
texCoords << (radius + x2) / (radius * 2.0f) << 1.0f - (z2 + radius) / (radius * 2.0f);
texCoords << (radius + x3) / (radius * 2.0f) << 1.0f - (z3 + radius) / (radius * 2.0f);
texCoords << (radius + x4) / (radius * 2.0f) << 1.0f - (z4 + radius) / (radius * 2.0f);
texCoords << (radius + x3) / (radius * 2.0f) << 1.0f - (z3 + radius) / (radius * 2.0f);
texCoords << (radius + x2) / (radius * 2.0f) << 1.0f - (z2 + radius) / (radius * 2.0f);
}
}
verts << x1 << 0.0 << z1;
verts << x2 << 0.0 << z2;
verts << x3 << 0.0 << z3;
verts << x4 << 0.0 << z4;
verts << x3 << 0.0 << z3;
verts << x2 << 0.0 << z2;
}
2011-01-30 14:34:42 +00:00
}
delete m_cubeCapBuffer;
m_cubeCapBuffer = new GLVertexBuffer(GLVertexBuffer::Static);
2011-01-30 14:34:42 +00:00
m_cubeCapBuffer->setData(verts.count() / 3, 3, verts.constData(), texture ? texCoords.constData() : NULL);
}
void CubeEffect::paintSphereCap()
2011-01-30 14:34:42 +00:00
{
QRect rect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop());
float cubeAngle = (float)((float)(effects->numberOfDesktops() - 2) / (float)effects->numberOfDesktops() * 180.0f);
float zTexture = rect.width() / 2 * tan(45.0f * M_PI / 180.0f);
float radius = (rect.width() * 0.5) / cos(cubeAngle * 0.5 * M_PI / 180.0);
float angle = acos((rect.height() * 0.5) / radius) * 180.0 / M_PI;
angle /= 30;
bool texture = texturedCaps && effects->numberOfDesktops() > 3 && capTexture;
QVector<float> verts;
QVector<float> texCoords;
2011-01-30 14:34:42 +00:00
for (int i = 0; i < 30; i++) {
float topAngle = angle * i * M_PI / 180.0;
float bottomAngle = angle * (i + 1) * M_PI / 180.0;
float yTop = rect.height() * 0.5 - radius * cos(topAngle);
yTop -= (yTop - rect.height() * 0.5) * capDeformationFactor;
float yBottom = rect.height() * 0.5 - radius * cos(bottomAngle);
yBottom -= (yBottom - rect.height() * 0.5) * capDeformationFactor;
for (int j = 0; j < 36; j++) {
const float x1 = radius * sin(topAngle) * sin((90.0 + j * 10.0) * M_PI / 180.0);
const float z1 = radius * sin(topAngle) * cos((90.0 + j * 10.0) * M_PI / 180.0);
const float x2 = radius * sin(bottomAngle) * sin((90.0 + j * 10.0) * M_PI / 180.00);
const float z2 = radius * sin(bottomAngle) * cos((90.0 + j * 10.0) * M_PI / 180.0);
const float x3 = radius * sin(bottomAngle) * sin((90.0 + (j + 1) * 10.0) * M_PI / 180.0);
const float z3 = radius * sin(bottomAngle) * cos((90.0 + (j + 1) * 10.0) * M_PI / 180.0);
const float x4 = radius * sin(topAngle) * sin((90.0 + (j + 1) * 10.0) * M_PI / 180.0);
const float z4 = radius * sin(topAngle) * cos((90.0 + (j + 1) * 10.0) * M_PI / 180.0);
if (texture) {
if (capTexture->isYInverted()) {
texCoords << x4 / (rect.width()) + 0.5 << 0.5 + z4 / zTexture * 0.5;
texCoords << x1 / (rect.width()) + 0.5 << 0.5 + z1 / zTexture * 0.5;
texCoords << x2 / (rect.width()) + 0.5 << 0.5 + z2 / zTexture * 0.5;
texCoords << x2 / (rect.width()) + 0.5 << 0.5 + z2 / zTexture * 0.5;
texCoords << x3 / (rect.width()) + 0.5 << 0.5 + z3 / zTexture * 0.5;
texCoords << x4 / (rect.width()) + 0.5 << 0.5 + z4 / zTexture * 0.5;
} else {
texCoords << x4 / (rect.width()) + 0.5 << 0.5 - z4 / zTexture * 0.5;
texCoords << x1 / (rect.width()) + 0.5 << 0.5 - z1 / zTexture * 0.5;
texCoords << x2 / (rect.width()) + 0.5 << 0.5 - z2 / zTexture * 0.5;
texCoords << x2 / (rect.width()) + 0.5 << 0.5 - z2 / zTexture * 0.5;
texCoords << x3 / (rect.width()) + 0.5 << 0.5 - z3 / zTexture * 0.5;
texCoords << x4 / (rect.width()) + 0.5 << 0.5 - z4 / zTexture * 0.5;
}
}
verts << x4 << yTop << z4;
verts << x1 << yTop << z1;
verts << x2 << yBottom << z2;
verts << x2 << yBottom << z2;
verts << x3 << yBottom << z3;
verts << x4 << yTop << z4;
}
2011-01-30 14:34:42 +00:00
}
delete m_cubeCapBuffer;
m_cubeCapBuffer = new GLVertexBuffer(GLVertexBuffer::Static);
2011-01-30 14:34:42 +00:00
m_cubeCapBuffer->setData(verts.count() / 3, 3, verts.constData(), texture ? texCoords.constData() : NULL);
}
void CubeEffect::postPaintScreen()
2011-01-30 14:34:42 +00:00
{
effects->postPaintScreen();
2011-01-30 14:34:42 +00:00
if (activated) {
if (start) {
if (timeLine.currentValue() == 1.0) {
start = false;
timeLine.setCurrentTime(0);
// more rotations?
2011-01-30 14:34:42 +00:00
if (!rotations.empty()) {
rotationDirection = rotations.dequeue();
rotating = true;
// change the curve shape if current shape is not easeInOut
if (currentShape != QTimeLine::EaseInOutCurve) {
// more rotations follow -> linear curve
2011-01-30 14:34:42 +00:00
if (!rotations.empty()) {
currentShape = QTimeLine::LinearCurve;
2011-01-30 14:34:42 +00:00
}
// last rotation step -> easeOut curve
2011-01-30 14:34:42 +00:00
else {
currentShape = QTimeLine::EaseOutCurve;
}
2011-01-30 14:34:42 +00:00
timeLine.setCurveShape(currentShape);
} else {
// if there is at least one more rotation, we can change to easeIn
2011-01-30 14:34:42 +00:00
if (!rotations.empty()) {
currentShape = QTimeLine::EaseInCurve;
2011-01-30 14:34:42 +00:00
timeLine.setCurveShape(currentShape);
}
}
}
2011-01-30 14:34:42 +00:00
}
effects->addRepaintFull();
return; // schedule_close could have been called, start has to finish first
2011-01-30 14:34:42 +00:00
}
if (stop) {
if (timeLine.currentValue() == 1.0) {
2011-01-30 14:34:42 +00:00
effects->setCurrentDesktop(frontDesktop);
stop = false;
timeLine.setCurrentTime(0);
activated = false;
// set the new desktop
2011-01-30 14:34:42 +00:00
if (keyboard_grab)
effects->ungrabKeyboard();
keyboard_grab = false;
effects->stopMouseInterception(this);
2011-01-30 14:34:42 +00:00
effects->setActiveFullScreenEffect(0);
delete m_cubeCapBuffer;
m_cubeCapBuffer = NULL;
if (desktopNameFrame)
desktopNameFrame->free();
}
2011-01-30 14:34:42 +00:00
effects->addRepaintFull();
}
if (rotating || verticalRotating) {
if (rotating && timeLine.currentValue() == 1.0) {
timeLine.setCurrentTime(0.0);
rotating = false;
desktopChangedWhileRotating = false;
manualAngle = 0.0;
// more rotations?
2011-01-30 14:34:42 +00:00
if (!rotations.empty()) {
rotationDirection = rotations.dequeue();
rotating = true;
// change the curve shape if current shape is not easeInOut
if (currentShape != QTimeLine::EaseInOutCurve) {
// more rotations follow -> linear curve
2011-01-30 14:34:42 +00:00
if (!rotations.empty()) {
currentShape = QTimeLine::LinearCurve;
2011-01-30 14:34:42 +00:00
}
// last rotation step -> easeOut curve
2011-01-30 14:34:42 +00:00
else {
currentShape = QTimeLine::EaseOutCurve;
}
2011-01-30 14:34:42 +00:00
timeLine.setCurveShape(currentShape);
} else {
// if there is at least one more rotation, we can change to easeIn
2011-01-30 14:34:42 +00:00
if (!rotations.empty()) {
currentShape = QTimeLine::EaseInCurve;
2011-01-30 14:34:42 +00:00
timeLine.setCurveShape(currentShape);
}
}
2011-01-30 14:34:42 +00:00
} else {
// reset curve shape if there are no more rotations
if (currentShape != QTimeLine::EaseInOutCurve) {
currentShape = QTimeLine::EaseInOutCurve;
2011-01-30 14:34:42 +00:00
timeLine.setCurveShape(currentShape);
}
}
2011-01-30 14:34:42 +00:00
}
if (verticalRotating && verticalTimeLine.currentValue() == 1.0) {
verticalTimeLine.setCurrentTime(0);
verticalRotating = false;
manualVerticalAngle = 0.0;
// more rotations?
2011-01-30 14:34:42 +00:00
if (!verticalRotations.empty()) {
verticalRotationDirection = verticalRotations.dequeue();
verticalRotating = true;
2011-01-30 14:34:42 +00:00
if (verticalRotationDirection == Upwards) {
if (verticalPosition == Normal)
verticalPosition = Up;
2011-01-30 14:34:42 +00:00
if (verticalPosition == Down)
verticalPosition = Normal;
2011-01-30 14:34:42 +00:00
}
if (verticalRotationDirection == Downwards) {
if (verticalPosition == Normal)
verticalPosition = Down;
2011-01-30 14:34:42 +00:00
if (verticalPosition == Up)
verticalPosition = Normal;
}
}
2011-01-30 14:34:42 +00:00
}
effects->addRepaintFull();
return; // rotation has to end before cube is closed
2011-01-30 14:34:42 +00:00
}
if (schedule_close) {
schedule_close = false;
stop = true;
effects->addRepaintFull();
}
}
2011-01-30 14:34:42 +00:00
}
2011-01-30 14:34:42 +00:00
void CubeEffect::prePaintWindow(EffectWindow* w, WindowPrePaintData& data, int time)
{
if (activated) {
if (cube_painting) {
if (mode == Cylinder || mode == Sphere) {
int leftDesktop = frontDesktop - 1;
int rightDesktop = frontDesktop + 1;
2011-01-30 14:34:42 +00:00
if (leftDesktop == 0)
leftDesktop = effects->numberOfDesktops();
2011-01-30 14:34:42 +00:00
if (rightDesktop > effects->numberOfDesktops())
rightDesktop = 1;
2011-01-30 14:34:42 +00:00
if (painting_desktop == frontDesktop)
data.quads = data.quads.makeGrid(40);
else if (painting_desktop == leftDesktop || painting_desktop == rightDesktop)
data.quads = data.quads.makeGrid(100);
else
2011-01-30 14:34:42 +00:00
data.quads = data.quads.makeGrid(250);
}
if (w->isOnDesktop(painting_desktop)) {
QRect rect = effects->clientArea(FullArea, activeScreen, painting_desktop);
if (w->x() < rect.x()) {
data.quads = data.quads.splitAtX(-w->x());
}
2011-01-30 14:34:42 +00:00
if (w->x() + w->width() > rect.x() + rect.width()) {
data.quads = data.quads.splitAtX(rect.width() - w->x());
}
2011-01-30 14:34:42 +00:00
if (w->y() < rect.y()) {
data.quads = data.quads.splitAtY(-w->y());
}
if (w->y() + w->height() > rect.y() + rect.height()) {
data.quads = data.quads.splitAtY(rect.height() - w->y());
}
if (useZOrdering && !w->isDesktop() && !w->isDock() && !w->isOnAllDesktops())
data.setTransformed();
w->enablePainting(EffectWindow::PAINT_DISABLED_BY_DESKTOP);
} else {
// check for windows belonging to the previous desktop
2011-01-30 14:34:42 +00:00
int prev_desktop = painting_desktop - 1;
if (prev_desktop == 0)
prev_desktop = effects->numberOfDesktops();
2011-01-30 14:34:42 +00:00
if (w->isOnDesktop(prev_desktop) && mode == Cube && !useZOrdering) {
QRect rect = effects->clientArea(FullArea, activeScreen, prev_desktop);
if (w->x() + w->width() > rect.x() + rect.width()) {
w->enablePainting(EffectWindow::PAINT_DISABLED_BY_DESKTOP);
data.quads = data.quads.splitAtX(rect.width() - w->x());
if (w->y() < rect.y()) {
data.quads = data.quads.splitAtY(-w->y());
}
if (w->y() + w->height() > rect.y() + rect.height()) {
data.quads = data.quads.splitAtY(rect.height() - w->y());
}
data.setTransformed();
2011-01-30 14:34:42 +00:00
effects->prePaintWindow(w, data, time);
return;
}
2011-01-30 14:34:42 +00:00
}
// check for windows belonging to the next desktop
2011-01-30 14:34:42 +00:00
int next_desktop = painting_desktop + 1;
if (next_desktop > effects->numberOfDesktops())
next_desktop = 1;
2011-01-30 14:34:42 +00:00
if (w->isOnDesktop(next_desktop) && mode == Cube && !useZOrdering) {
QRect rect = effects->clientArea(FullArea, activeScreen, next_desktop);
if (w->x() < rect.x()) {
w->enablePainting(EffectWindow::PAINT_DISABLED_BY_DESKTOP);
data.quads = data.quads.splitAtX(-w->x());
if (w->y() < rect.y()) {
data.quads = data.quads.splitAtY(-w->y());
}
if (w->y() + w->height() > rect.y() + rect.height()) {
data.quads = data.quads.splitAtY(rect.height() - w->y());
}
data.setTransformed();
2011-01-30 14:34:42 +00:00
effects->prePaintWindow(w, data, time);
return;
}
}
2011-01-30 14:34:42 +00:00
w->disablePainting(EffectWindow::PAINT_DISABLED_BY_DESKTOP);
}
}
}
2011-01-30 14:34:42 +00:00
effects->prePaintWindow(w, data, time);
}
2011-01-30 14:34:42 +00:00
void CubeEffect::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data)
{
ShaderManager *shaderManager = ShaderManager::instance();
GLShader *shader = NULL;
QMatrix4x4 origMatrix;
2011-01-30 14:34:42 +00:00
if (activated && cube_painting) {
region= infiniteRegion(); // we need to explicitly prevent any clipping, bug #325432
shader = shaderManager->pushShader(ShaderManager::GenericShader);
//qCDebug(KWINEFFECTS) << w->caption();
float opacity = cubeOpacity;
2011-01-30 14:34:42 +00:00
if (start) {
opacity = 1.0 - (1.0 - opacity) * timeLine.currentValue();
2011-01-30 14:34:42 +00:00
if (reflectionPainting)
opacity = 0.5 + (cubeOpacity - 0.5) * timeLine.currentValue();
// fade in windows belonging to different desktops
2011-01-30 14:34:42 +00:00
if (painting_desktop == effects->currentDesktop() && (!w->isOnDesktop(painting_desktop)))
opacity = timeLine.currentValue() * cubeOpacity;
2011-01-30 14:34:42 +00:00
}
if (stop) {
opacity = 1.0 - (1.0 - opacity) * (1.0 - timeLine.currentValue());
2011-01-30 14:34:42 +00:00
if (reflectionPainting)
opacity = 0.5 + (cubeOpacity - 0.5) * (1.0 - timeLine.currentValue());
// fade out windows belonging to different desktops
2011-01-30 14:34:42 +00:00
if (painting_desktop == effects->currentDesktop() && (!w->isOnDesktop(painting_desktop)))
opacity = cubeOpacity * (1.0 - timeLine.currentValue());
2011-01-30 14:34:42 +00:00
}
// z-Ordering
2011-01-30 14:34:42 +00:00
if (!w->isDesktop() && !w->isDock() && useZOrdering && !w->isOnAllDesktops()) {
float zOrdering = (effects->stackingOrder().indexOf(w) + 1) * zOrderingFactor;
if (start)
zOrdering *= timeLine.currentValue();
2011-01-30 14:34:42 +00:00
if (stop)
zOrdering *= (1.0 - timeLine.currentValue());
data.translate(0.0, 0.0, zOrdering);
2011-01-30 14:34:42 +00:00
}
// check for windows belonging to the previous desktop
2011-01-30 14:34:42 +00:00
int prev_desktop = painting_desktop - 1;
if (prev_desktop == 0)
prev_desktop = effects->numberOfDesktops();
2011-01-30 14:34:42 +00:00
int next_desktop = painting_desktop + 1;
if (next_desktop > effects->numberOfDesktops())
next_desktop = 1;
2011-01-30 14:34:42 +00:00
if (w->isOnDesktop(prev_desktop) && (mask & PAINT_WINDOW_TRANSFORMED)) {
QRect rect = effects->clientArea(FullArea, activeScreen, prev_desktop);
WindowQuadList new_quads;
2011-01-30 14:34:42 +00:00
foreach (const WindowQuad & quad, data.quads) {
if (quad.right() > rect.width() - w->x()) {
new_quads.append(quad);
}
2011-01-30 14:34:42 +00:00
}
data.quads = new_quads;
data.setXTranslation(-rect.width());
2011-01-30 14:34:42 +00:00
}
if (w->isOnDesktop(next_desktop) && (mask & PAINT_WINDOW_TRANSFORMED)) {
QRect rect = effects->clientArea(FullArea, activeScreen, next_desktop);
WindowQuadList new_quads;
2011-01-30 14:34:42 +00:00
foreach (const WindowQuad & quad, data.quads) {
if (w->x() + quad.right() <= rect.x()) {
new_quads.append(quad);
}
2011-01-30 14:34:42 +00:00
}
data.quads = new_quads;
data.setXTranslation(rect.width());
2011-01-30 14:34:42 +00:00
}
QRect rect = effects->clientArea(FullArea, activeScreen, painting_desktop);
2011-01-30 14:34:42 +00:00
if (start || stop) {
// we have to change opacity values for fade in/out of windows which are shown on front-desktop
2011-01-30 14:34:42 +00:00
if (prev_desktop == effects->currentDesktop() && w->x() < rect.x()) {
if (start)
opacity = timeLine.currentValue() * cubeOpacity;
2011-01-30 14:34:42 +00:00
if (stop)
opacity = cubeOpacity * (1.0 - timeLine.currentValue());
2011-01-30 14:34:42 +00:00
}
if (next_desktop == effects->currentDesktop() && w->x() + w->width() > rect.x() + rect.width()) {
if (start)
opacity = timeLine.currentValue() * cubeOpacity;
2011-01-30 14:34:42 +00:00
if (stop)
opacity = cubeOpacity * (1.0 - timeLine.currentValue());
}
2011-01-30 14:34:42 +00:00
}
// HACK set opacity to 0.99 in case of fully opaque to ensure that windows are painted in correct sequence
// bug #173214
2011-01-30 14:34:42 +00:00
if (opacity > 0.99f)
opacity = 0.99f;
2011-01-30 14:34:42 +00:00
if (opacityDesktopOnly && !w->isDesktop())
opacity = 0.99f;
data.multiplyOpacity(opacity);
2011-01-30 14:34:42 +00:00
if (w->isOnDesktop(painting_desktop) && w->x() < rect.x()) {
WindowQuadList new_quads;
2011-01-30 14:34:42 +00:00
foreach (const WindowQuad & quad, data.quads) {
if (quad.right() > -w->x()) {
new_quads.append(quad);
}
}
2011-01-30 14:34:42 +00:00
data.quads = new_quads;
}
if (w->isOnDesktop(painting_desktop) && w->x() + w->width() > rect.x() + rect.width()) {
WindowQuadList new_quads;
2011-01-30 14:34:42 +00:00
foreach (const WindowQuad & quad, data.quads) {
if (quad.right() <= rect.width() - w->x()) {
new_quads.append(quad);
}
}
2011-01-30 14:34:42 +00:00
data.quads = new_quads;
}
if (w->y() < rect.y()) {
WindowQuadList new_quads;
2011-01-30 14:34:42 +00:00
foreach (const WindowQuad & quad, data.quads) {
if (quad.bottom() > -w->y()) {
new_quads.append(quad);
}
}
2011-01-30 14:34:42 +00:00
data.quads = new_quads;
}
if (w->y() + w->height() > rect.y() + rect.height()) {
WindowQuadList new_quads;
2011-01-30 14:34:42 +00:00
foreach (const WindowQuad & quad, data.quads) {
if (quad.bottom() <= rect.height() - w->y()) {
new_quads.append(quad);
}
}
2011-01-30 14:34:42 +00:00
data.quads = new_quads;
}
origMatrix = shader->getUniformMatrix4x4("screenTransformation");
GLShader *currentShader = shader;
if (mode == Cylinder) {
shaderManager->pushShader(cylinderShader);
cylinderShader->setUniform("xCoord", (float)w->x());
cylinderShader->setUniform("cubeAngle", (effects->numberOfDesktops() - 2) / (float)effects->numberOfDesktops() * 90.0f);
float factor = 0.0f;
if (start)
factor = 1.0f - timeLine.currentValue();
if (stop)
factor = timeLine.currentValue();
cylinderShader->setUniform("timeLine", factor);
currentShader = cylinderShader;
}
if (mode == Sphere) {
shaderManager->pushShader(sphereShader);
sphereShader->setUniform("u_offset", QVector2D(w->x(), w->y()));
sphereShader->setUniform("cubeAngle", (effects->numberOfDesktops() - 2) / (float)effects->numberOfDesktops() * 90.0f);
float factor = 0.0f;
if (start)
factor = 1.0f - timeLine.currentValue();
if (stop)
factor = timeLine.currentValue();
sphereShader->setUniform("timeLine", factor);
currentShader = sphereShader;
}
if (reflectionPainting) {
currentShader->setUniform(GLShader::ScreenTransformation, m_reflectionMatrix * m_rotationMatrix * origMatrix);
} else {
currentShader->setUniform(GLShader::ScreenTransformation, m_rotationMatrix*origMatrix);
}
data.shader = currentShader;
2011-01-30 14:34:42 +00:00
}
effects->paintWindow(w, mask, region, data);
if (activated && cube_painting) {
if (mode == Cylinder || mode == Sphere) {
shaderManager->popShader();
} else {
shader->setUniform(GLShader::ScreenTransformation, origMatrix);
}
shaderManager->popShader();
2011-01-30 14:34:42 +00:00
if (w->isDesktop() && effects->numScreens() > 1 && paintCaps) {
QRect rect = effects->clientArea(FullArea, activeScreen, painting_desktop);
QRegion paint = QRegion(rect);
for (int i = 0; i < effects->numScreens(); i++) {
if (i == w->screen())
continue;
2011-01-30 14:34:42 +00:00
paint = paint.subtracted(QRegion(effects->clientArea(ScreenArea, i, painting_desktop)));
}
paint = paint.subtracted(QRegion(w->geometry()));
// in case of free area in multiscreen setup fill it with cap color
2011-01-30 14:34:42 +00:00
if (!paint.isEmpty()) {
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
QVector<float> verts;
float quadSize = 0.0f;
2011-01-30 14:34:42 +00:00
int leftDesktop = frontDesktop - 1;
int rightDesktop = frontDesktop + 1;
2011-01-30 14:34:42 +00:00
if (leftDesktop == 0)
leftDesktop = effects->numberOfDesktops();
2011-01-30 14:34:42 +00:00
if (rightDesktop > effects->numberOfDesktops())
rightDesktop = 1;
2011-01-30 14:34:42 +00:00
if (painting_desktop == frontDesktop)
quadSize = 100.0f;
2011-01-30 14:34:42 +00:00
else if (painting_desktop == leftDesktop || painting_desktop == rightDesktop)
quadSize = 150.0f;
else
quadSize = 250.0f;
2011-01-30 14:34:42 +00:00
foreach (const QRect & paintRect, paint.rects()) {
for (int i = 0; i <= (paintRect.height() / quadSize); i++) {
for (int j = 0; j <= (paintRect.width() / quadSize); j++) {
verts << qMin(paintRect.x() + (j + 1)*quadSize, (float)paintRect.x() + paintRect.width()) << paintRect.y() + i*quadSize;
verts << paintRect.x() + j*quadSize << paintRect.y() + i*quadSize;
verts << paintRect.x() + j*quadSize << qMin(paintRect.y() + (i + 1)*quadSize, (float)paintRect.y() + paintRect.height());
verts << paintRect.x() + j*quadSize << qMin(paintRect.y() + (i + 1)*quadSize, (float)paintRect.y() + paintRect.height());
verts << qMin(paintRect.x() + (j + 1)*quadSize, (float)paintRect.x() + paintRect.width()) << qMin(paintRect.y() + (i + 1)*quadSize, (float)paintRect.y() + paintRect.height());
verts << qMin(paintRect.x() + (j + 1)*quadSize, (float)paintRect.x() + paintRect.width()) << paintRect.y() + i*quadSize;
}
}
2011-01-30 14:34:42 +00:00
}
bool capShader = false;
if (effects->compositingType() == OpenGL2Compositing && m_capShader && m_capShader->isValid()) {
capShader = true;
ShaderManager::instance()->pushShader(m_capShader);
m_capShader->setUniform("u_mirror", 0);
m_capShader->setUniform("u_untextured", 1);
if (reflectionPainting) {
m_capShader->setUniform(GLShader::ScreenTransformation, m_reflectionMatrix * m_rotationMatrix * origMatrix);
} else {
m_capShader->setUniform(GLShader::ScreenTransformation, m_rotationMatrix * origMatrix);
}
m_capShader->setUniform(GLShader::WindowTransformation, QMatrix4x4());
}
GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer();
vbo->reset();
QColor color = capColor;
capColor.setAlphaF(cubeOpacity);
vbo->setColor(color);
2011-01-30 14:34:42 +00:00
vbo->setData(verts.size() / 2, 2, verts.constData(), NULL);
if (!capShader || mode == Cube) {
// TODO: use sphere and cylinder shaders
vbo->render(GL_TRIANGLES);
}
if (capShader) {
ShaderManager::instance()->popShader();
}
2011-01-30 14:34:42 +00:00
glDisable(GL_BLEND);
}
2011-01-30 14:34:42 +00:00
}
}
2011-01-30 14:34:42 +00:00
}
2011-01-30 14:34:42 +00:00
bool CubeEffect::borderActivated(ElectricBorder border)
{
if (!borderActivate.contains(border) &&
!borderActivateCylinder.contains(border) &&
!borderActivateSphere.contains(border))
return false;
2011-01-30 14:34:42 +00:00
if (effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this)
return false;
2011-01-30 14:34:42 +00:00
if (borderActivate.contains(border)) {
if (!activated || (activated && mode == Cube))
toggleCube();
else
return false;
2011-01-30 14:34:42 +00:00
}
if (borderActivateCylinder.contains(border)) {
if (!activated || (activated && mode == Cylinder))
toggleCylinder();
else
return false;
2011-01-30 14:34:42 +00:00
}
if (borderActivateSphere.contains(border)) {
if (!activated || (activated && mode == Sphere))
toggleSphere();
else
return false;
}
2011-01-30 14:34:42 +00:00
return true;
}
void CubeEffect::toggleCube()
2011-01-30 14:34:42 +00:00
{
qCDebug(KWINEFFECTS) << "toggle cube";
2011-01-30 14:34:42 +00:00
toggle(Cube);
}
void CubeEffect::toggleCylinder()
2011-01-30 14:34:42 +00:00
{
qCDebug(KWINEFFECTS) << "toggle cylinder";
2011-01-30 14:34:42 +00:00
if (!useShaders)
useShaders = loadShader();
2011-01-30 14:34:42 +00:00
if (useShaders)
toggle(Cylinder);
else
qCritical() << "Sorry shaders are not available - cannot activate Cylinder";
2011-01-30 14:34:42 +00:00
}
void CubeEffect::toggleSphere()
2011-01-30 14:34:42 +00:00
{
qCDebug(KWINEFFECTS) << "toggle sphere";
2011-01-30 14:34:42 +00:00
if (!useShaders)
useShaders = loadShader();
2011-01-30 14:34:42 +00:00
if (useShaders)
toggle(Sphere);
else
qCritical() << "Sorry shaders are not available - cannot activate Sphere";
2011-01-30 14:34:42 +00:00
}
2011-01-30 14:34:42 +00:00
void CubeEffect::toggle(CubeMode newMode)
{
if ((effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this) ||
effects->numberOfDesktops() < 2)
return;
2011-01-30 14:34:42 +00:00
if (!activated) {
mode = newMode;
2011-01-30 14:34:42 +00:00
setActive(true);
} else {
setActive(false);
}
2011-01-30 14:34:42 +00:00
}
2011-01-30 14:34:42 +00:00
void CubeEffect::grabbedKeyboardEvent(QKeyEvent* e)
{
if (stop)
return;
// taken from desktopgrid.cpp
2011-01-30 14:34:42 +00:00
if (e->type() == QEvent::KeyPress) {
// check for global shortcuts
// HACK: keyboard grab disables the global shortcuts so we have to check for global shortcut (bug 156155)
2011-01-30 14:34:42 +00:00
if (mode == Cube && cubeShortcut.contains(e->key() + e->modifiers())) {
toggleCube();
return;
2011-01-30 14:34:42 +00:00
}
if (mode == Cylinder && cylinderShortcut.contains(e->key() + e->modifiers())) {
toggleCylinder();
return;
2011-01-30 14:34:42 +00:00
}
if (mode == Sphere && sphereShortcut.contains(e->key() + e->modifiers())) {
toggleSphere();
return;
2011-01-30 14:34:42 +00:00
}
int desktop = -1;
// switch by F<number> or just <number>
2011-01-30 14:34:42 +00:00
if (e->key() >= Qt::Key_F1 && e->key() <= Qt::Key_F35)
desktop = e->key() - Qt::Key_F1 + 1;
2011-01-30 14:34:42 +00:00
else if (e->key() >= Qt::Key_0 && e->key() <= Qt::Key_9)
desktop = e->key() == Qt::Key_0 ? 10 : e->key() - Qt::Key_0;
2011-01-30 14:34:42 +00:00
if (desktop != -1) {
if (desktop <= effects->numberOfDesktops()) {
// we have to rotate to chosen desktop
// and end effect when rotation finished
2011-01-30 14:34:42 +00:00
rotateToDesktop(desktop);
setActive(false);
}
2011-01-30 14:34:42 +00:00
return;
}
switch(e->key()) {
// wrap only on autorepeat
case Qt::Key_Left:
// rotate to previous desktop
qCDebug(KWINEFFECTS) << "left";
2011-01-30 14:34:42 +00:00
if (!rotating && !start) {
rotating = true;
if (invertKeys)
rotationDirection = Right;
else
2011-01-30 14:34:42 +00:00
rotationDirection = Left;
} else {
if (rotations.count() < effects->numberOfDesktops()) {
if (invertKeys)
rotations.enqueue(Right);
else
2011-01-30 14:34:42 +00:00
rotations.enqueue(Left);
}
}
break;
case Qt::Key_Right:
// rotate to next desktop
qCDebug(KWINEFFECTS) << "right";
2011-01-30 14:34:42 +00:00
if (!rotating && !start) {
rotating = true;
if (invertKeys)
rotationDirection = Left;
else
2011-01-30 14:34:42 +00:00
rotationDirection = Right;
} else {
if (rotations.count() < effects->numberOfDesktops()) {
if (invertKeys)
rotations.enqueue(Left);
else
rotations.enqueue(Right);
}
}
break;
case Qt::Key_Up:
qCDebug(KWINEFFECTS) << "up";
2011-01-30 14:34:42 +00:00
if (invertKeys) {
if (verticalPosition != Down) {
if (!verticalRotating) {
verticalRotating = true;
verticalRotationDirection = Downwards;
2011-01-30 14:34:42 +00:00
if (verticalPosition == Normal)
verticalPosition = Down;
if (verticalPosition == Up)
verticalPosition = Normal;
} else {
verticalRotations.enqueue(Downwards);
}
2011-01-30 14:34:42 +00:00
} else if (manualVerticalAngle > 0.0 && !verticalRotating) {
// rotate to down position from the manual position
verticalRotating = true;
verticalRotationDirection = Downwards;
verticalPosition = Down;
manualVerticalAngle -= 90.0;
}
} else {
if (verticalPosition != Up) {
if (!verticalRotating) {
verticalRotating = true;
verticalRotationDirection = Upwards;
2011-01-30 14:34:42 +00:00
if (verticalPosition == Normal)
verticalPosition = Up;
if (verticalPosition == Down)
verticalPosition = Normal;
} else {
verticalRotations.enqueue(Upwards);
}
2011-01-30 14:34:42 +00:00
} else if (manualVerticalAngle < 0.0 && !verticalRotating) {
// rotate to up position from the manual position
verticalRotating = true;
verticalRotationDirection = Upwards;
verticalPosition = Up;
manualVerticalAngle += 90.0;
}
}
break;
case Qt::Key_Down:
qCDebug(KWINEFFECTS) << "down";
2011-01-30 14:34:42 +00:00
if (invertKeys) {
if (verticalPosition != Up) {
if (!verticalRotating) {
verticalRotating = true;
verticalRotationDirection = Upwards;
2011-01-30 14:34:42 +00:00
if (verticalPosition == Normal)
verticalPosition = Up;
if (verticalPosition == Down)
verticalPosition = Normal;
} else {
verticalRotations.enqueue(Upwards);
}
2011-01-30 14:34:42 +00:00
} else if (manualVerticalAngle < 0.0 && !verticalRotating) {
// rotate to up position from the manual position
verticalRotating = true;
verticalRotationDirection = Upwards;
verticalPosition = Up;
manualVerticalAngle += 90.0;
}
} else {
if (verticalPosition != Down) {
if (!verticalRotating) {
verticalRotating = true;
verticalRotationDirection = Downwards;
2011-01-30 14:34:42 +00:00
if (verticalPosition == Normal)
verticalPosition = Down;
if (verticalPosition == Up)
verticalPosition = Normal;
} else {
verticalRotations.enqueue(Downwards);
}
2011-01-30 14:34:42 +00:00
} else if (manualVerticalAngle > 0.0 && !verticalRotating) {
// rotate to down position from the manual position
verticalRotating = true;
verticalRotationDirection = Downwards;
verticalPosition = Down;
manualVerticalAngle -= 90.0;
}
}
2011-01-30 14:34:42 +00:00
break;
case Qt::Key_Escape:
rotateToDesktop(effects->currentDesktop());
setActive(false);
return;
case Qt::Key_Enter:
case Qt::Key_Return:
case Qt::Key_Space:
setActive(false);
return;
case Qt::Key_Plus:
zoom -= 10.0;
zoom = qMax(-zPosition, zoom);
rotateCube();
break;
case Qt::Key_Minus:
zoom += 10.0f;
rotateCube();
break;
default:
break;
}
2011-01-30 14:34:42 +00:00
effects->addRepaintFull();
}
2011-01-30 14:34:42 +00:00
}
2011-01-30 14:34:42 +00:00
void CubeEffect::rotateToDesktop(int desktop)
{
int tempFrontDesktop = frontDesktop;
2011-01-30 14:34:42 +00:00
if (!rotations.empty()) {
// all scheduled rotations will be removed as a speed up
rotations.clear();
2011-01-30 14:34:42 +00:00
}
if (rotating && !desktopChangedWhileRotating) {
// front desktop will change during the actual rotation - this has to be considered
2011-01-30 14:34:42 +00:00
if (rotationDirection == Left) {
tempFrontDesktop++;
2011-01-30 14:34:42 +00:00
} else if (rotationDirection == Right) {
tempFrontDesktop--;
2011-01-30 14:34:42 +00:00
}
if (tempFrontDesktop > effects->numberOfDesktops())
tempFrontDesktop = 1;
2011-01-30 14:34:42 +00:00
else if (tempFrontDesktop == 0)
tempFrontDesktop = effects->numberOfDesktops();
2011-01-30 14:34:42 +00:00
}
// find the fastest rotation path from tempFrontDesktop to desktop
int rightRotations = tempFrontDesktop - desktop;
2011-01-30 14:34:42 +00:00
if (rightRotations < 0)
rightRotations += effects->numberOfDesktops();
int leftRotations = desktop - tempFrontDesktop;
2011-01-30 14:34:42 +00:00
if (leftRotations < 0)
leftRotations += effects->numberOfDesktops();
2011-01-30 14:34:42 +00:00
if (leftRotations <= rightRotations) {
for (int i = 0; i < leftRotations; i++) {
rotations.enqueue(Left);
}
2011-01-30 14:34:42 +00:00
} else {
for (int i = 0; i < rightRotations; i++) {
rotations.enqueue(Right);
}
2011-01-30 14:34:42 +00:00
}
if (!start && !rotating && !rotations.empty()) {
rotating = true;
rotationDirection = rotations.dequeue();
2011-01-30 14:34:42 +00:00
}
// change timeline curve if more rotations are following
2011-01-30 14:34:42 +00:00
if (!rotations.empty()) {
currentShape = QTimeLine::EaseInCurve;
2011-01-30 14:34:42 +00:00
timeLine.setCurveShape(currentShape);
}
2011-01-30 14:34:42 +00:00
}
2011-01-30 14:34:42 +00:00
void CubeEffect::setActive(bool active)
{
foreach (CubeInsideEffect * inside, m_cubeInsideEffects) {
inside->setActive(true);
}
if (active) {
QString capPath = CubeConfig::capPath();
if (texturedCaps && !capTexture && !capPath.isEmpty()) {
QFutureWatcher<QImage> *watcher = new QFutureWatcher<QImage>(this);
connect(watcher, SIGNAL(finished()), SLOT(slotCubeCapLoaded()));
watcher->setFuture(QtConcurrent::run(this, &CubeEffect::loadCubeCap, capPath));
}
QString wallpaperPath = CubeConfig::wallpaper().toLocalFile();
if (!wallpaper && !wallpaperPath.isEmpty()) {
QFutureWatcher<QImage> *watcher = new QFutureWatcher<QImage>(this);
connect(watcher, SIGNAL(finished()), SLOT(slotWallPaperLoaded()));
watcher->setFuture(QtConcurrent::run(this, &CubeEffect::loadWallPaper, wallpaperPath));
}
activated = true;
activeScreen = effects->activeScreen();
2011-01-30 14:34:42 +00:00
keyboard_grab = effects->grabKeyboard(this);
effects->startMouseInterception(this, Qt::OpenHandCursor);
frontDesktop = effects->currentDesktop();
zoom = 0.0;
2011-01-30 14:34:42 +00:00
zOrderingFactor = zPosition / (effects->stackingOrder().count() - 1);
start = true;
2011-01-30 14:34:42 +00:00
effects->setActiveFullScreenEffect(this);
qCDebug(KWINEFFECTS) << "Cube is activated";
verticalPosition = Normal;
verticalRotating = false;
manualAngle = 0.0;
manualVerticalAngle = 0.0;
desktopChangedWhileRotating = false;
2011-01-30 14:34:42 +00:00
if (reflection) {
QRect rect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop());
#ifndef KWIN_HAVE_OPENGLES
// clip parts above the reflection area
double eqn[4] = {0.0, 1.0, 0.0, 0.0};
glPushMatrix();
2011-01-30 14:34:42 +00:00
glTranslatef(0.0, rect.height(), 0.0);
glClipPlane(GL_CLIP_PLANE0, eqn);
glPopMatrix();
#endif
2011-01-30 14:34:42 +00:00
float temporaryCoeff = float(rect.width()) / tan(M_PI / float(effects->numberOfDesktops()));
mAddedHeightCoeff1 = sqrt(float(rect.height()) * float(rect.height()) + temporaryCoeff * temporaryCoeff);
mAddedHeightCoeff2 = sqrt(float(rect.height()) * float(rect.height()) + float(rect.width()) * float(rect.width()) + temporaryCoeff * temporaryCoeff);
}
m_rotationMatrix.setToIdentity();
effects->addRepaintFull();
2011-01-30 14:34:42 +00:00
} else {
schedule_close = true;
// we have to add a repaint, to start the deactivating
effects->addRepaintFull();
}
2011-01-30 14:34:42 +00:00
}
void CubeEffect::windowInputMouseEvent(QEvent* e)
2011-01-30 14:34:42 +00:00
{
if (!activated)
return;
2011-01-30 14:34:42 +00:00
if (tabBoxMode)
return;
2011-01-30 14:34:42 +00:00
if (stop)
return;
QMouseEvent *mouse = dynamic_cast< QMouseEvent* >(e);
if (!mouse)
return;
static QPoint oldpos;
static QElapsedTimer dblClckTime;
static int dblClckCounter(0);
if (mouse->type() == QEvent::MouseMove && mouse->buttons().testFlag(Qt::LeftButton)) {
const QPoint pos = mouse->pos();
QRect rect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop());
bool repaint = false;
// vertical movement only if there is not a rotation
2011-01-30 14:34:42 +00:00
if (!verticalRotating) {
// display height corresponds to 180*
int deltaY = pos.y() - oldpos.y();
2011-01-30 14:34:42 +00:00
float deltaVerticalDegrees = (float)deltaY / rect.height() * 180.0f;
if (invertMouse)
manualVerticalAngle += deltaVerticalDegrees;
else
manualVerticalAngle -= deltaVerticalDegrees;
2011-01-30 14:34:42 +00:00
if (deltaVerticalDegrees != 0.0)
repaint = true;
2011-01-30 14:34:42 +00:00
}
// horizontal movement only if there is not a rotation
2011-01-30 14:34:42 +00:00
if (!rotating) {
// display width corresponds to sum of angles of the polyhedron
int deltaX = oldpos.x() - pos.x();
2011-01-30 14:34:42 +00:00
float deltaDegrees = (float)deltaX / rect.width() * 360.0f;
if (deltaX == 0) {
if (pos.x() == 0)
deltaDegrees = 5.0f;
if (pos.x() == rect.width() - 1)
deltaDegrees = -5.0f;
2011-01-30 14:34:42 +00:00
}
if (invertMouse)
manualAngle += deltaDegrees;
else
manualAngle -= deltaDegrees;
2011-01-30 14:34:42 +00:00
if (deltaDegrees != 0.0)
repaint = true;
2011-01-30 14:34:42 +00:00
}
if (repaint) {
rotateCube();
effects->addRepaintFull();
}
oldpos = pos;
2011-01-30 14:34:42 +00:00
}
else if (mouse->type() == QEvent::MouseButtonPress && mouse->button() == Qt::LeftButton) {
oldpos = mouse->pos();
if (dblClckTime.elapsed() > QApplication::doubleClickInterval())
dblClckCounter = 0;
if (!dblClckCounter)
dblClckTime.start();
}
else if (mouse->type() == QEvent::MouseButtonRelease) {
effects->defineCursor(Qt::OpenHandCursor);
if (mouse->button() == Qt::LeftButton && ++dblClckCounter == 2) {
dblClckCounter = 0;
if (dblClckTime.elapsed() < QApplication::doubleClickInterval()) {
setActive(false);
return;
}
}
else if (mouse->button() == Qt::XButton1) {
2011-01-30 14:34:42 +00:00
if (!rotating && !start) {
rotating = true;
2011-01-30 14:34:42 +00:00
if (invertMouse)
rotationDirection = Right;
else
rotationDirection = Left;
2011-01-30 14:34:42 +00:00
} else {
if (rotations.count() < effects->numberOfDesktops()) {
if (invertMouse)
rotations.enqueue(Right);
else
2011-01-30 14:34:42 +00:00
rotations.enqueue(Left);
}
}
2011-01-30 14:34:42 +00:00
effects->addRepaintFull();
} else if (mouse->button() == Qt::XButton2) {
2011-01-30 14:34:42 +00:00
if (!rotating && !start) {
rotating = true;
2011-01-30 14:34:42 +00:00
if (invertMouse)
rotationDirection = Left;
else
rotationDirection = Right;
2011-01-30 14:34:42 +00:00
} else {
if (rotations.count() < effects->numberOfDesktops()) {
if (invertMouse)
rotations.enqueue(Left);
else
2011-01-30 14:34:42 +00:00
rotations.enqueue(Right);
}
}
2011-01-30 14:34:42 +00:00
effects->addRepaintFull();
} else if (mouse->button() == Qt::RightButton || (mouse->button() == Qt::LeftButton && closeOnMouseRelease)) {
setActive(false);
}
}
2011-01-30 14:34:42 +00:00
}
void CubeEffect::slotTabBoxAdded(int mode)
2011-01-30 14:34:42 +00:00
{
if (activated)
return;
2011-01-30 14:34:42 +00:00
if (effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this)
return;
2011-01-30 14:34:42 +00:00
if (useForTabBox && mode == TabBoxDesktopListMode) {
effects->refTabBox();
tabBoxMode = true;
2011-01-30 14:34:42 +00:00
setActive(true);
rotateToDesktop(effects->currentTabBoxDesktop());
}
2011-01-30 14:34:42 +00:00
}
void CubeEffect::slotTabBoxUpdated()
2011-01-30 14:34:42 +00:00
{
if (activated) {
rotateToDesktop(effects->currentTabBoxDesktop());
effects->addRepaintFull();
}
2011-01-30 14:34:42 +00:00
}
void CubeEffect::slotTabBoxClosed()
2011-01-30 14:34:42 +00:00
{
if (activated) {
effects->unrefTabBox();
tabBoxMode = false;
2011-01-30 14:34:42 +00:00
setActive(false);
}
2011-01-30 14:34:42 +00:00
}
void CubeEffect::globalShortcutChanged(QAction *action, const QKeySequence &seq)
2011-01-30 14:34:42 +00:00
{
if (action->objectName() == QStringLiteral("Cube")) {
cubeShortcut.clear();
cubeShortcut.append(seq);
} else if (action->objectName() == QStringLiteral("Cylinder")) {
cylinderShortcut.clear();
cylinderShortcut.append(seq);
} else if (action->objectName() == QStringLiteral("Sphere")) {
sphereShortcut.clear();
sphereShortcut.append(seq);
}
2011-01-30 14:34:42 +00:00
}
void* CubeEffect::proxy()
2011-01-30 14:34:42 +00:00
{
return &m_proxy;
2011-01-30 14:34:42 +00:00
}
void CubeEffect::registerCubeInsideEffect(CubeInsideEffect* effect)
2011-01-30 14:34:42 +00:00
{
m_cubeInsideEffects.append(effect);
}
void CubeEffect::unregisterCubeInsideEffect(CubeInsideEffect* effect)
2011-01-30 14:34:42 +00:00
{
m_cubeInsideEffects.removeAll(effect);
}
bool CubeEffect::isActive() const
{
return activated;
}
} // namespace