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 ®ion, 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 ®ion, 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 ®ion, 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