diff --git a/autotests/mock_effectshandler.h b/autotests/mock_effectshandler.h index 0de19a8b3572414ae8d8097735750d9ef0583584..39119ce3df30023d77ee87da3e901e6ba7afcaeb 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/abstract_output.h b/src/abstract_output.h index 64e03c781d360cba5c3fc1cbca851a7905df7e41..cc47116830d870b4b0ef8096e8e959b2a2df786b 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. @@ -232,6 +245,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 89616149902a44e853381da0b15c1b355b32afb5..5ba940ded7c01a0b22741dcaa716169b0409ae33 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/abstract_wayland_output.h b/src/abstract_wayland_output.h index ea6f6d801c807286a08f0776c9d6d030b3446776..2b07e37c8421cbbc399c5168771bba66e00f0f51 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/composite.cpp b/src/composite.cpp index 5e88c364fca2e87e7feedd8603c5176fbdc53381..f29f83968551788b16ebb6429e20b1f612668835 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 1615e315a1c11c4e70e0569a54496a63888419e8..2d51d168acc8c827f343faffe4f175113b1c49c3 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 5460b853bb69a2b84041796731c08fd83cbda461..ff8e45e09a418c1a3fc99f199ce7c06321bd9f23 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 //**************************************** @@ -1726,6 +1732,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); @@ -1752,6 +1760,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 c61b8b4c531f563dae3495d301189cb2c2245105..f2aefdb4a1c0cd6d7b9019ecf3a50ae66c6dd6f1 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); @@ -369,6 +370,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/effects/CMakeLists.txt b/src/effects/CMakeLists.txt index 28f18e124f7f8c399fd18204c45219f2df00c472..f15f6cb4a418c147ba1d588c0892678310abf5e9 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 08932c8963270c8b78ff44d3495e2c61b5efee32..8ab05deb96358ac73056023b9db5fb737979e3a6 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 ebf77e05492f0586ae8773e9420c335796fc2925..d86a0e3657c8778d1856ee933d52babb28ec993d 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 0000000000000000000000000000000000000000..60a306eae45d2108e8c11edf6d62b88d14855305 --- /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 0000000000000000000000000000000000000000..6a93721f32ba15cbe44aea0b73c0a71f6209ad8a --- /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 0000000000000000000000000000000000000000..07098a525908ab38cc97886323709c171bb652ef --- /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/libkwineffects/kwineffects.h b/src/libkwineffects/kwineffects.h index 9f69f0e6fe571f6ed02696e52e6e84699eb5a13c..d2cfa875cda6fc9196ccb5274e798ce435862d9c 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. @@ -1853,6 +1858,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. @@ -1873,6 +1892,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(); }; /** @@ -3726,7 +3760,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: diff --git a/src/plugins/scenes/opengl/scene_opengl.cpp b/src/plugins/scenes/opengl/scene_opengl.cpp index fa86259133fdfab6a5f8f76c4570c1b6f2a9e248..ea9533592c4a06d0c4a63f7e43158efbc42f73c8 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 29f26483aad8c49b85f6478d1ab4d35d7b36f452..b60579c3feb72a8b27009caddab159d469d3d7a6 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 a28852b43f441fa7bc0165199f0331483323439f..5297680dda9b989d0d24486f09a5ac61cd6ad26e 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();