/* KWin - the KDE window manager This file is part of the KDE project. SPDX-FileCopyrightText: 2007 Philip Falkner SPDX-FileCopyrightText: 2009 Martin Gräßlin SPDX-FileCopyrightText: 2018 Vlad Zahorodnii SPDX-License-Identifier: GPL-2.0-or-later */ // own #include "sheet.h" // KConfigSkeleton #include "sheetconfig.h" // Qt #include namespace KWin { SheetEffect::SheetEffect() { initConfig(); reconfigure(ReconfigureAll); connect(effects, &EffectsHandler::windowAdded, this, &SheetEffect::slotWindowAdded); connect(effects, &EffectsHandler::windowClosed, this, &SheetEffect::slotWindowClosed); connect(effects, &EffectsHandler::windowDeleted, this, &SheetEffect::slotWindowDeleted); } void SheetEffect::reconfigure(ReconfigureFlags flags) { Q_UNUSED(flags) SheetConfig::self()->read(); // TODO: Rename AnimationTime config key to Duration. const int d = animationTime(SheetConfig::animationTime() != 0 ? SheetConfig::animationTime() : 300); m_duration = std::chrono::milliseconds(static_cast(d)); } void SheetEffect::prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime) { auto animationIt = m_animations.begin(); while (animationIt != m_animations.end()) { std::chrono::milliseconds delta = std::chrono::milliseconds::zero(); if (animationIt->lastPresentTime.count()) { delta = presentTime - animationIt->lastPresentTime; } animationIt->lastPresentTime = presentTime; (*animationIt).timeLine.update(delta); ++animationIt; } data.mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS; effects->prePaintScreen(data, presentTime); } void SheetEffect::prePaintWindow(EffectWindow *w, WindowPrePaintData &data, std::chrono::milliseconds presentTime) { if (m_animations.contains(w)) { data.setTransformed(); w->enablePainting(EffectWindow::PAINT_DISABLED_BY_DELETE); } effects->prePaintWindow(w, data, presentTime); } void SheetEffect::paintWindow(EffectWindow *w, int mask, QRegion region, WindowPaintData &data) { auto animationIt = m_animations.constFind(w); if (animationIt == m_animations.constEnd()) { effects->paintWindow(w, mask, region, data); return; } // Perspective projection distorts objects near edges of the viewport // in undesired way. To fix this, the center of the window will be // moved to the origin, after applying perspective projection, the // center is moved back to its "original" projected position. Overall, // this is how the window will be transformed: // [move to the origin] -> [scale] -> [rotate] -> [translate] -> // -> [perspective projection] -> [reverse "move to the origin"] const QMatrix4x4 oldProjMatrix = data.screenProjectionMatrix(); const QRectF windowGeo = w->geometry(); const QVector3D invOffset = oldProjMatrix.map(QVector3D(windowGeo.center())); QMatrix4x4 invOffsetMatrix; invOffsetMatrix.translate(invOffset.x(), invOffset.y()); data.setProjectionMatrix(invOffsetMatrix * oldProjMatrix); // Move the center of the window to the origin. const QRectF screenGeo = effects->virtualScreenGeometry(); const QPointF offset = screenGeo.center() - windowGeo.center(); data.translate(offset.x(), offset.y()); const qreal t = (*animationIt).timeLine.value(); data.setRotationAxis(Qt::XAxis); data.setRotationAngle(interpolate(60.0, 0.0, t)); data *= QVector3D(1.0, t, t); data.translate(0.0, -interpolate(w->y() - (*animationIt).parentY, 0.0, t)); data.multiplyOpacity(t); effects->paintWindow(w, mask, region, data); } void SheetEffect::postPaintWindow(EffectWindow *w) { auto animationIt = m_animations.begin(); while (animationIt != m_animations.end()) { EffectWindow *w = animationIt.key(); w->addRepaintFull(); if ((*animationIt).timeLine.done()) { if (w->isDeleted()) { w->unrefWindow(); } animationIt = m_animations.erase(animationIt); } else { ++animationIt; } } if (m_animations.isEmpty()) { effects->addRepaintFull(); } effects->postPaintWindow(w); } bool SheetEffect::isActive() const { return !m_animations.isEmpty(); } bool SheetEffect::supported() { return effects->isOpenGLCompositing() && effects->animationsSupported(); } void SheetEffect::slotWindowAdded(EffectWindow *w) { if (effects->activeFullScreenEffect()) { return; } if (!isSheetWindow(w)) { return; } Animation &animation = m_animations[w]; animation.parentY = 0; animation.timeLine.reset(); animation.timeLine.setDuration(m_duration); animation.timeLine.setDirection(TimeLine::Forward); animation.timeLine.setEasingCurve(QEasingCurve::Linear); const auto windows = effects->stackingOrder(); auto parentIt = std::find_if(windows.constBegin(), windows.constEnd(), [w](EffectWindow *p) { return p->findModal() == w; }); if (parentIt != windows.constEnd()) { animation.parentY = (*parentIt)->y(); } w->setData(WindowAddedGrabRole, QVariant::fromValue(static_cast(this))); w->addRepaintFull(); } void SheetEffect::slotWindowClosed(EffectWindow *w) { if (effects->activeFullScreenEffect()) { return; } if (!isSheetWindow(w)) { return; } w->refWindow(); Animation &animation = m_animations[w]; animation.timeLine.reset(); animation.parentY = 0; animation.timeLine.setDuration(m_duration); animation.timeLine.setDirection(TimeLine::Backward); animation.timeLine.setEasingCurve(QEasingCurve::Linear); const auto windows = effects->stackingOrder(); auto parentIt = std::find_if(windows.constBegin(), windows.constEnd(), [w](EffectWindow *p) { return p->findModal() == w; }); if (parentIt != windows.constEnd()) { animation.parentY = (*parentIt)->y(); } w->setData(WindowClosedGrabRole, QVariant::fromValue(static_cast(this))); w->addRepaintFull(); } void SheetEffect::slotWindowDeleted(EffectWindow *w) { m_animations.remove(w); } bool SheetEffect::isSheetWindow(EffectWindow *w) const { return w->isModal(); } } // namespace KWin