From 34cfc6680bd65c3a414d9766cd7c0fa21961c40b Mon Sep 17 00:00:00 2001 From: Aleix Pol Date: Tue, 27 Jul 2021 19:42:21 +0200 Subject: [PATCH 1/5] effects: Include signals notifying about outputs changing --- src/abstract_output.h | 19 +++++++++++++++++++ src/abstract_wayland_output.cpp | 3 +++ src/effects.cpp | 2 ++ src/libkwineffects/kwineffects.h | 15 +++++++++++++++ 4 files changed, 39 insertions(+) diff --git a/src/abstract_output.h b/src/abstract_output.h index 64e03c781d..a93af2a360 100644 --- a/src/abstract_output.h +++ b/src/abstract_output.h @@ -232,6 +232,25 @@ Q_SIGNALS: */ void wakeUp(); + /** + * Notifies that the output is about to change configuration based on a + * user interaction. + * + * Be it because it gets a transformation or moved around. + * + * Only to be used for effects + */ + void aboutToChange(); + + /** + * Notifies that the output changed based on a user interaction. + * + * Be it because it gets a transformation or moved around. + * + * Only to be used for effects + */ + void changed(); + private: Q_DISABLE_COPY(AbstractOutput) int m_directScanoutCount = 0; diff --git a/src/abstract_wayland_output.cpp b/src/abstract_wayland_output.cpp index 8961614990..5ba940ded7 100644 --- a/src/abstract_wayland_output.cpp +++ b/src/abstract_wayland_output.cpp @@ -147,6 +147,8 @@ void AbstractWaylandOutput::setSubPixelInternal(SubPixel subPixel) void AbstractWaylandOutput::applyChanges(const KWaylandServer::OutputChangeSet *changeSet) { + Q_EMIT aboutToChange(); + qCDebug(KWIN_CORE) << "Apply changes to the Wayland output."; bool emitModeChanged = false; bool overallSizeCheckNeeded = false; @@ -183,6 +185,7 @@ void AbstractWaylandOutput::applyChanges(const KWaylandServer::OutputChangeSet * qCDebug(KWIN_CORE) << "Setting VRR Policy:" << changeSet->vrrPolicy(); setVrrPolicy(static_cast(changeSet->vrrPolicy())); } + Q_EMIT changed(); overallSizeCheckNeeded |= emitModeChanged; if (overallSizeCheckNeeded) { diff --git a/src/effects.cpp b/src/effects.cpp index 5460b853bb..e80bc18e5b 100644 --- a/src/effects.cpp +++ b/src/effects.cpp @@ -1726,6 +1726,8 @@ EffectScreenImpl::EffectScreenImpl(AbstractOutput *output, QObject *parent) : EffectScreen(parent) , m_platformOutput(output) { + connect(output, &AbstractOutput::aboutToChange, this, &EffectScreen::aboutToChange); + connect(output, &AbstractOutput::changed, this, &EffectScreen::changed); connect(output, &AbstractOutput::wakeUp, this, &EffectScreen::wakeUp); connect(output, &AbstractOutput::aboutToTurnOff, this, &EffectScreen::aboutToTurnOff); connect(output, &AbstractOutput::scaleChanged, this, &EffectScreen::devicePixelRatioChanged); diff --git a/src/libkwineffects/kwineffects.h b/src/libkwineffects/kwineffects.h index 9f69f0e6fe..53e3d2f5a5 100644 --- a/src/libkwineffects/kwineffects.h +++ b/src/libkwineffects/kwineffects.h @@ -1873,6 +1873,21 @@ Q_SIGNALS: * This signal is emitted when the device pixel ratio of this screen changes. */ void devicePixelRatioChanged(); + + /** + * Notifies that the output is about to change configuration based on a + * user interaction. + * + * Be it because it gets a transformation or moved around. + */ + void aboutToChange(); + + /** + * Notifies that the output changed based on a user interaction. + * + * Be it because it gets a transformation or moved around. + */ + void changed(); }; /** -- GitLab From 24cc93ec5b9a640e9a6ffd2c22b723da20ef001e Mon Sep 17 00:00:00 2001 From: Aleix Pol Date: Thu, 29 Jul 2021 04:07:13 +0200 Subject: [PATCH 2/5] effects: Allow exposing an EffectScreen::transform This way an effect can know the transformation that is applied on a display. --- src/abstract_output.h | 13 +++++++++++++ src/abstract_wayland_output.h | 14 +------------- src/effects.cpp | 5 +++++ src/effects.h | 1 + src/libkwineffects/kwineffects.h | 14 ++++++++++++++ 5 files changed, 34 insertions(+), 13 deletions(-) diff --git a/src/abstract_output.h b/src/abstract_output.h index a93af2a360..cc47116830 100644 --- a/src/abstract_output.h +++ b/src/abstract_output.h @@ -207,6 +207,19 @@ public: */ static std::chrono::milliseconds dimAnimationTime(); + enum class Transform { + Normal, + Rotated90, + Rotated180, + Rotated270, + Flipped, + Flipped90, + Flipped180, + Flipped270 + }; + Q_ENUM(Transform) + virtual Transform transform() const { return Transform::Normal; } + Q_SIGNALS: /** * This signal is emitted when the geometry of this output has changed. diff --git a/src/abstract_wayland_output.h b/src/abstract_wayland_output.h index ea6f6d801c..2b07e37c84 100644 --- a/src/abstract_wayland_output.h +++ b/src/abstract_wayland_output.h @@ -32,18 +32,6 @@ class KWIN_EXPORT AbstractWaylandOutput : public AbstractOutput { Q_OBJECT public: - enum class Transform { - Normal, - Rotated90, - Rotated180, - Rotated270, - Flipped, - Flipped90, - Flipped180, - Flipped270 - }; - Q_ENUM(Transform) - enum class ModeFlag : uint { Current = 0x1, Preferred = 0x2, @@ -106,7 +94,7 @@ public: * - Rotated 270° and flipped along the horizontal axis is inv. portrait + inv. landscape + * portrait */ - Transform transform() const; + Transform transform() const override; int refreshRate() const override; diff --git a/src/effects.cpp b/src/effects.cpp index e80bc18e5b..d79347beb7 100644 --- a/src/effects.cpp +++ b/src/effects.cpp @@ -1754,6 +1754,11 @@ QRect EffectScreenImpl::geometry() const return m_platformOutput->geometry(); } +EffectScreen::Transform EffectScreenImpl::transform() const +{ + return EffectScreen::Transform(m_platformOutput->transform()); +} + //**************************************** // EffectWindowImpl //**************************************** diff --git a/src/effects.h b/src/effects.h index c61b8b4c53..2c17452de2 100644 --- a/src/effects.h +++ b/src/effects.h @@ -369,6 +369,7 @@ public: QString name() const override; qreal devicePixelRatio() const override; QRect geometry() const override; + Transform transform() const override; private: AbstractOutput *m_platformOutput; diff --git a/src/libkwineffects/kwineffects.h b/src/libkwineffects/kwineffects.h index 53e3d2f5a5..de93144896 100644 --- a/src/libkwineffects/kwineffects.h +++ b/src/libkwineffects/kwineffects.h @@ -1853,6 +1853,20 @@ public: */ virtual QRect geometry() const = 0; + + enum class Transform { + Normal, + Rotated90, + Rotated180, + Rotated270, + Flipped, + Flipped90, + Flipped180, + Flipped270 + }; + Q_ENUM(Transform) + virtual Transform transform() const = 0; + Q_SIGNALS: /** * Notifies that the display will be dimmed in @p time ms. -- GitLab From 4d1f30301b5669bd2497eeabc7cbc087be41f54a Mon Sep 17 00:00:00 2001 From: Aleix Pol Date: Thu, 29 Jul 2021 15:01:03 +0200 Subject: [PATCH 3/5] Make KWin::TimeLine::progress() public This way we can have animations that act slightly different in different parts of the animation --- src/libkwineffects/kwineffects.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libkwineffects/kwineffects.h b/src/libkwineffects/kwineffects.h index de93144896..187c40f0a6 100644 --- a/src/libkwineffects/kwineffects.h +++ b/src/libkwineffects/kwineffects.h @@ -3755,7 +3755,11 @@ public: TimeLine &operator=(const TimeLine &other); -private: + /** + * @returns a value between 0 and 1 defining the progress of the timeline + * + * @since 5.23 + */ qreal progress() const; private: -- GitLab From 9a4bbdf226a4e1b06b210a37b66688b40b33dc62 Mon Sep 17 00:00:00 2001 From: Aleix Pol Date: Wed, 28 Jul 2021 17:14:25 +0200 Subject: [PATCH 4/5] Make it possible for an effect to render a screen Useful in case it needs the contents of the screen but they can't be blitted, as the state of the framebuffer is unknown. --- autotests/mock_effectshandler.h | 3 ++ src/composite.cpp | 18 ++++++---- src/composite.h | 2 ++ src/effects.cpp | 6 ++++ src/effects.h | 1 + src/libkwineffects/kwineffects.h | 5 +++ src/plugins/scenes/opengl/scene_opengl.cpp | 22 +----------- src/scene.cpp | 39 ++++++++++++++++++++++ src/scene.h | 5 +++ 9 files changed, 74 insertions(+), 27 deletions(-) diff --git a/autotests/mock_effectshandler.h b/autotests/mock_effectshandler.h index 0de19a8b35..39119ce3df 100644 --- a/autotests/mock_effectshandler.h +++ b/autotests/mock_effectshandler.h @@ -282,6 +282,9 @@ public: return nullptr; } + void renderScreen(KWin::EffectScreen *screen) override { + Q_UNUSED(screen); + } private: bool m_animationsSuported = true; }; diff --git a/src/composite.cpp b/src/composite.cpp index 5e88c364fc..f29f839685 100644 --- a/src/composite.cpp +++ b/src/composite.cpp @@ -573,12 +573,8 @@ void Compositor::handleFrameRequested(RenderLoop *renderLoop) composite(renderLoop); } -void Compositor::composite(RenderLoop *renderLoop) +QList Compositor::windowsToRender() const { - const auto &output = m_renderLoops[renderLoop]; - - fTraceDuration("Paint (", output ? output->name() : QStringLiteral("screens"), ")"); - // Create a list of all windows in the stacking order QList windows = Workspace::self()->xStackingOrder(); @@ -606,6 +602,16 @@ void Compositor::composite(RenderLoop *renderLoop) } } } + return windows; +} + +void Compositor::composite(RenderLoop *renderLoop) +{ + const auto &output = m_renderLoops[renderLoop]; + + fTraceDuration("Paint (", output ? output->name() : QStringLiteral("screens"), ")"); + + const auto windows = windowsToRender(); const QRegion repaints = m_scene->repaints(output); m_scene->resetRepaints(output); @@ -616,7 +622,7 @@ void Compositor::composite(RenderLoop *renderLoop) const std::chrono::milliseconds frameTime = std::chrono::duration_cast(renderLoop->lastPresentationTimestamp()); - for (Toplevel *window : qAsConst(windows)) { + for (Toplevel *window : windows) { if (!window->readyForPainting()) { continue; } diff --git a/src/composite.h b/src/composite.h index 1615e315a1..2d51d168ac 100644 --- a/src/composite.h +++ b/src/composite.h @@ -22,6 +22,7 @@ class AbstractOutput; class CompositorSelectionOwner; class RenderLoop; class Scene; +class Toplevel; class X11Client; class X11SyncManager; @@ -86,6 +87,7 @@ public: // for delayed supportproperty management of effects void keepSupportProperty(xcb_atom_t atom); void removeSupportProperty(xcb_atom_t atom); + QList windowsToRender() const; Q_SIGNALS: void compositingToggled(bool active); diff --git a/src/effects.cpp b/src/effects.cpp index d79347beb7..ff8e45e09a 100644 --- a/src/effects.cpp +++ b/src/effects.cpp @@ -1718,6 +1718,12 @@ void EffectsHandlerImpl::slotOutputDisabled(AbstractOutput *output) } } +void EffectsHandlerImpl::renderScreen(EffectScreen *screen) +{ + auto output = static_cast(screen)->platformOutput(); + scene()->paintScreen(output, Compositor::self()->windowsToRender()); +} + //**************************************** // EffectScreenImpl //**************************************** diff --git a/src/effects.h b/src/effects.h index 2c17452de2..f2aefdb4a1 100644 --- a/src/effects.h +++ b/src/effects.h @@ -265,6 +265,7 @@ public: EffectScreen *screenAt(const QPoint &point) const override; EffectScreen *findScreen(const QString &name) const override; EffectScreen *findScreen(int screenId) const override; + void renderScreen(EffectScreen *screen) override; public Q_SLOTS: void slotCurrentTabAboutToChange(EffectWindow* from, EffectWindow* to); diff --git a/src/libkwineffects/kwineffects.h b/src/libkwineffects/kwineffects.h index 187c40f0a6..d2cfa875cd 100644 --- a/src/libkwineffects/kwineffects.h +++ b/src/libkwineffects/kwineffects.h @@ -1360,6 +1360,11 @@ public: virtual EffectScreen *findScreen(const QString &name) const = 0; virtual EffectScreen *findScreen(int screenId) const = 0; + /** + * Renders @p screen in the current render target + */ + virtual void renderScreen(EffectScreen *screen) = 0; + Q_SIGNALS: /** * This signal is emitted whenever a new @a screen is added to the system. diff --git a/src/plugins/scenes/opengl/scene_opengl.cpp b/src/plugins/scenes/opengl/scene_opengl.cpp index fa86259133..ea9533592c 100644 --- a/src/plugins/scenes/opengl/scene_opengl.cpp +++ b/src/plugins/scenes/opengl/scene_opengl.cpp @@ -732,27 +732,7 @@ SceneOpenGL2::~SceneOpenGL2() void SceneOpenGL2::updateProjectionMatrix(const QRect &rect) { - // Create a perspective projection with a 60° field-of-view, - // and an aspect ratio of 1.0. - m_projectionMatrix.setToIdentity(); - const float fovY = std::tan(qDegreesToRadians(60.0f) / 2); - const float aspect = 1.0f; - const float zNear = 0.1f; - const float zFar = 100.0f; - - const float yMax = zNear * fovY; - const float yMin = -yMax; - const float xMin = yMin * aspect; - const float xMax = yMax * aspect; - - m_projectionMatrix.frustum(xMin, xMax, yMin, yMax, zNear, zFar); - - const float scaleFactor = 1.1 * fovY / yMax; - m_projectionMatrix.translate(xMin * scaleFactor, yMax * scaleFactor, -1.1); - m_projectionMatrix.scale( (xMax - xMin) * scaleFactor / rect.width(), - -(yMax - yMin) * scaleFactor / rect.height(), - 0.001); - m_projectionMatrix.translate(-rect.x(), -rect.y()); + m_projectionMatrix = Scene::createProjectionMatrix(rect); } void SceneOpenGL2::paintSimpleScreen(int mask, const QRegion ®ion) diff --git a/src/scene.cpp b/src/scene.cpp index 29f26483aa..4b441f8735 100644 --- a/src/scene.cpp +++ b/src/scene.cpp @@ -74,6 +74,7 @@ #include "shadow.h" #include "wayland_server.h" #include "composite.h" +#include namespace KWin { @@ -125,6 +126,44 @@ void Scene::removeRepaints(AbstractOutput *output) m_repaints.remove(output); } + +QMatrix4x4 Scene::createProjectionMatrix(const QRect &rect) +{ + // Create a perspective projection with a 60° field-of-view, + // and an aspect ratio of 1.0. + QMatrix4x4 ret; + ret.setToIdentity(); + const float fovY = std::tan(qDegreesToRadians(60.0f) / 2); + const float aspect = 1.0f; + const float zNear = 0.1f; + const float zFar = 100.0f; + + const float yMax = zNear * fovY; + const float yMin = -yMax; + const float xMin = yMin * aspect; + const float xMax = yMax * aspect; + + ret.frustum(xMin, xMax, yMin, yMax, zNear, zFar); + + const float scaleFactor = 1.1 * fovY / yMax; + ret.translate(xMin * scaleFactor, yMax * scaleFactor, -1.1); + ret.scale( (xMax - xMin) * scaleFactor / rect.width(), + -(yMax - yMin) * scaleFactor / rect.height(), + 0.001); + ret.translate(-rect.x(), -rect.y()); + return ret; +} + +void Scene::paintScreen(AbstractOutput *output, const QList &toplevels) +{ + createStackingOrder(toplevels); + + const QRect geo = output->geometry(); + QRegion update = geo, repaint = geo, valid; + + paintScreen(geo, repaint, &update, &valid, output->renderLoop(), createProjectionMatrix(output->geometry())); + clearStackingOrder(); +} // returns mask and possibly modified region void Scene::paintScreen(const QRegion &damage, const QRegion &repaint, QRegion *updateRegion, QRegion *validRegion, RenderLoop *renderLoop, diff --git a/src/scene.h b/src/scene.h index a28852b43f..5297680dda 100644 --- a/src/scene.h +++ b/src/scene.h @@ -75,6 +75,9 @@ public: virtual void paint(AbstractOutput *output, const QRegion &damage, const QList &windows, RenderLoop *renderLoop) = 0; + + void paintScreen(AbstractOutput *output, const QList &toplevels); + /** * Adds the Toplevel to the Scene. * @@ -181,6 +184,8 @@ public: virtual void paintDesktop(int desktop, int mask, const QRegion ®ion, ScreenPaintData &data); + static QMatrix4x4 createProjectionMatrix(const QRect &rect); + Q_SIGNALS: void frameRendered(); void resetCompositing(); -- GitLab From 30959c2efb8991e34970e96d814c6098c5c88422 Mon Sep 17 00:00:00 2001 From: Aleix Pol Date: Tue, 27 Jul 2021 19:43:22 +0200 Subject: [PATCH 5/5] Include a transform effect It should be used to decorate changes on the display like rotation. --- src/effects/CMakeLists.txt | 1 + src/effects/effect_builtins.cpp | 16 ++ src/effects/effect_builtins.h | 1 + src/effects/screentransform/CMakeLists.txt | 7 + .../screentransform/screentransform.cpp | 205 ++++++++++++++++++ src/effects/screentransform/screentransform.h | 65 ++++++ src/scene.cpp | 2 +- 7 files changed, 296 insertions(+), 1 deletion(-) create mode 100644 src/effects/screentransform/CMakeLists.txt create mode 100644 src/effects/screentransform/screentransform.cpp create mode 100644 src/effects/screentransform/screentransform.h diff --git a/src/effects/CMakeLists.txt b/src/effects/CMakeLists.txt index 28f18e124f..f15f6cb4a4 100644 --- a/src/effects/CMakeLists.txt +++ b/src/effects/CMakeLists.txt @@ -171,6 +171,7 @@ add_subdirectory(diminactive) include(fallapart/CMakeLists.txt) include(highlightwindow/CMakeLists.txt) include(kscreen/CMakeLists.txt) +include(screentransform/CMakeLists.txt) add_subdirectory(magiclamp) add_subdirectory(overview) add_subdirectory(presentwindows) diff --git a/src/effects/effect_builtins.cpp b/src/effects/effect_builtins.cpp index 08932c8963..8ab05deb96 100644 --- a/src/effects/effect_builtins.cpp +++ b/src/effects/effect_builtins.cpp @@ -17,6 +17,7 @@ #include "presentwindows/presentwindows.h" #include "screenedge/screenedgeeffect.h" #include "screenshot/screenshot.h" +#include "screentransform/screentransform.h" #include "slidingpopups/slidingpopups.h" // Common effects only relevant to desktop #include "desktopgrid/desktopgrid.h" @@ -404,6 +405,21 @@ EFFECT_FALLBACK #endif EFFECT_FALLBACK QString() + }, { + QStringLiteral("screentransform"), + i18ndc("kwin_effects", "Name of a KWin Effect", "Transform"), + i18ndc("kwin_effects", "Comment describing the KWin Effect", "Animates display transformations"), + QStringLiteral("Appearance"), + QString(), + QUrl(), + true, + true, +#ifdef EFFECT_BUILTINS + &createHelper, + &ScreenTransformEffect::supported, + nullptr, +#endif + EFFECT_FALLBACK QString() }, { QStringLiteral("sheet"), i18ndc("kwin_effects", "Name of a KWin Effect", "Sheet"), diff --git a/src/effects/effect_builtins.h b/src/effects/effect_builtins.h index ebf77e0549..d86a0e3657 100644 --- a/src/effects/effect_builtins.h +++ b/src/effects/effect_builtins.h @@ -43,6 +43,7 @@ enum class BuiltInEffect Resize, ScreenEdge, ScreenShot, + ScreenTransform, Sheet, ShowFps, ShowPaint, diff --git a/src/effects/screentransform/CMakeLists.txt b/src/effects/screentransform/CMakeLists.txt new file mode 100644 index 0000000000..60a306eae4 --- /dev/null +++ b/src/effects/screentransform/CMakeLists.txt @@ -0,0 +1,7 @@ +####################################### +# Effect + +set(kwin4_effect_builtins_sources ${kwin4_effect_builtins_sources} + screentransform/screentransform.cpp +) + diff --git a/src/effects/screentransform/screentransform.cpp b/src/effects/screentransform/screentransform.cpp new file mode 100644 index 0000000000..6a93721f32 --- /dev/null +++ b/src/effects/screentransform/screentransform.cpp @@ -0,0 +1,205 @@ +/* + KWin - the KDE window manager + This file is part of the KDE project. + + SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez + + SPDX-License-Identifier: GPL-2.0-or-later +*/ +// own +#include "screentransform.h" +#include "kscreenconfig.h" +#include "kwinglutils.h" +#include + +namespace KWin +{ +ScreenTransformEffect::ScreenTransformEffect() + : Effect() +{ + initConfig(); + reconfigure(ReconfigureAll); + + const QList screens = effects->screens(); + for (auto screen : screens) { + addScreen(screen); + } + connect(effects, &EffectsHandler::screenAdded, this, &ScreenTransformEffect::addScreen); + connect(effects, &EffectsHandler::screenRemoved, this, &ScreenTransformEffect::removeScreen); +} + +ScreenTransformEffect::~ScreenTransformEffect() = default; + +bool ScreenTransformEffect::supported() +{ + return effects->compositingType() == OpenGLCompositing && effects->waylandDisplay() && effects->animationsSupported(); +} + +qreal transformAngle(EffectScreen::Transform current, EffectScreen::Transform old) +{ + auto ensureShort = [](int angle) { + return angle > 180 ? angle - 360 : angle < -180 ? angle + 360 : angle; + }; + // % 4 to ignore flipped cases (for now) + return ensureShort((int(current) % 4 - int(old) % 4) * 90); +} + +void ScreenTransformEffect::addScreen(EffectScreen *screen) +{ + connect(screen, &EffectScreen::changed, this, [this, screen] { + auto &state = m_states[screen]; + if (screen->transform() == state.m_oldTransform) { + effects->makeOpenGLContextCurrent(); + m_states.remove(screen); + return; + } + state.m_timeLine.setDuration(std::chrono::milliseconds(animationTime(250))); + state.m_timeLine.setEasingCurve(QEasingCurve::OutCirc); + state.m_lastPresentTime = std::chrono::milliseconds::zero(); + state.m_angle = transformAngle(screen->transform(), state.m_oldTransform); + Q_ASSERT(state.m_angle != 0); + effects->addRepaintFull(); + }); + connect(screen, &EffectScreen::aboutToChange, this, [this, screen] { + effects->makeOpenGLContextCurrent(); + auto &state = m_states[screen]; + state.m_oldTransform = screen->transform(); + state.m_texture.reset(new GLTexture(GL_RGBA8, screen->geometry().size() * screen->devicePixelRatio())); + + // Rendering the current scene into a texture + const bool c = state.m_texture->create(); + Q_ASSERT(c); + GLRenderTarget renderTarget(*state.m_texture); + GLRenderTarget::pushRenderTarget(&renderTarget); + + GLVertexBuffer::setVirtualScreenGeometry(screen->geometry()); + GLRenderTarget::setVirtualScreenGeometry(screen->geometry()); + GLVertexBuffer::setVirtualScreenScale(screen->devicePixelRatio()); + GLRenderTarget::setVirtualScreenScale(screen->devicePixelRatio()); + + effects->renderScreen(screen); + state.m_captured = true; + GLRenderTarget::popRenderTarget(); + }); +} + +void ScreenTransformEffect::removeScreen(EffectScreen *screen) +{ + effects->makeOpenGLContextCurrent(); + m_states.remove(screen); + effects->doneOpenGLContextCurrent(); +} + +void ScreenTransformEffect::reconfigure(ReconfigureFlags flags) +{ + Q_UNUSED(flags) + KscreenConfig::self()->read(); +} + +void ScreenTransformEffect::prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime) +{ + if (isScreenTransforming(data.screen)) { + std::chrono::milliseconds delta = std::chrono::milliseconds::zero(); + auto &state = m_states[data.screen]; + if (state.m_lastPresentTime.count()) { + delta = presentTime - state.m_lastPresentTime; + } + state.m_lastPresentTime = presentTime; + if (state.isSecondHalf()) { + data.mask |= PAINT_SCREEN_TRANSFORMED; + } + + state.m_timeLine.update(delta); + if (state.m_timeLine.done()) { + m_states.remove(data.screen); + } + } + + effects->prePaintScreen(data, presentTime); +} + +void ScreenTransformEffect::paintScreen(int mask, const QRegion ®ion, KWin::ScreenPaintData &data) +{ + auto screen = data.screen(); + if (isScreenTransforming(screen)) { + auto &state = m_states[screen]; + if (state.isSecondHalf()) { + data.setRotationAngle(state.m_angle / 2 * (1 - state.m_timeLine.value())); + auto center = screen->geometry().center(); + data.setRotationOrigin(QVector3D(center.x(), center.y(), 0)); + effects->addRepaintFull(); + } + } + + effects->paintScreen(mask, region, data); + if (isScreenTransforming(screen)) { + auto &state = m_states[screen]; + + if (!state.isSecondHalf()) { + Q_ASSERT(state.m_texture); + + ShaderBinder binder(ShaderTrait::MapTexture); + GLShader *shader(binder.shader()); + if (!shader) { + return; + } + const QRect screenGeometry = screen->geometry(); + const QRect textureRect = {0, 0, state.m_texture->width(), state.m_texture->height()}; + + QMatrix4x4 matrix(data.projectionMatrix()); + // Go to the former texture centre + matrix.translate(screenGeometry.width() / 2, screenGeometry.height() / 2); + // Invert the transformation + matrix.rotate(state.m_angle / 2 * (1 + 1 - state.m_timeLine.value()), 0, 0, 1); + // Go to the screen centre + matrix.translate(-textureRect.width() / 2, -textureRect.height() / 2); + shader->setUniform(GLShader::ModelViewProjectionMatrix, matrix); + + state.m_texture->bind(); + state.m_texture->render(screen->geometry(), textureRect); + state.m_texture->unbind(); + } + effects->addRepaintFull(); + } +} + +void ScreenTransformEffect::prePaintWindow(EffectWindow *w, WindowPrePaintData &data, std::chrono::milliseconds presentTime) +{ + auto screen = effects->findScreen(w->screen()); + if (isScreenTransforming(screen)) { + auto &state = m_states[screen]; + if (!state.isSecondHalf()) { + data.setTranslucent(); + } + } + effects->prePaintWindow(w, data, presentTime); +} + +void ScreenTransformEffect::paintWindow(EffectWindow *w, int mask, QRegion region, WindowPaintData &data) +{ + auto screen = effects->findScreen(w->screen()); + if (isScreenTransforming(screen)) { + auto &state = m_states[screen]; + if (!state.isSecondHalf()) { + // During the first half we want all on 0 because we'll be rendering our texture + data.multiplyOpacity(0.0); + data.multiplyBrightness(0.0); + } + } + effects->paintWindow(w, mask, region, data); +} + +bool ScreenTransformEffect::isActive() const +{ + return !m_states.isEmpty(); +} + +bool ScreenTransformEffect::isScreenTransforming(EffectScreen *screen) const +{ + auto it = m_states.constFind(screen); + return it != m_states.constEnd() && it->m_captured; +} + +ScreenTransformEffect::ScreenState::~ScreenState() = default; + +} // namespace KWin diff --git a/src/effects/screentransform/screentransform.h b/src/effects/screentransform/screentransform.h new file mode 100644 index 0000000000..07098a5259 --- /dev/null +++ b/src/effects/screentransform/screentransform.h @@ -0,0 +1,65 @@ +/* + KWin - the KDE window manager + This file is part of the KDE project. + + SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez + + SPDX-License-Identifier: GPL-2.0-or-later +*/ +#ifndef KWIN_SCREENTRANSFORM_H +#define KWIN_SCREENTRANSFORM_H + +#include + +namespace KWin +{ +class GLRenderTarget; +class GLTexture; + +class ScreenTransformEffect : public Effect +{ + Q_OBJECT + +public: + ScreenTransformEffect(); + ~ScreenTransformEffect() override; + + void prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime) override; + void paintScreen(int mask, const QRegion ®ion, KWin::ScreenPaintData &data) override; + void prePaintWindow(EffectWindow *w, WindowPrePaintData &data, std::chrono::milliseconds presentTime) override; + void paintWindow(EffectWindow *w, int mask, QRegion region, WindowPaintData &data) override; + + void reconfigure(ReconfigureFlags flags) override; + bool isActive() const override; + + int requestedEffectChainPosition() const override + { + return 99; + } + static bool supported(); + +private: + struct ScreenState { + ~ScreenState(); + bool isSecondHalf() const + { + return m_timeLine.progress() > 0.5; + } + + TimeLine m_timeLine; + QSharedPointer m_texture; + std::chrono::milliseconds m_lastPresentTime = std::chrono::milliseconds::zero(); + EffectScreen::Transform m_oldTransform; + qreal m_angle = 0; + bool m_captured = false; + }; + + void addScreen(EffectScreen *screen); + void removeScreen(EffectScreen *screen); + bool isScreenTransforming(EffectScreen *screen) const; + + QHash m_states; +}; + +} // namespace KWin +#endif // KWIN_SCREENTRANSFORM_H diff --git a/src/scene.cpp b/src/scene.cpp index 4b441f8735..b60579c3fe 100644 --- a/src/scene.cpp +++ b/src/scene.cpp @@ -74,7 +74,7 @@ #include "shadow.h" #include "wayland_server.h" #include "composite.h" -#include +#include namespace KWin { -- GitLab