File indexing completed on 2024-11-10 04:56:58
0001 /* 0002 KWin - the KDE window manager 0003 This file is part of the KDE project. 0004 0005 SPDX-FileCopyrightText: 2007 Lubos Lunak <l.lunak@kde.org> 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 0010 #include "fallapart.h" 0011 #include "effect/effecthandler.h" 0012 // KConfigSkeleton 0013 #include "fallapartconfig.h" 0014 0015 #include <QEasingCurve> 0016 0017 #include <cmath> 0018 0019 Q_LOGGING_CATEGORY(KWIN_FALLAPART, "kwin_effect_fallapart", QtWarningMsg) 0020 0021 static const QSet<QString> s_blacklist{ 0022 // Spectacle needs to be blacklisted in order to stay out of its own screenshots. 0023 QStringLiteral("spectacle spectacle"), // x11 0024 QStringLiteral("spectacle org.kde.spectacle"), // wayland 0025 }; 0026 0027 namespace KWin 0028 { 0029 0030 bool FallApartEffect::supported() 0031 { 0032 return OffscreenEffect::supported() && effects->animationsSupported(); 0033 } 0034 0035 FallApartEffect::FallApartEffect() 0036 { 0037 FallApartConfig::instance(effects->config()); 0038 reconfigure(ReconfigureAll); 0039 connect(effects, &EffectsHandler::windowClosed, this, &FallApartEffect::slotWindowClosed); 0040 connect(effects, &EffectsHandler::windowDataChanged, this, &FallApartEffect::slotWindowDataChanged); 0041 } 0042 0043 void FallApartEffect::reconfigure(ReconfigureFlags) 0044 { 0045 FallApartConfig::self()->read(); 0046 blockSize = FallApartConfig::blockSize(); 0047 } 0048 0049 void FallApartEffect::prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime) 0050 { 0051 if (!windows.isEmpty()) { 0052 data.mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS; 0053 } 0054 effects->prePaintScreen(data, presentTime); 0055 } 0056 0057 void FallApartEffect::prePaintWindow(EffectWindow *w, WindowPrePaintData &data, std::chrono::milliseconds presentTime) 0058 { 0059 auto animationIt = windows.find(w); 0060 if (animationIt != windows.end() && isRealWindow(w)) { 0061 int time = 0; 0062 if (animationIt->lastPresentTime.count()) { 0063 time = (presentTime - animationIt->lastPresentTime).count(); 0064 } 0065 animationIt->lastPresentTime = presentTime; 0066 0067 animationIt->progress += time / animationTime(1000.); 0068 data.setTransformed(); 0069 } 0070 effects->prePaintWindow(w, data, presentTime); 0071 } 0072 0073 void FallApartEffect::apply(EffectWindow *w, int mask, WindowPaintData &data, WindowQuadList &quads) 0074 { 0075 auto animationIt = windows.constFind(w); 0076 if (animationIt != windows.constEnd() && isRealWindow(w)) { 0077 QEasingCurve easing(QEasingCurve::InCubic); 0078 const qreal t = easing.valueForProgress(animationIt->progress); 0079 // Request the window to be divided into cells 0080 quads = quads.makeGrid(blockSize); 0081 int cnt = 0; 0082 for (WindowQuad &quad : quads) { 0083 // make fragments move in various directions, based on where 0084 // they are (left pieces generally move to the left, etc.) 0085 QPointF p1(quad[0].x(), quad[0].y()); 0086 double xdiff = 0; 0087 if (p1.x() < w->width() / 2) { 0088 xdiff = -(w->width() / 2 - p1.x()) / w->width() * 100; 0089 } 0090 if (p1.x() > w->width() / 2) { 0091 xdiff = (p1.x() - w->width() / 2) / w->width() * 100; 0092 } 0093 double ydiff = 0; 0094 if (p1.y() < w->height() / 2) { 0095 ydiff = -(w->height() / 2 - p1.y()) / w->height() * 100; 0096 } 0097 if (p1.y() > w->height() / 2) { 0098 ydiff = (p1.y() - w->height() / 2) / w->height() * 100; 0099 } 0100 double modif = t * 64; 0101 srandom(cnt); // change direction randomly but consistently 0102 xdiff += (rand() % 21 - 10); 0103 ydiff += (rand() % 21 - 10); 0104 for (int j = 0; 0105 j < 4; 0106 ++j) { 0107 quad[j].move(quad[j].x() + xdiff * modif, quad[j].y() + ydiff * modif); 0108 } 0109 // also make the fragments rotate around their center 0110 QPointF center((quad[0].x() + quad[1].x() + quad[2].x() + quad[3].x()) / 4, 0111 (quad[0].y() + quad[1].y() + quad[2].y() + quad[3].y()) / 4); 0112 double adiff = (rand() % 720 - 360) / 360. * 2 * M_PI; // spin randomly 0113 for (int j = 0; 0114 j < 4; 0115 ++j) { 0116 double x = quad[j].x() - center.x(); 0117 double y = quad[j].y() - center.y(); 0118 double angle = atan2(y, x); 0119 angle += animationIt->progress * adiff; 0120 double dist = sqrt(x * x + y * y); 0121 x = dist * cos(angle); 0122 y = dist * sin(angle); 0123 quad[j].move(center.x() + x, center.y() + y); 0124 } 0125 ++cnt; 0126 } 0127 data.multiplyOpacity(interpolate(1.0, 0.0, t)); 0128 } 0129 } 0130 0131 void FallApartEffect::postPaintScreen() 0132 { 0133 for (auto it = windows.begin(); it != windows.end();) { 0134 if (it->progress < 1) { 0135 ++it; 0136 } else { 0137 unredirect(it.key()); 0138 it = windows.erase(it); 0139 } 0140 } 0141 0142 effects->addRepaintFull(); 0143 effects->postPaintScreen(); 0144 } 0145 0146 bool FallApartEffect::isRealWindow(EffectWindow *w) 0147 { 0148 // TODO: isSpecialWindow is rather generic, maybe tell windowtypes separately? 0149 /* 0150 qCDebug(KWIN_FALLAPART) << "--" << w->caption() << "--------------------------------"; 0151 qCDebug(KWIN_FALLAPART) << "Tooltip:" << w->isTooltip(); 0152 qCDebug(KWIN_FALLAPART) << "Toolbar:" << w->isToolbar(); 0153 qCDebug(KWIN_FALLAPART) << "Desktop:" << w->isDesktop(); 0154 qCDebug(KWIN_FALLAPART) << "Special:" << w->isSpecialWindow(); 0155 qCDebug(KWIN_FALLAPART) << "TopMenu:" << w->isTopMenu(); 0156 qCDebug(KWIN_FALLAPART) << "Notific:" << w->isNotification(); 0157 qCDebug(KWIN_FALLAPART) << "Splash:" << w->isSplash(); 0158 qCDebug(KWIN_FALLAPART) << "Normal:" << w->isNormalWindow(); 0159 */ 0160 if (w->isPopupWindow()) { 0161 return false; 0162 } 0163 if (w->isX11Client() && !w->isManaged()) { 0164 return false; 0165 } 0166 if (!w->isNormalWindow()) { 0167 return false; 0168 } 0169 return true; 0170 } 0171 0172 void FallApartEffect::slotWindowClosed(EffectWindow *c) 0173 { 0174 if (effects->activeFullScreenEffect()) { 0175 return; 0176 } 0177 if (!isRealWindow(c)) { 0178 return; 0179 } 0180 if (!c->isVisible()) { 0181 return; 0182 } 0183 if (s_blacklist.contains(c->windowClass())) { 0184 return; 0185 } 0186 const void *e = c->data(WindowClosedGrabRole).value<void *>(); 0187 if (e && e != this) { 0188 return; 0189 } 0190 c->setData(WindowClosedGrabRole, QVariant::fromValue(static_cast<void *>(this))); 0191 0192 FallApartAnimation &animation = windows[c]; 0193 animation.progress = 0; 0194 animation.deletedRef = EffectWindowDeletedRef(c); 0195 0196 redirect(c); 0197 } 0198 0199 void FallApartEffect::slotWindowDataChanged(EffectWindow *w, int role) 0200 { 0201 if (role != WindowClosedGrabRole) { 0202 return; 0203 } 0204 0205 if (w->data(role).value<void *>() == this) { 0206 return; 0207 } 0208 0209 auto it = windows.find(w); 0210 if (it != windows.end()) { 0211 unredirect(it.key()); 0212 windows.erase(it); 0213 } 0214 } 0215 0216 bool FallApartEffect::isActive() const 0217 { 0218 return !windows.isEmpty(); 0219 } 0220 0221 } // namespace 0222 0223 #include "moc_fallapart.cpp"