File indexing completed on 2025-03-23 11:13:56
0001 /* 0002 KWin - the KDE window manager 0003 This file is part of the KDE project. 0004 0005 SPDX-FileCopyrightText: 2006 Lubos Lunak <l.lunak@kde.org> 0006 SPDX-FileCopyrightText: 2009 Lucas Murray <lmurray@undefinedfire.com> 0007 SPDX-FileCopyrightText: 2018 Vlad Zahorodnii <vlad.zahorodnii@kde.org> 0008 0009 SPDX-License-Identifier: GPL-2.0-or-later 0010 */ 0011 0012 #include "kwineffects.h" 0013 0014 #include "config-kwin.h" 0015 0016 #include <QFontMetrics> 0017 #include <QMatrix4x4> 0018 #include <QPainter> 0019 #include <QPixmap> 0020 #include <QTimeLine> 0021 #include <QVariant> 0022 #include <QWindow> 0023 #include <QtMath> 0024 0025 #include <kconfiggroup.h> 0026 #include <ksharedconfig.h> 0027 0028 #include <optional> 0029 0030 namespace KWin 0031 { 0032 0033 void WindowPrePaintData::setTranslucent() 0034 { 0035 mask |= Effect::PAINT_WINDOW_TRANSLUCENT; 0036 mask &= ~Effect::PAINT_WINDOW_OPAQUE; 0037 opaque = QRegion(); // cannot clip, will be transparent 0038 } 0039 0040 void WindowPrePaintData::setTransformed() 0041 { 0042 mask |= Effect::PAINT_WINDOW_TRANSFORMED; 0043 } 0044 0045 class PaintDataPrivate 0046 { 0047 public: 0048 PaintDataPrivate() 0049 : scale(1., 1., 1.) 0050 , rotationAxis(0, 0, 1.) 0051 , rotationAngle(0.) 0052 { 0053 } 0054 QVector3D scale; 0055 QVector3D translation; 0056 0057 QVector3D rotationAxis; 0058 QVector3D rotationOrigin; 0059 qreal rotationAngle; 0060 }; 0061 0062 PaintData::PaintData() 0063 : d(std::make_unique<PaintDataPrivate>()) 0064 { 0065 } 0066 0067 PaintData::~PaintData() = default; 0068 0069 qreal PaintData::xScale() const 0070 { 0071 return d->scale.x(); 0072 } 0073 0074 qreal PaintData::yScale() const 0075 { 0076 return d->scale.y(); 0077 } 0078 0079 qreal PaintData::zScale() const 0080 { 0081 return d->scale.z(); 0082 } 0083 0084 void PaintData::setScale(const QVector2D &scale) 0085 { 0086 d->scale.setX(scale.x()); 0087 d->scale.setY(scale.y()); 0088 } 0089 0090 void PaintData::setScale(const QVector3D &scale) 0091 { 0092 d->scale = scale; 0093 } 0094 void PaintData::setXScale(qreal scale) 0095 { 0096 d->scale.setX(scale); 0097 } 0098 0099 void PaintData::setYScale(qreal scale) 0100 { 0101 d->scale.setY(scale); 0102 } 0103 0104 void PaintData::setZScale(qreal scale) 0105 { 0106 d->scale.setZ(scale); 0107 } 0108 0109 const QVector3D &PaintData::scale() const 0110 { 0111 return d->scale; 0112 } 0113 0114 void PaintData::setXTranslation(qreal translate) 0115 { 0116 d->translation.setX(translate); 0117 } 0118 0119 void PaintData::setYTranslation(qreal translate) 0120 { 0121 d->translation.setY(translate); 0122 } 0123 0124 void PaintData::setZTranslation(qreal translate) 0125 { 0126 d->translation.setZ(translate); 0127 } 0128 0129 void PaintData::translate(qreal x, qreal y, qreal z) 0130 { 0131 translate(QVector3D(x, y, z)); 0132 } 0133 0134 void PaintData::translate(const QVector3D &t) 0135 { 0136 d->translation += t; 0137 } 0138 0139 qreal PaintData::xTranslation() const 0140 { 0141 return d->translation.x(); 0142 } 0143 0144 qreal PaintData::yTranslation() const 0145 { 0146 return d->translation.y(); 0147 } 0148 0149 qreal PaintData::zTranslation() const 0150 { 0151 return d->translation.z(); 0152 } 0153 0154 const QVector3D &PaintData::translation() const 0155 { 0156 return d->translation; 0157 } 0158 0159 qreal PaintData::rotationAngle() const 0160 { 0161 return d->rotationAngle; 0162 } 0163 0164 QVector3D PaintData::rotationAxis() const 0165 { 0166 return d->rotationAxis; 0167 } 0168 0169 QVector3D PaintData::rotationOrigin() const 0170 { 0171 return d->rotationOrigin; 0172 } 0173 0174 void PaintData::setRotationAngle(qreal angle) 0175 { 0176 d->rotationAngle = angle; 0177 } 0178 0179 void PaintData::setRotationAxis(Qt::Axis axis) 0180 { 0181 switch (axis) { 0182 case Qt::XAxis: 0183 setRotationAxis(QVector3D(1, 0, 0)); 0184 break; 0185 case Qt::YAxis: 0186 setRotationAxis(QVector3D(0, 1, 0)); 0187 break; 0188 case Qt::ZAxis: 0189 setRotationAxis(QVector3D(0, 0, 1)); 0190 break; 0191 } 0192 } 0193 0194 void PaintData::setRotationAxis(const QVector3D &axis) 0195 { 0196 d->rotationAxis = axis; 0197 } 0198 0199 void PaintData::setRotationOrigin(const QVector3D &origin) 0200 { 0201 d->rotationOrigin = origin; 0202 } 0203 0204 QMatrix4x4 PaintData::toMatrix(qreal deviceScale) const 0205 { 0206 QMatrix4x4 ret; 0207 if (d->translation != QVector3D(0, 0, 0)) { 0208 ret.translate(d->translation * deviceScale); 0209 } 0210 if (d->scale != QVector3D(1, 1, 1)) { 0211 ret.scale(d->scale); 0212 } 0213 0214 if (d->rotationAngle != 0) { 0215 ret.translate(d->rotationOrigin * deviceScale); 0216 ret.rotate(d->rotationAngle, d->rotationAxis); 0217 ret.translate(-d->rotationOrigin * deviceScale); 0218 } 0219 0220 return ret; 0221 } 0222 0223 class WindowPaintDataPrivate 0224 { 0225 public: 0226 qreal opacity; 0227 qreal saturation; 0228 qreal brightness; 0229 int screen; 0230 qreal crossFadeProgress; 0231 QMatrix4x4 projectionMatrix; 0232 std::optional<qreal> renderTargetScale = std::nullopt; 0233 }; 0234 0235 WindowPaintData::WindowPaintData() 0236 : WindowPaintData(QMatrix4x4()) 0237 { 0238 } 0239 0240 WindowPaintData::WindowPaintData(const QMatrix4x4 &projectionMatrix) 0241 : PaintData() 0242 , d(std::make_unique<WindowPaintDataPrivate>()) 0243 { 0244 setProjectionMatrix(projectionMatrix); 0245 setOpacity(1.0); 0246 setSaturation(1.0); 0247 setBrightness(1.0); 0248 setScreen(0); 0249 setCrossFadeProgress(0.0); 0250 } 0251 0252 WindowPaintData::WindowPaintData(const WindowPaintData &other) 0253 : PaintData() 0254 , d(std::make_unique<WindowPaintDataPrivate>()) 0255 { 0256 setXScale(other.xScale()); 0257 setYScale(other.yScale()); 0258 setZScale(other.zScale()); 0259 translate(other.translation()); 0260 setRotationOrigin(other.rotationOrigin()); 0261 setRotationAxis(other.rotationAxis()); 0262 setRotationAngle(other.rotationAngle()); 0263 setOpacity(other.opacity()); 0264 setSaturation(other.saturation()); 0265 setBrightness(other.brightness()); 0266 setScreen(other.screen()); 0267 setCrossFadeProgress(other.crossFadeProgress()); 0268 setProjectionMatrix(other.projectionMatrix()); 0269 } 0270 0271 WindowPaintData::~WindowPaintData() = default; 0272 0273 qreal WindowPaintData::opacity() const 0274 { 0275 return d->opacity; 0276 } 0277 0278 qreal WindowPaintData::saturation() const 0279 { 0280 return d->saturation; 0281 } 0282 0283 qreal WindowPaintData::brightness() const 0284 { 0285 return d->brightness; 0286 } 0287 0288 int WindowPaintData::screen() const 0289 { 0290 return d->screen; 0291 } 0292 0293 void WindowPaintData::setOpacity(qreal opacity) 0294 { 0295 d->opacity = opacity; 0296 } 0297 0298 void WindowPaintData::setSaturation(qreal saturation) const 0299 { 0300 d->saturation = saturation; 0301 } 0302 0303 void WindowPaintData::setBrightness(qreal brightness) 0304 { 0305 d->brightness = brightness; 0306 } 0307 0308 void WindowPaintData::setScreen(int screen) const 0309 { 0310 d->screen = screen; 0311 } 0312 0313 qreal WindowPaintData::crossFadeProgress() const 0314 { 0315 return d->crossFadeProgress; 0316 } 0317 0318 void WindowPaintData::setCrossFadeProgress(qreal factor) 0319 { 0320 d->crossFadeProgress = std::clamp(factor, 0.0, 1.0); 0321 } 0322 0323 qreal WindowPaintData::multiplyOpacity(qreal factor) 0324 { 0325 d->opacity *= factor; 0326 return d->opacity; 0327 } 0328 0329 qreal WindowPaintData::multiplySaturation(qreal factor) 0330 { 0331 d->saturation *= factor; 0332 return d->saturation; 0333 } 0334 0335 qreal WindowPaintData::multiplyBrightness(qreal factor) 0336 { 0337 d->brightness *= factor; 0338 return d->brightness; 0339 } 0340 0341 void WindowPaintData::setProjectionMatrix(const QMatrix4x4 &matrix) 0342 { 0343 d->projectionMatrix = matrix; 0344 } 0345 0346 QMatrix4x4 WindowPaintData::projectionMatrix() const 0347 { 0348 return d->projectionMatrix; 0349 } 0350 0351 QMatrix4x4 &WindowPaintData::rprojectionMatrix() 0352 { 0353 return d->projectionMatrix; 0354 } 0355 0356 WindowPaintData &WindowPaintData::operator*=(qreal scale) 0357 { 0358 this->setXScale(this->xScale() * scale); 0359 this->setYScale(this->yScale() * scale); 0360 this->setZScale(this->zScale() * scale); 0361 return *this; 0362 } 0363 0364 WindowPaintData &WindowPaintData::operator*=(const QVector2D &scale) 0365 { 0366 this->setXScale(this->xScale() * scale.x()); 0367 this->setYScale(this->yScale() * scale.y()); 0368 return *this; 0369 } 0370 0371 WindowPaintData &WindowPaintData::operator*=(const QVector3D &scale) 0372 { 0373 this->setXScale(this->xScale() * scale.x()); 0374 this->setYScale(this->yScale() * scale.y()); 0375 this->setZScale(this->zScale() * scale.z()); 0376 return *this; 0377 } 0378 0379 WindowPaintData &WindowPaintData::operator+=(const QPointF &translation) 0380 { 0381 return this->operator+=(QVector3D(translation)); 0382 } 0383 0384 WindowPaintData &WindowPaintData::operator+=(const QPoint &translation) 0385 { 0386 return this->operator+=(QVector3D(translation)); 0387 } 0388 0389 WindowPaintData &WindowPaintData::operator+=(const QVector2D &translation) 0390 { 0391 return this->operator+=(QVector3D(translation)); 0392 } 0393 0394 WindowPaintData &WindowPaintData::operator+=(const QVector3D &translation) 0395 { 0396 translate(translation); 0397 return *this; 0398 } 0399 0400 std::optional<qreal> WindowPaintData::renderTargetScale() const 0401 { 0402 return d->renderTargetScale; 0403 } 0404 0405 void WindowPaintData::setRenderTargetScale(qreal scale) 0406 { 0407 d->renderTargetScale = scale; 0408 } 0409 0410 class ScreenPaintData::Private 0411 { 0412 public: 0413 QMatrix4x4 projectionMatrix; 0414 EffectScreen *screen = nullptr; 0415 }; 0416 0417 ScreenPaintData::ScreenPaintData() 0418 : d(new Private()) 0419 { 0420 } 0421 0422 ScreenPaintData::ScreenPaintData(const QMatrix4x4 &projectionMatrix, EffectScreen *screen) 0423 : d(new Private()) 0424 { 0425 d->projectionMatrix = projectionMatrix; 0426 d->screen = screen; 0427 } 0428 0429 ScreenPaintData::~ScreenPaintData() = default; 0430 0431 ScreenPaintData::ScreenPaintData(const ScreenPaintData &other) 0432 : d(new Private()) 0433 { 0434 d->projectionMatrix = other.d->projectionMatrix; 0435 d->screen = other.d->screen; 0436 } 0437 0438 ScreenPaintData &ScreenPaintData::operator=(const ScreenPaintData &rhs) 0439 { 0440 d->projectionMatrix = rhs.d->projectionMatrix; 0441 d->screen = rhs.d->screen; 0442 return *this; 0443 } 0444 0445 QMatrix4x4 ScreenPaintData::projectionMatrix() const 0446 { 0447 return d->projectionMatrix; 0448 } 0449 0450 EffectScreen *ScreenPaintData::screen() const 0451 { 0452 return d->screen; 0453 } 0454 0455 //**************************************** 0456 // Effect 0457 //**************************************** 0458 0459 Effect::Effect(QObject *parent) 0460 : QObject(parent) 0461 { 0462 } 0463 0464 Effect::~Effect() 0465 { 0466 } 0467 0468 void Effect::reconfigure(ReconfigureFlags) 0469 { 0470 } 0471 0472 void *Effect::proxy() 0473 { 0474 return nullptr; 0475 } 0476 0477 void Effect::windowInputMouseEvent(QEvent *) 0478 { 0479 } 0480 0481 void Effect::grabbedKeyboardEvent(QKeyEvent *) 0482 { 0483 } 0484 0485 bool Effect::borderActivated(ElectricBorder) 0486 { 0487 return false; 0488 } 0489 0490 void Effect::prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime) 0491 { 0492 effects->prePaintScreen(data, presentTime); 0493 } 0494 0495 void Effect::paintScreen(int mask, const QRegion ®ion, ScreenPaintData &data) 0496 { 0497 effects->paintScreen(mask, region, data); 0498 } 0499 0500 void Effect::postPaintScreen() 0501 { 0502 effects->postPaintScreen(); 0503 } 0504 0505 void Effect::prePaintWindow(EffectWindow *w, WindowPrePaintData &data, std::chrono::milliseconds presentTime) 0506 { 0507 effects->prePaintWindow(w, data, presentTime); 0508 } 0509 0510 void Effect::paintWindow(EffectWindow *w, int mask, QRegion region, WindowPaintData &data) 0511 { 0512 effects->paintWindow(w, mask, region, data); 0513 } 0514 0515 void Effect::postPaintWindow(EffectWindow *w) 0516 { 0517 effects->postPaintWindow(w); 0518 } 0519 0520 bool Effect::provides(Feature) 0521 { 0522 return false; 0523 } 0524 0525 bool Effect::isActive() const 0526 { 0527 return true; 0528 } 0529 0530 QString Effect::debug(const QString &) const 0531 { 0532 return QString(); 0533 } 0534 0535 void Effect::drawWindow(EffectWindow *w, int mask, const QRegion ®ion, WindowPaintData &data) 0536 { 0537 effects->drawWindow(w, mask, region, data); 0538 } 0539 0540 void Effect::setPositionTransformations(WindowPaintData &data, QRect ®ion, EffectWindow *w, 0541 const QRect &r, Qt::AspectRatioMode aspect) 0542 { 0543 QSizeF size = w->size(); 0544 size.scale(r.size(), aspect); 0545 data.setXScale(size.width() / double(w->width())); 0546 data.setYScale(size.height() / double(w->height())); 0547 int width = int(w->width() * data.xScale()); 0548 int height = int(w->height() * data.yScale()); 0549 int x = r.x() + (r.width() - width) / 2; 0550 int y = r.y() + (r.height() - height) / 2; 0551 region = QRect(x, y, width, height); 0552 data.setXTranslation(x - w->x()); 0553 data.setYTranslation(y - w->y()); 0554 } 0555 0556 QPoint Effect::cursorPos() 0557 { 0558 return effects->cursorPos(); 0559 } 0560 0561 double Effect::animationTime(const KConfigGroup &cfg, const QString &key, int defaultTime) 0562 { 0563 int time = cfg.readEntry(key, 0); 0564 return time != 0 ? time : std::max(defaultTime * effects->animationTimeFactor(), 1.); 0565 } 0566 0567 double Effect::animationTime(int defaultTime) 0568 { 0569 // at least 1ms, otherwise 0ms times can break some things 0570 return std::max(defaultTime * effects->animationTimeFactor(), 1.); 0571 } 0572 0573 int Effect::requestedEffectChainPosition() const 0574 { 0575 return 0; 0576 } 0577 0578 xcb_connection_t *Effect::xcbConnection() const 0579 { 0580 return effects->xcbConnection(); 0581 } 0582 0583 xcb_window_t Effect::x11RootWindow() const 0584 { 0585 return effects->x11RootWindow(); 0586 } 0587 0588 bool Effect::touchDown(qint32 id, const QPointF &pos, std::chrono::microseconds time) 0589 { 0590 return false; 0591 } 0592 0593 bool Effect::touchMotion(qint32 id, const QPointF &pos, std::chrono::microseconds time) 0594 { 0595 return false; 0596 } 0597 0598 bool Effect::touchUp(qint32 id, std::chrono::microseconds time) 0599 { 0600 return false; 0601 } 0602 0603 bool Effect::perform(Feature feature, const QVariantList &arguments) 0604 { 0605 return false; 0606 } 0607 0608 bool Effect::tabletToolEvent(QTabletEvent *event) 0609 { 0610 return false; 0611 } 0612 0613 bool Effect::tabletToolButtonEvent(uint button, bool pressed, quint64 tabletToolId) 0614 { 0615 return false; 0616 } 0617 0618 bool Effect::tabletPadButtonEvent(uint button, bool pressed, void *tabletPadId) 0619 { 0620 return false; 0621 } 0622 0623 bool Effect::tabletPadStripEvent(int number, int position, bool isFinger, void *tabletPadId) 0624 { 0625 return false; 0626 } 0627 0628 bool Effect::tabletPadRingEvent(int number, int position, bool isFinger, void *tabletPadId) 0629 { 0630 return false; 0631 } 0632 0633 bool Effect::blocksDirectScanout() const 0634 { 0635 return true; 0636 } 0637 0638 //**************************************** 0639 // EffectFactory 0640 //**************************************** 0641 EffectPluginFactory::EffectPluginFactory() 0642 { 0643 } 0644 0645 EffectPluginFactory::~EffectPluginFactory() 0646 { 0647 } 0648 0649 bool EffectPluginFactory::enabledByDefault() const 0650 { 0651 return true; 0652 } 0653 0654 bool EffectPluginFactory::isSupported() const 0655 { 0656 return true; 0657 } 0658 0659 //**************************************** 0660 // EffectsHandler 0661 //**************************************** 0662 0663 EffectsHandler::EffectsHandler(CompositingType type) 0664 : compositing_type(type) 0665 { 0666 if (compositing_type == NoCompositing) { 0667 return; 0668 } 0669 KWin::effects = this; 0670 connect(this, QOverload<int, int>::of(&EffectsHandler::desktopChanged), this, &EffectsHandler::desktopChangedLegacy); 0671 } 0672 0673 EffectsHandler::~EffectsHandler() 0674 { 0675 // All effects should already be unloaded by Impl dtor 0676 Q_ASSERT(loaded_effects.count() == 0); 0677 KWin::effects = nullptr; 0678 } 0679 0680 CompositingType EffectsHandler::compositingType() const 0681 { 0682 return compositing_type; 0683 } 0684 0685 bool EffectsHandler::isOpenGLCompositing() const 0686 { 0687 return compositing_type & OpenGLCompositing; 0688 } 0689 0690 QRectF EffectsHandler::mapToRenderTarget(const QRectF &rect) const 0691 { 0692 const QRectF targetRect = renderTargetRect(); 0693 const qreal targetScale = renderTargetScale(); 0694 0695 return QRectF((rect.x() - targetRect.x()) * targetScale, 0696 (rect.y() - targetRect.y()) * targetScale, 0697 rect.width() * targetScale, 0698 rect.height() * targetScale); 0699 } 0700 0701 QRegion EffectsHandler::mapToRenderTarget(const QRegion ®ion) const 0702 { 0703 QRegion result; 0704 for (const QRect &rect : region) { 0705 result += mapToRenderTarget(QRectF(rect)).toRect(); 0706 } 0707 return result; 0708 } 0709 0710 EffectsHandler *effects = nullptr; 0711 0712 EffectScreen::EffectScreen(QObject *parent) 0713 : QObject(parent) 0714 { 0715 } 0716 0717 QPointF EffectScreen::mapToGlobal(const QPointF &pos) const 0718 { 0719 return pos + geometry().topLeft(); 0720 } 0721 0722 QPointF EffectScreen::mapFromGlobal(const QPointF &pos) const 0723 { 0724 return pos - geometry().topLeft(); 0725 } 0726 0727 //**************************************** 0728 // EffectWindow 0729 //**************************************** 0730 0731 class Q_DECL_HIDDEN EffectWindow::Private 0732 { 0733 public: 0734 Private(EffectWindow *q); 0735 0736 EffectWindow *q; 0737 }; 0738 0739 EffectWindow::Private::Private(EffectWindow *q) 0740 : q(q) 0741 { 0742 } 0743 0744 EffectWindow::EffectWindow() 0745 : d(new Private(this)) 0746 { 0747 } 0748 0749 EffectWindow::~EffectWindow() 0750 { 0751 } 0752 0753 bool EffectWindow::isOnActivity(const QString &activity) const 0754 { 0755 const QStringList _activities = activities(); 0756 return _activities.isEmpty() || _activities.contains(activity); 0757 } 0758 0759 bool EffectWindow::isOnAllActivities() const 0760 { 0761 return activities().isEmpty(); 0762 } 0763 0764 void EffectWindow::setMinimized(bool min) 0765 { 0766 if (min) { 0767 minimize(); 0768 } else { 0769 unminimize(); 0770 } 0771 } 0772 0773 bool EffectWindow::isOnCurrentActivity() const 0774 { 0775 return isOnActivity(effects->currentActivity()); 0776 } 0777 0778 bool EffectWindow::isOnCurrentDesktop() const 0779 { 0780 return isOnDesktop(effects->currentDesktop()); 0781 } 0782 0783 bool EffectWindow::isOnDesktop(int d) const 0784 { 0785 const QVector<uint> ds = desktops(); 0786 return ds.isEmpty() || ds.contains(d); 0787 } 0788 0789 bool EffectWindow::isOnAllDesktops() const 0790 { 0791 return desktops().isEmpty(); 0792 } 0793 0794 bool EffectWindow::hasDecoration() const 0795 { 0796 return contentsRect() != QRect(0, 0, width(), height()); 0797 } 0798 0799 bool EffectWindow::isVisible() const 0800 { 0801 return !isMinimized() 0802 && isOnCurrentDesktop() 0803 && isOnCurrentActivity(); 0804 } 0805 0806 //**************************************** 0807 // EffectWindowGroup 0808 //**************************************** 0809 0810 EffectWindowGroup::~EffectWindowGroup() 0811 { 0812 } 0813 0814 /*************************************************************** 0815 WindowQuad 0816 ***************************************************************/ 0817 0818 WindowQuad WindowQuad::makeSubQuad(double x1, double y1, double x2, double y2) const 0819 { 0820 Q_ASSERT(x1 < x2 && y1 < y2 && x1 >= left() && x2 <= right() && y1 >= top() && y2 <= bottom()); 0821 WindowQuad ret(*this); 0822 // vertices are clockwise starting from topleft 0823 ret.verts[0].px = x1; 0824 ret.verts[3].px = x1; 0825 ret.verts[1].px = x2; 0826 ret.verts[2].px = x2; 0827 ret.verts[0].py = y1; 0828 ret.verts[1].py = y1; 0829 ret.verts[2].py = y2; 0830 ret.verts[3].py = y2; 0831 0832 const double xOrigin = left(); 0833 const double yOrigin = top(); 0834 0835 const double widthReciprocal = 1 / (right() - xOrigin); 0836 const double heightReciprocal = 1 / (bottom() - yOrigin); 0837 0838 for (int i = 0; i < 4; ++i) { 0839 const double w1 = (ret.verts[i].px - xOrigin) * widthReciprocal; 0840 const double w2 = (ret.verts[i].py - yOrigin) * heightReciprocal; 0841 0842 // Use bilinear interpolation to compute the texture coords. 0843 ret.verts[i].tx = (1 - w1) * (1 - w2) * verts[0].tx + w1 * (1 - w2) * verts[1].tx + w1 * w2 * verts[2].tx + (1 - w1) * w2 * verts[3].tx; 0844 ret.verts[i].ty = (1 - w1) * (1 - w2) * verts[0].ty + w1 * (1 - w2) * verts[1].ty + w1 * w2 * verts[2].ty + (1 - w1) * w2 * verts[3].ty; 0845 } 0846 0847 return ret; 0848 } 0849 0850 /*************************************************************** 0851 WindowQuadList 0852 ***************************************************************/ 0853 0854 WindowQuadList WindowQuadList::splitAtX(double x) const 0855 { 0856 WindowQuadList ret; 0857 ret.reserve(count()); 0858 for (const WindowQuad &quad : *this) { 0859 bool wholeleft = true; 0860 bool wholeright = true; 0861 for (int i = 0; i < 4; ++i) { 0862 if (quad[i].x() < x) { 0863 wholeright = false; 0864 } 0865 if (quad[i].x() > x) { 0866 wholeleft = false; 0867 } 0868 } 0869 if (wholeleft || wholeright) { // is whole in one split part 0870 ret.append(quad); 0871 continue; 0872 } 0873 if (quad.top() == quad.bottom() || quad.left() == quad.right()) { // quad has no size 0874 ret.append(quad); 0875 continue; 0876 } 0877 ret.append(quad.makeSubQuad(quad.left(), quad.top(), x, quad.bottom())); 0878 ret.append(quad.makeSubQuad(x, quad.top(), quad.right(), quad.bottom())); 0879 } 0880 return ret; 0881 } 0882 0883 WindowQuadList WindowQuadList::splitAtY(double y) const 0884 { 0885 WindowQuadList ret; 0886 ret.reserve(count()); 0887 for (const WindowQuad &quad : *this) { 0888 bool wholetop = true; 0889 bool wholebottom = true; 0890 for (int i = 0; i < 4; ++i) { 0891 if (quad[i].y() < y) { 0892 wholebottom = false; 0893 } 0894 if (quad[i].y() > y) { 0895 wholetop = false; 0896 } 0897 } 0898 if (wholetop || wholebottom) { // is whole in one split part 0899 ret.append(quad); 0900 continue; 0901 } 0902 if (quad.top() == quad.bottom() || quad.left() == quad.right()) { // quad has no size 0903 ret.append(quad); 0904 continue; 0905 } 0906 ret.append(quad.makeSubQuad(quad.left(), quad.top(), quad.right(), y)); 0907 ret.append(quad.makeSubQuad(quad.left(), y, quad.right(), quad.bottom())); 0908 } 0909 return ret; 0910 } 0911 0912 WindowQuadList WindowQuadList::makeGrid(int maxQuadSize) const 0913 { 0914 if (empty()) { 0915 return *this; 0916 } 0917 0918 // Find the bounding rectangle 0919 double left = first().left(); 0920 double right = first().right(); 0921 double top = first().top(); 0922 double bottom = first().bottom(); 0923 0924 for (const WindowQuad &quad : std::as_const(*this)) { 0925 left = std::min(left, quad.left()); 0926 right = std::max(right, quad.right()); 0927 top = std::min(top, quad.top()); 0928 bottom = std::max(bottom, quad.bottom()); 0929 } 0930 0931 WindowQuadList ret; 0932 0933 for (const WindowQuad &quad : std::as_const(*this)) { 0934 const double quadLeft = quad.left(); 0935 const double quadRight = quad.right(); 0936 const double quadTop = quad.top(); 0937 const double quadBottom = quad.bottom(); 0938 0939 // sanity check, see BUG 390953 0940 if (quadLeft == quadRight || quadTop == quadBottom) { 0941 ret.append(quad); 0942 continue; 0943 } 0944 0945 // Compute the top-left corner of the first intersecting grid cell 0946 const double xBegin = left + qFloor((quadLeft - left) / maxQuadSize) * maxQuadSize; 0947 const double yBegin = top + qFloor((quadTop - top) / maxQuadSize) * maxQuadSize; 0948 0949 // Loop over all intersecting cells and add sub-quads 0950 for (double y = yBegin; y < quadBottom; y += maxQuadSize) { 0951 const double y0 = std::max(y, quadTop); 0952 const double y1 = std::min(quadBottom, y + maxQuadSize); 0953 0954 for (double x = xBegin; x < quadRight; x += maxQuadSize) { 0955 const double x0 = std::max(x, quadLeft); 0956 const double x1 = std::min(quadRight, x + maxQuadSize); 0957 0958 ret.append(quad.makeSubQuad(x0, y0, x1, y1)); 0959 } 0960 } 0961 } 0962 0963 return ret; 0964 } 0965 0966 WindowQuadList WindowQuadList::makeRegularGrid(int xSubdivisions, int ySubdivisions) const 0967 { 0968 if (empty()) { 0969 return *this; 0970 } 0971 0972 // Find the bounding rectangle 0973 double left = first().left(); 0974 double right = first().right(); 0975 double top = first().top(); 0976 double bottom = first().bottom(); 0977 0978 for (const WindowQuad &quad : *this) { 0979 left = std::min(left, quad.left()); 0980 right = std::max(right, quad.right()); 0981 top = std::min(top, quad.top()); 0982 bottom = std::max(bottom, quad.bottom()); 0983 } 0984 0985 double xIncrement = (right - left) / xSubdivisions; 0986 double yIncrement = (bottom - top) / ySubdivisions; 0987 0988 WindowQuadList ret; 0989 0990 for (const WindowQuad &quad : *this) { 0991 const double quadLeft = quad.left(); 0992 const double quadRight = quad.right(); 0993 const double quadTop = quad.top(); 0994 const double quadBottom = quad.bottom(); 0995 0996 // sanity check, see BUG 390953 0997 if (quadLeft == quadRight || quadTop == quadBottom) { 0998 ret.append(quad); 0999 continue; 1000 } 1001 1002 // Compute the top-left corner of the first intersecting grid cell 1003 const double xBegin = left + qFloor((quadLeft - left) / xIncrement) * xIncrement; 1004 const double yBegin = top + qFloor((quadTop - top) / yIncrement) * yIncrement; 1005 1006 // Loop over all intersecting cells and add sub-quads 1007 for (double y = yBegin; y < quadBottom; y += yIncrement) { 1008 const double y0 = std::max(y, quadTop); 1009 const double y1 = std::min(quadBottom, y + yIncrement); 1010 1011 for (double x = xBegin; x < quadRight; x += xIncrement) { 1012 const double x0 = std::max(x, quadLeft); 1013 const double x1 = std::min(quadRight, x + xIncrement); 1014 1015 ret.append(quad.makeSubQuad(x0, y0, x1, y1)); 1016 } 1017 } 1018 } 1019 1020 return ret; 1021 } 1022 1023 void RenderGeometry::copy(std::span<GLVertex2D> destination) 1024 { 1025 Q_ASSERT(int(destination.size()) >= size()); 1026 for (std::size_t i = 0; i < destination.size(); ++i) { 1027 destination[i] = at(i); 1028 } 1029 } 1030 1031 void RenderGeometry::appendWindowVertex(const WindowVertex &windowVertex, qreal deviceScale) 1032 { 1033 GLVertex2D glVertex; 1034 switch (m_vertexSnappingMode) { 1035 case VertexSnappingMode::None: 1036 glVertex.position = QVector2D(windowVertex.x(), windowVertex.y()) * deviceScale; 1037 break; 1038 case VertexSnappingMode::Round: 1039 glVertex.position = roundVector(QVector2D(windowVertex.x(), windowVertex.y()) * deviceScale); 1040 break; 1041 } 1042 glVertex.texcoord = QVector2D(windowVertex.u(), windowVertex.v()); 1043 append(glVertex); 1044 } 1045 1046 void RenderGeometry::appendWindowQuad(const WindowQuad &quad, qreal deviceScale) 1047 { 1048 // Geometry assumes we're rendering triangles, so add the quad's 1049 // vertices as two triangles. Vertex order is top-left, bottom-left, 1050 // top-right followed by top-right, bottom-left, bottom-right. 1051 appendWindowVertex(quad[0], deviceScale); 1052 appendWindowVertex(quad[3], deviceScale); 1053 appendWindowVertex(quad[1], deviceScale); 1054 1055 appendWindowVertex(quad[1], deviceScale); 1056 appendWindowVertex(quad[3], deviceScale); 1057 appendWindowVertex(quad[2], deviceScale); 1058 } 1059 1060 void RenderGeometry::appendSubQuad(const WindowQuad &quad, const QRectF &subquad, qreal deviceScale) 1061 { 1062 std::array<GLVertex2D, 4> vertices; 1063 vertices[0].position = QVector2D(subquad.topLeft()); 1064 vertices[1].position = QVector2D(subquad.topRight()); 1065 vertices[2].position = QVector2D(subquad.bottomRight()); 1066 vertices[3].position = QVector2D(subquad.bottomLeft()); 1067 1068 const auto deviceQuad = QRectF{QPointF(std::round(quad.left() * deviceScale), std::round(quad.top() * deviceScale)), 1069 QPointF(std::round(quad.right() * deviceScale), std::round(quad.bottom() * deviceScale))}; 1070 1071 const QPointF origin = deviceQuad.topLeft(); 1072 const QSizeF size = deviceQuad.size(); 1073 1074 #pragma GCC unroll 4 1075 for (int i = 0; i < 4; ++i) { 1076 const double weight1 = (vertices[i].position.x() - origin.x()) / size.width(); 1077 const double weight2 = (vertices[i].position.y() - origin.y()) / size.height(); 1078 const double oneMinW1 = 1.0 - weight1; 1079 const double oneMinW2 = 1.0 - weight2; 1080 1081 const float u = oneMinW1 * oneMinW2 * quad[0].u() + weight1 * oneMinW2 * quad[1].u() 1082 + weight1 * weight2 * quad[2].u() + oneMinW1 * weight2 * quad[3].u(); 1083 const float v = oneMinW1 * oneMinW2 * quad[0].v() + weight1 * oneMinW2 * quad[1].v() 1084 + weight1 * weight2 * quad[2].v() + oneMinW1 * weight2 * quad[3].v(); 1085 vertices[i].texcoord = QVector2D(u, v); 1086 } 1087 1088 append(vertices[0]); 1089 append(vertices[3]); 1090 append(vertices[1]); 1091 1092 append(vertices[1]); 1093 append(vertices[3]); 1094 append(vertices[2]); 1095 } 1096 1097 void RenderGeometry::postProcessTextureCoordinates(const QMatrix4x4 &textureMatrix) 1098 { 1099 if (!textureMatrix.isIdentity()) { 1100 const QVector2D coeff(textureMatrix(0, 0), textureMatrix(1, 1)); 1101 const QVector2D offset(textureMatrix(0, 3), textureMatrix(1, 3)); 1102 1103 for (auto &vertex : (*this)) { 1104 vertex.texcoord = vertex.texcoord * coeff + offset; 1105 } 1106 } 1107 } 1108 1109 /*************************************************************** 1110 Motion1D 1111 ***************************************************************/ 1112 1113 Motion1D::Motion1D(double initial, double strength, double smoothness) 1114 : Motion<double>(initial, strength, smoothness) 1115 { 1116 } 1117 1118 Motion1D::Motion1D(const Motion1D &other) 1119 : Motion<double>(other) 1120 { 1121 } 1122 1123 Motion1D::~Motion1D() 1124 { 1125 } 1126 1127 /*************************************************************** 1128 Motion2D 1129 ***************************************************************/ 1130 1131 Motion2D::Motion2D(QPointF initial, double strength, double smoothness) 1132 : Motion<QPointF>(initial, strength, smoothness) 1133 { 1134 } 1135 1136 Motion2D::Motion2D(const Motion2D &other) 1137 : Motion<QPointF>(other) 1138 { 1139 } 1140 1141 Motion2D::~Motion2D() 1142 { 1143 } 1144 1145 /*************************************************************** 1146 WindowMotionManager 1147 ***************************************************************/ 1148 1149 WindowMotionManager::WindowMotionManager(bool useGlobalAnimationModifier) 1150 : m_useGlobalAnimationModifier(useGlobalAnimationModifier) 1151 1152 { 1153 // TODO: Allow developer to modify motion attributes 1154 } // TODO: What happens when the window moves by an external force? 1155 1156 WindowMotionManager::~WindowMotionManager() 1157 { 1158 } 1159 1160 void WindowMotionManager::manage(EffectWindow *w) 1161 { 1162 if (m_managedWindows.contains(w)) { 1163 return; 1164 } 1165 1166 double strength = 0.08; 1167 double smoothness = 4.0; 1168 if (m_useGlobalAnimationModifier && effects->animationTimeFactor()) { 1169 // If the factor is == 0 then we just skip the calculation completely 1170 strength = 0.08 / effects->animationTimeFactor(); 1171 smoothness = effects->animationTimeFactor() * 4.0; 1172 } 1173 1174 WindowMotion &motion = m_managedWindows[w]; 1175 motion.translation.setStrength(strength); 1176 motion.translation.setSmoothness(smoothness); 1177 motion.scale.setStrength(strength * 1.33); 1178 motion.scale.setSmoothness(smoothness / 2.0); 1179 1180 motion.translation.setValue(w->pos()); 1181 motion.scale.setValue(QPointF(1.0, 1.0)); 1182 } 1183 1184 void WindowMotionManager::unmanage(EffectWindow *w) 1185 { 1186 m_movingWindowsSet.remove(w); 1187 m_managedWindows.remove(w); 1188 } 1189 1190 void WindowMotionManager::unmanageAll() 1191 { 1192 m_managedWindows.clear(); 1193 m_movingWindowsSet.clear(); 1194 } 1195 1196 void WindowMotionManager::calculate(int time) 1197 { 1198 if (!effects->animationTimeFactor()) { 1199 // Just skip it completely if the user wants no animation 1200 m_movingWindowsSet.clear(); 1201 QHash<EffectWindow *, WindowMotion>::iterator it = m_managedWindows.begin(); 1202 for (; it != m_managedWindows.end(); ++it) { 1203 WindowMotion *motion = &it.value(); 1204 motion->translation.finish(); 1205 motion->scale.finish(); 1206 } 1207 } 1208 1209 QHash<EffectWindow *, WindowMotion>::iterator it = m_managedWindows.begin(); 1210 for (; it != m_managedWindows.end(); ++it) { 1211 WindowMotion *motion = &it.value(); 1212 int stopped = 0; 1213 1214 // TODO: What happens when distance() == 0 but we are still moving fast? 1215 // TODO: Motion needs to be calculated from the window's center 1216 1217 Motion2D *trans = &motion->translation; 1218 if (trans->distance().isNull()) { 1219 ++stopped; 1220 } else { 1221 // Still moving 1222 trans->calculate(time); 1223 const short fx = trans->target().x() <= trans->startValue().x() ? -1 : 1; 1224 const short fy = trans->target().y() <= trans->startValue().y() ? -1 : 1; 1225 if (trans->distance().x() * fx / 0.5 < 1.0 && trans->velocity().x() * fx / 0.2 < 1.0 1226 && trans->distance().y() * fy / 0.5 < 1.0 && trans->velocity().y() * fy / 0.2 < 1.0) { 1227 // Hide tiny oscillations 1228 motion->translation.finish(); 1229 ++stopped; 1230 } 1231 } 1232 1233 Motion2D *scale = &motion->scale; 1234 if (scale->distance().isNull()) { 1235 ++stopped; 1236 } else { 1237 // Still scaling 1238 scale->calculate(time); 1239 const short fx = scale->target().x() < 1.0 ? -1 : 1; 1240 const short fy = scale->target().y() < 1.0 ? -1 : 1; 1241 if (scale->distance().x() * fx / 0.001 < 1.0 && scale->velocity().x() * fx / 0.05 < 1.0 1242 && scale->distance().y() * fy / 0.001 < 1.0 && scale->velocity().y() * fy / 0.05 < 1.0) { 1243 // Hide tiny oscillations 1244 motion->scale.finish(); 1245 ++stopped; 1246 } 1247 } 1248 1249 // We just finished this window's motion 1250 if (stopped == 2) { 1251 m_movingWindowsSet.remove(it.key()); 1252 } 1253 } 1254 } 1255 1256 void WindowMotionManager::reset() 1257 { 1258 QHash<EffectWindow *, WindowMotion>::iterator it = m_managedWindows.begin(); 1259 for (; it != m_managedWindows.end(); ++it) { 1260 WindowMotion *motion = &it.value(); 1261 EffectWindow *window = it.key(); 1262 motion->translation.setTarget(window->pos()); 1263 motion->translation.finish(); 1264 motion->scale.setTarget(QPointF(1.0, 1.0)); 1265 motion->scale.finish(); 1266 } 1267 } 1268 1269 void WindowMotionManager::reset(EffectWindow *w) 1270 { 1271 QHash<EffectWindow *, WindowMotion>::iterator it = m_managedWindows.find(w); 1272 if (it == m_managedWindows.end()) { 1273 return; 1274 } 1275 1276 WindowMotion *motion = &it.value(); 1277 motion->translation.setTarget(w->pos()); 1278 motion->translation.finish(); 1279 motion->scale.setTarget(QPointF(1.0, 1.0)); 1280 motion->scale.finish(); 1281 } 1282 1283 void WindowMotionManager::apply(EffectWindow *w, WindowPaintData &data) 1284 { 1285 QHash<EffectWindow *, WindowMotion>::iterator it = m_managedWindows.find(w); 1286 if (it == m_managedWindows.end()) { 1287 return; 1288 } 1289 1290 // TODO: Take into account existing scale so that we can work with multiple managers (E.g. Present windows + grid) 1291 WindowMotion *motion = &it.value(); 1292 data += (motion->translation.value() - QPointF(w->x(), w->y())); 1293 data *= QVector2D(motion->scale.value()); 1294 } 1295 1296 void WindowMotionManager::moveWindow(EffectWindow *w, QPoint target, double scale, double yScale) 1297 { 1298 QHash<EffectWindow *, WindowMotion>::iterator it = m_managedWindows.find(w); 1299 Q_ASSERT(it != m_managedWindows.end()); // Notify the effect author that they did something wrong 1300 1301 WindowMotion *motion = &it.value(); 1302 1303 if (yScale == 0.0) { 1304 yScale = scale; 1305 } 1306 QPointF scalePoint(scale, yScale); 1307 1308 if (motion->translation.value() == target && motion->scale.value() == scalePoint) { 1309 return; // Window already at that position 1310 } 1311 1312 motion->translation.setTarget(target); 1313 motion->scale.setTarget(scalePoint); 1314 1315 m_movingWindowsSet << w; 1316 } 1317 1318 QRectF WindowMotionManager::transformedGeometry(EffectWindow *w) const 1319 { 1320 QHash<EffectWindow *, WindowMotion>::const_iterator it = m_managedWindows.constFind(w); 1321 if (it == m_managedWindows.end()) { 1322 return w->frameGeometry(); 1323 } 1324 1325 const WindowMotion *motion = &it.value(); 1326 QRectF geometry(w->frameGeometry()); 1327 1328 // TODO: Take into account existing scale so that we can work with multiple managers (E.g. Present windows + grid) 1329 geometry.moveTo(motion->translation.value()); 1330 geometry.setWidth(geometry.width() * motion->scale.value().x()); 1331 geometry.setHeight(geometry.height() * motion->scale.value().y()); 1332 1333 return geometry; 1334 } 1335 1336 void WindowMotionManager::setTransformedGeometry(EffectWindow *w, const QRectF &geometry) 1337 { 1338 QHash<EffectWindow *, WindowMotion>::iterator it = m_managedWindows.find(w); 1339 if (it == m_managedWindows.end()) { 1340 return; 1341 } 1342 WindowMotion *motion = &it.value(); 1343 motion->translation.setValue(geometry.topLeft()); 1344 motion->scale.setValue(QPointF(geometry.width() / qreal(w->width()), geometry.height() / qreal(w->height()))); 1345 } 1346 1347 QRectF WindowMotionManager::targetGeometry(EffectWindow *w) const 1348 { 1349 QHash<EffectWindow *, WindowMotion>::const_iterator it = m_managedWindows.constFind(w); 1350 if (it == m_managedWindows.end()) { 1351 return w->frameGeometry(); 1352 } 1353 1354 const WindowMotion *motion = &it.value(); 1355 QRectF geometry(w->frameGeometry()); 1356 1357 // TODO: Take into account existing scale so that we can work with multiple managers (E.g. Present windows + grid) 1358 geometry.moveTo(motion->translation.target()); 1359 geometry.setWidth(geometry.width() * motion->scale.target().x()); 1360 geometry.setHeight(geometry.height() * motion->scale.target().y()); 1361 1362 return geometry; 1363 } 1364 1365 EffectWindow *WindowMotionManager::windowAtPoint(QPoint point, bool useStackingOrder) const 1366 { 1367 // TODO: Stacking order uses EffectsHandler::stackingOrder() then filters by m_managedWindows 1368 QHash<EffectWindow *, WindowMotion>::ConstIterator it = m_managedWindows.constBegin(); 1369 while (it != m_managedWindows.constEnd()) { 1370 if (transformedGeometry(it.key()).contains(point)) { 1371 return it.key(); 1372 } 1373 ++it; 1374 } 1375 1376 return nullptr; 1377 } 1378 1379 /*************************************************************** 1380 EffectFramePrivate 1381 ***************************************************************/ 1382 class EffectFramePrivate 1383 { 1384 public: 1385 EffectFramePrivate(); 1386 ~EffectFramePrivate(); 1387 1388 bool crossFading; 1389 qreal crossFadeProgress; 1390 QMatrix4x4 screenProjectionMatrix; 1391 }; 1392 1393 EffectFramePrivate::EffectFramePrivate() 1394 : crossFading(false) 1395 , crossFadeProgress(1.0) 1396 { 1397 } 1398 1399 EffectFramePrivate::~EffectFramePrivate() 1400 { 1401 } 1402 1403 /*************************************************************** 1404 EffectFrame 1405 ***************************************************************/ 1406 EffectFrame::EffectFrame() 1407 : d(std::make_unique<EffectFramePrivate>()) 1408 { 1409 } 1410 1411 EffectFrame::~EffectFrame() = default; 1412 1413 qreal EffectFrame::crossFadeProgress() const 1414 { 1415 return d->crossFadeProgress; 1416 } 1417 1418 void EffectFrame::setCrossFadeProgress(qreal progress) 1419 { 1420 d->crossFadeProgress = progress; 1421 } 1422 1423 bool EffectFrame::isCrossFade() const 1424 { 1425 return d->crossFading; 1426 } 1427 1428 void EffectFrame::enableCrossFade(bool enable) 1429 { 1430 d->crossFading = enable; 1431 } 1432 1433 /*************************************************************** 1434 TimeLine 1435 ***************************************************************/ 1436 1437 class Q_DECL_HIDDEN TimeLine::Data : public QSharedData 1438 { 1439 public: 1440 std::chrono::milliseconds duration; 1441 Direction direction; 1442 QEasingCurve easingCurve; 1443 1444 std::chrono::milliseconds elapsed = std::chrono::milliseconds::zero(); 1445 std::optional<std::chrono::milliseconds> lastTimestamp = std::nullopt; 1446 bool done = false; 1447 RedirectMode sourceRedirectMode = RedirectMode::Relaxed; 1448 RedirectMode targetRedirectMode = RedirectMode::Strict; 1449 }; 1450 1451 TimeLine::TimeLine(std::chrono::milliseconds duration, Direction direction) 1452 : d(new Data) 1453 { 1454 Q_ASSERT(duration > std::chrono::milliseconds::zero()); 1455 d->duration = duration; 1456 d->direction = direction; 1457 } 1458 1459 TimeLine::TimeLine(const TimeLine &other) 1460 : d(other.d) 1461 { 1462 } 1463 1464 TimeLine::~TimeLine() = default; 1465 1466 qreal TimeLine::progress() const 1467 { 1468 return static_cast<qreal>(d->elapsed.count()) / d->duration.count(); 1469 } 1470 1471 qreal TimeLine::value() const 1472 { 1473 const qreal t = progress(); 1474 return d->easingCurve.valueForProgress( 1475 d->direction == Backward ? 1.0 - t : t); 1476 } 1477 1478 void TimeLine::advance(std::chrono::milliseconds timestamp) 1479 { 1480 if (d->done) { 1481 return; 1482 } 1483 1484 std::chrono::milliseconds delta = std::chrono::milliseconds::zero(); 1485 if (d->lastTimestamp.has_value()) { 1486 delta = timestamp - d->lastTimestamp.value(); 1487 } 1488 1489 Q_ASSERT(delta >= std::chrono::milliseconds::zero()); 1490 d->lastTimestamp = timestamp; 1491 1492 d->elapsed += delta; 1493 if (d->elapsed >= d->duration) { 1494 d->elapsed = d->duration; 1495 d->done = true; 1496 d->lastTimestamp = std::nullopt; 1497 } 1498 } 1499 1500 std::chrono::milliseconds TimeLine::elapsed() const 1501 { 1502 return d->elapsed; 1503 } 1504 1505 void TimeLine::setElapsed(std::chrono::milliseconds elapsed) 1506 { 1507 Q_ASSERT(elapsed >= std::chrono::milliseconds::zero()); 1508 if (elapsed == d->elapsed) { 1509 return; 1510 } 1511 1512 reset(); 1513 1514 d->elapsed = elapsed; 1515 1516 if (d->elapsed >= d->duration) { 1517 d->elapsed = d->duration; 1518 d->done = true; 1519 d->lastTimestamp = std::nullopt; 1520 } 1521 } 1522 1523 std::chrono::milliseconds TimeLine::duration() const 1524 { 1525 return d->duration; 1526 } 1527 1528 void TimeLine::setDuration(std::chrono::milliseconds duration) 1529 { 1530 Q_ASSERT(duration > std::chrono::milliseconds::zero()); 1531 if (duration == d->duration) { 1532 return; 1533 } 1534 d->elapsed = std::chrono::milliseconds(qRound(progress() * duration.count())); 1535 d->duration = duration; 1536 if (d->elapsed == d->duration) { 1537 d->done = true; 1538 d->lastTimestamp = std::nullopt; 1539 } 1540 } 1541 1542 TimeLine::Direction TimeLine::direction() const 1543 { 1544 return d->direction; 1545 } 1546 1547 void TimeLine::setDirection(TimeLine::Direction direction) 1548 { 1549 if (d->direction == direction) { 1550 return; 1551 } 1552 1553 d->direction = direction; 1554 1555 if (d->elapsed > std::chrono::milliseconds::zero() 1556 || d->sourceRedirectMode == RedirectMode::Strict) { 1557 d->elapsed = d->duration - d->elapsed; 1558 } 1559 1560 if (d->done && d->targetRedirectMode == RedirectMode::Relaxed) { 1561 d->done = false; 1562 } 1563 1564 if (d->elapsed >= d->duration) { 1565 d->done = true; 1566 d->lastTimestamp = std::nullopt; 1567 } 1568 } 1569 1570 void TimeLine::toggleDirection() 1571 { 1572 setDirection(d->direction == Forward ? Backward : Forward); 1573 } 1574 1575 QEasingCurve TimeLine::easingCurve() const 1576 { 1577 return d->easingCurve; 1578 } 1579 1580 void TimeLine::setEasingCurve(const QEasingCurve &easingCurve) 1581 { 1582 d->easingCurve = easingCurve; 1583 } 1584 1585 void TimeLine::setEasingCurve(QEasingCurve::Type type) 1586 { 1587 d->easingCurve.setType(type); 1588 } 1589 1590 bool TimeLine::running() const 1591 { 1592 return d->elapsed != std::chrono::milliseconds::zero() 1593 && d->elapsed != d->duration; 1594 } 1595 1596 bool TimeLine::done() const 1597 { 1598 return d->done; 1599 } 1600 1601 void TimeLine::reset() 1602 { 1603 d->lastTimestamp = std::nullopt; 1604 d->elapsed = std::chrono::milliseconds::zero(); 1605 d->done = false; 1606 } 1607 1608 TimeLine::RedirectMode TimeLine::sourceRedirectMode() const 1609 { 1610 return d->sourceRedirectMode; 1611 } 1612 1613 void TimeLine::setSourceRedirectMode(RedirectMode mode) 1614 { 1615 d->sourceRedirectMode = mode; 1616 } 1617 1618 TimeLine::RedirectMode TimeLine::targetRedirectMode() const 1619 { 1620 return d->targetRedirectMode; 1621 } 1622 1623 void TimeLine::setTargetRedirectMode(RedirectMode mode) 1624 { 1625 d->targetRedirectMode = mode; 1626 } 1627 1628 TimeLine &TimeLine::operator=(const TimeLine &other) 1629 { 1630 d = other.d; 1631 return *this; 1632 } 1633 1634 } // namespace 1635 1636 #include "moc_kwineffects.cpp" 1637 #include "moc_kwinglobals.cpp"