diff --git a/autotests/integration/dont_crash_cancel_animation.cpp b/autotests/integration/dont_crash_cancel_animation.cpp index 53cdae3be1..55dc6b85ff 100644 --- a/autotests/integration/dont_crash_cancel_animation.cpp +++ b/autotests/integration/dont_crash_cancel_animation.cpp @@ -71,7 +71,7 @@ void DontCrashCancelAnimationFromAnimationEndedTest::cleanup() void DontCrashCancelAnimationFromAnimationEndedTest::testScript() { // load a scripted effect which deletes animation data - ScriptedEffect *effect = ScriptedEffect::create(QStringLiteral("crashy"), QFINDTESTDATA("data/anim-data-delete-effect/effect.js"), 10, QString(), true); + ScriptedEffect *effect = ScriptedEffect::create(QStringLiteral("crashy"), QFINDTESTDATA("data/anim-data-delete-effect/effect.js"), 10, QString()); QVERIFY(effect); const auto children = effects->children(); diff --git a/src/effect/offscreeneffect.cpp b/src/effect/offscreeneffect.cpp index b2ac70ac38..b82a57a9b0 100644 --- a/src/effect/offscreeneffect.cpp +++ b/src/effect/offscreeneffect.cpp @@ -11,6 +11,7 @@ #include "effect/effecthandler.h" #include "opengl/gltexture.h" #include "opengl/glutils.h" +#include "scene/windowitem.h" namespace KWin { @@ -34,6 +35,7 @@ public: GLShader *m_shader = nullptr; RenderGeometry::VertexSnappingMode m_vertexSnappingMode = RenderGeometry::VertexSnappingMode::Round; QMetaObject::Connection m_windowDamagedConnection; + ItemEffect m_windowEffect; }; class OffscreenEffectPrivate @@ -65,7 +67,7 @@ void OffscreenEffect::redirect(EffectWindow *window) } offscreenData = std::make_unique(); offscreenData->setVertexSnappingMode(d->vertexSnappingMode); - + offscreenData->m_windowEffect = ItemEffect(window->windowItem()); offscreenData->m_windowDamagedConnection = connect(window, &EffectWindow::windowDamaged, this, &OffscreenEffect::handleWindowDamaged); @@ -274,6 +276,11 @@ void OffscreenEffect::setVertexSnappingMode(RenderGeometry::VertexSnappingMode m } } +bool OffscreenEffect::blocksDirectScanout() const +{ + return false; +} + class CrossFadeWindowData : public OffscreenData { public: @@ -356,6 +363,7 @@ void CrossFadeEffect::redirect(EffectWindow *window) return; } offscreenData = std::make_unique(); + offscreenData->m_windowEffect = ItemEffect(window->windowItem()); // Avoid including blur and contrast effects. During a normal painting cycle they // won't be included, but since we call effects->drawWindow() outside usual compositing @@ -393,6 +401,11 @@ void CrossFadeEffect::setShader(EffectWindow *window, GLShader *shader) } } +bool CrossFadeEffect::blocksDirectScanout() const +{ + return false; +} + } // namespace KWin #include "moc_offscreeneffect.cpp" diff --git a/src/effect/offscreeneffect.h b/src/effect/offscreeneffect.h index 8b518ed3e7..095f9aac91 100644 --- a/src/effect/offscreeneffect.h +++ b/src/effect/offscreeneffect.h @@ -72,6 +72,8 @@ protected: */ void setVertexSnappingMode(RenderGeometry::VertexSnappingMode mode); + bool blocksDirectScanout() const override; + private Q_SLOTS: void handleWindowDamaged(EffectWindow *window); void handleWindowDeleted(EffectWindow *window); @@ -121,6 +123,8 @@ public: **/ void setShader(EffectWindow *window, GLShader *shader); + bool blocksDirectScanout() const override; + static bool supported(); private: diff --git a/src/plugins/backgroundcontrast/contrast.cpp b/src/plugins/backgroundcontrast/contrast.cpp index 9330939c3c..31fc4e08cb 100644 --- a/src/plugins/backgroundcontrast/contrast.cpp +++ b/src/plugins/backgroundcontrast/contrast.cpp @@ -13,6 +13,8 @@ #include "core/rendertarget.h" #include "core/renderviewport.h" #include "effect/effecthandler.h" +#include "scene/surfaceitem.h" +#include "scene/windowitem.h" #include "wayland/contrast.h" #include "wayland/display.h" #include "wayland/surface.h" @@ -174,6 +176,7 @@ void ContrastEffect::updateContrastRegion(EffectWindow *w) Data &data = m_windowData[w]; data.colorMatrix = matrix; data.contrastRegion = region; + data.surfaceEffect = ItemEffect(w->windowItem()->surfaceItem()); } else { if (auto it = m_windowData.find(w); it != m_windowData.end()) { effects->makeOpenGLContextCurrent(); diff --git a/src/plugins/backgroundcontrast/contrast.h b/src/plugins/backgroundcontrast/contrast.h index 6f5f639448..5bc8f85c45 100644 --- a/src/plugins/backgroundcontrast/contrast.h +++ b/src/plugins/backgroundcontrast/contrast.h @@ -10,6 +10,7 @@ #include "effect/effect.h" #include "opengl/glplatform.h" #include "opengl/glutils.h" +#include "scene/item.h" #include #include @@ -75,6 +76,7 @@ private: QRegion contrastRegion; std::unique_ptr texture; std::unique_ptr fbo; + ItemEffect surfaceEffect; }; std::unordered_map m_windowData; static ContrastManagerInterface *s_contrastManager; diff --git a/src/plugins/blur/blur.cpp b/src/plugins/blur/blur.cpp index 96a9174130..53b2b1fa7e 100644 --- a/src/plugins/blur/blur.cpp +++ b/src/plugins/blur/blur.cpp @@ -15,6 +15,9 @@ #include "core/renderviewport.h" #include "effect/effecthandler.h" #include "opengl/glplatform.h" +#include "scene/decorationitem.h" +#include "scene/surfaceitem.h" +#include "scene/windowitem.h" #include "wayland/blur.h" #include "wayland/display.h" #include "wayland/surface.h" @@ -263,6 +266,12 @@ void BlurEffect::updateBlurRegion(EffectWindow *w) BlurEffectData &data = m_windows[w]; data.content = content; data.frame = frame; + if (content) { + data.surfaceEffect = ItemEffect(w->windowItem()->surfaceItem()); + } + if (frame) { + data.surfaceEffect = ItemEffect(w->windowItem()->decorationItem()); + } } else { if (auto it = m_windows.find(w); it != m_windows.end()) { effects->makeOpenGLContextCurrent(); diff --git a/src/plugins/blur/blur.h b/src/plugins/blur/blur.h index 9e488b949a..94b6c50eef 100644 --- a/src/plugins/blur/blur.h +++ b/src/plugins/blur/blur.h @@ -9,6 +9,7 @@ #include "effect/effect.h" #include "opengl/glutils.h" +#include "scene/item.h" #include @@ -31,9 +32,11 @@ struct BlurEffectData { /// The region that should be blurred behind the window std::optional content; + ItemEffect surfaceEffect; /// The region that should be blurred behind the frame std::optional frame; + ItemEffect decorationEffect; /// The render data per screen. Screens can have different color spaces. std::unordered_map render; diff --git a/src/plugins/fadingpopups/package/metadata.json b/src/plugins/fadingpopups/package/metadata.json index ef6ebbbf42..ed51821a7b 100644 --- a/src/plugins/fadingpopups/package/metadata.json +++ b/src/plugins/fadingpopups/package/metadata.json @@ -105,7 +105,6 @@ "Name[zh_CN]": "气泡显隐渐变动画", "Name[zh_TW]": "淡化彈出視窗" }, - "X-KDE-BlocksDirectScanout": false, "X-KDE-Ordering": 60, "X-Plasma-API": "javascript" } diff --git a/src/plugins/glide/glide.cpp b/src/plugins/glide/glide.cpp index 6239ca7bcd..e9f6daa82f 100644 --- a/src/plugins/glide/glide.cpp +++ b/src/plugins/glide/glide.cpp @@ -19,6 +19,7 @@ #include "core/rendertarget.h" #include "core/renderviewport.h" #include "effect/effecthandler.h" +#include "scene/windowitem.h" // Qt #include @@ -79,7 +80,7 @@ void GlideEffect::prePaintWindow(EffectWindow *w, WindowPrePaintData &data, std: { auto animationIt = m_animations.find(w); if (animationIt != m_animations.end()) { - (*animationIt).timeLine.advance(presentTime); + animationIt->second.timeLine.advance(presentTime); data.setTransformed(); } @@ -88,13 +89,13 @@ void GlideEffect::prePaintWindow(EffectWindow *w, WindowPrePaintData &data, std: void GlideEffect::apply(EffectWindow *window, int mask, WindowPaintData &data, WindowQuadList &quads) { - auto animationIt = m_animations.constFind(window); - if (animationIt == m_animations.constEnd()) { + auto animationIt = m_animations.find(window); + if (animationIt == m_animations.end()) { return; } const GlideParams params = window->isDeleted() ? m_outParams : m_inParams; - const qreal t = (*animationIt).timeLine.value(); + const qreal t = animationIt->second.timeLine.value(); const QRectF rect = window->expandedGeometry().translated(-window->pos()); const float fovY = std::tan(qDegreesToRadians(60.0f) / 2); @@ -162,8 +163,8 @@ void GlideEffect::postPaintWindow(EffectWindow *w) if (auto animationIt = m_animations.find(w); animationIt != m_animations.end()) { w->addRepaintFull(); - if ((*animationIt).timeLine.done()) { - unredirect(animationIt.key()); + if (animationIt->second.timeLine.done()) { + unredirect(animationIt->first); animationIt = m_animations.erase(animationIt); } else { ++animationIt; @@ -175,7 +176,7 @@ void GlideEffect::postPaintWindow(EffectWindow *w) bool GlideEffect::isActive() const { - return !m_animations.isEmpty(); + return !m_animations.empty(); } bool GlideEffect::supported() @@ -210,6 +211,7 @@ void GlideEffect::windowAdded(EffectWindow *w) animation.timeLine.setDirection(TimeLine::Forward); animation.timeLine.setDuration(m_duration); animation.timeLine.setEasingCurve(QEasingCurve::InCurve); + animation.effect = ItemEffect(w->windowItem()); redirect(w); effects->addRepaintFull(); @@ -255,7 +257,7 @@ void GlideEffect::windowDataChanged(EffectWindow *w, int role) auto animationIt = m_animations.find(w); if (animationIt != m_animations.end()) { - unredirect(animationIt.key()); + unredirect(animationIt->first); m_animations.erase(animationIt); } } @@ -301,6 +303,11 @@ bool GlideEffect::isGlideWindow(EffectWindow *w) const || w->isDialog(); } +bool GlideEffect::blocksDirectScanout() const +{ + return false; +} + } // namespace KWin #include "moc_glide.cpp" diff --git a/src/plugins/glide/glide.h b/src/plugins/glide/glide.h index 48ae6e302d..913859a7c1 100644 --- a/src/plugins/glide/glide.h +++ b/src/plugins/glide/glide.h @@ -16,6 +16,9 @@ #include "effect/effectwindow.h" #include "effect/offscreeneffect.h" #include "effect/timeline.h" +#include "scene/item.h" + +#include namespace KWin { @@ -24,6 +27,7 @@ struct GlideAnimation { EffectWindowDeletedRef deletedRef; TimeLine timeLine; + ItemEffect effect; }; class GlideEffect : public OffscreenEffect @@ -68,6 +72,7 @@ public: qreal outRotationAngle() const; qreal outDistance() const; qreal outOpacity() const; + bool blocksDirectScanout() const override; protected: void apply(EffectWindow *window, int mask, WindowPaintData &data, WindowQuadList &quads) override; @@ -81,7 +86,7 @@ private: bool isGlideWindow(EffectWindow *w) const; std::chrono::milliseconds m_duration; - QHash m_animations; + std::unordered_map m_animations; struct GlideParams { diff --git a/src/plugins/slidingpopups/slidingpopups.cpp b/src/plugins/slidingpopups/slidingpopups.cpp index 80a6c14d8c..bfa8616f45 100644 --- a/src/plugins/slidingpopups/slidingpopups.cpp +++ b/src/plugins/slidingpopups/slidingpopups.cpp @@ -12,6 +12,7 @@ #include "slidingpopupsconfig.h" #include "effect/effecthandler.h" +#include "scene/windowitem.h" #include "wayland/display.h" #include "wayland/slide.h" #include "wayland/surface.h" @@ -107,13 +108,8 @@ void SlidingPopupsEffect::reconfigure(ReconfigureFlags flags) m_slideOutDuration = std::chrono::milliseconds( static_cast(animationTime(SlidingPopupsConfig::slideOutTime() != 0 ? std::chrono::milliseconds(SlidingPopupsConfig::slideOutTime()) : 200ms))); - auto animationIt = m_animations.begin(); - while (animationIt != m_animations.end()) { - const auto duration = ((*animationIt).kind == AnimationKind::In) - ? m_slideInDuration - : m_slideOutDuration; - (*animationIt).timeLine.setDuration(duration); - ++animationIt; + for (auto &[window, animation] : m_animations) { + animation.timeLine.setDuration(animation.kind == AnimationKind::In ? m_slideInDuration : m_slideOutDuration); } auto dataIt = m_animationsData.begin(); @@ -132,7 +128,7 @@ void SlidingPopupsEffect::prePaintWindow(EffectWindow *w, WindowPrePaintData &da return; } - (*animationIt).timeLine.advance(presentTime); + animationIt->second.timeLine.advance(presentTime); data.setTransformed(); effects->prePaintWindow(w, data, presentTime); @@ -140,8 +136,8 @@ void SlidingPopupsEffect::prePaintWindow(EffectWindow *w, WindowPrePaintData &da void SlidingPopupsEffect::paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, QRegion region, WindowPaintData &data) { - auto animationIt = m_animations.constFind(w); - if (animationIt == m_animations.constEnd()) { + auto animationIt = m_animations.find(w); + if (animationIt == m_animations.end()) { effects->paintWindow(renderTarget, viewport, w, mask, region, data); return; } @@ -152,7 +148,7 @@ void SlidingPopupsEffect::paintWindow(const RenderTarget &renderTarget, const Re const QRectF screenRect = effects->clientArea(FullScreenArea, w->screen(), effects->currentDesktop()); int splitPoint = 0; const QRectF geo = w->expandedGeometry(); - const qreal t = (*animationIt).timeLine.value(); + const qreal t = animationIt->second.timeLine.value(); switch (animData.location) { case Location::Left: @@ -197,7 +193,7 @@ void SlidingPopupsEffect::postPaintWindow(EffectWindow *w) auto animationIt = m_animations.find(w); if (animationIt != m_animations.end()) { effects->addRepaint(w->expandedGeometry()); - if ((*animationIt).timeLine.done()) { + if (animationIt->second.timeLine.done()) { if (!w->isDeleted()) { w->setData(WindowForceBackgroundContrastRole, QVariant()); w->setData(WindowForceBlurRole, QVariant()); @@ -292,7 +288,7 @@ void SlidingPopupsEffect::slotPropertyNotify(EffectWindow *w, long atom) if (w->data(WindowClosedGrabRole).value() == this) { w->setData(WindowClosedGrabRole, QVariant()); } - m_animations.remove(w); + m_animations.erase(w); m_animationsData.remove(w); return; } @@ -531,6 +527,7 @@ void SlidingPopupsEffect::slideIn(EffectWindow *w) animation.timeLine.setDirection(TimeLine::Forward); animation.timeLine.setDuration((*dataIt).slideInDuration); animation.timeLine.setEasingCurve(QEasingCurve::OutCubic); + animation.windowEffect = ItemEffect(w->windowItem()); // If the opposite animation (Out) was active and it had shorter duration, // at this point, the timeline can end up in the "done" state. Thus, we have @@ -586,12 +583,10 @@ void SlidingPopupsEffect::slideOut(EffectWindow *w) void SlidingPopupsEffect::stopAnimations() { - for (auto it = m_animations.constBegin(); it != m_animations.constEnd(); ++it) { - EffectWindow *w = it.key(); - - if (!w->isDeleted()) { - w->setData(WindowForceBackgroundContrastRole, QVariant()); - w->setData(WindowForceBlurRole, QVariant()); + for (const auto &[window, animation] : m_animations) { + if (!window->isDeleted()) { + window->setData(WindowForceBackgroundContrastRole, QVariant()); + window->setData(WindowForceBlurRole, QVariant()); } } @@ -600,7 +595,12 @@ void SlidingPopupsEffect::stopAnimations() bool SlidingPopupsEffect::isActive() const { - return !m_animations.isEmpty(); + return !m_animations.empty(); +} + +bool SlidingPopupsEffect::blocksDirectScanout() const +{ + return false; } } // namespace diff --git a/src/plugins/slidingpopups/slidingpopups.h b/src/plugins/slidingpopups/slidingpopups.h index 26d38174fa..cd8dbe0155 100644 --- a/src/plugins/slidingpopups/slidingpopups.h +++ b/src/plugins/slidingpopups/slidingpopups.h @@ -16,6 +16,7 @@ #include "effect/effect.h" #include "effect/effectwindow.h" #include "effect/timeline.h" +#include "scene/item.h" namespace KWin { @@ -49,6 +50,7 @@ public: int slideOutDuration() const; bool eventFilter(QObject *watched, QEvent *event) override; + bool blocksDirectScanout() const override; private Q_SLOTS: void slotWindowAdded(EffectWindow *w); @@ -91,8 +93,9 @@ private: EffectWindowVisibleRef visibleRef; AnimationKind kind; TimeLine timeLine; + ItemEffect windowEffect; }; - QHash m_animations; + std::unordered_map m_animations; enum class Location { Left, diff --git a/src/scene/item.cpp b/src/scene/item.cpp index dddce5776a..30a5585644 100644 --- a/src/scene/item.cpp +++ b/src/scene/item.cpp @@ -12,6 +12,34 @@ namespace KWin { +ItemEffect::ItemEffect(Item *item) + : m_item(item) +{ + item->addEffect(); +} + +ItemEffect::ItemEffect(ItemEffect &&move) + : m_item(std::exchange(move.m_item, nullptr)) +{ +} + +ItemEffect::ItemEffect() +{ +} + +ItemEffect::~ItemEffect() +{ + if (m_item) { + m_item->removeEffect(); + } +} + +ItemEffect &ItemEffect::operator=(ItemEffect &&move) +{ + std::swap(m_item, move.m_item); + return *this; +} + Item::Item(Item *parent) { setParentItem(parent); @@ -516,6 +544,22 @@ void Item::setPresentationHint(PresentationModeHint hint) m_presentationHint = hint; } +bool Item::hasEffects() const +{ + return m_effectCount != 0; +} + +void Item::addEffect() +{ + m_effectCount++; +} + +void Item::removeEffect() +{ + Q_ASSERT(m_effectCount > 0); + m_effectCount--; +} + } // namespace KWin #include "moc_item.cpp" diff --git a/src/scene/item.h b/src/scene/item.h index 6441fd6f17..39f2ecaaa7 100644 --- a/src/scene/item.h +++ b/src/scene/item.h @@ -24,6 +24,23 @@ class SceneDelegate; class Scene; class SyncReleasePoint; class DrmDevice; +class Item; + +class KWIN_EXPORT ItemEffect +{ +public: + explicit ItemEffect(Item *item); + explicit ItemEffect(const ItemEffect ©) = delete; + explicit ItemEffect(ItemEffect &&move); + explicit ItemEffect(); + virtual ~ItemEffect(); + + ItemEffect &operator=(const ItemEffect ©) = delete; + ItemEffect &operator=(ItemEffect &&move); + +private: + QPointer m_item; +}; /** * The Item class is the base class for items in the scene. @@ -116,6 +133,10 @@ public: RenderingIntent renderingIntent() const; PresentationModeHint presentationHint() const; + bool hasEffects() const; + void addEffect(); + void removeEffect(); + Q_SIGNALS: void childAdded(Item *item); /** @@ -173,6 +194,7 @@ private: ColorDescription m_colorDescription = ColorDescription::sRGB; RenderingIntent m_renderingIntent = RenderingIntent::Perceptual; PresentationModeHint m_presentationHint = PresentationModeHint::VSync; + int m_effectCount = 0; }; } // namespace KWin diff --git a/src/scene/workspacescene.cpp b/src/scene/workspacescene.cpp index 301ae68f4a..8cefd8148d 100644 --- a/src/scene/workspacescene.cpp +++ b/src/scene/workspacescene.cpp @@ -177,7 +177,7 @@ static bool addCandidates(SurfaceItem *item, QList &candidates, s } } } - if (candidates.size() >= maxCount) { + if (candidates.size() >= maxCount || item->hasEffects()) { return false; } candidates.push_back(item); @@ -202,7 +202,7 @@ QList WorkspaceScene::scanoutCandidates(ssize_t maxCount) const WindowItem *windowItem = stacking_order[i]; Window *window = windowItem->window(); if (window->isOnOutput(painted_screen) && window->opacity() > 0 && windowItem->isVisible()) { - if (!window->isClient() || window->opacity() != 1.0 || !window->isFullScreen()) { + if (!window->isClient() || window->opacity() != 1.0 || !window->isFullScreen() || window->windowItem()->hasEffects()) { return {}; } if (!windowItem->surfaceItem()) { diff --git a/src/scripting/scriptedeffect.cpp b/src/scripting/scriptedeffect.cpp index 3e3694603f..337a1f9974 100644 --- a/src/scripting/scriptedeffect.cpp +++ b/src/scripting/scriptedeffect.cpp @@ -165,10 +165,10 @@ ScriptedEffect *ScriptedEffect::create(const KPluginMetaData &effect) return nullptr; } - return ScriptedEffect::create(name, scriptFile, effect.value(QStringLiteral("X-KDE-Ordering"), 0), effect.value(QStringLiteral("X-KWin-Exclusive-Category")), effect.value(QStringLiteral("X-KDE-BlocksDirectScanout"), true)); + return ScriptedEffect::create(name, scriptFile, effect.value(QStringLiteral("X-KDE-Ordering"), 0), effect.value(QStringLiteral("X-KWin-Exclusive-Category"))); } -ScriptedEffect *ScriptedEffect::create(const QString &effectName, const QString &pathToScript, int chainPosition, const QString &exclusiveCategory, bool blocksDirectScanout) +ScriptedEffect *ScriptedEffect::create(const QString &effectName, const QString &pathToScript, int chainPosition, const QString &exclusiveCategory) { ScriptedEffect *effect = new ScriptedEffect(); effect->m_exclusiveCategory = exclusiveCategory; @@ -177,7 +177,6 @@ ScriptedEffect *ScriptedEffect::create(const QString &effectName, const QString return nullptr; } effect->m_chainPosition = chainPosition; - effect->m_blocksDirectScanout = blocksDirectScanout; return effect; } @@ -307,11 +306,6 @@ bool ScriptedEffect::isActiveFullScreenEffect() const return effects->activeFullScreenEffect() == this; } -bool ScriptedEffect::blocksDirectScanout() const -{ - return m_blocksDirectScanout; -} - QList ScriptedEffect::touchEdgesForAction(const QString &action) const { QList ret; diff --git a/src/scripting/scriptedeffect.h b/src/scripting/scriptedeffect.h index fab32aee8e..8ef3157976 100644 --- a/src/scripting/scriptedeffect.h +++ b/src/scripting/scriptedeffect.h @@ -80,7 +80,7 @@ public: } QString activeConfig() const; void setActiveConfig(const QString &name); - static ScriptedEffect *create(const QString &effectName, const QString &pathToScript, int chainPosition, const QString &exclusiveCategory, bool blocksDirectScanout); + static ScriptedEffect *create(const QString &effectName, const QString &pathToScript, int chainPosition, const QString &exclusiveCategory); static ScriptedEffect *create(const KPluginMetaData &effect); static bool supported(); ~ScriptedEffect() override; @@ -182,7 +182,6 @@ public: QString pluginId() const; bool isActiveFullScreenEffect() const; - bool blocksDirectScanout() const override; public Q_SLOTS: bool borderActivated(ElectricBorder border) override; @@ -223,6 +222,5 @@ private: Effect *m_activeFullScreenEffect = nullptr; std::map> m_shaders; uint m_nextShaderId{1u}; - bool m_blocksDirectScanout = true; }; }