diff --git a/src/effects/CMakeLists.txt b/src/effects/CMakeLists.txt index f8a2c141fa..835f39e418 100644 --- a/src/effects/CMakeLists.txt +++ b/src/effects/CMakeLists.txt @@ -86,6 +86,7 @@ add_subdirectory(touchpoints) add_subdirectory(zoom) # OpenGL-specific effects +add_subdirectory(blendchanges) add_subdirectory(blur) add_subdirectory(backgroundcontrast) add_subdirectory(glide) diff --git a/src/effects/blendchanges/CMakeLists.txt b/src/effects/blendchanges/CMakeLists.txt new file mode 100644 index 0000000000..395e4a61ad --- /dev/null +++ b/src/effects/blendchanges/CMakeLists.txt @@ -0,0 +1,14 @@ +####################################### +# Effect + +set(blendchanges_SOURCES + main.cpp + blendchanges.cpp +) + +kwin4_add_effect_module(kwin4_effect_blend ${blendchanges_SOURCES}) +target_link_libraries(kwin4_effect_blend PRIVATE + kwineffects + kwinglutils + Qt::DBus +) diff --git a/src/effects/blendchanges/blendchanges.cpp b/src/effects/blendchanges/blendchanges.cpp new file mode 100644 index 0000000000..1d45aaeabd --- /dev/null +++ b/src/effects/blendchanges/blendchanges.cpp @@ -0,0 +1,115 @@ +/* + KWin - the KDE window manager + This file is part of the KDE project. + + SPDX-FileCopyrightText: 2022 David Edmundson + + SPDX-License-Identifier: GPL-2.0-or-later +*/ +// own +#include "blendchanges.h" +#include "kwinglutils.h" + +#include +#include + +namespace KWin +{ +BlendChanges::BlendChanges() + : DeformEffect() +{ + QDBusConnection::sessionBus().registerObject(QStringLiteral("/org/kde/KWin/BlendChanges"), + QStringLiteral("org.kde.KWin.BlendChanges"), + this, + QDBusConnection::ExportAllSlots); + + setLive(false); + m_timeline.setEasingCurve(QEasingCurve::InOutCubic); +} + +BlendChanges::~BlendChanges() = default; + +bool BlendChanges::supported() +{ + return effects->compositingType() == OpenGLCompositing && effects->animationsSupported(); +} + +void KWin::BlendChanges::start(int delay) +{ + int animationDuration = animationTime(400); + + if (!supported() || m_state != Off) { + return; + } + const EffectWindowList allWindows = effects->stackingOrder(); + for (auto window : allWindows) { + redirect(window); + } + + QTimer::singleShot(delay, this, [this, animationDuration]() { + m_timeline.setDuration(std::chrono::milliseconds(animationDuration)); + effects->addRepaintFull(); + m_state = Blending; + }); + + m_state = ShowingCache; +} + +void BlendChanges::drawWindow(EffectWindow *window, int mask, const QRegion ®ion, WindowPaintData &data) +{ + // draw the new picture underneath at full opacity + if (m_state != ShowingCache) { + Effect::drawWindow(window, mask, region, data); + } + // then the old on top, it works better than changing both alphas with the current blend mode + if (m_state != Off) { + DeformEffect::drawWindow(window, mask, region, data); + } +} + +void BlendChanges::deform(EffectWindow *window, int mask, WindowPaintData &data, WindowQuadList &quads) +{ + Q_UNUSED(window) + Q_UNUSED(mask) + Q_UNUSED(quads) + data.setOpacity(1.0 - m_timeline.value()); +} + +bool BlendChanges::isActive() const +{ + return m_state != Off; +} + +void BlendChanges::postPaintScreen() +{ + if (m_timeline.done()) { + m_lastPresentTime = std::chrono::milliseconds::zero(); + m_timeline.reset(); + m_state = Off; + + const EffectWindowList allWindows = effects->stackingOrder(); + for (auto window : allWindows) { + unredirect(window); + } + } + effects->addRepaintFull(); +} + +void BlendChanges::prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime) +{ + if (m_state == Off) { + return; + } + if (m_state == Blending) { + std::chrono::milliseconds delta(0); + if (m_lastPresentTime.count()) { + delta = presentTime - m_lastPresentTime; + } + m_lastPresentTime = presentTime; + m_timeline.update(delta); + } + + effects->prePaintScreen(data, presentTime); +} + +} // namespace KWin diff --git a/src/effects/blendchanges/blendchanges.h b/src/effects/blendchanges/blendchanges.h new file mode 100644 index 0000000000..51653eb7ae --- /dev/null +++ b/src/effects/blendchanges/blendchanges.h @@ -0,0 +1,53 @@ +/* + KWin - the KDE window manager + This file is part of the KDE project. + + SPDX-FileCopyrightText: 2022 David Edmundson + + SPDX-License-Identifier: GPL-2.0-or-later +*/ +#pragma once +#include +#include +#include + +namespace KWin +{ + +class BlendChanges : public DeformEffect +{ + Q_OBJECT + +public: + BlendChanges(); + ~BlendChanges() override; + + static bool supported(); + + // Effect interface + void prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime) override; + void postPaintScreen() override; + void drawWindow(EffectWindow *window, int mask, const QRegion ®ion, WindowPaintData &data) override; + void deform(EffectWindow *window, int mask, WindowPaintData &data, WindowQuadList &quads) override; + bool isActive() const override; + +public Q_SLOTS: + /** + * Called from DBus, this should be called before triggering any changes + * delay (ms) refers to how long to keep the current frame before starting a crossfade + * We should expect all clients to have repainted by the time this expires + */ + void start(int delay = 300); + +private: + TimeLine m_timeline; + std::chrono::milliseconds m_lastPresentTime = std::chrono::milliseconds::zero(); + enum State { + Off, + ShowingCache, + Blending + }; + State m_state = Off; +}; + +} // namespace KWin diff --git a/src/effects/blendchanges/main.cpp b/src/effects/blendchanges/main.cpp new file mode 100644 index 0000000000..9bf8e36fb0 --- /dev/null +++ b/src/effects/blendchanges/main.cpp @@ -0,0 +1,18 @@ +/* + SPDX-FileCopyrightText: 2022 David Edmundson + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#include "blendchanges.h" + +namespace KWin +{ + +KWIN_EFFECT_FACTORY_SUPPORTED(BlendChanges, + "metadata.json.stripped", + return BlendChanges::supported();) + +} // namespace KWin + +#include "main.moc" diff --git a/src/effects/blendchanges/metadata.json b/src/effects/blendchanges/metadata.json new file mode 100644 index 0000000000..33ef3158dc --- /dev/null +++ b/src/effects/blendchanges/metadata.json @@ -0,0 +1,13 @@ +{ + "KPlugin": { + "Name": "Blend Changes", + "Description": "Animates system style changes", + "Category": "Appearance", + "EnabledByDefault": true, + "Id": "blendchanges", + "License": "GPL" + }, + "org.kde.kwin.effect": { + "internal": true + } +} diff --git a/src/libkwineffects/kwindeformeffect.cpp b/src/libkwineffects/kwindeformeffect.cpp index 32a7ed2895..0b01735b89 100644 --- a/src/libkwineffects/kwindeformeffect.cpp +++ b/src/libkwineffects/kwindeformeffect.cpp @@ -29,6 +29,7 @@ public: const WindowPaintData &data, const WindowQuadList &quads); GLTexture *maybeRender(EffectWindow *window, DeformOffscreenData *offscreenData); + bool live = true; }; DeformEffect::DeformEffect(QObject *parent) @@ -47,6 +48,12 @@ bool DeformEffect::supported() return effects->isOpenGLCompositing(); } +void DeformEffect::setLive(bool live) +{ + Q_ASSERT(d->windows.isEmpty()); + d->live = live; +} + void DeformEffect::redirect(EffectWindow *window) { DeformOffscreenData *&offscreenData = d->windows[window]; @@ -58,6 +65,11 @@ void DeformEffect::redirect(EffectWindow *window) if (d->windows.count() == 1) { setupConnections(); } + + if (!d->live) { + effects->makeOpenGLContextCurrent(); + d->maybeRender(window, offscreenData); + } } void DeformEffect::unredirect(EffectWindow *window) @@ -205,8 +217,10 @@ void DeformEffect::handleWindowDeleted(EffectWindow *window) void DeformEffect::setupConnections() { - d->windowDamagedConnection = - connect(effects, &EffectsHandler::windowDamaged, this, &DeformEffect::handleWindowDamaged); + if (d->live) { + d->windowDamagedConnection = + connect(effects, &EffectsHandler::windowDamaged, this, &DeformEffect::handleWindowDamaged); + } d->windowDeletedConnection = connect(effects, &EffectsHandler::windowDeleted, this, &DeformEffect::handleWindowDeleted); } diff --git a/src/libkwineffects/kwindeformeffect.h b/src/libkwineffects/kwindeformeffect.h index a228668e0a..720b56d328 100644 --- a/src/libkwineffects/kwindeformeffect.h +++ b/src/libkwineffects/kwindeformeffect.h @@ -37,10 +37,16 @@ public: static bool supported(); -private: - void drawWindow(EffectWindow *window, int mask, const QRegion ®ion, WindowPaintData &data) override; + /** + * If set our offscreen texture will be updated with the latest contents + * It should be set before redirecting windows + * The default is true + */ + void setLive(bool live); protected: + void drawWindow(EffectWindow *window, int mask, const QRegion ®ion, WindowPaintData &data) override; + /** * This function must be called when the effect wants to animate the specified * @a window.