1 /*
2     KWin - the KDE window manager
3     This file is part of the KDE project.
4 
5     SPDX-FileCopyrightText: 2006 Lubos Lunak <l.lunak@kde.org>
6     SPDX-FileCopyrightText: 2009 Lucas Murray <lmurray@undefinedfire.com>
7     SPDX-FileCopyrightText: 2018 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
8 
9     SPDX-License-Identifier: GPL-2.0-or-later
10 */
11 
12 #include "kwineffects.h"
13 
14 #include "config-kwin.h"
15 
16 #include <QVariant>
17 #include <QTimeLine>
18 #include <QFontMetrics>
19 #include <QPainter>
20 #include <QPixmap>
21 #include <QtMath>
22 
23 #include <ksharedconfig.h>
24 #include <kconfiggroup.h>
25 
26 #include <KWaylandServer/surface_interface.h>
27 
28 #if defined(__SSE2__)
29 #  include <emmintrin.h>
30 #endif
31 
32 
33 namespace KWin
34 {
35 
setTranslucent()36 void WindowPrePaintData::setTranslucent()
37 {
38     mask |= Effect::PAINT_WINDOW_TRANSLUCENT;
39     mask &= ~Effect::PAINT_WINDOW_OPAQUE;
40     clip = QRegion(); // cannot clip, will be transparent
41 }
42 
setTransformed()43 void WindowPrePaintData::setTransformed()
44 {
45     mask |= Effect::PAINT_WINDOW_TRANSFORMED;
46 }
47 
48 class PaintDataPrivate {
49 public:
PaintDataPrivate()50     PaintDataPrivate()
51         :  scale(1., 1., 1.)
52         , rotationAxis(0, 0, 1.)
53         , rotationAngle(0.)
54     {}
55     QVector3D scale;
56     QVector3D translation;
57 
58     QVector3D rotationAxis;
59     QVector3D rotationOrigin;
60     qreal rotationAngle;
61 };
62 
PaintData()63 PaintData::PaintData()
64     : d(new PaintDataPrivate())
65 {
66 }
67 
~PaintData()68 PaintData::~PaintData()
69 {
70     delete d;
71 }
72 
xScale() const73 qreal PaintData::xScale() const
74 {
75     return d->scale.x();
76 }
77 
yScale() const78 qreal PaintData::yScale() const
79 {
80     return d->scale.y();
81 }
82 
zScale() const83 qreal PaintData::zScale() const
84 {
85     return d->scale.z();
86 }
87 
setScale(const QVector2D & scale)88 void PaintData::setScale(const QVector2D &scale)
89 {
90     d->scale.setX(scale.x());
91     d->scale.setY(scale.y());
92 }
93 
setScale(const QVector3D & scale)94 void PaintData::setScale(const QVector3D &scale)
95 {
96     d->scale = scale;
97 }
setXScale(qreal scale)98 void PaintData::setXScale(qreal scale)
99 {
100     d->scale.setX(scale);
101 }
102 
setYScale(qreal scale)103 void PaintData::setYScale(qreal scale)
104 {
105     d->scale.setY(scale);
106 }
107 
setZScale(qreal scale)108 void PaintData::setZScale(qreal scale)
109 {
110     d->scale.setZ(scale);
111 }
112 
scale() const113 const QVector3D &PaintData::scale() const
114 {
115     return d->scale;
116 }
117 
setXTranslation(qreal translate)118 void PaintData::setXTranslation(qreal translate)
119 {
120     d->translation.setX(translate);
121 }
122 
setYTranslation(qreal translate)123 void PaintData::setYTranslation(qreal translate)
124 {
125     d->translation.setY(translate);
126 }
127 
setZTranslation(qreal translate)128 void PaintData::setZTranslation(qreal translate)
129 {
130     d->translation.setZ(translate);
131 }
132 
translate(qreal x,qreal y,qreal z)133 void PaintData::translate(qreal x, qreal y, qreal z)
134 {
135     translate(QVector3D(x, y, z));
136 }
137 
translate(const QVector3D & t)138 void PaintData::translate(const QVector3D &t)
139 {
140     d->translation += t;
141 }
142 
xTranslation() const143 qreal PaintData::xTranslation() const
144 {
145     return d->translation.x();
146 }
147 
yTranslation() const148 qreal PaintData::yTranslation() const
149 {
150     return d->translation.y();
151 }
152 
zTranslation() const153 qreal PaintData::zTranslation() const
154 {
155     return d->translation.z();
156 }
157 
translation() const158 const QVector3D &PaintData::translation() const
159 {
160     return d->translation;
161 }
162 
rotationAngle() const163 qreal PaintData::rotationAngle() const
164 {
165     return d->rotationAngle;
166 }
167 
rotationAxis() const168 QVector3D PaintData::rotationAxis() const
169 {
170     return d->rotationAxis;
171 }
172 
rotationOrigin() const173 QVector3D PaintData::rotationOrigin() const
174 {
175     return d->rotationOrigin;
176 }
177 
setRotationAngle(qreal angle)178 void PaintData::setRotationAngle(qreal angle)
179 {
180     d->rotationAngle = angle;
181 }
182 
setRotationAxis(Qt::Axis axis)183 void PaintData::setRotationAxis(Qt::Axis axis)
184 {
185     switch (axis) {
186     case Qt::XAxis:
187         setRotationAxis(QVector3D(1, 0, 0));
188         break;
189     case Qt::YAxis:
190         setRotationAxis(QVector3D(0, 1, 0));
191         break;
192     case Qt::ZAxis:
193         setRotationAxis(QVector3D(0, 0, 1));
194         break;
195     }
196 }
197 
setRotationAxis(const QVector3D & axis)198 void PaintData::setRotationAxis(const QVector3D &axis)
199 {
200     d->rotationAxis = axis;
201 }
202 
setRotationOrigin(const QVector3D & origin)203 void PaintData::setRotationOrigin(const QVector3D &origin)
204 {
205     d->rotationOrigin = origin;
206 }
207 
208 class WindowPaintDataPrivate {
209 public:
210     qreal opacity;
211     qreal saturation;
212     qreal brightness;
213     int screen;
214     qreal crossFadeProgress;
215     QMatrix4x4 pMatrix;
216     QMatrix4x4 mvMatrix;
217     QMatrix4x4 screenProjectionMatrix;
218 };
219 
WindowPaintData(EffectWindow * w)220 WindowPaintData::WindowPaintData(EffectWindow *w)
221     : WindowPaintData(w, QMatrix4x4())
222 {
223 }
224 
WindowPaintData(EffectWindow * w,const QMatrix4x4 & screenProjectionMatrix)225 WindowPaintData::WindowPaintData(EffectWindow* w, const QMatrix4x4 &screenProjectionMatrix)
226     : PaintData()
227     , shader(nullptr)
228     , d(new WindowPaintDataPrivate())
229 {
230     d->screenProjectionMatrix = screenProjectionMatrix;
231     setOpacity(w->opacity());
232     setSaturation(1.0);
233     setBrightness(1.0);
234     setScreen(0);
235     setCrossFadeProgress(1.0);
236 }
237 
WindowPaintData(const WindowPaintData & other)238 WindowPaintData::WindowPaintData(const WindowPaintData &other)
239     : PaintData()
240     , shader(other.shader)
241     , d(new WindowPaintDataPrivate())
242 {
243     setXScale(other.xScale());
244     setYScale(other.yScale());
245     setZScale(other.zScale());
246     translate(other.translation());
247     setRotationOrigin(other.rotationOrigin());
248     setRotationAxis(other.rotationAxis());
249     setRotationAngle(other.rotationAngle());
250     setOpacity(other.opacity());
251     setSaturation(other.saturation());
252     setBrightness(other.brightness());
253     setScreen(other.screen());
254     setCrossFadeProgress(other.crossFadeProgress());
255     setProjectionMatrix(other.projectionMatrix());
256     setModelViewMatrix(other.modelViewMatrix());
257     d->screenProjectionMatrix = other.d->screenProjectionMatrix;
258 }
259 
~WindowPaintData()260 WindowPaintData::~WindowPaintData()
261 {
262     delete d;
263 }
264 
opacity() const265 qreal WindowPaintData::opacity() const
266 {
267     return d->opacity;
268 }
269 
saturation() const270 qreal WindowPaintData::saturation() const
271 {
272     return d->saturation;
273 }
274 
brightness() const275 qreal WindowPaintData::brightness() const
276 {
277     return d->brightness;
278 }
279 
screen() const280 int WindowPaintData::screen() const
281 {
282     return d->screen;
283 }
284 
setOpacity(qreal opacity)285 void WindowPaintData::setOpacity(qreal opacity)
286 {
287     d->opacity = opacity;
288 }
289 
setSaturation(qreal saturation) const290 void WindowPaintData::setSaturation(qreal saturation) const
291 {
292     d->saturation = saturation;
293 }
294 
setBrightness(qreal brightness)295 void WindowPaintData::setBrightness(qreal brightness)
296 {
297     d->brightness = brightness;
298 }
299 
setScreen(int screen) const300 void WindowPaintData::setScreen(int screen) const
301 {
302     d->screen = screen;
303 }
304 
crossFadeProgress() const305 qreal WindowPaintData::crossFadeProgress() const
306 {
307     return d->crossFadeProgress;
308 }
309 
setCrossFadeProgress(qreal factor)310 void WindowPaintData::setCrossFadeProgress(qreal factor)
311 {
312     d->crossFadeProgress = qBound(qreal(0.0), factor, qreal(1.0));
313 }
314 
multiplyOpacity(qreal factor)315 qreal WindowPaintData::multiplyOpacity(qreal factor)
316 {
317     d->opacity *= factor;
318     return d->opacity;
319 }
320 
multiplySaturation(qreal factor)321 qreal WindowPaintData::multiplySaturation(qreal factor)
322 {
323     d->saturation *= factor;
324     return d->saturation;
325 }
326 
multiplyBrightness(qreal factor)327 qreal WindowPaintData::multiplyBrightness(qreal factor)
328 {
329     d->brightness *= factor;
330     return d->brightness;
331 }
332 
setProjectionMatrix(const QMatrix4x4 & matrix)333 void WindowPaintData::setProjectionMatrix(const QMatrix4x4 &matrix)
334 {
335     d->pMatrix = matrix;
336 }
337 
projectionMatrix() const338 QMatrix4x4 WindowPaintData::projectionMatrix() const
339 {
340     return d->pMatrix;
341 }
342 
rprojectionMatrix()343 QMatrix4x4 &WindowPaintData::rprojectionMatrix()
344 {
345     return d->pMatrix;
346 }
347 
setModelViewMatrix(const QMatrix4x4 & matrix)348 void WindowPaintData::setModelViewMatrix(const QMatrix4x4 &matrix)
349 {
350     d->mvMatrix = matrix;
351 }
352 
modelViewMatrix() const353 QMatrix4x4 WindowPaintData::modelViewMatrix() const
354 {
355     return d->mvMatrix;
356 }
357 
rmodelViewMatrix()358 QMatrix4x4 &WindowPaintData::rmodelViewMatrix()
359 {
360     return d->mvMatrix;
361 }
362 
operator *=(qreal scale)363 WindowPaintData &WindowPaintData::operator*=(qreal scale)
364 {
365     this->setXScale(this->xScale() * scale);
366     this->setYScale(this->yScale() * scale);
367     this->setZScale(this->zScale() * scale);
368     return *this;
369 }
370 
operator *=(const QVector2D & scale)371 WindowPaintData &WindowPaintData::operator*=(const QVector2D &scale)
372 {
373     this->setXScale(this->xScale() * scale.x());
374     this->setYScale(this->yScale() * scale.y());
375     return *this;
376 }
377 
operator *=(const QVector3D & scale)378 WindowPaintData &WindowPaintData::operator*=(const QVector3D &scale)
379 {
380     this->setXScale(this->xScale() * scale.x());
381     this->setYScale(this->yScale() * scale.y());
382     this->setZScale(this->zScale() * scale.z());
383     return *this;
384 }
385 
operator +=(const QPointF & translation)386 WindowPaintData &WindowPaintData::operator+=(const QPointF &translation)
387 {
388     return this->operator+=(QVector3D(translation));
389 }
390 
operator +=(const QPoint & translation)391 WindowPaintData &WindowPaintData::operator+=(const QPoint &translation)
392 {
393     return this->operator+=(QVector3D(translation));
394 }
395 
operator +=(const QVector2D & translation)396 WindowPaintData &WindowPaintData::operator+=(const QVector2D &translation)
397 {
398     return this->operator+=(QVector3D(translation));
399 }
400 
operator +=(const QVector3D & translation)401 WindowPaintData &WindowPaintData::operator+=(const QVector3D &translation)
402 {
403     translate(translation);
404     return *this;
405 }
406 
screenProjectionMatrix() const407 QMatrix4x4 WindowPaintData::screenProjectionMatrix() const
408 {
409     return d->screenProjectionMatrix;
410 }
411 
412 class ScreenPaintData::Private
413 {
414 public:
415     QMatrix4x4 projectionMatrix;
416     EffectScreen *screen = nullptr;
417 };
418 
ScreenPaintData()419 ScreenPaintData::ScreenPaintData()
420     : PaintData()
421     , d(new Private())
422 {
423 }
424 
ScreenPaintData(const QMatrix4x4 & projectionMatrix,EffectScreen * screen)425 ScreenPaintData::ScreenPaintData(const QMatrix4x4 &projectionMatrix, EffectScreen *screen)
426     : PaintData()
427     , d(new Private())
428 {
429     d->projectionMatrix = projectionMatrix;
430     d->screen = screen;
431 }
432 
433 ScreenPaintData::~ScreenPaintData() = default;
434 
ScreenPaintData(const ScreenPaintData & other)435 ScreenPaintData::ScreenPaintData(const ScreenPaintData &other)
436     : PaintData()
437     , d(new Private())
438 {
439     translate(other.translation());
440     setXScale(other.xScale());
441     setYScale(other.yScale());
442     setZScale(other.zScale());
443     setRotationOrigin(other.rotationOrigin());
444     setRotationAxis(other.rotationAxis());
445     setRotationAngle(other.rotationAngle());
446     d->projectionMatrix = other.d->projectionMatrix;
447     d->screen = other.d->screen;
448 }
449 
operator =(const ScreenPaintData & rhs)450 ScreenPaintData &ScreenPaintData::operator=(const ScreenPaintData &rhs)
451 {
452     setXScale(rhs.xScale());
453     setYScale(rhs.yScale());
454     setZScale(rhs.zScale());
455     setXTranslation(rhs.xTranslation());
456     setYTranslation(rhs.yTranslation());
457     setZTranslation(rhs.zTranslation());
458     setRotationOrigin(rhs.rotationOrigin());
459     setRotationAxis(rhs.rotationAxis());
460     setRotationAngle(rhs.rotationAngle());
461     d->projectionMatrix = rhs.d->projectionMatrix;
462     d->screen = rhs.d->screen;
463     return *this;
464 }
465 
operator *=(qreal scale)466 ScreenPaintData &ScreenPaintData::operator*=(qreal scale)
467 {
468     setXScale(this->xScale() * scale);
469     setYScale(this->yScale() * scale);
470     setZScale(this->zScale() * scale);
471     return *this;
472 }
473 
operator *=(const QVector2D & scale)474 ScreenPaintData &ScreenPaintData::operator*=(const QVector2D &scale)
475 {
476     setXScale(this->xScale() * scale.x());
477     setYScale(this->yScale() * scale.y());
478     return *this;
479 }
480 
operator *=(const QVector3D & scale)481 ScreenPaintData &ScreenPaintData::operator*=(const QVector3D &scale)
482 {
483     setXScale(this->xScale() * scale.x());
484     setYScale(this->yScale() * scale.y());
485     setZScale(this->zScale() * scale.z());
486     return *this;
487 }
488 
operator +=(const QPointF & translation)489 ScreenPaintData &ScreenPaintData::operator+=(const QPointF &translation)
490 {
491     return this->operator+=(QVector3D(translation));
492 }
493 
operator +=(const QPoint & translation)494 ScreenPaintData &ScreenPaintData::operator+=(const QPoint &translation)
495 {
496     return this->operator+=(QVector3D(translation));
497 }
498 
operator +=(const QVector2D & translation)499 ScreenPaintData &ScreenPaintData::operator+=(const QVector2D &translation)
500 {
501     return this->operator+=(QVector3D(translation));
502 }
503 
operator +=(const QVector3D & translation)504 ScreenPaintData &ScreenPaintData::operator+=(const QVector3D &translation)
505 {
506     translate(translation);
507     return *this;
508 }
509 
projectionMatrix() const510 QMatrix4x4 ScreenPaintData::projectionMatrix() const
511 {
512     return d->projectionMatrix;
513 }
514 
screen() const515 EffectScreen *ScreenPaintData::screen() const
516 {
517     return d->screen;
518 }
519 
520 //****************************************
521 // Effect
522 //****************************************
523 
Effect(QObject * parent)524 Effect::Effect(QObject *parent)
525     : QObject(parent)
526 {
527 }
528 
~Effect()529 Effect::~Effect()
530 {
531 }
532 
reconfigure(ReconfigureFlags)533 void Effect::reconfigure(ReconfigureFlags)
534 {
535 }
536 
proxy()537 void* Effect::proxy()
538 {
539     return nullptr;
540 }
541 
windowInputMouseEvent(QEvent *)542 void Effect::windowInputMouseEvent(QEvent*)
543 {
544 }
545 
grabbedKeyboardEvent(QKeyEvent *)546 void Effect::grabbedKeyboardEvent(QKeyEvent*)
547 {
548 }
549 
borderActivated(ElectricBorder)550 bool Effect::borderActivated(ElectricBorder)
551 {
552     return false;
553 }
554 
prePaintScreen(ScreenPrePaintData & data,std::chrono::milliseconds presentTime)555 void Effect::prePaintScreen(ScreenPrePaintData& data, std::chrono::milliseconds presentTime)
556 {
557     effects->prePaintScreen(data, presentTime);
558 }
559 
paintScreen(int mask,const QRegion & region,ScreenPaintData & data)560 void Effect::paintScreen(int mask, const QRegion &region, ScreenPaintData& data)
561 {
562     effects->paintScreen(mask, region, data);
563 }
564 
postPaintScreen()565 void Effect::postPaintScreen()
566 {
567     effects->postPaintScreen();
568 }
569 
prePaintWindow(EffectWindow * w,WindowPrePaintData & data,std::chrono::milliseconds presentTime)570 void Effect::prePaintWindow(EffectWindow* w, WindowPrePaintData& data, std::chrono::milliseconds presentTime)
571 {
572     effects->prePaintWindow(w, data, presentTime);
573 }
574 
paintWindow(EffectWindow * w,int mask,QRegion region,WindowPaintData & data)575 void Effect::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data)
576 {
577     effects->paintWindow(w, mask, region, data);
578 }
579 
postPaintWindow(EffectWindow * w)580 void Effect::postPaintWindow(EffectWindow* w)
581 {
582     effects->postPaintWindow(w);
583 }
584 
paintEffectFrame(KWin::EffectFrame * frame,const QRegion & region,double opacity,double frameOpacity)585 void Effect::paintEffectFrame(KWin::EffectFrame* frame, const QRegion &region, double opacity, double frameOpacity)
586 {
587     effects->paintEffectFrame(frame, region, opacity, frameOpacity);
588 }
589 
provides(Feature)590 bool Effect::provides(Feature)
591 {
592     return false;
593 }
594 
isActive() const595 bool Effect::isActive() const
596 {
597     return true;
598 }
599 
debug(const QString &) const600 QString Effect::debug(const QString &) const
601 {
602     return QString();
603 }
604 
drawWindow(EffectWindow * w,int mask,const QRegion & region,WindowPaintData & data)605 void Effect::drawWindow(EffectWindow* w, int mask, const QRegion &region, WindowPaintData& data)
606 {
607     effects->drawWindow(w, mask, region, data);
608 }
609 
setPositionTransformations(WindowPaintData & data,QRect & region,EffectWindow * w,const QRect & r,Qt::AspectRatioMode aspect)610 void Effect::setPositionTransformations(WindowPaintData& data, QRect& region, EffectWindow* w,
611                                         const QRect& r, Qt::AspectRatioMode aspect)
612 {
613     QSize size = w->size();
614     size.scale(r.size(), aspect);
615     data.setXScale(size.width() / double(w->width()));
616     data.setYScale(size.height() / double(w->height()));
617     int width = int(w->width() * data.xScale());
618     int height = int(w->height() * data.yScale());
619     int x = r.x() + (r.width() - width) / 2;
620     int y = r.y() + (r.height() - height) / 2;
621     region = QRect(x, y, width, height);
622     data.setXTranslation(x - w->x());
623     data.setYTranslation(y - w->y());
624 }
625 
cursorPos()626 QPoint Effect::cursorPos()
627 {
628     return effects->cursorPos();
629 }
630 
animationTime(const KConfigGroup & cfg,const QString & key,int defaultTime)631 double Effect::animationTime(const KConfigGroup& cfg, const QString& key, int defaultTime)
632 {
633     int time = cfg.readEntry(key, 0);
634     return time != 0 ? time : qMax(defaultTime * effects->animationTimeFactor(), 1.);
635 }
636 
animationTime(int defaultTime)637 double Effect::animationTime(int defaultTime)
638 {
639     // at least 1ms, otherwise 0ms times can break some things
640     return qMax(defaultTime * effects->animationTimeFactor(), 1.);
641 }
642 
requestedEffectChainPosition() const643 int Effect::requestedEffectChainPosition() const
644 {
645     return 0;
646 }
647 
xcbConnection() const648 xcb_connection_t *Effect::xcbConnection() const
649 {
650     return effects->xcbConnection();
651 }
652 
x11RootWindow() const653 xcb_window_t Effect::x11RootWindow() const
654 {
655     return effects->x11RootWindow();
656 }
657 
touchDown(qint32 id,const QPointF & pos,quint32 time)658 bool Effect::touchDown(qint32 id, const QPointF &pos, quint32 time)
659 {
660     Q_UNUSED(id)
661     Q_UNUSED(pos)
662     Q_UNUSED(time)
663     return false;
664 }
665 
touchMotion(qint32 id,const QPointF & pos,quint32 time)666 bool Effect::touchMotion(qint32 id, const QPointF &pos, quint32 time)
667 {
668     Q_UNUSED(id)
669     Q_UNUSED(pos)
670     Q_UNUSED(time)
671     return false;
672 }
673 
touchUp(qint32 id,quint32 time)674 bool Effect::touchUp(qint32 id, quint32 time)
675 {
676     Q_UNUSED(id)
677     Q_UNUSED(time)
678     return false;
679 }
680 
perform(Feature feature,const QVariantList & arguments)681 bool Effect::perform(Feature feature, const QVariantList &arguments)
682 {
683     Q_UNUSED(feature)
684     Q_UNUSED(arguments)
685     return false;
686 }
687 
blocksDirectScanout() const688 bool Effect::blocksDirectScanout() const
689 {
690     return true;
691 }
692 
693 //****************************************
694 // EffectFactory
695 //****************************************
EffectPluginFactory()696 EffectPluginFactory::EffectPluginFactory()
697 {
698 }
699 
~EffectPluginFactory()700 EffectPluginFactory::~EffectPluginFactory()
701 {
702 }
703 
enabledByDefault() const704 bool EffectPluginFactory::enabledByDefault() const
705 {
706     return true;
707 }
708 
isSupported() const709 bool EffectPluginFactory::isSupported() const
710 {
711     return true;
712 }
713 
714 //****************************************
715 // EffectsHandler
716 //****************************************
717 
EffectsHandler(CompositingType type)718 EffectsHandler::EffectsHandler(CompositingType type)
719     : compositing_type(type)
720 {
721     if (compositing_type == NoCompositing)
722         return;
723     KWin::effects = this;
724     connect(this, QOverload<int, int>::of(&EffectsHandler::desktopChanged), this, &EffectsHandler::desktopChangedLegacy);
725 }
726 
~EffectsHandler()727 EffectsHandler::~EffectsHandler()
728 {
729     // All effects should already be unloaded by Impl dtor
730     Q_ASSERT(loaded_effects.count() == 0);
731     KWin::effects = nullptr;
732 }
733 
compositingType() const734 CompositingType EffectsHandler::compositingType() const
735 {
736     return compositing_type;
737 }
738 
isOpenGLCompositing() const739 bool EffectsHandler::isOpenGLCompositing() const
740 {
741     return compositing_type & OpenGLCompositing;
742 }
743 
744 EffectsHandler* effects = nullptr;
745 
EffectScreen(QObject * parent)746 EffectScreen::EffectScreen(QObject *parent)
747     : QObject(parent)
748 {
749 }
750 
751 //****************************************
752 // EffectWindow
753 //****************************************
754 
755 class Q_DECL_HIDDEN EffectWindow::Private
756 {
757 public:
758     Private(EffectWindow *q);
759 
760     EffectWindow *q;
761 };
762 
Private(EffectWindow * q)763 EffectWindow::Private::Private(EffectWindow *q)
764     : q(q)
765 {
766 }
767 
EffectWindow(QObject * parent)768 EffectWindow::EffectWindow(QObject *parent)
769     : QObject(parent)
770     , d(new Private(this))
771 {
772 }
773 
~EffectWindow()774 EffectWindow::~EffectWindow()
775 {
776 }
777 
isOnActivity(const QString & activity) const778 bool EffectWindow::isOnActivity(const QString &activity) const
779 {
780     const QStringList _activities = activities();
781     return _activities.isEmpty() || _activities.contains(activity);
782 }
783 
isOnAllActivities() const784 bool EffectWindow::isOnAllActivities() const
785 {
786     return activities().isEmpty();
787 }
788 
setMinimized(bool min)789 void EffectWindow::setMinimized(bool min)
790 {
791     if (min) {
792         minimize();
793     } else {
794         unminimize();
795     }
796 }
797 
isOnCurrentActivity() const798 bool EffectWindow::isOnCurrentActivity() const
799 {
800     return isOnActivity(effects->currentActivity());
801 }
802 
isOnCurrentDesktop() const803 bool EffectWindow::isOnCurrentDesktop() const
804 {
805     return isOnDesktop(effects->currentDesktop());
806 }
807 
isOnDesktop(int d) const808 bool EffectWindow::isOnDesktop(int d) const
809 {
810     const QVector<uint> ds = desktops();
811     return ds.isEmpty() || ds.contains(d);
812 }
813 
isOnAllDesktops() const814 bool EffectWindow::isOnAllDesktops() const
815 {
816     return desktops().isEmpty();
817 }
818 
hasDecoration() const819 bool EffectWindow::hasDecoration() const
820 {
821     return contentsRect() != QRect(0, 0, width(), height());
822 }
823 
isVisible() const824 bool EffectWindow::isVisible() const
825 {
826     return !isMinimized()
827            && isOnCurrentDesktop()
828            && isOnCurrentActivity();
829 }
830 
831 //****************************************
832 // EffectWindowGroup
833 //****************************************
834 
~EffectWindowGroup()835 EffectWindowGroup::~EffectWindowGroup()
836 {
837 }
838 
839 /***************************************************************
840  WindowQuad
841 ***************************************************************/
842 
makeSubQuad(double x1,double y1,double x2,double y2) const843 WindowQuad WindowQuad::makeSubQuad(double x1, double y1, double x2, double y2) const
844 {
845     Q_ASSERT(x1 < x2 && y1 < y2 && x1 >= left() && x2 <= right() && y1 >= top() && y2 <= bottom());
846     WindowQuad ret(*this);
847     // vertices are clockwise starting from topleft
848     ret.verts[ 0 ].px = x1;
849     ret.verts[ 3 ].px = x1;
850     ret.verts[ 1 ].px = x2;
851     ret.verts[ 2 ].px = x2;
852     ret.verts[ 0 ].py = y1;
853     ret.verts[ 1 ].py = y1;
854     ret.verts[ 2 ].py = y2;
855     ret.verts[ 3 ].py = y2;
856 
857     const double xOrigin = left();
858     const double yOrigin = top();
859 
860     const double widthReciprocal  = 1 / (right() - xOrigin);
861     const double heightReciprocal = 1 / (bottom() - yOrigin);
862 
863     for (int i = 0; i < 4; ++i) {
864         const double w1 = (ret.verts[i].px - xOrigin) * widthReciprocal;
865         const double w2 = (ret.verts[i].py - yOrigin) * heightReciprocal;
866 
867         // Use bilinear interpolation to compute the texture coords.
868         ret.verts[i].tx = (1 - w1) * (1 - w2) * verts[0].tx +
869                 w1 * (1 - w2) * verts[1].tx +
870                 w1 * w2 * verts[2].tx + (1 - w1) * w2 * verts[3].tx;
871         ret.verts[i].ty = (1 - w1) * (1 - w2) * verts[0].ty +
872                 w1 * (1 - w2) * verts[1].ty +
873                 w1 * w2 * verts[2].ty + (1 - w1) * w2 * verts[3].ty;
874     }
875 
876     return ret;
877 }
878 
879 /***************************************************************
880  WindowQuadList
881 ***************************************************************/
882 
splitAtX(double x) const883 WindowQuadList WindowQuadList::splitAtX(double x) const
884 {
885     WindowQuadList ret;
886     ret.reserve(count());
887     for (const WindowQuad & quad : *this) {
888         bool wholeleft = true;
889         bool wholeright = true;
890         for (int i = 0;
891                 i < 4;
892                 ++i) {
893             if (quad[ i ].x() < x)
894                 wholeright = false;
895             if (quad[ i ].x() > x)
896                 wholeleft = false;
897         }
898         if (wholeleft || wholeright) { // is whole in one split part
899             ret.append(quad);
900             continue;
901         }
902         if (quad.top() == quad.bottom() || quad.left() == quad.right()) { // quad has no size
903             ret.append(quad);
904             continue;
905         }
906         ret.append(quad.makeSubQuad(quad.left(), quad.top(), x, quad.bottom()));
907         ret.append(quad.makeSubQuad(x, quad.top(), quad.right(), quad.bottom()));
908     }
909     return ret;
910 }
911 
splitAtY(double y) const912 WindowQuadList WindowQuadList::splitAtY(double y) const
913 {
914     WindowQuadList ret;
915     ret.reserve(count());
916     for (const WindowQuad & quad : *this) {
917         bool wholetop = true;
918         bool wholebottom = true;
919         for (int i = 0;
920                 i < 4;
921                 ++i) {
922             if (quad[ i ].y() < y)
923                 wholebottom = false;
924             if (quad[ i ].y() > y)
925                 wholetop = false;
926         }
927         if (wholetop || wholebottom) { // is whole in one split part
928             ret.append(quad);
929             continue;
930         }
931         if (quad.top() == quad.bottom() || quad.left() == quad.right()) { // quad has no size
932             ret.append(quad);
933             continue;
934         }
935         ret.append(quad.makeSubQuad(quad.left(), quad.top(), quad.right(), y));
936         ret.append(quad.makeSubQuad(quad.left(), y, quad.right(), quad.bottom()));
937     }
938     return ret;
939 }
940 
makeGrid(int maxQuadSize) const941 WindowQuadList WindowQuadList::makeGrid(int maxQuadSize) const
942 {
943     if (empty())
944         return *this;
945 
946     // Find the bounding rectangle
947     double left   = first().left();
948     double right  = first().right();
949     double top    = first().top();
950     double bottom = first().bottom();
951 
952     Q_FOREACH (const WindowQuad &quad, *this) {
953         left   = qMin(left,   quad.left());
954         right  = qMax(right,  quad.right());
955         top    = qMin(top,    quad.top());
956         bottom = qMax(bottom, quad.bottom());
957     }
958 
959     WindowQuadList ret;
960 
961     for (const WindowQuad &quad : *this) {
962         const double quadLeft   = quad.left();
963         const double quadRight  = quad.right();
964         const double quadTop    = quad.top();
965         const double quadBottom = quad.bottom();
966 
967         // sanity check, see BUG 390953
968         if (quadLeft == quadRight || quadTop == quadBottom) {
969             ret.append(quad);
970             continue;
971         }
972 
973         // Compute the top-left corner of the first intersecting grid cell
974         const double xBegin = left + qFloor((quadLeft - left) / maxQuadSize) * maxQuadSize;
975         const double yBegin = top  + qFloor((quadTop  - top)  / maxQuadSize) * maxQuadSize;
976 
977         // Loop over all intersecting cells and add sub-quads
978         for (double y = yBegin; y < quadBottom; y += maxQuadSize) {
979             const double y0 = qMax(y, quadTop);
980             const double y1 = qMin(quadBottom, y + maxQuadSize);
981 
982             for (double x = xBegin; x < quadRight; x += maxQuadSize) {
983                 const double x0 = qMax(x, quadLeft);
984                 const double x1 = qMin(quadRight, x + maxQuadSize);
985 
986                 ret.append(quad.makeSubQuad(x0, y0, x1, y1));
987             }
988         }
989     }
990 
991     return ret;
992 }
993 
makeRegularGrid(int xSubdivisions,int ySubdivisions) const994 WindowQuadList WindowQuadList::makeRegularGrid(int xSubdivisions, int ySubdivisions) const
995 {
996     if (empty())
997         return *this;
998 
999     // Find the bounding rectangle
1000     double left   = first().left();
1001     double right  = first().right();
1002     double top    = first().top();
1003     double bottom = first().bottom();
1004 
1005     for (const WindowQuad &quad : *this) {
1006         left   = qMin(left,   quad.left());
1007         right  = qMax(right,  quad.right());
1008         top    = qMin(top,    quad.top());
1009         bottom = qMax(bottom, quad.bottom());
1010     }
1011 
1012     double xIncrement = (right - left) / xSubdivisions;
1013     double yIncrement = (bottom - top) / ySubdivisions;
1014 
1015     WindowQuadList ret;
1016 
1017     for (const WindowQuad &quad : *this) {
1018         const double quadLeft   = quad.left();
1019         const double quadRight  = quad.right();
1020         const double quadTop    = quad.top();
1021         const double quadBottom = quad.bottom();
1022 
1023         // sanity check, see BUG 390953
1024         if (quadLeft == quadRight || quadTop == quadBottom) {
1025             ret.append(quad);
1026             continue;
1027         }
1028 
1029         // Compute the top-left corner of the first intersecting grid cell
1030         const double xBegin = left + qFloor((quadLeft - left) / xIncrement) * xIncrement;
1031         const double yBegin = top  + qFloor((quadTop  - top)  / yIncrement) * yIncrement;
1032 
1033         // Loop over all intersecting cells and add sub-quads
1034         for (double y = yBegin; y < quadBottom; y += yIncrement) {
1035             const double y0 = qMax(y, quadTop);
1036             const double y1 = qMin(quadBottom, y + yIncrement);
1037 
1038             for (double x = xBegin; x < quadRight; x += xIncrement) {
1039                 const double x0 = qMax(x, quadLeft);
1040                 const double x1 = qMin(quadRight, x + xIncrement);
1041 
1042                 ret.append(quad.makeSubQuad(x0, y0, x1, y1));
1043             }
1044         }
1045     }
1046 
1047     return ret;
1048 }
1049 
1050 #ifndef GL_TRIANGLES
1051 #  define GL_TRIANGLES      0x0004
1052 #endif
1053 
1054 #ifndef GL_QUADS
1055 #  define GL_QUADS          0x0007
1056 #endif
1057 
makeInterleavedArrays(unsigned int type,GLVertex2D * vertices,const QMatrix4x4 & textureMatrix) const1058 void WindowQuadList::makeInterleavedArrays(unsigned int type, GLVertex2D *vertices, const QMatrix4x4 &textureMatrix) const
1059 {
1060     // Since we know that the texture matrix just scales and translates
1061     // we can use this information to optimize the transformation
1062     const QVector2D coeff(textureMatrix(0, 0), textureMatrix(1, 1));
1063     const QVector2D offset(textureMatrix(0, 3), textureMatrix(1, 3));
1064 
1065     GLVertex2D *vertex = vertices;
1066 
1067     Q_ASSERT(type == GL_QUADS || type == GL_TRIANGLES);
1068 
1069     switch (type)
1070     {
1071     case GL_QUADS:
1072 #if defined(__SSE2__)
1073         if (!(intptr_t(vertex) & 0xf)) {
1074             for (const WindowQuad &quad : *this) {
1075                 alignas(16) GLVertex2D v[4];
1076 
1077                 for (int j = 0; j < 4; j++) {
1078                     const WindowVertex &wv = quad[j];
1079 
1080                     v[j].position = QVector2D(wv.x(), wv.y());
1081                     v[j].texcoord = QVector2D(wv.u(), wv.v()) * coeff + offset;
1082                 }
1083 
1084                 const __m128i *srcP = reinterpret_cast<const __m128i *>(&v);
1085                 __m128i *dstP = reinterpret_cast<__m128i *>(vertex);
1086 
1087                 _mm_stream_si128(&dstP[0], _mm_load_si128(&srcP[0])); // Top-left
1088                 _mm_stream_si128(&dstP[1], _mm_load_si128(&srcP[1])); // Top-right
1089                 _mm_stream_si128(&dstP[2], _mm_load_si128(&srcP[2])); // Bottom-right
1090                 _mm_stream_si128(&dstP[3], _mm_load_si128(&srcP[3])); // Bottom-left
1091 
1092                 vertex += 4;
1093             }
1094         } else
1095 #endif // __SSE2__
1096         {
1097             for (const WindowQuad &quad : *this) {
1098                 for (int j = 0; j < 4; j++) {
1099                     const WindowVertex &wv = quad[j];
1100 
1101                     GLVertex2D v;
1102                     v.position = QVector2D(wv.x(), wv.y());
1103                     v.texcoord = QVector2D(wv.u(), wv.v()) * coeff + offset;
1104 
1105                     *(vertex++) = v;
1106                 }
1107             }
1108         }
1109         break;
1110 
1111     case GL_TRIANGLES:
1112 #if defined(__SSE2__)
1113         if (!(intptr_t(vertex) & 0xf)) {
1114             for (const WindowQuad &quad : *this) {
1115                 alignas(16) GLVertex2D v[4];
1116 
1117                 for (int j = 0; j < 4; j++) {
1118                     const WindowVertex &wv = quad[j];
1119 
1120                     v[j].position = QVector2D(wv.x(), wv.y());
1121                     v[j].texcoord = QVector2D(wv.u(), wv.v()) * coeff + offset;
1122                 }
1123 
1124                 const __m128i *srcP = reinterpret_cast<const __m128i *>(&v);
1125                 __m128i *dstP = reinterpret_cast<__m128i *>(vertex);
1126 
1127                 __m128i src[4];
1128                 src[0] = _mm_load_si128(&srcP[0]); // Top-left
1129                 src[1] = _mm_load_si128(&srcP[1]); // Top-right
1130                 src[2] = _mm_load_si128(&srcP[2]); // Bottom-right
1131                 src[3] = _mm_load_si128(&srcP[3]); // Bottom-left
1132 
1133                 // First triangle
1134                 _mm_stream_si128(&dstP[0], src[1]); // Top-right
1135                 _mm_stream_si128(&dstP[1], src[0]); // Top-left
1136                 _mm_stream_si128(&dstP[2], src[3]); // Bottom-left
1137 
1138                 // Second triangle
1139                 _mm_stream_si128(&dstP[3], src[3]); // Bottom-left
1140                 _mm_stream_si128(&dstP[4], src[2]); // Bottom-right
1141                 _mm_stream_si128(&dstP[5], src[1]); // Top-right
1142 
1143                 vertex += 6;
1144             }
1145         } else
1146 #endif // __SSE2__
1147         {
1148             for (const WindowQuad &quad : *this) {
1149                 GLVertex2D v[4]; // Four unique vertices / quad
1150 
1151                 for (int j = 0; j < 4; j++) {
1152                     const WindowVertex &wv = quad[j];
1153 
1154                     v[j].position = QVector2D(wv.x(), wv.y());
1155                     v[j].texcoord = QVector2D(wv.u(), wv.v()) * coeff + offset;
1156                 }
1157 
1158                 // First triangle
1159                 *(vertex++) = v[1]; // Top-right
1160                 *(vertex++) = v[0]; // Top-left
1161                 *(vertex++) = v[3]; // Bottom-left
1162 
1163                 // Second triangle
1164                 *(vertex++) = v[3]; // Bottom-left
1165                 *(vertex++) = v[2]; // Bottom-right
1166                 *(vertex++) = v[1]; // Top-right
1167             }
1168         }
1169         break;
1170 
1171     default:
1172         break;
1173     }
1174 }
1175 
makeArrays(float ** vertices,float ** texcoords,const QSizeF & size,bool yInverted) const1176 void WindowQuadList::makeArrays(float **vertices, float **texcoords, const QSizeF &size, bool yInverted) const
1177 {
1178     *vertices = new float[count() * 6 * 2];
1179     *texcoords = new float[count() * 6 * 2];
1180 
1181     float *vpos = *vertices;
1182     float *tpos = *texcoords;
1183 
1184      // Note: The positions in a WindowQuad are stored in clockwise order
1185     const int index[] = { 1, 0, 3, 3, 2, 1 };
1186 
1187     for (const WindowQuad &quad : *this) {
1188         for (int j = 0; j < 6; j++) {
1189             const WindowVertex &wv = quad[index[j]];
1190 
1191             *vpos++ = wv.x();
1192             *vpos++ = wv.y();
1193 
1194             *tpos++ = wv.u() / size.width();
1195             *tpos++ = yInverted ? (wv.v() / size.height()) : (1.0 - wv.v() / size.height());
1196         }
1197     }
1198 }
1199 
1200 /***************************************************************
1201  Motion1D
1202 ***************************************************************/
1203 
Motion1D(double initial,double strength,double smoothness)1204 Motion1D::Motion1D(double initial, double strength, double smoothness)
1205     : Motion<double>(initial, strength, smoothness)
1206 {
1207 }
1208 
Motion1D(const Motion1D & other)1209 Motion1D::Motion1D(const Motion1D &other)
1210     : Motion<double>(other)
1211 {
1212 }
1213 
~Motion1D()1214 Motion1D::~Motion1D()
1215 {
1216 }
1217 
1218 /***************************************************************
1219  Motion2D
1220 ***************************************************************/
1221 
Motion2D(QPointF initial,double strength,double smoothness)1222 Motion2D::Motion2D(QPointF initial, double strength, double smoothness)
1223     : Motion<QPointF>(initial, strength, smoothness)
1224 {
1225 }
1226 
Motion2D(const Motion2D & other)1227 Motion2D::Motion2D(const Motion2D &other)
1228     : Motion<QPointF>(other)
1229 {
1230 }
1231 
~Motion2D()1232 Motion2D::~Motion2D()
1233 {
1234 }
1235 
1236 /***************************************************************
1237  WindowMotionManager
1238 ***************************************************************/
1239 
WindowMotionManager(bool useGlobalAnimationModifier)1240 WindowMotionManager::WindowMotionManager(bool useGlobalAnimationModifier)
1241     :   m_useGlobalAnimationModifier(useGlobalAnimationModifier)
1242 
1243 {
1244     // TODO: Allow developer to modify motion attributes
1245 } // TODO: What happens when the window moves by an external force?
1246 
~WindowMotionManager()1247 WindowMotionManager::~WindowMotionManager()
1248 {
1249 }
1250 
manage(EffectWindow * w)1251 void WindowMotionManager::manage(EffectWindow *w)
1252 {
1253     if (m_managedWindows.contains(w))
1254         return;
1255 
1256     double strength = 0.08;
1257     double smoothness = 4.0;
1258     if (m_useGlobalAnimationModifier && effects->animationTimeFactor()) {
1259         // If the factor is == 0 then we just skip the calculation completely
1260         strength = 0.08 / effects->animationTimeFactor();
1261         smoothness = effects->animationTimeFactor() * 4.0;
1262     }
1263 
1264     WindowMotion &motion = m_managedWindows[ w ];
1265     motion.translation.setStrength(strength);
1266     motion.translation.setSmoothness(smoothness);
1267     motion.scale.setStrength(strength * 1.33);
1268     motion.scale.setSmoothness(smoothness / 2.0);
1269 
1270     motion.translation.setValue(w->pos());
1271     motion.scale.setValue(QPointF(1.0, 1.0));
1272 }
1273 
unmanage(EffectWindow * w)1274 void WindowMotionManager::unmanage(EffectWindow *w)
1275 {
1276     m_movingWindowsSet.remove(w);
1277     m_managedWindows.remove(w);
1278 }
1279 
unmanageAll()1280 void WindowMotionManager::unmanageAll()
1281 {
1282     m_managedWindows.clear();
1283     m_movingWindowsSet.clear();
1284 }
1285 
calculate(int time)1286 void WindowMotionManager::calculate(int time)
1287 {
1288     if (!effects->animationTimeFactor()) {
1289         // Just skip it completely if the user wants no animation
1290         m_movingWindowsSet.clear();
1291         QHash<EffectWindow*, WindowMotion>::iterator it = m_managedWindows.begin();
1292         for (; it != m_managedWindows.end(); ++it) {
1293             WindowMotion *motion = &it.value();
1294             motion->translation.finish();
1295             motion->scale.finish();
1296         }
1297     }
1298 
1299     QHash<EffectWindow*, WindowMotion>::iterator it = m_managedWindows.begin();
1300     for (; it != m_managedWindows.end(); ++it) {
1301         WindowMotion *motion = &it.value();
1302         int stopped = 0;
1303 
1304         // TODO: What happens when distance() == 0 but we are still moving fast?
1305         // TODO: Motion needs to be calculated from the window's center
1306 
1307         Motion2D *trans = &motion->translation;
1308         if (trans->distance().isNull())
1309             ++stopped;
1310         else {
1311             // Still moving
1312             trans->calculate(time);
1313             const short fx = trans->target().x() <= trans->startValue().x() ? -1 : 1;
1314             const short fy = trans->target().y() <= trans->startValue().y() ? -1 : 1;
1315             if (trans->distance().x()*fx/0.5 < 1.0 && trans->velocity().x()*fx/0.2 < 1.0 &&
1316                 trans->distance().y()*fy/0.5 < 1.0 && trans->velocity().y()*fy/0.2 < 1.0) {
1317                 // Hide tiny oscillations
1318                 motion->translation.finish();
1319                 ++stopped;
1320             }
1321         }
1322 
1323         Motion2D *scale = &motion->scale;
1324         if (scale->distance().isNull())
1325             ++stopped;
1326         else {
1327             // Still scaling
1328             scale->calculate(time);
1329             const short fx = scale->target().x() < 1.0 ? -1 : 1;
1330             const short fy = scale->target().y() < 1.0 ? -1 : 1;
1331             if (scale->distance().x()*fx/0.001 < 1.0 && scale->velocity().x()*fx/0.05 < 1.0 &&
1332                 scale->distance().y()*fy/0.001 < 1.0 && scale->velocity().y()*fy/0.05 < 1.0) {
1333                 // Hide tiny oscillations
1334                 motion->scale.finish();
1335                 ++stopped;
1336             }
1337         }
1338 
1339         // We just finished this window's motion
1340         if (stopped == 2)
1341             m_movingWindowsSet.remove(it.key());
1342     }
1343 }
1344 
reset()1345 void WindowMotionManager::reset()
1346 {
1347     QHash<EffectWindow*, WindowMotion>::iterator it = m_managedWindows.begin();
1348     for (; it != m_managedWindows.end(); ++it) {
1349         WindowMotion *motion = &it.value();
1350         EffectWindow *window = it.key();
1351         motion->translation.setTarget(window->pos());
1352         motion->translation.finish();
1353         motion->scale.setTarget(QPointF(1.0, 1.0));
1354         motion->scale.finish();
1355     }
1356 }
1357 
reset(EffectWindow * w)1358 void WindowMotionManager::reset(EffectWindow *w)
1359 {
1360     QHash<EffectWindow*, WindowMotion>::iterator it = m_managedWindows.find(w);
1361     if (it == m_managedWindows.end())
1362         return;
1363 
1364     WindowMotion *motion = &it.value();
1365     motion->translation.setTarget(w->pos());
1366     motion->translation.finish();
1367     motion->scale.setTarget(QPointF(1.0, 1.0));
1368     motion->scale.finish();
1369 }
1370 
apply(EffectWindow * w,WindowPaintData & data)1371 void WindowMotionManager::apply(EffectWindow *w, WindowPaintData &data)
1372 {
1373     QHash<EffectWindow*, WindowMotion>::iterator it = m_managedWindows.find(w);
1374     if (it == m_managedWindows.end())
1375         return;
1376 
1377     // TODO: Take into account existing scale so that we can work with multiple managers (E.g. Present windows + grid)
1378     WindowMotion *motion = &it.value();
1379     data += (motion->translation.value() - QPointF(w->x(), w->y()));
1380     data *= QVector2D(motion->scale.value());
1381 }
1382 
moveWindow(EffectWindow * w,QPoint target,double scale,double yScale)1383 void WindowMotionManager::moveWindow(EffectWindow *w, QPoint target, double scale, double yScale)
1384 {
1385     QHash<EffectWindow*, WindowMotion>::iterator it = m_managedWindows.find(w);
1386     if (it == m_managedWindows.end())
1387         abort(); // Notify the effect author that they did something wrong
1388 
1389     WindowMotion *motion = &it.value();
1390 
1391     if (yScale == 0.0)
1392         yScale = scale;
1393     QPointF scalePoint(scale, yScale);
1394 
1395     if (motion->translation.value() == target && motion->scale.value() == scalePoint)
1396         return; // Window already at that position
1397 
1398     motion->translation.setTarget(target);
1399     motion->scale.setTarget(scalePoint);
1400 
1401     m_movingWindowsSet << w;
1402 }
1403 
transformedGeometry(EffectWindow * w) const1404 QRectF WindowMotionManager::transformedGeometry(EffectWindow *w) const
1405 {
1406     QHash<EffectWindow*, WindowMotion>::const_iterator it = m_managedWindows.constFind(w);
1407     if (it == m_managedWindows.end())
1408         return w->frameGeometry();
1409 
1410     const WindowMotion *motion = &it.value();
1411     QRectF geometry(w->frameGeometry());
1412 
1413     // TODO: Take into account existing scale so that we can work with multiple managers (E.g. Present windows + grid)
1414     geometry.moveTo(motion->translation.value());
1415     geometry.setWidth(geometry.width() * motion->scale.value().x());
1416     geometry.setHeight(geometry.height() * motion->scale.value().y());
1417 
1418     return geometry;
1419 }
1420 
setTransformedGeometry(EffectWindow * w,const QRectF & geometry)1421 void WindowMotionManager::setTransformedGeometry(EffectWindow *w, const QRectF &geometry)
1422 {
1423     QHash<EffectWindow*, WindowMotion>::iterator it = m_managedWindows.find(w);
1424     if (it == m_managedWindows.end())
1425         return;
1426     WindowMotion *motion = &it.value();
1427     motion->translation.setValue(geometry.topLeft());
1428     motion->scale.setValue(QPointF(geometry.width() / qreal(w->width()), geometry.height() / qreal(w->height())));
1429 }
1430 
targetGeometry(EffectWindow * w) const1431 QRectF WindowMotionManager::targetGeometry(EffectWindow *w) const
1432 {
1433     QHash<EffectWindow*, WindowMotion>::const_iterator it = m_managedWindows.constFind(w);
1434     if (it == m_managedWindows.end())
1435         return w->frameGeometry();
1436 
1437     const WindowMotion *motion = &it.value();
1438     QRectF geometry(w->frameGeometry());
1439 
1440     // TODO: Take into account existing scale so that we can work with multiple managers (E.g. Present windows + grid)
1441     geometry.moveTo(motion->translation.target());
1442     geometry.setWidth(geometry.width() * motion->scale.target().x());
1443     geometry.setHeight(geometry.height() * motion->scale.target().y());
1444 
1445     return geometry;
1446 }
1447 
windowAtPoint(QPoint point,bool useStackingOrder) const1448 EffectWindow* WindowMotionManager::windowAtPoint(QPoint point, bool useStackingOrder) const
1449 {
1450     Q_UNUSED(useStackingOrder);
1451     // TODO: Stacking order uses EffectsHandler::stackingOrder() then filters by m_managedWindows
1452     QHash< EffectWindow*, WindowMotion >::ConstIterator it = m_managedWindows.constBegin();
1453     while (it != m_managedWindows.constEnd()) {
1454         if (transformedGeometry(it.key()).contains(point))
1455             return it.key();
1456         ++it;
1457     }
1458 
1459     return nullptr;
1460 }
1461 
1462 /***************************************************************
1463  EffectFramePrivate
1464 ***************************************************************/
1465 class EffectFramePrivate
1466 {
1467 public:
1468     EffectFramePrivate();
1469     ~EffectFramePrivate();
1470 
1471     bool crossFading;
1472     qreal crossFadeProgress;
1473     QMatrix4x4 screenProjectionMatrix;
1474 };
1475 
EffectFramePrivate()1476 EffectFramePrivate::EffectFramePrivate()
1477     : crossFading(false)
1478     , crossFadeProgress(1.0)
1479 {
1480 }
1481 
~EffectFramePrivate()1482 EffectFramePrivate::~EffectFramePrivate()
1483 {
1484 }
1485 
1486 /***************************************************************
1487  EffectFrame
1488 ***************************************************************/
EffectFrame()1489 EffectFrame::EffectFrame()
1490     : d(new EffectFramePrivate)
1491 {
1492 }
1493 
~EffectFrame()1494 EffectFrame::~EffectFrame()
1495 {
1496     delete d;
1497 }
1498 
crossFadeProgress() const1499 qreal EffectFrame::crossFadeProgress() const
1500 {
1501     return d->crossFadeProgress;
1502 }
1503 
setCrossFadeProgress(qreal progress)1504 void EffectFrame::setCrossFadeProgress(qreal progress)
1505 {
1506     d->crossFadeProgress = progress;
1507 }
1508 
isCrossFade() const1509 bool EffectFrame::isCrossFade() const
1510 {
1511     return d->crossFading;
1512 }
1513 
enableCrossFade(bool enable)1514 void EffectFrame::enableCrossFade(bool enable)
1515 {
1516     d->crossFading = enable;
1517 }
1518 
screenProjectionMatrix() const1519 QMatrix4x4 EffectFrame::screenProjectionMatrix() const
1520 {
1521     return d->screenProjectionMatrix;
1522 }
1523 
setScreenProjectionMatrix(const QMatrix4x4 & spm)1524 void EffectFrame::setScreenProjectionMatrix(const QMatrix4x4 &spm)
1525 {
1526     d->screenProjectionMatrix = spm;
1527 }
1528 
1529 /***************************************************************
1530  TimeLine
1531 ***************************************************************/
1532 
1533 class Q_DECL_HIDDEN TimeLine::Data : public QSharedData
1534 {
1535 public:
1536     std::chrono::milliseconds duration;
1537     Direction direction;
1538     QEasingCurve easingCurve;
1539 
1540     std::chrono::milliseconds elapsed = std::chrono::milliseconds::zero();
1541     bool done = false;
1542     RedirectMode sourceRedirectMode = RedirectMode::Relaxed;
1543     RedirectMode targetRedirectMode = RedirectMode::Strict;
1544 };
1545 
TimeLine(std::chrono::milliseconds duration,Direction direction)1546 TimeLine::TimeLine(std::chrono::milliseconds duration, Direction direction)
1547     : d(new Data)
1548 {
1549     Q_ASSERT(duration > std::chrono::milliseconds::zero());
1550     d->duration = duration;
1551     d->direction = direction;
1552 }
1553 
TimeLine(const TimeLine & other)1554 TimeLine::TimeLine(const TimeLine &other)
1555     : d(other.d)
1556 {
1557 }
1558 
1559 TimeLine::~TimeLine() = default;
1560 
progress() const1561 qreal TimeLine::progress() const
1562 {
1563     return static_cast<qreal>(d->elapsed.count()) / d->duration.count();
1564 }
1565 
value() const1566 qreal TimeLine::value() const
1567 {
1568     const qreal t = progress();
1569     return d->easingCurve.valueForProgress(
1570         d->direction == Backward ? 1.0 - t : t);
1571 }
1572 
update(std::chrono::milliseconds delta)1573 void TimeLine::update(std::chrono::milliseconds delta)
1574 {
1575     Q_ASSERT(delta >= std::chrono::milliseconds::zero());
1576     if (d->done) {
1577         return;
1578     }
1579     d->elapsed += delta;
1580     if (d->elapsed >= d->duration) {
1581         d->done = true;
1582         d->elapsed = d->duration;
1583     }
1584 }
1585 
elapsed() const1586 std::chrono::milliseconds TimeLine::elapsed() const
1587 {
1588     return d->elapsed;
1589 }
1590 
setElapsed(std::chrono::milliseconds elapsed)1591 void TimeLine::setElapsed(std::chrono::milliseconds elapsed)
1592 {
1593     Q_ASSERT(elapsed >= std::chrono::milliseconds::zero());
1594     if (elapsed == d->elapsed) {
1595         return;
1596     }
1597     reset();
1598     update(elapsed);
1599 }
1600 
duration() const1601 std::chrono::milliseconds TimeLine::duration() const
1602 {
1603     return d->duration;
1604 }
1605 
setDuration(std::chrono::milliseconds duration)1606 void TimeLine::setDuration(std::chrono::milliseconds duration)
1607 {
1608     Q_ASSERT(duration > std::chrono::milliseconds::zero());
1609     if (duration == d->duration) {
1610         return;
1611     }
1612     d->elapsed = std::chrono::milliseconds(qRound(progress() * duration.count()));
1613     d->duration = duration;
1614     if (d->elapsed == d->duration) {
1615         d->done = true;
1616     }
1617 }
1618 
direction() const1619 TimeLine::Direction TimeLine::direction() const
1620 {
1621     return d->direction;
1622 }
1623 
setDirection(TimeLine::Direction direction)1624 void TimeLine::setDirection(TimeLine::Direction direction)
1625 {
1626     if (d->direction == direction) {
1627         return;
1628     }
1629 
1630     d->direction = direction;
1631 
1632     if (d->elapsed > std::chrono::milliseconds::zero()
1633             || d->sourceRedirectMode == RedirectMode::Strict) {
1634         d->elapsed = d->duration - d->elapsed;
1635     }
1636 
1637     if (d->done && d->targetRedirectMode == RedirectMode::Relaxed) {
1638         d->done = false;
1639     }
1640 
1641     if (d->elapsed >= d->duration) {
1642         d->done = true;
1643     }
1644 }
1645 
toggleDirection()1646 void TimeLine::toggleDirection()
1647 {
1648     setDirection(d->direction == Forward ? Backward : Forward);
1649 }
1650 
easingCurve() const1651 QEasingCurve TimeLine::easingCurve() const
1652 {
1653     return d->easingCurve;
1654 }
1655 
setEasingCurve(const QEasingCurve & easingCurve)1656 void TimeLine::setEasingCurve(const QEasingCurve &easingCurve)
1657 {
1658     d->easingCurve = easingCurve;
1659 }
1660 
setEasingCurve(QEasingCurve::Type type)1661 void TimeLine::setEasingCurve(QEasingCurve::Type type)
1662 {
1663     d->easingCurve.setType(type);
1664 }
1665 
running() const1666 bool TimeLine::running() const
1667 {
1668     return d->elapsed != std::chrono::milliseconds::zero()
1669         && d->elapsed != d->duration;
1670 }
1671 
done() const1672 bool TimeLine::done() const
1673 {
1674     return d->done;
1675 }
1676 
reset()1677 void TimeLine::reset()
1678 {
1679     d->elapsed = std::chrono::milliseconds::zero();
1680     d->done = false;
1681 }
1682 
sourceRedirectMode() const1683 TimeLine::RedirectMode TimeLine::sourceRedirectMode() const
1684 {
1685     return d->sourceRedirectMode;
1686 }
1687 
setSourceRedirectMode(RedirectMode mode)1688 void TimeLine::setSourceRedirectMode(RedirectMode mode)
1689 {
1690     d->sourceRedirectMode = mode;
1691 }
1692 
targetRedirectMode() const1693 TimeLine::RedirectMode TimeLine::targetRedirectMode() const
1694 {
1695     return d->targetRedirectMode;
1696 }
1697 
setTargetRedirectMode(RedirectMode mode)1698 void TimeLine::setTargetRedirectMode(RedirectMode mode)
1699 {
1700     d->targetRedirectMode = mode;
1701 }
1702 
operator =(const TimeLine & other)1703 TimeLine &TimeLine::operator=(const TimeLine &other)
1704 {
1705     d = other.d;
1706     return *this;
1707 }
1708 
1709 } // namespace
1710 
1711 #include "moc_kwinglobals.cpp"
1712