Changeset View
Changeset View
Standalone View
Standalone View
effects/sheet/sheet.cpp
1 | /******************************************************************** | 1 | /******************************************************************** | ||
---|---|---|---|---|---|
2 | KWin - the KDE window manager | 2 | KWin - the KDE window manager | ||
3 | This file is part of the KDE project. | 3 | This file is part of the KDE project. | ||
4 | 4 | | |||
5 | Copyright (C) 2007 Philip Falkner <philip.falkner@gmail.com> | 5 | Copyright (C) 2007 Philip Falkner <philip.falkner@gmail.com> | ||
6 | Copyright (C) 2009 Martin Gräßlin <mgraesslin@kde.org> | 6 | Copyright (C) 2009 Martin Gräßlin <mgraesslin@kde.org> | ||
7 | Copyright (C) 2018 Vlad Zagorodniy <vladzzag@gmail.com> | ||||
7 | 8 | | |||
8 | This program is free software; you can redistribute it and/or modify | 9 | This program is free software; you can redistribute it and/or modify | ||
9 | it under the terms of the GNU General Public License as published by | 10 | it under the terms of the GNU General Public License as published by | ||
10 | the Free Software Foundation; either version 2 of the License, or | 11 | the Free Software Foundation; either version 2 of the License, or | ||
11 | (at your option) any later version. | 12 | (at your option) any later version. | ||
12 | 13 | | |||
13 | This program is distributed in the hope that it will be useful, | 14 | This program is distributed in the hope that it will be useful, | ||
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | GNU General Public License for more details. | 17 | GNU General Public License for more details. | ||
17 | 18 | | |||
18 | You should have received a copy of the GNU General Public License | 19 | You should have received a copy of the GNU General Public License | ||
19 | along with this program. If not, see <http://www.gnu.org/licenses/>. | 20 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
20 | *********************************************************************/ | 21 | *********************************************************************/ | ||
21 | 22 | | |||
23 | // own | ||||
22 | #include "sheet.h" | 24 | #include "sheet.h" | ||
23 | #include "sheetconfig.h" | | |||
24 | 25 | | |||
25 | #include <QTimeLine> | 26 | // KConfigSkeleton | ||
26 | #include <QGraphicsRotation> | 27 | #include "sheetconfig.h" | ||
27 | #include <QVector3D> | | |||
28 | 28 | | |||
29 | // Effect is based on fade effect by Philip Falkner | 29 | // Qt | ||
30 | #include <QMatrix4x4> | ||||
30 | 31 | | |||
31 | namespace KWin | 32 | namespace KWin | ||
32 | { | 33 | { | ||
33 | 34 | | |||
34 | SheetEffect::SheetEffect() | 35 | SheetEffect::SheetEffect() | ||
35 | { | 36 | { | ||
36 | initConfig<SheetConfig>(); | 37 | initConfig<SheetConfig>(); | ||
37 | reconfigure(ReconfigureAll); | 38 | reconfigure(ReconfigureAll); | ||
38 | connect(effects, SIGNAL(windowAdded(KWin::EffectWindow*)), this, SLOT(slotWindowAdded(KWin::EffectWindow*))); | | |||
39 | connect(effects, SIGNAL(windowClosed(KWin::EffectWindow*)), this, SLOT(slotWindowClosed(KWin::EffectWindow*))); | | |||
40 | connect(effects, SIGNAL(windowDeleted(KWin::EffectWindow*)), this, SLOT(slotWindowDeleted(KWin::EffectWindow*))); | | |||
41 | } | | |||
42 | 39 | | |||
43 | bool SheetEffect::supported() | 40 | connect(effects, &EffectsHandler::windowAdded, this, &SheetEffect::slotWindowAdded); | ||
44 | { | 41 | connect(effects, &EffectsHandler::windowClosed, this, &SheetEffect::slotWindowClosed); | ||
45 | return effects->isOpenGLCompositing() && effects->animationsSupported(); | 42 | connect(effects, &EffectsHandler::windowDeleted, this, &SheetEffect::slotWindowDeleted); | ||
46 | } | 43 | } | ||
47 | 44 | | |||
48 | void SheetEffect::reconfigure(ReconfigureFlags) | 45 | void SheetEffect::reconfigure(ReconfigureFlags flags) | ||
49 | { | 46 | { | ||
47 | Q_UNUSED(flags) | ||||
48 | | ||||
50 | SheetConfig::self()->read(); | 49 | SheetConfig::self()->read(); | ||
51 | duration = animationTime(SheetConfig::animationTime() != 0 ? SheetConfig::animationTime() : 500); | 50 | | ||
51 | // TODO: Rename AnimationTime config key to Duration. | ||||
52 | const int d = animationTime(SheetConfig::animationTime() != 0 | ||||
53 | ? SheetConfig::animationTime() | ||||
54 | : 500); | ||||
55 | m_duration = std::chrono::milliseconds(static_cast<int>(d)); | ||||
52 | } | 56 | } | ||
53 | 57 | | |||
54 | void SheetEffect::prePaintScreen(ScreenPrePaintData& data, int time) | 58 | void SheetEffect::prePaintScreen(ScreenPrePaintData &data, int time) | ||
55 | { | 59 | { | ||
56 | if (!windows.isEmpty()) { | 60 | const std::chrono::milliseconds delta(time); | ||
57 | data.mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS; | 61 | | ||
58 | screenTime = time; | 62 | auto animationIt = m_animations.begin(); | ||
63 | while (animationIt != m_animations.end()) { | ||||
64 | (*animationIt).timeLine.update(delta); | ||||
65 | ++animationIt; | ||||
59 | } | 66 | } | ||
67 | | ||||
68 | data.mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS; | ||||
69 | | ||||
60 | effects->prePaintScreen(data, time); | 70 | effects->prePaintScreen(data, time); | ||
61 | } | 71 | } | ||
62 | 72 | | |||
63 | void SheetEffect::prePaintWindow(EffectWindow* w, WindowPrePaintData& data, int time) | 73 | void SheetEffect::prePaintWindow(EffectWindow *w, WindowPrePaintData &data, int time) | ||
64 | { | 74 | { | ||
65 | InfoMap::iterator info = windows.find(w); | 75 | if (m_animations.contains(w)) { | ||
66 | if (info != windows.end()) { | | |||
67 | data.setTransformed(); | 76 | data.setTransformed(); | ||
68 | if (info->added) | | |||
69 | info->timeLine->setCurrentTime(info->timeLine->currentTime() + screenTime); | | |||
70 | else if (info->closed) { | | |||
71 | info->timeLine->setCurrentTime(info->timeLine->currentTime() - screenTime); | | |||
72 | if (info->deleted) | | |||
73 | w->enablePainting(EffectWindow::PAINT_DISABLED_BY_DELETE); | 77 | w->enablePainting(EffectWindow::PAINT_DISABLED_BY_DELETE); | ||
74 | } | 78 | } | ||
75 | } | | |||
76 | 79 | | |||
77 | effects->prePaintWindow(w, data, time); | 80 | effects->prePaintWindow(w, data, time); | ||
78 | | ||||
79 | // if the window isn't to be painted, then let's make sure | | |||
80 | // to track its progress | | |||
81 | if (info != windows.end() && !w->isPaintingEnabled() && !effects->activeFullScreenEffect()) | | |||
82 | w->addRepaintFull(); | | |||
83 | } | 81 | } | ||
84 | 82 | | |||
85 | void SheetEffect::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data) | 83 | void SheetEffect::paintWindow(EffectWindow *w, int mask, QRegion region, WindowPaintData &data) | ||
86 | { | 84 | { | ||
87 | InfoMap::const_iterator info = windows.constFind(w); | 85 | auto animationIt = m_animations.constFind(w); | ||
88 | if (info == windows.constEnd()) { | 86 | if (animationIt == m_animations.constEnd()) { | ||
89 | effects->paintWindow(w, mask, region, data); | 87 | effects->paintWindow(w, mask, region, data); | ||
90 | return; | 88 | return; | ||
91 | } | 89 | } | ||
92 | 90 | | |||
93 | // Perspective projection distorts objects near edges of the viewport | 91 | // Perspective projection distorts objects near edges of the viewport | ||
94 | // in undesired way. To fix this, the center of the window will be | 92 | // in undesired way. To fix this, the center of the window will be | ||
95 | // moved to the origin, after applying perspective projection, the | 93 | // moved to the origin, after applying perspective projection, the | ||
96 | // center is moved back to its "original" projected position. Overall, | 94 | // center is moved back to its "original" projected position. Overall, | ||
97 | // this is how the window will be transformed: | 95 | // this is how the window will be transformed: | ||
98 | // [move to the origin] -> [scale] -> [rotate] -> [translate] -> | 96 | // [move to the origin] -> [scale] -> [rotate] -> [translate] -> | ||
99 | // -> [perspective projection] -> [reverse "move to the origin"] | 97 | // -> [perspective projection] -> [reverse "move to the origin"] | ||
100 | const QMatrix4x4 oldProjMatrix = data.screenProjectionMatrix(); | 98 | const QMatrix4x4 oldProjMatrix = data.screenProjectionMatrix(); | ||
101 | const QRectF windowGeo = w->geometry(); | 99 | const QRectF windowGeo = w->geometry(); | ||
102 | const QVector3D invOffset = oldProjMatrix.map(QVector3D(windowGeo.center())); | 100 | const QVector3D invOffset = oldProjMatrix.map(QVector3D(windowGeo.center())); | ||
103 | QMatrix4x4 invOffsetMatrix; | 101 | QMatrix4x4 invOffsetMatrix; | ||
104 | invOffsetMatrix.translate(invOffset.x(), invOffset.y()); | 102 | invOffsetMatrix.translate(invOffset.x(), invOffset.y()); | ||
105 | data.setProjectionMatrix(invOffsetMatrix * oldProjMatrix); | 103 | data.setProjectionMatrix(invOffsetMatrix * oldProjMatrix); | ||
106 | 104 | | |||
107 | // Move the center of the window to the origin. | 105 | // Move the center of the window to the origin. | ||
108 | const QRectF screenGeo = effects->virtualScreenGeometry(); | 106 | const QRectF screenGeo = effects->virtualScreenGeometry(); | ||
109 | const QPointF offset = screenGeo.center() - windowGeo.center(); | 107 | const QPointF offset = screenGeo.center() - windowGeo.center(); | ||
110 | data.translate(offset.x(), offset.y()); | 108 | data.translate(offset.x(), offset.y()); | ||
111 | 109 | | |||
112 | const double progress = info->timeLine->currentValue(); | 110 | const qreal t = (*animationIt).timeLine.value(); | ||
113 | QGraphicsRotation rot; | | |||
114 | data.setRotationAxis(Qt::XAxis); | 111 | data.setRotationAxis(Qt::XAxis); | ||
115 | data.setRotationAngle(60.0 * (1.0 - progress)); | 112 | data.setRotationAngle(interpolate(60.0, 0.0, t)); | ||
116 | data *= QVector3D(1.0, progress, progress); | 113 | data *= QVector3D(1.0, t, t); | ||
117 | data.translate(0.0, - (w->y() - info->parentY) * (1.0 - progress)); | 114 | data.translate(0.0, -interpolate(w->y() - (*animationIt).parentY, 0.0, t)); | ||
118 | 115 | | |||
119 | effects->paintWindow(w, mask, region, data); | 116 | effects->paintWindow(w, mask, region, data); | ||
120 | } | 117 | } | ||
121 | 118 | | |||
122 | void SheetEffect::postPaintWindow(EffectWindow* w) | 119 | void SheetEffect::postPaintWindow(EffectWindow *w) | ||
123 | { | 120 | { | ||
124 | InfoMap::iterator info = windows.find(w); | 121 | auto animationIt = m_animations.begin(); | ||
125 | if (info != windows.end()) { | 122 | while (animationIt != m_animations.end()) { | ||
126 | if (info->added && info->timeLine->currentValue() == 1.0) { | 123 | EffectWindow *w = animationIt.key(); | ||
127 | windows.remove(w); | 124 | w->addRepaintFull(); | ||
128 | effects->addRepaintFull(); | 125 | if ((*animationIt).timeLine.done()) { | ||
129 | } else if (info->closed && info->timeLine->currentValue() == 0.0) { | 126 | if (w->isDeleted()) { | ||
130 | info->closed = false; | | |||
131 | if (info->deleted) { | | |||
132 | windows.remove(w); | | |||
133 | w->unrefWindow(); | 127 | w->unrefWindow(); | ||
134 | } | 128 | } | ||
135 | effects->addRepaintFull(); | 129 | animationIt = m_animations.erase(animationIt); | ||
130 | } else { | ||||
131 | ++animationIt; | ||||
136 | } | 132 | } | ||
137 | if (info->added || info->closed) | | |||
138 | w->addRepaintFull(); | | |||
139 | } | 133 | } | ||
134 | | ||||
135 | if (m_animations.isEmpty()) { | ||||
136 | effects->addRepaintFull(); | ||||
137 | } | ||||
138 | | ||||
140 | effects->postPaintWindow(w); | 139 | effects->postPaintWindow(w); | ||
141 | } | 140 | } | ||
142 | 141 | | |||
142 | bool SheetEffect::isActive() const | ||||
143 | { | ||||
144 | return !m_animations.isEmpty(); | ||||
145 | } | ||||
146 | | ||||
147 | bool SheetEffect::supported() | ||||
148 | { | ||||
149 | return effects->isOpenGLCompositing() | ||||
150 | && effects->animationsSupported(); | ||||
151 | } | ||||
152 | | ||||
143 | void SheetEffect::slotWindowAdded(EffectWindow* w) | 153 | void SheetEffect::slotWindowAdded(EffectWindow *w) | ||
144 | { | 154 | { | ||
145 | if (!isSheetWindow(w)) | 155 | if (effects->activeFullScreenEffect()) { | ||
146 | return; | 156 | return; | ||
157 | } | ||||
147 | 158 | | |||
148 | InfoMap::iterator it = windows.find(w); | 159 | if (!isSheetWindow(w)) { | ||
149 | WindowInfo *info = (it == windows.end()) ? &windows[w] : &it.value(); | 160 | return; | ||
150 | info->added = true; | | |||
151 | info->closed = false; | | |||
152 | info->deleted = false; | | |||
153 | delete info->timeLine; | | |||
154 | info->timeLine = new QTimeLine(duration); | | |||
155 | const EffectWindowList stack = effects->stackingOrder(); | | |||
156 | // find parent | | |||
157 | foreach (EffectWindow * window, stack) { | | |||
158 | if (window->findModal() == w) { | | |||
159 | info->parentY = window->y(); | | |||
160 | break; | | |||
161 | } | 161 | } | ||
162 | | ||||
163 | Animation &animation = m_animations[w]; | ||||
164 | animation.parentY = 0; | ||||
165 | animation.timeLine.reset(); | ||||
166 | animation.timeLine.setDuration(m_duration); | ||||
167 | animation.timeLine.setDirection(TimeLine::Forward); | ||||
168 | animation.timeLine.setEasingCurve(QEasingCurve::Linear); | ||||
169 | | ||||
170 | const auto windows = effects->stackingOrder(); | ||||
171 | auto parentIt = std::find_if(windows.constBegin(), windows.constEnd(), | ||||
172 | [w](EffectWindow *p) { | ||||
173 | return p->findModal() == w; | ||||
174 | }); | ||||
175 | if (parentIt != windows.constEnd()) { | ||||
176 | animation.parentY = (*parentIt)->y(); | ||||
162 | } | 177 | } | ||
163 | 178 | | |||
164 | w->setData(WindowAddedGrabRole, QVariant::fromValue(static_cast<void*>(this))); | 179 | w->setData(WindowAddedGrabRole, QVariant::fromValue(static_cast<void*>(this))); | ||
165 | 180 | | |||
166 | w->addRepaintFull(); | 181 | w->addRepaintFull(); | ||
167 | } | 182 | } | ||
168 | 183 | | |||
169 | void SheetEffect::slotWindowClosed(EffectWindow* w) | 184 | void SheetEffect::slotWindowClosed(EffectWindow *w) | ||
170 | { | 185 | { | ||
171 | if (!isSheetWindow(w)) | 186 | if (effects->activeFullScreenEffect()) { | ||
187 | return; | ||||
188 | } | ||||
189 | | ||||
190 | if (!isSheetWindow(w)) { | ||||
172 | return; | 191 | return; | ||
192 | } | ||||
173 | 193 | | |||
174 | w->refWindow(); | 194 | w->refWindow(); | ||
175 | 195 | | |||
176 | InfoMap::iterator it = windows.find(w); | 196 | Animation &animation = m_animations[w]; | ||
177 | WindowInfo *info = (it == windows.end()) ? &windows[w] : &it.value(); | 197 | | ||
178 | info->added = false; | 198 | animation.timeLine.reset(); | ||
179 | info->closed = true; | 199 | animation.parentY = 0; | ||
180 | info->deleted = true; | 200 | animation.timeLine.setDuration(m_duration); | ||
181 | delete info->timeLine; | 201 | animation.timeLine.setDirection(TimeLine::Backward); | ||
182 | info->timeLine = new QTimeLine(duration); | 202 | animation.timeLine.setEasingCurve(QEasingCurve::Linear); | ||
183 | info->timeLine->setCurrentTime(duration); | 203 | | ||
184 | 204 | const auto windows = effects->stackingOrder(); | |||
185 | bool found = false; | 205 | auto parentIt = std::find_if(windows.constBegin(), windows.constEnd(), | ||
186 | // find parent | 206 | [w](EffectWindow *p) { | ||
187 | const EffectWindowList stack = effects->stackingOrder(); | 207 | return p->findModal() == w; | ||
188 | foreach (EffectWindow * window, stack) { | 208 | }); | ||
189 | if (window->findModal() == w) { | 209 | if (parentIt != windows.constEnd()) { | ||
190 | info->parentY = window->y(); | 210 | animation.parentY = (*parentIt)->y(); | ||
191 | found = true; | | |||
192 | break; | | |||
193 | } | | |||
194 | } | 211 | } | ||
195 | if (!found) | | |||
196 | info->parentY = 0; | | |||
197 | 212 | | |||
198 | w->setData(WindowClosedGrabRole, QVariant::fromValue(static_cast<void*>(this))); | 213 | w->setData(WindowClosedGrabRole, QVariant::fromValue(static_cast<void*>(this))); | ||
199 | 214 | | |||
200 | w->addRepaintFull(); | 215 | w->addRepaintFull(); | ||
201 | } | 216 | } | ||
202 | 217 | | |||
203 | void SheetEffect::slotWindowDeleted(EffectWindow* w) | 218 | void SheetEffect::slotWindowDeleted(EffectWindow *w) | ||
204 | { | 219 | { | ||
205 | windows.remove(w); | 220 | m_animations.remove(w); | ||
206 | } | 221 | } | ||
207 | 222 | | |||
208 | bool SheetEffect::isSheetWindow(EffectWindow* w) | 223 | bool SheetEffect::isSheetWindow(EffectWindow *w) const | ||
209 | { | 224 | { | ||
210 | return w->isModal(); | 225 | return w->isModal(); | ||
211 | } | 226 | } | ||
212 | 227 | | |||
213 | bool SheetEffect::isActive() const | 228 | } // namespace KWin | ||
214 | { | | |||
215 | return !windows.isEmpty(); | | |||
216 | } | | |||
217 | | ||||
218 | SheetEffect::WindowInfo::WindowInfo() | | |||
219 | : deleted(false) | | |||
220 | , added(false) | | |||
221 | , closed(false) | | |||
222 | , timeLine(0) | | |||
223 | , parentY(0) | | |||
224 | { | | |||
225 | } | | |||
226 | | ||||
227 | SheetEffect::WindowInfo::~WindowInfo() | | |||
228 | { | | |||
229 | delete timeLine; | | |||
230 | } | | |||
231 | | ||||
232 | } // namespace | |