Changeset View
Changeset View
Standalone View
Standalone View
effects/fullscreen/fullscreen.cpp
- This file was added.
1 | /******************************************************************** | ||||
---|---|---|---|---|---|
2 | KWin - the KDE window manager | ||||
3 | This file is part of the KDE project. | ||||
4 | | ||||
5 | Copyright (C) 2018 Vlad Zagorodniy <vladzzag@gmail.com> | ||||
6 | | ||||
7 | This program is free software; you can redistribute it and/or modify | ||||
8 | it under the terms of the GNU General Public License as published by | ||||
9 | the Free Software Foundation; either version 2 of the License, or | ||||
10 | (at your option) any later version. | ||||
11 | | ||||
12 | This program is distributed in the hope that it will be useful, | ||||
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
15 | GNU General Public License for more details. | ||||
16 | | ||||
17 | You should have received a copy of the GNU General Public License | ||||
18 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
19 | *********************************************************************/ | ||||
20 | | ||||
21 | #include "fullscreen.h" | ||||
22 | | ||||
23 | #include <kwinconfig.h> | ||||
24 | | ||||
25 | namespace KWin | ||||
26 | { | ||||
27 | | ||||
28 | FullScreenEffect::FullScreenEffect() | ||||
29 | { | ||||
30 | reconfigure(ReconfigureAll); | ||||
31 | connect(effects, &EffectsHandler::windowFullScreenChanged, | ||||
32 | this, &FullScreenEffect::windowFullScreenChanged); | ||||
33 | connect(effects, &EffectsHandler::windowDeleted, | ||||
34 | this, &FullScreenEffect::windowDeleted); | ||||
35 | | ||||
36 | } | ||||
37 | | ||||
38 | void FullScreenEffect::reconfigure(ReconfigureFlags) | ||||
39 | { | ||||
40 | m_duration = animationTime(200); | ||||
41 | } | ||||
42 | | ||||
43 | void FullScreenEffect::prePaintScreen(ScreenPrePaintData& data, int time) | ||||
44 | { | ||||
45 | if (! m_animations.empty()) { | ||||
46 | auto it = m_animations.begin(); | ||||
47 | while (it != m_animations.end()) { | ||||
48 | AnimData* anim = &it.value(); | ||||
49 | anim->timeline.update(time); | ||||
50 | ++it; | ||||
51 | } | ||||
52 | data.mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS; | ||||
53 | } | ||||
54 | effects->prePaintScreen(data, time); | ||||
55 | } | ||||
56 | | ||||
57 | void FullScreenEffect::postPaintScreen() | ||||
58 | { | ||||
59 | if (! m_animations.empty()) { | ||||
60 | auto it = m_animations.begin(); | ||||
61 | while (it != m_animations.end()) { | ||||
62 | AnimData* anim = &it.value(); | ||||
63 | if (anim->timeline.done()) { | ||||
64 | EffectWindow* w = it.key(); | ||||
65 | w->unreferencePreviousWindowPixmap(); | ||||
66 | it = m_animations.erase(it); | ||||
67 | } else { | ||||
68 | ++it; | ||||
69 | } | ||||
70 | } | ||||
71 | effects->addRepaintFull(); | ||||
72 | } | ||||
73 | effects->postPaintScreen(); | ||||
74 | } | ||||
75 | | ||||
76 | void FullScreenEffect::prePaintWindow(EffectWindow* w, WindowPrePaintData& data, int time) | ||||
77 | { | ||||
78 | if (m_animations.contains(w)) { | ||||
79 | data.setTransformed(); | ||||
80 | } | ||||
81 | effects->prePaintWindow(w, data, time); | ||||
82 | } | ||||
83 | | ||||
84 | void FullScreenEffect::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data) | ||||
85 | { | ||||
86 | if (m_animations.contains(w)) { | ||||
87 | AnimData* anim = &m_animations[w]; | ||||
88 | | ||||
89 | // Funky stuff: Even though direction of the timeline has been changed, | ||||
90 | // some extra steps should be taken. Normally, when we want to revert back | ||||
91 | // an animation, which is currently in progress, we change direction of | ||||
92 | // a timeline associated with this animation. This works fine until some of | ||||
93 | // endpoints changes. So, let's take a look at our case: | ||||
94 | // | ||||
95 | // startGeo endGeo | ||||
96 | // (A)---------------------------------------------(B) | ||||
97 | // | ||||
98 | // @p w's geometry is interpolated between startGeo and endGeo: | ||||
99 | // | ||||
100 | // (A)(X)------------------------------------------(B) : t = 0 | ||||
101 | // (A)--------------------(X)----------------------(B) : t = 0.5 | ||||
102 | // (A)------------------------------------------(X)(B) : t = 1 | ||||
103 | // | ||||
104 | // Let's assume, for example, @p w has entered fullscreen mode. So, | ||||
105 | // A is @p w's geometry before it entered fullscreen mode and B is | ||||
106 | // @p w's geometry in fullscreen mode. In addition to the assumption | ||||
107 | // above, let's say t = 0.1. | ||||
108 | // | ||||
109 | // (A)---(X)---------------------------------------(B) | ||||
110 | // | ||||
111 | // ... and @p w left fullscreen mode. Now, @p w's geometry is interpolated | ||||
112 | // between B and A. Also, timeline changed its direction(e.g. it goes | ||||
113 | // from 0.1 to 0.0): | ||||
114 | // | ||||
115 | // (B)---(X)---------------------------------------(A) | ||||
116 | // | ||||
117 | // In other words, @p w's geometry moves toward B, which is totally wrong. | ||||
118 | // That's why progress should be reversed if timeline goes backwad. | ||||
119 | // | ||||
120 | // (B)---------------------------------------(X)---(A) | ||||
121 | // | ||||
122 | float transformProgress = (anim->timeline.direction() == Timeline::Backward) | ||||
123 | ? anim->timeline.rprogress() | ||||
124 | : anim->timeline.progress(); | ||||
125 | | ||||
126 | // Morph rectangle `anim->startGeo` into rectangle `anim->endGeo`. | ||||
127 | QVector2D pos(interpolate(anim->startGeo.x(), anim->endGeo.x(), transformProgress), | ||||
128 | interpolate(anim->startGeo.y(), anim->endGeo.y(), transformProgress)); | ||||
129 | QVector2D shift = pos - QVector2D(anim->endGeo.x(), anim->endGeo.y()); | ||||
130 | data += shift; | ||||
131 | | ||||
132 | Q_ASSERT(anim->endGeo.width() > 0 && anim->endGeo.height() > 0); | ||||
133 | QVector2D size(interpolate(anim->startGeo.width(), anim->endGeo.width(), transformProgress), | ||||
134 | interpolate(anim->startGeo.height(), anim->endGeo.height(), transformProgress)); | ||||
135 | QVector2D scale = size / QVector2D(anim->endGeo.width(), anim->endGeo.height()); | ||||
136 | data *= scale; | ||||
137 | | ||||
138 | // Cross fade between previous and current window pixmap. | ||||
139 | float crossFadeProgress = anim->timeline.progress(); | ||||
140 | data.setCrossFadeProgress(crossFadeProgress); | ||||
141 | } | ||||
142 | | ||||
143 | effects->paintWindow(w, mask, region, data); | ||||
144 | } | ||||
145 | | ||||
146 | void FullScreenEffect::windowFullScreenChanged(EffectWindow* w, bool entered, const QRect &oldGeometry) | ||||
147 | { | ||||
148 | if (effects->activeFullScreenEffect()) { | ||||
149 | return; | ||||
150 | } | ||||
151 | | ||||
152 | AnimData* anim = nullptr; | ||||
153 | | ||||
154 | if (m_animations.contains(w)) { | ||||
155 | anim = &m_animations[w]; | ||||
156 | anim->timeline.toggleDirection(); | ||||
157 | } else { | ||||
158 | anim = &m_animations[w]; | ||||
159 | anim->timeline.setDuration(m_duration); | ||||
160 | anim->timeline.setDirection(Timeline::Forward); | ||||
161 | anim->timeline.setEasingCurve(QEasingCurve::OutCubic); | ||||
162 | w->referencePreviousWindowPixmap(); | ||||
163 | } | ||||
164 | | ||||
165 | anim->startGeo = oldGeometry; | ||||
166 | anim->endGeo = w->geometry(); | ||||
167 | } | ||||
168 | | ||||
169 | void FullScreenEffect::windowDeleted(EffectWindow* w) | ||||
170 | { | ||||
171 | if (! m_animations.contains(w)) { | ||||
172 | return; | ||||
173 | } | ||||
174 | w->unreferencePreviousWindowPixmap(); | ||||
175 | m_animations.remove(w); | ||||
176 | } | ||||
177 | | ||||
178 | } |