diff --git a/effects/cube/cube.cpp b/effects/cube/cube.cpp index 3e924511d..3c7aad0e2 100644 --- a/effects/cube/cube.cpp +++ b/effects/cube/cube.cpp @@ -1,1921 +1,1736 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2008 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "cube.h" // KConfigSkeleton #include "cubeconfig.h" #include "cube_inside.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace KWin { CubeEffect::CubeEffect() : 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) , 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) - , 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_cubeCapBuffer(NULL) , m_proxy(this) , m_cubeAction(new QAction(this)) , m_cylinderAction(new QAction(this)) , m_sphereAction(new QAction(this)) { initConfig(); desktopNameFont.setBold(true); desktopNameFont.setPointSize(14); if (effects->compositingType() == OpenGL2Compositing) { m_reflectionShader = ShaderManager::instance()->generateShaderFromResources(ShaderTrait::MapTexture, QString(), QStringLiteral("cube-reflection.glsl")); m_capShader = ShaderManager::instance()->generateShaderFromResources(ShaderTrait::MapTexture, QString(), QStringLiteral("cube-cap.glsl")); } 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())); reconfigure(ReconfigureAll); } bool CubeEffect::supported() { return effects->isOpenGLCompositing(); } void CubeEffect::reconfigure(ReconfigureFlags) { CubeConfig::self()->read(); foreach (ElectricBorder border, borderActivate) { effects->unreserveElectricBorder(border, this); } foreach (ElectricBorder border, borderActivateCylinder) { effects->unreserveElectricBorder(border, this); } foreach (ElectricBorder border, borderActivateSphere) { effects->unreserveElectricBorder(border, this); } borderActivate.clear(); borderActivateCylinder.clear(); borderActivateSphere.clear(); QList borderList = QList(); borderList.append(int(ElectricNone)); borderList = CubeConfig::borderActivate(); foreach (int i, borderList) { borderActivate.append(ElectricBorder(i)); effects->reserveElectricBorder(ElectricBorder(i), this); } borderList.clear(); borderList.append(int(ElectricNone)); borderList = CubeConfig::borderActivateCylinder(); foreach (int i, borderList) { borderActivateCylinder.append(ElectricBorder(i)); effects->reserveElectricBorder(ElectricBorder(i), this); } borderList.clear(); borderList.append(int(ElectricNone)); borderList = CubeConfig::borderActivateSphere(); foreach (int i, borderList) { borderActivateSphere.append(ElectricBorder(i)); effects->reserveElectricBorder(ElectricBorder(i), this); } 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); timeLine.setDuration(rotationDuration); verticalTimeLine.setCurveShape(QTimeLine::EaseInOutCurve); verticalTimeLine.setDuration(rotationDuration); // do not connect the shortcut if we use cylinder or sphere if (!shortcutsRegistered) { QAction* cubeAction = m_cubeAction; cubeAction->setObjectName(QStringLiteral("Cube")); cubeAction->setText(i18n("Desktop Cube")); KGlobalAccel::self()->setDefaultShortcut(cubeAction, QList() << Qt::CTRL + Qt::Key_F11); KGlobalAccel::self()->setShortcut(cubeAction, QList() << 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 = m_cylinderAction; cylinderAction->setObjectName(QStringLiteral("Cylinder")); cylinderAction->setText(i18n("Desktop Cylinder")); KGlobalAccel::self()->setShortcut(cylinderAction, QList()); effects->registerGlobalShortcut(QKeySequence(), cylinderAction); cylinderShortcut = KGlobalAccel::self()->shortcut(cylinderAction); QAction* sphereAction = m_sphereAction; sphereAction->setObjectName(QStringLiteral("Sphere")); sphereAction->setText(i18n("Desktop Sphere")); KGlobalAccel::self()->setShortcut(sphereAction, QList()); sphereShortcut = KGlobalAccel::self()->shortcut(sphereAction); effects->registerGlobalShortcut(QKeySequence(), sphereAction); 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; } // set the cap color on the shader if (m_capShader && m_capShader->isValid()) { ShaderBinder binder(m_capShader); m_capShader->setUniform(GLShader::Color, capColor); } // touch borders const QVector relevantBorders{ElectricLeft, ElectricTop, ElectricRight, ElectricBottom}; for (auto e : relevantBorders) { effects->unregisterTouchBorder(e, m_cubeAction); effects->unregisterTouchBorder(e, m_sphereAction); effects->unregisterTouchBorder(e, m_cylinderAction); } auto touchEdge = [&relevantBorders] (const QList touchBorders, QAction *action) { for (int i : touchBorders) { if (!relevantBorders.contains(ElectricBorder(i))) { continue; } effects->registerTouchBorder(ElectricBorder(i), action); } }; touchEdge(CubeConfig::touchBorderActivate(), m_cubeAction); touchEdge(CubeConfig::touchBorderActivateCylinder(), m_cylinderAction); touchEdge(CubeConfig::touchBorderActivateSphere(), m_sphereAction); } CubeEffect::~CubeEffect() { delete wallpaper; delete capTexture; delete cylinderShader; delete sphereShader; delete desktopNameFrame; delete m_reflectionShader; delete m_capShader; delete m_cubeCapBuffer; } QImage CubeEffect::loadCubeCap(const QString &capPath) { if (!texturedCaps) { return QImage(); } return QImage(capPath); } void CubeEffect::slotCubeCapLoaded() { QFutureWatcher *watcher = dynamic_cast*>(sender()); if (!watcher) { // not invoked from future watcher return; } QImage img = watcher->result(); if (!img.isNull()) { effects->makeOpenGLContextCurrent(); capTexture = new GLTexture(img); capTexture->setFilter(GL_LINEAR); if (!GLPlatform::instance()->isGLES()) { capTexture->setWrapMode(GL_CLAMP_TO_BORDER); } // 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 *watcher = dynamic_cast*>(sender()); if (!watcher) { // not invoked from future watcher return; } QImage img = watcher->result(); if (!img.isNull()) { effects->makeOpenGLContextCurrent(); wallpaper = new GLTexture(img); effects->addRepaintFull(); } watcher->deleteLater(); } bool CubeEffect::loadShader() { effects->makeOpenGLContextCurrent(); if (!(GLPlatform::instance()->supports(GLSL) && (effects->compositingType() == OpenGL2Compositing))) return false; cylinderShader = ShaderManager::instance()->generateShaderFromResources(ShaderTrait::MapTexture | ShaderTrait::AdjustSaturation | ShaderTrait::Modulate, QStringLiteral("cylinder.vert"), QString()); if (!cylinderShader->isValid()) { qCCritical(KWINEFFECTS) << "The cylinder shader failed to load!"; return false; } else { ShaderBinder binder(cylinderShader); cylinderShader->setUniform("sampler", 0); QRect rect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop()); cylinderShader->setUniform("width", (float)rect.width() * 0.5f); } sphereShader = ShaderManager::instance()->generateShaderFromResources(ShaderTrait::MapTexture | ShaderTrait::AdjustSaturation | ShaderTrait::Modulate, QStringLiteral("sphere.vert"), QString()); if (!sphereShader->isValid()) { qCCritical(KWINEFFECTS) << "The sphere shader failed to load!"; return false; } else { ShaderBinder binder(sphereShader); sphereShader->setUniform("sampler", 0); 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)); } return true; } +void CubeEffect::startAnimation(AnimationState state) +{ + QTimeLine::CurveShape shape; + /* If this is first and only animation -> EaseInOut + * there is more -> EaseIn + * If there was an animation before, and this is the last one -> EaseOut + * there is more -> Linear */ + if (animationState == AnimationState::None) { + shape = animations.empty() ? QTimeLine::EaseInOutCurve : QTimeLine::EaseInCurve; + } else { + shape = animations.empty() ? QTimeLine::EaseOutCurve : QTimeLine::LinearCurve; + } + timeLine.setCurveShape(shape); + timeLine.setCurrentTime(0); + startAngle = currentAngle; + startFrontDesktop = frontDesktop; + animationState = state; +} + +void CubeEffect::startVerticalAnimation(VerticalAnimationState state) +{ + /* Ignore if there is nowhere to rotate */ + if ((qFuzzyIsNull(verticalCurrentAngle - 90.0f) && state == VerticalAnimationState::Upwards) || + (qFuzzyIsNull(verticalCurrentAngle + 90.0f) && state == VerticalAnimationState::Downwards)) { + return; + } + verticalTimeLine.setCurrentTime(0); + verticalStartAngle = verticalCurrentAngle; + verticalAnimationState = state; +} + void CubeEffect::prePaintScreen(ScreenPrePaintData& data, int time) { if (activated) { data.mask |= PAINT_SCREEN_TRANSFORMED | Effect::PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS | PAINT_SCREEN_BACKGROUND_FIRST; - - if (rotating || start || stop) { - timeLine.setCurrentTime(timeLine.currentTime() + time); - rotateCube(); + if (animationState == AnimationState::None && !animations.empty()) { + startAnimation(animations.dequeue()); + } + if (verticalAnimationState == VerticalAnimationState::None && !verticalAnimations.empty()) { + startVerticalAnimation(verticalAnimations.dequeue()); } - if (verticalRotating) { - verticalTimeLine.setCurrentTime(verticalTimeLine.currentTime() + time); + + if (animationState != AnimationState::None || verticalAnimationState != VerticalAnimationState::None) { + if (animationState != AnimationState::None) { + timeLine.setCurrentTime(timeLine.currentTime() + time); + } + if (verticalAnimationState != VerticalAnimationState::None) { + verticalTimeLine.setCurrentTime(verticalTimeLine.currentTime() + time); + } rotateCube(); } } effects->prePaintScreen(data, time); } void CubeEffect::paintScreen(int mask, QRegion region, ScreenPaintData& data) { if (activated) { QRect rect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop()); // background float clearColor[4]; glGetFloatv(GL_COLOR_CLEAR_VALUE, clearColor); glClearColor(backgroundColor.redF(), backgroundColor.greenF(), backgroundColor.blueF(), 1.0); glClear(GL_COLOR_BUFFER_BIT); glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); // wallpaper if (wallpaper) { ShaderBinder binder(ShaderTrait::MapTexture); binder.shader()->setUniform(GLShader::ModelViewProjectionMatrix, data.projectionMatrix()); wallpaper->bind(); wallpaper->render(region, rect); wallpaper->unbind(); } glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // some veriables needed for painting the caps 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; - if (start) + if (animationState == AnimationState::Start) { zTranslate *= timeLine.currentValue(); - if (stop) + } else if (animationState == AnimationState::Stop) { zTranslate *= (1.0 - timeLine.currentValue()); + } // reflection - if (reflection && mode != Sphere) { + if (reflection) { // we can use a huge scale factor (needed to calculate the rearground vertices) 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 - 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); + double translate = 0.0; + if (mode == Cube) { + double addedHeight1 = -rect.height() * cos(verticalCurrentAngle*M_PI/180.0f) - rect.width() * sin(fabs(verticalCurrentAngle)*M_PI/180.0f)/tan(M_PI/effects->numberOfDesktops()); + double addedHeight2 = -rect.width() * sin(fabs(verticalCurrentAngle)*M_PI/180.0f)*tan(M_PI*0.5f/effects->numberOfDesktops()); + if (verticalCurrentAngle > 0.0f && effects->numberOfDesktops() & 1) + translate = cos(fabs(currentAngle)*effects->numberOfDesktops()*M_PI/360.0f) * addedHeight2 + addedHeight1 - float(rect.height()); + else + translate = sin(fabs(currentAngle)*effects->numberOfDesktops()*M_PI/360.0f) * addedHeight2 + addedHeight1 - float(rect.height()); + } else if (mode == Cylinder) { + double addedHeight1 = -rect.height() * cos(verticalCurrentAngle*M_PI/180.0f) - rect.width() * sin(fabs(verticalCurrentAngle)*M_PI/180.0f)/tan(M_PI/effects->numberOfDesktops()); + translate = addedHeight1 - float(rect.height()); } else { - m_reflectionMatrix.translate(0.0, sin(fabs(manualAngle) * M_PI / 360.0f * float(effects->numberOfDesktops())) * addedHeight2 + addedHeight1 - float(rect.height()), 0.0); + float radius = (rect.width() * 0.5) / cos(cubeAngle * 0.5 * M_PI / 180.0); + translate = -rect.height()-2*radius; } + m_reflectionMatrix.translate(0.0f, translate, 0.0f); reflectionPainting = true; glEnable(GL_CULL_FACE); paintCap(true, -point - zTranslate, data.projectionMatrix()); // cube glCullFace(GL_BACK); paintCube(mask, region, data); glCullFace(GL_FRONT); paintCube(mask, region, data); paintCap(false, -point - zTranslate, data.projectionMatrix()); glDisable(GL_CULL_FACE); reflectionPainting = false; 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 }; // foreground float alpha = 0.7; - if (start) + if (animationState == AnimationState::Start) { alpha = 0.3 + 0.4 * timeLine.currentValue(); - if (stop) + } else if (animationState == AnimationState::Stop) { alpha = 0.3 + 0.4 * (1.0 - timeLine.currentValue()); + } 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 = data.projectionMatrix(); windowTransformation.translate(rect.x() + rect.width() * 0.5f, 0.0, 0.0); m_reflectionShader->setUniform(GLShader::ModelViewProjectionMatrix, windowTransformation); m_reflectionShader->setUniform("u_alpha", alpha); QVector verts; QVector 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); } glDisable(GL_BLEND); } glEnable(GL_CULL_FACE); // caps paintCap(false, -point - zTranslate, data.projectionMatrix()); // cube glCullFace(GL_FRONT); paintCube(mask, region, data); glCullFace(GL_BACK); paintCube(mask, region, data); // cap paintCap(true, -point - zTranslate, data.projectionMatrix()); glDisable(GL_CULL_FACE); glDisable(GL_BLEND); // desktop name box - inspired from coverswitch if (displayDesktopName) { double opacity = 1.0; - if (start) + if (animationState == AnimationState::Start) { opacity = timeLine.currentValue(); - if (stop) + } else if (animationState == AnimationState::Stop) { opacity = 1.0 - timeLine.currentValue(); + } 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); } desktopNameFrame->setGeometry(frameRect); desktopNameFrame->setText(effects->desktopName(frontDesktop)); desktopNameFrame->render(region, opacity); } } else { effects->paintScreen(mask, region, data); } } void CubeEffect::rotateCube() { QRect rect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop()); - m_rotationMatrix.setToIdentity(); float internalCubeAngle = 360.0f / effects->numberOfDesktops(); float zTranslate = zPosition + zoom; - if (start) - zTranslate *= timeLine.currentValue(); - if (stop) - zTranslate *= (1.0 - timeLine.currentValue()); - // Rotation of the cube 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 - if (manualVerticalAngle <= -90.0) { - manualVerticalAngle += 90.0; - if (verticalPosition == Normal) - verticalPosition = Down; - if (verticalPosition == Up) - verticalPosition = Normal; + /* Animations */ + if (animationState == AnimationState::Start) { + zTranslate *= timeLine.currentValue(); + } else if (animationState == AnimationState::Stop) { + currentAngle = startAngle * (1.0 - timeLine.currentValue()); + zTranslate *= (1.0 - timeLine.currentValue()); + } else if (animationState != AnimationState::None) { + /* Left or right */ + float endAngle = animationState == AnimationState::Right ? internalCubeAngle : -internalCubeAngle; + currentAngle = startAngle + timeLine.currentValue() * (endAngle - startAngle); + frontDesktop = startFrontDesktop; + } + /* Switching to next desktop: either by mouse or due to animation */ + if (currentAngle > internalCubeAngle * 0.5f) { + currentAngle -= internalCubeAngle; + frontDesktop--; + if (frontDesktop < 1) { + frontDesktop = effects->numberOfDesktops(); } - if (manualVerticalAngle >= 90.0) { - manualVerticalAngle -= 90.0; - if (verticalPosition == Normal) - verticalPosition = Up; - if (verticalPosition == Down) - verticalPosition = Normal; + } + if (currentAngle < -internalCubeAngle * 0.5f) { + currentAngle += internalCubeAngle; + frontDesktop++; + if (frontDesktop > effects->numberOfDesktops()) { + frontDesktop = 1; } - float angle = 0.0; - if (verticalPosition == Up) { - angle = 90.0; - if (!verticalRotating) { - if (manualVerticalAngle < 0.0) - angle += manualVerticalAngle; - else - manualVerticalAngle = 0.0; - } - } else if (verticalPosition == Down) { - angle = -90.0; - if (!verticalRotating) { - if (manualVerticalAngle > 0.0) - angle += manualVerticalAngle; - else - manualVerticalAngle = 0.0; - } - } else { - angle = manualVerticalAngle; + } + /* Vertical animations */ + if (verticalAnimationState != VerticalAnimationState::None) { + float verticalEndAngle = 0.0; + if (verticalAnimationState == VerticalAnimationState::Upwards && verticalStartAngle >= 0.0) { + verticalEndAngle = 90.0; } - if (verticalRotating) { - angle *= verticalTimeLine.currentValue(); - if (verticalPosition == Normal && verticalRotationDirection == Upwards) - angle = -90.0 + 90 * verticalTimeLine.currentValue(); - if (verticalPosition == Normal && verticalRotationDirection == Downwards) - angle = 90.0 - 90 * verticalTimeLine.currentValue(); - angle += manualVerticalAngle * (1.0 - verticalTimeLine.currentValue()); + if (verticalAnimationState == VerticalAnimationState::Downwards && verticalStartAngle <= 0.0) { + verticalEndAngle = -90.0; } - if (stop) - angle *= (1.0 - timeLine.currentValue()); + // This also handles the "VerticalAnimationState::Stop" correctly, since it has endAngle = 0.0 + verticalCurrentAngle = verticalStartAngle + verticalTimeLine.currentValue() * (verticalEndAngle - verticalStartAngle); + } + /* Updating rotation matrix */ + if (verticalAnimationState != VerticalAnimationState::None || verticalCurrentAngle != 0.0f) { m_rotationMatrix.translate(rect.width() / 2, rect.height() / 2, -point - zTranslate); - m_rotationMatrix.rotate(angle, 1.0, 0.0, 0.0); + m_rotationMatrix.rotate(verticalCurrentAngle, 1.0, 0.0, 0.0); m_rotationMatrix.translate(-rect.width() / 2, -rect.height() / 2, point + zTranslate); } - if (rotating || (manualAngle != 0.0)) { - int tempFrontDesktop = frontDesktop; - if (manualAngle > internalCubeAngle * 0.5f) { - manualAngle -= internalCubeAngle; - tempFrontDesktop--; - if (tempFrontDesktop == 0) - tempFrontDesktop = effects->numberOfDesktops(); - } - if (manualAngle < -internalCubeAngle * 0.5f) { - manualAngle += internalCubeAngle; - tempFrontDesktop++; - if (tempFrontDesktop > effects->numberOfDesktops()) - tempFrontDesktop = 1; - } - float rotationAngle = internalCubeAngle * timeLine.currentValue(); - if (rotationAngle > internalCubeAngle * 0.5f) { - rotationAngle -= internalCubeAngle; - if (!desktopChangedWhileRotating) { - desktopChangedWhileRotating = true; - if (rotationDirection == Left) { - tempFrontDesktop++; - } else if (rotationDirection == Right) { - tempFrontDesktop--; - } - if (tempFrontDesktop > effects->numberOfDesktops()) - tempFrontDesktop = 1; - else if (tempFrontDesktop == 0) - tempFrontDesktop = effects->numberOfDesktops(); - } - } - // don't change front desktop during stop animation as this would break some logic - if (!stop) - frontDesktop = tempFrontDesktop; - if (rotationDirection == Left) { - rotationAngle *= -1; - } - if (stop) - rotationAngle = manualAngle * (1.0 - timeLine.currentValue()); - else - rotationAngle += manualAngle * (1.0 - timeLine.currentValue()); + if (animationState != AnimationState::None || currentAngle != 0.0f) { m_rotationMatrix.translate(rect.width() / 2, rect.height() / 2, -point - zTranslate); - m_rotationMatrix.rotate(rotationAngle, 0.0, 1.0, 0.0); + m_rotationMatrix.rotate(currentAngle, 0.0, 1.0, 0.0); m_rotationMatrix.translate(-rect.width() / 2, -rect.height() / 2, point + zTranslate); } } 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; - if (start) + if (animationState == AnimationState::Start) { zTranslate *= timeLine.currentValue(); - if (stop) + } else if (animationState == AnimationState::Stop) { zTranslate *= (1.0 - timeLine.currentValue()); + } // Rotation of the cube 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); for (int i = 0; i < effects->numberOfDesktops(); i++) { // start painting the cube painting_desktop = (i + frontDesktop) % effects->numberOfDesktops(); if (painting_desktop == 0) { painting_desktop = effects->numberOfDesktops(); } QMatrix4x4 matrix; matrix.translate(0, 0, -zTranslate); const QVector3D origin(rect.width() / 2, 0.0, -point); matrix.translate(origin); matrix.rotate(internalCubeAngle * i, 0, 1, 0); matrix.translate(-origin); m_currentFaceMatrix = matrix; effects->paintScreen(mask, region, data); } cube_painting = false; painting_desktop = effects->currentDesktop(); } void CubeEffect::paintCap(bool frontFirst, float zOffset, const QMatrix4x4 &projection) { 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) { switch(mode) { case Cube: paintCubeCap(); break; case Cylinder: paintCylinderCap(); break; case Sphere: paintSphereCap(); break; default: // impossible break; } } QMatrix4x4 capMvp; QMatrix4x4 capMatrix; 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) { + if (animationState == AnimationState::Start) { opacity *= timeLine.currentValue(); - } else if (stop) { + } else if (animationState == AnimationState::Stop) { opacity *= (1.0 - timeLine.currentValue()); } m_capShader->setUniform("u_opacity", opacity); m_capShader->setUniform("u_mirror", 1); if (reflectionPainting) { capMvp = projection * m_reflectionMatrix * m_rotationMatrix; } else { capMvp = projection * m_rotationMatrix; } m_capShader->setUniform(GLShader::ModelViewProjectionMatrix, capMvp * capMatrix); 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(GLShader::ModelViewProjectionMatrix, capMvp * capMatrix); 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(); } } } void CubeEffect::paintCubeCap() { 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 verts; QVector texCoords; 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; 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; currentWidth = tan(angle * 0.5 * M_PI / 180.0) * zTriangleDistance * (j + 1); int evenTriangles = 0; int oddTriangles = 0; 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; 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++; } 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++; } float texX1 = 0.0; float texX2 = 0.0; float texX3 = 0.0; float texY1 = 0.0; float texY2 = 0.0; float texY3 = 0.0; 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; } } verts << x1 << 0.0 << z1; if (texture) { texCoords << texX2 << texY2; } verts << x2 << 0.0 << z2; if (texture) { texCoords << texX3 << texY3; } verts << x3 << 0.0 << z3; } } } delete m_cubeCapBuffer; m_cubeCapBuffer = new GLVertexBuffer(GLVertexBuffer::Static); m_cubeCapBuffer->setData(verts.count() / 3, 3, verts.constData(), texture ? texCoords.constData() : NULL); } void CubeEffect::paintCylinderCap() { QRect rect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop()); float cubeAngle = (float)((float)(effects->numberOfDesktops() - 2) / (float)effects->numberOfDesktops() * 180.0f); 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 verts; QVector texCoords; for (int i = 1; i <= 30; i++) { int steps = 72; 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; } } delete m_cubeCapBuffer; m_cubeCapBuffer = new GLVertexBuffer(GLVertexBuffer::Static); m_cubeCapBuffer->setData(verts.count() / 3, 3, verts.constData(), texture ? texCoords.constData() : NULL); } void CubeEffect::paintSphereCap() { 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 verts; QVector texCoords; 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 yTop = (rect.height() * 0.5 - radius * cos(topAngle)); + yTop *= (1.0f-capDeformationFactor); float yBottom = rect.height() * 0.5 - radius * cos(bottomAngle); - yBottom -= (yBottom - rect.height() * 0.5) * capDeformationFactor; + yBottom *= (1.0f - 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; } } delete m_cubeCapBuffer; m_cubeCapBuffer = new GLVertexBuffer(GLVertexBuffer::Static); m_cubeCapBuffer->setData(verts.count() / 3, 3, verts.constData(), texture ? texCoords.constData() : NULL); } void CubeEffect::postPaintScreen() { effects->postPaintScreen(); - if (activated) { - if (start) { - if (timeLine.currentValue() == 1.0) { - start = false; - timeLine.setCurrentTime(0); - // more rotations? - 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 - if (!rotations.empty()) { - currentShape = QTimeLine::LinearCurve; - } - // last rotation step -> easeOut curve - else { - currentShape = QTimeLine::EaseOutCurve; - } - timeLine.setCurveShape(currentShape); - } else { - // if there is at least one more rotation, we can change to easeIn - if (!rotations.empty()) { - currentShape = QTimeLine::EaseInCurve; - timeLine.setCurveShape(currentShape); - } - } - } - } - effects->addRepaintFull(); - return; // schedule_close could have been called, start has to finish first - } - if (stop) { - if (timeLine.currentValue() == 1.0) { - effects->setCurrentDesktop(frontDesktop); - stop = false; - timeLine.setCurrentTime(0); - activated = false; - // set the new desktop - if (keyboard_grab) - effects->ungrabKeyboard(); - keyboard_grab = false; - effects->stopMouseInterception(this); - - effects->setActiveFullScreenEffect(0); - - delete m_cubeCapBuffer; - m_cubeCapBuffer = NULL; - if (desktopNameFrame) - desktopNameFrame->free(); - } - effects->addRepaintFull(); - } - if (rotating || verticalRotating) { - if (rotating && timeLine.currentValue() == 1.0) { - timeLine.setCurrentTime(0.0); - rotating = false; - desktopChangedWhileRotating = false; - manualAngle = 0.0; - // more rotations? - if (!rotations.empty()) { - rotationDirection = rotations.dequeue(); - rotating = true; - // change the curve shape if current shape is not easeInOut - if (currentShape != QTimeLine::EaseInOutCurve) { - // more rotations follow -> linear curve - if (!rotations.empty()) { - currentShape = QTimeLine::LinearCurve; - } - // last rotation step -> easeOut curve - else { - currentShape = QTimeLine::EaseOutCurve; - } - timeLine.setCurveShape(currentShape); - } else { - // if there is at least one more rotation, we can change to easeIn - if (!rotations.empty()) { - currentShape = QTimeLine::EaseInCurve; - timeLine.setCurveShape(currentShape); - } - } - } else { - // reset curve shape if there are no more rotations - if (currentShape != QTimeLine::EaseInOutCurve) { - currentShape = QTimeLine::EaseInOutCurve; - timeLine.setCurveShape(currentShape); - } - } - } - if (verticalRotating && verticalTimeLine.currentValue() == 1.0) { - verticalTimeLine.setCurrentTime(0); - verticalRotating = false; - manualVerticalAngle = 0.0; - // more rotations? - if (!verticalRotations.empty()) { - verticalRotationDirection = verticalRotations.dequeue(); - verticalRotating = true; - if (verticalRotationDirection == Upwards) { - if (verticalPosition == Normal) - verticalPosition = Up; - if (verticalPosition == Down) - verticalPosition = Normal; - } - if (verticalRotationDirection == Downwards) { - if (verticalPosition == Normal) - verticalPosition = Down; - if (verticalPosition == Up) - verticalPosition = Normal; - } - } - } - effects->addRepaintFull(); - return; // rotation has to end before cube is closed + if (!activated) + return; + + bool animation = (animationState != AnimationState::None || verticalAnimationState != VerticalAnimationState::None); + if (animationState != AnimationState::None && timeLine.currentValue() == 1.0) { + /* An animation have just finished! */ + if (animationState == AnimationState::Stop) { + /* If the stop animation is finished, we're done */ + if (keyboard_grab) + effects->ungrabKeyboard(); + keyboard_grab = false; + effects->stopMouseInterception(this); + effects->setCurrentDesktop(frontDesktop); + effects->setActiveFullScreenEffect(0); + delete m_cubeCapBuffer; + m_cubeCapBuffer = NULL; + if (desktopNameFrame) + desktopNameFrame->free(); + activated = false; + // User can press Esc several times, and several Stop animations can be added to queue. We don't want it + animationState = AnimationState::None; + animations.clear(); + verticalAnimationState = VerticalAnimationState::None; + verticalAnimations.clear(); + } else { + if (!animations.empty()) + startAnimation(animations.dequeue()); + else + animationState = AnimationState::None; } - if (schedule_close) { - schedule_close = false; - stop = true; - effects->addRepaintFull(); + } + /* Vertical animation have finished */ + if (verticalAnimationState != VerticalAnimationState::None && verticalTimeLine.currentValue() == 1.0) { + if (!verticalAnimations.empty()) { + startVerticalAnimation(verticalAnimations.dequeue()); + } else { + verticalAnimationState = VerticalAnimationState::None; } } + /* Repaint if there is any animation */ + if (animation) { + effects->addRepaintFull(); + } } 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; if (leftDesktop == 0) leftDesktop = effects->numberOfDesktops(); if (rightDesktop > effects->numberOfDesktops()) rightDesktop = 1; 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 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()); } if (w->x() + w->width() > rect.x() + rect.width()) { data.quads = data.quads.splitAtX(rect.width() - w->x()); } if (w->y() < rect.y()) { data.quads = data.quads.splitAtY(-w->y()); } if (w->y() + w->height() > rect.y() + rect.height()) { data.quads = data.quads.splitAtY(rect.height() - w->y()); } 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 int prev_desktop = painting_desktop - 1; if (prev_desktop == 0) prev_desktop = effects->numberOfDesktops(); 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(); effects->prePaintWindow(w, data, time); return; } } // check for windows belonging to the next desktop int next_desktop = painting_desktop + 1; if (next_desktop > effects->numberOfDesktops()) next_desktop = 1; if (w->isOnDesktop(next_desktop) && 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(); effects->prePaintWindow(w, data, time); return; } } w->disablePainting(EffectWindow::PAINT_DISABLED_BY_DESKTOP); } } } effects->prePaintWindow(w, data, time); } void CubeEffect::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data) { ShaderManager *shaderManager = ShaderManager::instance(); if (activated && cube_painting) { region= infiniteRegion(); // we need to explicitly prevent any clipping, bug #325432 //qCDebug(KWINEFFECTS) << w->caption(); float opacity = cubeOpacity; - if (start) { + if (animationState == AnimationState::Start) { opacity = 1.0 - (1.0 - opacity) * timeLine.currentValue(); if (reflectionPainting) opacity = 0.5 + (cubeOpacity - 0.5) * timeLine.currentValue(); // fade in windows belonging to different desktops if (painting_desktop == effects->currentDesktop() && (!w->isOnDesktop(painting_desktop))) opacity = timeLine.currentValue() * cubeOpacity; - } - if (stop) { + } else if (animationState == AnimationState::Stop) { opacity = 1.0 - (1.0 - opacity) * (1.0 - timeLine.currentValue()); if (reflectionPainting) opacity = 0.5 + (cubeOpacity - 0.5) * (1.0 - timeLine.currentValue()); // fade out windows belonging to different desktops if (painting_desktop == effects->currentDesktop() && (!w->isOnDesktop(painting_desktop))) opacity = cubeOpacity * (1.0 - timeLine.currentValue()); } // z-Ordering if (!w->isDesktop() && !w->isDock() && useZOrdering && !w->isOnAllDesktops()) { float zOrdering = (effects->stackingOrder().indexOf(w) + 1) * zOrderingFactor; - if (start) + if (animationState == AnimationState::Start) { zOrdering *= timeLine.currentValue(); - if (stop) + } else if (animationState == AnimationState::Stop) { zOrdering *= (1.0 - timeLine.currentValue()); + } data.translate(0.0, 0.0, zOrdering); } // check for windows belonging to the previous desktop int prev_desktop = painting_desktop - 1; if (prev_desktop == 0) prev_desktop = effects->numberOfDesktops(); int next_desktop = painting_desktop + 1; if (next_desktop > effects->numberOfDesktops()) next_desktop = 1; if (w->isOnDesktop(prev_desktop) && (mask & PAINT_WINDOW_TRANSFORMED)) { QRect rect = effects->clientArea(FullArea, activeScreen, prev_desktop); WindowQuadList new_quads; foreach (const WindowQuad & quad, data.quads) { if (quad.right() > rect.width() - w->x()) { new_quads.append(quad); } } data.quads = new_quads; data.setXTranslation(-rect.width()); } if (w->isOnDesktop(next_desktop) && (mask & PAINT_WINDOW_TRANSFORMED)) { QRect rect = effects->clientArea(FullArea, activeScreen, next_desktop); WindowQuadList new_quads; foreach (const WindowQuad & quad, data.quads) { if (w->x() + quad.right() <= rect.x()) { new_quads.append(quad); } } data.quads = new_quads; data.setXTranslation(rect.width()); } QRect rect = effects->clientArea(FullArea, activeScreen, painting_desktop); - if (start || stop) { + if (animationState == AnimationState::Start || animationState == AnimationState::Stop) { // we have to change opacity values for fade in/out of windows which are shown on front-desktop if (prev_desktop == effects->currentDesktop() && w->x() < rect.x()) { - if (start) + if (animationState == AnimationState::Start) { opacity = timeLine.currentValue() * cubeOpacity; - if (stop) + } else if (animationState == AnimationState::Stop) { opacity = cubeOpacity * (1.0 - timeLine.currentValue()); + } } if (next_desktop == effects->currentDesktop() && w->x() + w->width() > rect.x() + rect.width()) { - if (start) + if (animationState == AnimationState::Start) { opacity = timeLine.currentValue() * cubeOpacity; - if (stop) + } else if (animationState == AnimationState::Stop) { opacity = cubeOpacity * (1.0 - timeLine.currentValue()); + } } } // HACK set opacity to 0.99 in case of fully opaque to ensure that windows are painted in correct sequence // bug #173214 if (opacity > 0.99f) opacity = 0.99f; if (opacityDesktopOnly && !w->isDesktop()) opacity = 0.99f; data.multiplyOpacity(opacity); if (w->isOnDesktop(painting_desktop) && w->x() < rect.x()) { WindowQuadList new_quads; foreach (const WindowQuad & quad, data.quads) { if (quad.right() > -w->x()) { new_quads.append(quad); } } data.quads = new_quads; } if (w->isOnDesktop(painting_desktop) && w->x() + w->width() > rect.x() + rect.width()) { WindowQuadList new_quads; foreach (const WindowQuad & quad, data.quads) { if (quad.right() <= rect.width() - w->x()) { new_quads.append(quad); } } data.quads = new_quads; } if (w->y() < rect.y()) { WindowQuadList new_quads; foreach (const WindowQuad & quad, data.quads) { if (quad.bottom() > -w->y()) { new_quads.append(quad); } } data.quads = new_quads; } if (w->y() + w->height() > rect.y() + rect.height()) { WindowQuadList new_quads; foreach (const WindowQuad & quad, data.quads) { if (quad.bottom() <= rect.height() - w->y()) { new_quads.append(quad); } } data.quads = new_quads; } GLShader *currentShader = nullptr; 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) + if (animationState == AnimationState::Start) { factor = 1.0f - timeLine.currentValue(); - if (stop) + } else if (animationState == AnimationState::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) + if (animationState == AnimationState::Start) { factor = 1.0f - timeLine.currentValue(); - if (stop) + } else if (animationState == AnimationState::Stop) { factor = timeLine.currentValue(); + } sphereShader->setUniform("timeLine", factor); currentShader = sphereShader; } if (currentShader) { data.shader = currentShader; } data.setProjectionMatrix(data.screenProjectionMatrix()); if (reflectionPainting) { data.setModelViewMatrix(m_reflectionMatrix * m_rotationMatrix * m_currentFaceMatrix); } else { data.setModelViewMatrix(m_rotationMatrix * m_currentFaceMatrix); } } effects->paintWindow(w, mask, region, data); if (activated && cube_painting) { if (mode == Cylinder || mode == Sphere) { shaderManager->popShader(); } 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; 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 if (!paint.isEmpty()) { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); QVector verts; float quadSize = 0.0f; int leftDesktop = frontDesktop - 1; int rightDesktop = frontDesktop + 1; if (leftDesktop == 0) leftDesktop = effects->numberOfDesktops(); if (rightDesktop > effects->numberOfDesktops()) rightDesktop = 1; if (painting_desktop == frontDesktop) quadSize = 100.0f; else if (painting_desktop == leftDesktop || painting_desktop == rightDesktop) quadSize = 150.0f; else quadSize = 250.0f; 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; } } } 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); QMatrix4x4 mvp = data.screenProjectionMatrix(); if (reflectionPainting) { mvp = mvp * m_reflectionMatrix * m_rotationMatrix * m_currentFaceMatrix; } else { mvp = mvp * m_rotationMatrix * m_currentFaceMatrix; } m_capShader->setUniform(GLShader::ModelViewProjectionMatrix, mvp); } GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); vbo->reset(); QColor color = capColor; capColor.setAlphaF(cubeOpacity); vbo->setColor(color); 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(); } glDisable(GL_BLEND); } } } } bool CubeEffect::borderActivated(ElectricBorder border) { if (!borderActivate.contains(border) && !borderActivateCylinder.contains(border) && !borderActivateSphere.contains(border)) return false; if (effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this) return false; if (borderActivate.contains(border)) { if (!activated || (activated && mode == Cube)) toggleCube(); else return false; } if (borderActivateCylinder.contains(border)) { if (!activated || (activated && mode == Cylinder)) toggleCylinder(); else return false; } if (borderActivateSphere.contains(border)) { if (!activated || (activated && mode == Sphere)) toggleSphere(); else return false; } return true; } void CubeEffect::toggleCube() { qCDebug(KWINEFFECTS) << "toggle cube"; toggle(Cube); } void CubeEffect::toggleCylinder() { qCDebug(KWINEFFECTS) << "toggle cylinder"; if (!useShaders) useShaders = loadShader(); if (useShaders) toggle(Cylinder); } void CubeEffect::toggleSphere() { qCDebug(KWINEFFECTS) << "toggle sphere"; if (!useShaders) useShaders = loadShader(); if (useShaders) toggle(Sphere); } void CubeEffect::toggle(CubeMode newMode) { if ((effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this) || effects->numberOfDesktops() < 2) return; if (!activated) { mode = newMode; setActive(true); } else { setActive(false); } } void CubeEffect::grabbedKeyboardEvent(QKeyEvent* e) { - if (stop) + // If either stop is running or is scheduled - ignore all events + if ((!animations.isEmpty() && animations.last() == AnimationState::Stop) || animationState == AnimationState::Stop) { return; + } // taken from desktopgrid.cpp 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) if (mode == Cube && cubeShortcut.contains(e->key() + e->modifiers())) { toggleCube(); return; } if (mode == Cylinder && cylinderShortcut.contains(e->key() + e->modifiers())) { toggleCylinder(); return; } if (mode == Sphere && sphereShortcut.contains(e->key() + e->modifiers())) { toggleSphere(); return; } int desktop = -1; // switch by F or just if (e->key() >= Qt::Key_F1 && e->key() <= Qt::Key_F35) desktop = e->key() - Qt::Key_F1 + 1; else if (e->key() >= Qt::Key_0 && e->key() <= Qt::Key_9) desktop = e->key() == Qt::Key_0 ? 10 : e->key() - Qt::Key_0; if (desktop != -1) { if (desktop <= effects->numberOfDesktops()) { // we have to rotate to chosen desktop // and end effect when rotation finished rotateToDesktop(desktop); setActive(false); } return; } - switch(e->key()) { - // wrap only on autorepeat + + int key = e->key(); + if (invertKeys) { + if (key == Qt::Key_Left) + key = Qt::Key_Right; + else if (key == Qt::Key_Right) + key = Qt::Key_Left; + else if (key == Qt::Key_Up) + key = Qt::Key_Down; + else if (key == Qt::Key_Down) + key = Qt::Key_Up; + } + + switch(key) { + // wrap only on autorepeat case Qt::Key_Left: - // rotate to previous desktop qCDebug(KWINEFFECTS) << "left"; - if (!rotating && !start) { - rotating = true; - if (invertKeys) - rotationDirection = Right; - else - rotationDirection = Left; - } else { - if (rotations.count() < effects->numberOfDesktops()) { - if (invertKeys) - rotations.enqueue(Right); - else - rotations.enqueue(Left); - } - } + if (animations.count() < effects->numberOfDesktops()) + animations.enqueue(AnimationState::Left); break; case Qt::Key_Right: - // rotate to next desktop qCDebug(KWINEFFECTS) << "right"; - if (!rotating && !start) { - rotating = true; - if (invertKeys) - rotationDirection = Left; - else - rotationDirection = Right; - } else { - if (rotations.count() < effects->numberOfDesktops()) { - if (invertKeys) - rotations.enqueue(Left); - else - rotations.enqueue(Right); - } - } + if (animations.count() < effects->numberOfDesktops()) + animations.enqueue(AnimationState::Right); break; case Qt::Key_Up: qCDebug(KWINEFFECTS) << "up"; - if (invertKeys) { - if (verticalPosition != Down) { - if (!verticalRotating) { - verticalRotating = true; - verticalRotationDirection = Downwards; - if (verticalPosition == Normal) - verticalPosition = Down; - if (verticalPosition == Up) - verticalPosition = Normal; - } else { - verticalRotations.enqueue(Downwards); - } - } else if (manualVerticalAngle > 0.0 && !verticalRotating) { - // rotate to down position from the manual position - verticalRotating = true; - verticalRotationDirection = Downwards; - verticalPosition = Down; - manualVerticalAngle -= 90.0; - } - } else { - if (verticalPosition != Up) { - if (!verticalRotating) { - verticalRotating = true; - verticalRotationDirection = Upwards; - if (verticalPosition == Normal) - verticalPosition = Up; - if (verticalPosition == Down) - verticalPosition = Normal; - } else { - verticalRotations.enqueue(Upwards); - } - } else if (manualVerticalAngle < 0.0 && !verticalRotating) { - // rotate to up position from the manual position - verticalRotating = true; - verticalRotationDirection = Upwards; - verticalPosition = Up; - manualVerticalAngle += 90.0; - } - } + verticalAnimations.enqueue(VerticalAnimationState::Upwards); break; case Qt::Key_Down: qCDebug(KWINEFFECTS) << "down"; - if (invertKeys) { - if (verticalPosition != Up) { - if (!verticalRotating) { - verticalRotating = true; - verticalRotationDirection = Upwards; - if (verticalPosition == Normal) - verticalPosition = Up; - if (verticalPosition == Down) - verticalPosition = Normal; - } else { - verticalRotations.enqueue(Upwards); - } - } else if (manualVerticalAngle < 0.0 && !verticalRotating) { - // rotate to up position from the manual position - verticalRotating = true; - verticalRotationDirection = Upwards; - verticalPosition = Up; - manualVerticalAngle += 90.0; - } - } else { - if (verticalPosition != Down) { - if (!verticalRotating) { - verticalRotating = true; - verticalRotationDirection = Downwards; - if (verticalPosition == Normal) - verticalPosition = Down; - if (verticalPosition == Up) - verticalPosition = Normal; - } else { - verticalRotations.enqueue(Downwards); - } - } else if (manualVerticalAngle > 0.0 && !verticalRotating) { - // rotate to down position from the manual position - verticalRotating = true; - verticalRotationDirection = Downwards; - verticalPosition = Down; - manualVerticalAngle -= 90.0; - } - } - break; + verticalAnimations.enqueue(VerticalAnimationState::Downwards); + 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: + case Qt::Key_Equal: zoom -= 10.0; zoom = qMax(-zPosition, zoom); rotateCube(); break; case Qt::Key_Minus: zoom += 10.0f; rotateCube(); break; default: break; } - effects->addRepaintFull(); } + effects->addRepaintFull(); } void CubeEffect::rotateToDesktop(int desktop) { - int tempFrontDesktop = frontDesktop; - if (!rotations.empty()) { - // all scheduled rotations will be removed as a speed up - rotations.clear(); - } - if (rotating && !desktopChangedWhileRotating) { - // front desktop will change during the actual rotation - this has to be considered - if (rotationDirection == Left) { - tempFrontDesktop++; - } else if (rotationDirection == Right) { - tempFrontDesktop--; - } - if (tempFrontDesktop > effects->numberOfDesktops()) - tempFrontDesktop = 1; - else if (tempFrontDesktop == 0) - tempFrontDesktop = effects->numberOfDesktops(); - } - // find the fastest rotation path from tempFrontDesktop to desktop - int rightRotations = tempFrontDesktop - desktop; - if (rightRotations < 0) + // all scheduled animations will be removed as a speed up + animations.clear(); + verticalAnimations.clear(); + // we want only startAnimation to finish gracefully + // all the others can be interrupted + if (animationState != AnimationState::Start) { + animationState = AnimationState::None; + } + verticalAnimationState = VerticalAnimationState::None; + // find the fastest rotation path from frontDesktop to desktop + int rightRotations = frontDesktop - desktop; + if (rightRotations < 0) { rightRotations += effects->numberOfDesktops(); - int leftRotations = desktop - tempFrontDesktop; - if (leftRotations < 0) + } + int leftRotations = desktop - frontDesktop; + if (leftRotations < 0) { leftRotations += effects->numberOfDesktops(); + } if (leftRotations <= rightRotations) { for (int i = 0; i < leftRotations; i++) { - rotations.enqueue(Left); + animations.enqueue(AnimationState::Left); } } else { for (int i = 0; i < rightRotations; i++) { - rotations.enqueue(Right); + animations.enqueue(AnimationState::Right); } } - if (!start && !rotating && !rotations.empty()) { - rotating = true; - rotationDirection = rotations.dequeue(); + // we want the face of desktop to appear, it might need also vertical animation + if (verticalCurrentAngle > 0.0f) { + verticalAnimations.enqueue(VerticalAnimationState::Downwards); + } + if (verticalCurrentAngle < 0.0f) { + verticalAnimations.enqueue(VerticalAnimationState::Upwards); } - // change timeline curve if more rotations are following - if (!rotations.empty()) { - currentShape = QTimeLine::EaseInCurve; - timeLine.setCurveShape(currentShape); + /* Start immediately, so there is no pause: + * during that pause, actual frontDesktop might change + * if user moves his mouse fast, leading to incorrect desktop */ + if (animationState == AnimationState::None && !animations.empty()) { + startAnimation(animations.dequeue()); + } + if (verticalAnimationState == VerticalAnimationState::None && !verticalAnimations.empty()) { + startVerticalAnimation(verticalAnimations.dequeue()); } } 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 *watcher = new QFutureWatcher(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 *watcher = new QFutureWatcher(this); connect(watcher, SIGNAL(finished()), SLOT(slotWallPaperLoaded())); watcher->setFuture(QtConcurrent::run(this, &CubeEffect::loadWallPaper, wallpaperPath)); } activated = true; activeScreen = effects->activeScreen(); keyboard_grab = effects->grabKeyboard(this); effects->startMouseInterception(this, Qt::OpenHandCursor); frontDesktop = effects->currentDesktop(); zoom = 0.0; zOrderingFactor = zPosition / (effects->stackingOrder().count() - 1); - start = true; + animations.enqueue(AnimationState::Start); + animationState = AnimationState::None; + verticalAnimationState = VerticalAnimationState::None; effects->setActiveFullScreenEffect(this); qCDebug(KWINEFFECTS) << "Cube is activated"; - verticalPosition = Normal; - verticalRotating = false; - manualAngle = 0.0; - manualVerticalAngle = 0.0; - desktopChangedWhileRotating = false; + currentAngle = 0.0; + verticalCurrentAngle = 0.0; if (reflection) { QRect rect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop()); 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(); } else { - schedule_close = true; - // we have to add a repaint, to start the deactivating - effects->addRepaintFull(); + animations.enqueue(AnimationState::Stop); } + effects->addRepaintFull(); } void CubeEffect::windowInputMouseEvent(QEvent* e) { if (!activated) return; if (tabBoxMode) return; - if (stop) + if ((!animations.isEmpty() && animations.last() == AnimationState::Stop) || animationState == AnimationState::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 - if (!verticalRotating) { + if (verticalAnimationState == VerticalAnimationState::None) { // display height corresponds to 180* int deltaY = pos.y() - oldpos.y(); float deltaVerticalDegrees = (float)deltaY / rect.height() * 180.0f; if (invertMouse) - manualVerticalAngle += deltaVerticalDegrees; + verticalCurrentAngle += deltaVerticalDegrees; else - manualVerticalAngle -= deltaVerticalDegrees; + verticalCurrentAngle -= deltaVerticalDegrees; + // don't get too excited + verticalCurrentAngle = qBound(-90.0f, verticalCurrentAngle, 90.0f); + if (deltaVerticalDegrees != 0.0) repaint = true; } // horizontal movement only if there is not a rotation - if (!rotating) { + if (animationState == AnimationState::None) { // display width corresponds to sum of angles of the polyhedron int deltaX = oldpos.x() - pos.x(); 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; } if (invertMouse) - manualAngle += deltaDegrees; + currentAngle += deltaDegrees; else - manualAngle -= deltaDegrees; + currentAngle -= deltaDegrees; if (deltaDegrees != 0.0) repaint = true; } if (repaint) { rotateCube(); effects->addRepaintFull(); } oldpos = pos; } 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) { - if (!rotating && !start) { - rotating = true; + if (animations.count() < effects->numberOfDesktops()) { if (invertMouse) - rotationDirection = Right; + animations.enqueue(AnimationState::Right); else - rotationDirection = Left; - } else { - if (rotations.count() < effects->numberOfDesktops()) { - if (invertMouse) - rotations.enqueue(Right); - else - rotations.enqueue(Left); - } + animations.enqueue(AnimationState::Left); } effects->addRepaintFull(); } else if (mouse->button() == Qt::XButton2) { - if (!rotating && !start) { - rotating = true; + if (animations.count() < effects->numberOfDesktops()) { if (invertMouse) - rotationDirection = Left; + animations.enqueue(AnimationState::Left); else - rotationDirection = Right; - } else { - if (rotations.count() < effects->numberOfDesktops()) { - if (invertMouse) - rotations.enqueue(Left); - else - rotations.enqueue(Right); - } + animations.enqueue(AnimationState::Right); } effects->addRepaintFull(); } else if (mouse->button() == Qt::RightButton || (mouse->button() == Qt::LeftButton && closeOnMouseRelease)) { setActive(false); } } } void CubeEffect::slotTabBoxAdded(int mode) { if (activated) return; if (effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this) return; if (useForTabBox && mode == TabBoxDesktopListMode) { effects->refTabBox(); tabBoxMode = true; setActive(true); rotateToDesktop(effects->currentTabBoxDesktop()); } } void CubeEffect::slotTabBoxUpdated() { if (activated) { rotateToDesktop(effects->currentTabBoxDesktop()); effects->addRepaintFull(); } } void CubeEffect::slotTabBoxClosed() { if (activated) { effects->unrefTabBox(); tabBoxMode = false; setActive(false); } } void CubeEffect::globalShortcutChanged(QAction *action, const QKeySequence &seq) { 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); } } void* CubeEffect::proxy() { return &m_proxy; } void CubeEffect::registerCubeInsideEffect(CubeInsideEffect* effect) { m_cubeInsideEffects.append(effect); } void CubeEffect::unregisterCubeInsideEffect(CubeInsideEffect* effect) { m_cubeInsideEffects.removeAll(effect); } bool CubeEffect::isActive() const { return activated && !effects->isScreenLocked(); } } // namespace diff --git a/effects/cube/cube.h b/effects/cube/cube.h index 0de8ad53e..05dd22e94 100644 --- a/effects/cube/cube.h +++ b/effects/cube/cube.h @@ -1,257 +1,264 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2008 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #ifndef KWIN_CUBE_H #define KWIN_CUBE_H #include #include #include #include #include #include #include #include "cube_inside.h" #include "cube_proxy.h" namespace KWin { class CubeEffect : public Effect { Q_OBJECT Q_PROPERTY(qreal cubeOpacity READ configuredCubeOpacity) Q_PROPERTY(bool opacityDesktopOnly READ isOpacityDesktopOnly) Q_PROPERTY(bool displayDesktopName READ isDisplayDesktopName) Q_PROPERTY(bool reflection READ isReflection) Q_PROPERTY(int rotationDuration READ configuredRotationDuration) Q_PROPERTY(QColor backgroundColor READ configuredBackgroundColor) Q_PROPERTY(QColor capColor READ configuredCapColor) Q_PROPERTY(bool paintCaps READ isPaintCaps) Q_PROPERTY(bool closeOnMouseRelease READ isCloseOnMouseRelease) Q_PROPERTY(qreal zPosition READ configuredZPosition) Q_PROPERTY(bool useForTabBox READ isUseForTabBox) Q_PROPERTY(bool invertKeys READ isInvertKeys) Q_PROPERTY(bool invertMouse READ isInvertMouse) Q_PROPERTY(qreal capDeformationFactor READ configuredCapDeformationFactor) Q_PROPERTY(bool useZOrdering READ isUseZOrdering) Q_PROPERTY(bool texturedCaps READ isTexturedCaps) // TODO: electric borders: not a registered type public: CubeEffect(); ~CubeEffect(); virtual void reconfigure(ReconfigureFlags); virtual void prePaintScreen(ScreenPrePaintData& data, int time); virtual void paintScreen(int mask, QRegion region, ScreenPaintData& data); virtual void postPaintScreen(); virtual void prePaintWindow(EffectWindow* w, WindowPrePaintData& data, int time); virtual void paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data); virtual bool borderActivated(ElectricBorder border); virtual void grabbedKeyboardEvent(QKeyEvent* e); virtual void windowInputMouseEvent(QEvent* e); virtual bool isActive() const; int requestedEffectChainPosition() const override { return 50; } // proxy functions virtual void* proxy(); void registerCubeInsideEffect(CubeInsideEffect* effect); void unregisterCubeInsideEffect(CubeInsideEffect* effect); static bool supported(); // for properties qreal configuredCubeOpacity() const { return cubeOpacity; } bool isOpacityDesktopOnly() const { return opacityDesktopOnly; } bool isDisplayDesktopName() const { return displayDesktopName; } bool isReflection() const { return reflection; } int configuredRotationDuration() const { return rotationDuration; } QColor configuredBackgroundColor() const { return backgroundColor; } QColor configuredCapColor() const { return capColor; } bool isPaintCaps() const { return paintCaps; } bool isCloseOnMouseRelease() const { return closeOnMouseRelease; } qreal configuredZPosition() const { return zPosition; } bool isUseForTabBox() const { return useForTabBox; } bool isInvertKeys() const { return invertKeys; } bool isInvertMouse() const { return invertMouse; } qreal configuredCapDeformationFactor() const { return capDeformationFactor; } bool isUseZOrdering() const { return useZOrdering; } bool isTexturedCaps() const { return texturedCaps; } private Q_SLOTS: void toggleCube(); void toggleCylinder(); void toggleSphere(); // slots for global shortcut changed // needed to toggle the effect void globalShortcutChanged(QAction *action, const QKeySequence &seq); void slotTabBoxAdded(int mode); void slotTabBoxUpdated(); void slotTabBoxClosed(); void slotCubeCapLoaded(); void slotWallPaperLoaded(); private: - enum RotationDirection { + enum class AnimationState { + None, + Start, + Stop, Left, - Right, + Right + }; + enum class VerticalAnimationState { + None, Upwards, Downwards }; - enum VerticalRotationPosition { - Up, - Normal, - Down - }; enum CubeMode { Cube, Cylinder, Sphere }; void toggle(CubeMode newMode = Cube); void paintCube(int mask, QRegion region, ScreenPaintData& data); void paintCap(bool frontFirst, float zOffset, const QMatrix4x4 &projection); void paintCubeCap(); void paintCylinderCap(); void paintSphereCap(); bool loadShader(); void rotateCube(); void rotateToDesktop(int desktop); void setActive(bool active); QImage loadCubeCap(const QString &capPath); QImage loadWallPaper(const QString &file); + void startAnimation(AnimationState state); + void startVerticalAnimation(VerticalAnimationState state); + bool activated; bool cube_painting; bool keyboard_grab; bool schedule_close; QList borderActivate; QList borderActivateCylinder; QList borderActivateSphere; int painting_desktop; int frontDesktop; float cubeOpacity; bool opacityDesktopOnly; bool displayDesktopName; EffectFrame* desktopNameFrame; QFont desktopNameFont; bool reflection; bool rotating; bool verticalRotating; bool desktopChangedWhileRotating; bool paintCaps; - QTimeLine timeLine; - QTimeLine verticalTimeLine; - RotationDirection rotationDirection; - RotationDirection verticalRotationDirection; - VerticalRotationPosition verticalPosition; - QQueue rotations; - QQueue verticalRotations; QColor backgroundColor; QColor capColor; GLTexture* wallpaper; bool texturedCaps; GLTexture* capTexture; - float manualAngle; - float manualVerticalAngle; - QTimeLine::CurveShape currentShape; - bool start; - bool stop; + // animations + // Horizontal/start/stop + float startAngle; + float currentAngle; + int startFrontDesktop; + AnimationState animationState; + QTimeLine timeLine; + QQueue animations; + // vertical + float verticalStartAngle; + float verticalCurrentAngle; + VerticalAnimationState verticalAnimationState; + QTimeLine verticalTimeLine; + QQueue verticalAnimations; + bool reflectionPainting; int rotationDuration; int activeScreen; bool bottomCap; bool closeOnMouseRelease; float zoom; float zPosition; bool useForTabBox; bool invertKeys; bool invertMouse; bool tabBoxMode; bool shortcutsRegistered; CubeMode mode; bool useShaders; GLShader* cylinderShader; GLShader* sphereShader; GLShader* m_reflectionShader; GLShader* m_capShader; float capDeformationFactor; bool useZOrdering; float zOrderingFactor; bool useList; // needed for reflection float mAddedHeightCoeff1; float mAddedHeightCoeff2; QMatrix4x4 m_rotationMatrix; QMatrix4x4 m_reflectionMatrix; QMatrix4x4 m_textureMirrorMatrix; QMatrix4x4 m_currentFaceMatrix; GLVertexBuffer *m_cubeCapBuffer; // Shortcuts - needed to toggle the effect QList cubeShortcut; QList cylinderShortcut; QList sphereShortcut; // proxy CubeEffectProxy m_proxy; QList< CubeInsideEffect* > m_cubeInsideEffects; QAction *m_cubeAction; QAction *m_cylinderAction; QAction *m_sphereAction; }; } // namespace #endif