File indexing completed on 2024-11-10 04:57:08

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2007 Philip Falkner <philip.falkner@gmail.com>
0006     SPDX-FileCopyrightText: 2009 Martin Gräßlin <mgraesslin@kde.org>
0007     SPDX-FileCopyrightText: 2018 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
0008 
0009     SPDX-License-Identifier: GPL-2.0-or-later
0010 */
0011 
0012 // own
0013 #include "sheet.h"
0014 
0015 // KConfigSkeleton
0016 #include "sheetconfig.h"
0017 
0018 #include "core/renderviewport.h"
0019 #include "effect/effecthandler.h"
0020 
0021 // Qt
0022 #include <QMatrix4x4>
0023 #include <qmath.h>
0024 
0025 namespace KWin
0026 {
0027 
0028 static QMatrix4x4 createPerspectiveMatrix(const QRectF &rect, const qreal scale)
0029 {
0030     QMatrix4x4 ret;
0031 
0032     const float fovY = std::tan(qDegreesToRadians(60.0f) / 2);
0033     const float aspect = 1.0f;
0034     const float zNear = 0.1f;
0035     const float zFar = 100.0f;
0036 
0037     const float yMax = zNear * fovY;
0038     const float yMin = -yMax;
0039     const float xMin = yMin * aspect;
0040     const float xMax = yMax * aspect;
0041 
0042     ret.frustum(xMin, xMax, yMin, yMax, zNear, zFar);
0043 
0044     const auto deviceRect = scaledRect(rect, scale);
0045 
0046     const float scaleFactor = 1.1 * fovY / yMax;
0047     ret.translate(xMin * scaleFactor, yMax * scaleFactor, -1.1);
0048     ret.scale((xMax - xMin) * scaleFactor / deviceRect.width(),
0049               -(yMax - yMin) * scaleFactor / deviceRect.height(),
0050               0.001);
0051     ret.translate(-deviceRect.x(), -deviceRect.y());
0052 
0053     return ret;
0054 }
0055 
0056 SheetEffect::SheetEffect()
0057 {
0058     SheetConfig::instance(effects->config());
0059     reconfigure(ReconfigureAll);
0060 
0061     connect(effects, &EffectsHandler::windowAdded, this, &SheetEffect::slotWindowAdded);
0062     connect(effects, &EffectsHandler::windowClosed, this, &SheetEffect::slotWindowClosed);
0063 }
0064 
0065 void SheetEffect::reconfigure(ReconfigureFlags flags)
0066 {
0067     SheetConfig::self()->read();
0068 
0069     // TODO: Rename AnimationTime config key to Duration.
0070     const int d = animationTime(SheetConfig::animationTime() != 0
0071                                     ? SheetConfig::animationTime()
0072                                     : 300);
0073     m_duration = std::chrono::milliseconds(static_cast<int>(d));
0074 }
0075 
0076 void SheetEffect::prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime)
0077 {
0078     data.mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS;
0079 
0080     effects->prePaintScreen(data, presentTime);
0081 }
0082 
0083 void SheetEffect::prePaintWindow(EffectWindow *w, WindowPrePaintData &data, std::chrono::milliseconds presentTime)
0084 {
0085     auto animationIt = m_animations.find(w);
0086     if (animationIt != m_animations.end()) {
0087         (*animationIt).timeLine.advance(presentTime);
0088         data.setTransformed();
0089     }
0090 
0091     effects->prePaintWindow(w, data, presentTime);
0092 }
0093 
0094 void SheetEffect::paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, QRegion region, WindowPaintData &data)
0095 {
0096     auto animationIt = m_animations.constFind(w);
0097     if (animationIt == m_animations.constEnd()) {
0098         effects->paintWindow(renderTarget, viewport, w, mask, region, data);
0099         return;
0100     }
0101 
0102     // Perspective projection distorts objects near edges of the viewport
0103     // in undesired way. To fix this, the center of the window will be
0104     // moved to the origin, after applying perspective projection, the
0105     // center is moved back to its "original" projected position. Overall,
0106     // this is how the window will be transformed:
0107     //  [move to the origin] -> [scale] -> [rotate] -> [translate] ->
0108     //    -> [perspective projection] -> [reverse "move to the origin"]
0109     const QMatrix4x4 oldProjMatrix = createPerspectiveMatrix(viewport.renderRect(), viewport.scale());
0110     const QRectF windowGeo = w->frameGeometry();
0111     const QVector3D invOffset = oldProjMatrix.map(QVector3D(windowGeo.center()));
0112     QMatrix4x4 invOffsetMatrix;
0113     invOffsetMatrix.translate(invOffset.x(), invOffset.y());
0114     data.setProjectionMatrix(invOffsetMatrix * oldProjMatrix);
0115 
0116     // Move the center of the window to the origin.
0117     const QRectF screenGeo = effects->virtualScreenGeometry();
0118     const QPointF offset = screenGeo.center() - windowGeo.center();
0119     data.translate(offset.x(), offset.y());
0120 
0121     const qreal t = (*animationIt).timeLine.value();
0122     data.setRotationAxis(Qt::XAxis);
0123     data.setRotationAngle(interpolate(60.0, 0.0, t));
0124     data *= QVector3D(1.0, t, t);
0125     data.translate(0.0, -interpolate(w->y() - (*animationIt).parentY, 0.0, t));
0126 
0127     data.multiplyOpacity(t);
0128 
0129     effects->paintWindow(renderTarget, viewport, w, mask, region, data);
0130 }
0131 
0132 void SheetEffect::postPaintWindow(EffectWindow *w)
0133 {
0134     auto animationIt = m_animations.begin();
0135     while (animationIt != m_animations.end()) {
0136         EffectWindow *w = animationIt.key();
0137         w->addRepaintFull();
0138         if ((*animationIt).timeLine.done()) {
0139             animationIt = m_animations.erase(animationIt);
0140         } else {
0141             ++animationIt;
0142         }
0143     }
0144 
0145     if (m_animations.isEmpty()) {
0146         effects->addRepaintFull();
0147     }
0148 
0149     effects->postPaintWindow(w);
0150 }
0151 
0152 bool SheetEffect::isActive() const
0153 {
0154     return !m_animations.isEmpty();
0155 }
0156 
0157 bool SheetEffect::supported()
0158 {
0159     return effects->isOpenGLCompositing()
0160         && effects->animationsSupported();
0161 }
0162 
0163 void SheetEffect::slotWindowAdded(EffectWindow *w)
0164 {
0165     if (effects->activeFullScreenEffect()) {
0166         return;
0167     }
0168 
0169     if (!isSheetWindow(w)) {
0170         return;
0171     }
0172 
0173     Animation &animation = m_animations[w];
0174     animation.parentY = 0;
0175     animation.timeLine.reset();
0176     animation.timeLine.setDuration(m_duration);
0177     animation.timeLine.setDirection(TimeLine::Forward);
0178     animation.timeLine.setEasingCurve(QEasingCurve::Linear);
0179 
0180     const auto windows = effects->stackingOrder();
0181     auto parentIt = std::find_if(windows.constBegin(), windows.constEnd(),
0182                                  [w](EffectWindow *p) {
0183                                      return p->findModal() == w;
0184                                  });
0185     if (parentIt != windows.constEnd()) {
0186         animation.parentY = (*parentIt)->y();
0187     }
0188 
0189     w->setData(WindowAddedGrabRole, QVariant::fromValue(static_cast<void *>(this)));
0190 
0191     w->addRepaintFull();
0192 }
0193 
0194 void SheetEffect::slotWindowClosed(EffectWindow *w)
0195 {
0196     if (effects->activeFullScreenEffect()) {
0197         return;
0198     }
0199 
0200     if (!isSheetWindow(w) || w->skipsCloseAnimation()) {
0201         return;
0202     }
0203 
0204     Animation &animation = m_animations[w];
0205     animation.deletedRef = EffectWindowDeletedRef(w);
0206     animation.timeLine.reset();
0207     animation.parentY = 0;
0208     animation.timeLine.setDuration(m_duration);
0209     animation.timeLine.setDirection(TimeLine::Backward);
0210     animation.timeLine.setEasingCurve(QEasingCurve::Linear);
0211 
0212     const auto windows = effects->stackingOrder();
0213     auto parentIt = std::find_if(windows.constBegin(), windows.constEnd(),
0214                                  [w](EffectWindow *p) {
0215                                      return p->findModal() == w;
0216                                  });
0217     if (parentIt != windows.constEnd()) {
0218         animation.parentY = (*parentIt)->y();
0219     }
0220 
0221     w->setData(WindowClosedGrabRole, QVariant::fromValue(static_cast<void *>(this)));
0222 
0223     w->addRepaintFull();
0224 }
0225 
0226 bool SheetEffect::isSheetWindow(EffectWindow *w) const
0227 {
0228     return w->isModal();
0229 }
0230 
0231 } // namespace KWin
0232 
0233 #include "moc_sheet.cpp"