Include a transform effect
It should be used to decorate changes on the display like rotation.
This commit is contained in:
parent
9a4bbdf226
commit
30959c2efb
7 changed files with 296 additions and 1 deletions
|
@ -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)
|
||||
|
|
|
@ -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>,
|
||||
&ScreenTransformEffect::supported,
|
||||
nullptr,
|
||||
#endif
|
||||
EFFECT_FALLBACK QString()
|
||||
}, {
|
||||
QStringLiteral("sheet"),
|
||||
i18ndc("kwin_effects", "Name of a KWin Effect", "Sheet"),
|
||||
|
|
|
@ -43,6 +43,7 @@ enum class BuiltInEffect
|
|||
Resize,
|
||||
ScreenEdge,
|
||||
ScreenShot,
|
||||
ScreenTransform,
|
||||
Sheet,
|
||||
ShowFps,
|
||||
ShowPaint,
|
||||
|
|
7
src/effects/screentransform/CMakeLists.txt
Normal file
7
src/effects/screentransform/CMakeLists.txt
Normal file
|
@ -0,0 +1,7 @@
|
|||
#######################################
|
||||
# Effect
|
||||
|
||||
set(kwin4_effect_builtins_sources ${kwin4_effect_builtins_sources}
|
||||
screentransform/screentransform.cpp
|
||||
)
|
||||
|
205
src/effects/screentransform/screentransform.cpp
Normal file
205
src/effects/screentransform/screentransform.cpp
Normal file
|
@ -0,0 +1,205 @@
|
|||
/*
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez <aleixpol@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
// own
|
||||
#include "screentransform.h"
|
||||
#include "kscreenconfig.h"
|
||||
#include "kwinglutils.h"
|
||||
#include <QDebug>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
ScreenTransformEffect::ScreenTransformEffect()
|
||||
: Effect()
|
||||
{
|
||||
initConfig<KscreenConfig>();
|
||||
reconfigure(ReconfigureAll);
|
||||
|
||||
const QList<EffectScreen *> 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<KscreenConfig>(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
|
65
src/effects/screentransform/screentransform.h
Normal file
65
src/effects/screentransform/screentransform.h
Normal file
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez <aleixpol@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#ifndef KWIN_SCREENTRANSFORM_H
|
||||
#define KWIN_SCREENTRANSFORM_H
|
||||
|
||||
#include <kwineffects.h>
|
||||
|
||||
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<GLTexture> 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<EffectScreen *, ScreenState> m_states;
|
||||
};
|
||||
|
||||
} // namespace KWin
|
||||
#endif // KWIN_SCREENTRANSFORM_H
|
|
@ -74,7 +74,7 @@
|
|||
#include "shadow.h"
|
||||
#include "wayland_server.h"
|
||||
#include "composite.h"
|
||||
#include <qmath.h>
|
||||
#include <QtMath>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue