Move decoration renderer to decoration item

Currently, the implementation of the DecoratedClient and the decoration
renderer are strongly coupled. This poses a problem with the item based
design as the ultimate goal is to have scene items construct paint nodes
which are then fed to the renderer. The DecorationItem has to have
control over the decoration texture. Another issue is that the scene
cannot smoothly cross-fade between two window states if the decoration
is removed, e.g. from fullscreen mode to normal and vice versa.

This change moves the decoration renderer to the decoration item. With
the introduction of a generic scene texture atlas, we hope to get rid of
the decoration renderer altogether.
This commit is contained in:
Vlad Zahorodnii 2021-04-26 19:38:40 +03:00
parent 7f32594692
commit f46c7bae8d
28 changed files with 328 additions and 537 deletions

View file

@ -37,7 +37,6 @@ set(kwin_SRCS
decorations/decoratedclient.cpp
decorations/decorationbridge.cpp
decorations/decorationpalette.cpp
decorations/decorationrenderer.cpp
decorations/decorations_logging.cpp
decorations/settings.cpp
deleted.cpp

View file

@ -1,30 +1,137 @@
/*
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "decorationitem.h"
#include "abstract_client.h"
#include "composite.h"
#include "decorations/decoratedclient.h"
#include "scene.h"
#include "utils.h"
#include <KDecoration2/Decoration>
#include <KDecoration2/DecoratedClient>
#include <QPainter>
namespace KWin
{
DecorationRenderer::DecorationRenderer(Decoration::DecoratedClientImpl *client)
: m_client(client)
, m_imageSizesDirty(true)
{
connect(client->decoration(), &KDecoration2::Decoration::damaged,
this, &DecorationRenderer::addDamage);
connect(client->client(), &AbstractClient::screenScaleChanged,
this, &DecorationRenderer::invalidate);
connect(client->decoration(), &KDecoration2::Decoration::bordersChanged,
this, &DecorationRenderer::invalidate);
connect(client->decoratedClient(), &KDecoration2::DecoratedClient::sizeChanged,
this, &DecorationRenderer::invalidate);
invalidate();
}
Decoration::DecoratedClientImpl *DecorationRenderer::client() const
{
return m_client;
}
void DecorationRenderer::invalidate()
{
addDamage(m_client->client()->rect());
m_imageSizesDirty = true;
}
QRegion DecorationRenderer::damage() const
{
return m_damage;
}
void DecorationRenderer::addDamage(const QRegion &region)
{
m_damage += region;
emit damaged(region);
}
void DecorationRenderer::resetDamage()
{
m_damage = QRegion();
}
QImage DecorationRenderer::renderToImage(const QRect &geo)
{
Q_ASSERT(m_client);
auto dpr = client()->client()->screenScale();
// Guess the pixel format of the X pixmap into which the QImage will be copied.
QImage::Format format;
const int depth = client()->client()->depth();
switch (depth) {
case 30:
format = QImage::Format_A2RGB30_Premultiplied;
break;
case 24:
case 32:
format = QImage::Format_ARGB32_Premultiplied;
break;
default:
qCCritical(KWIN_CORE) << "Unsupported client depth" << depth;
format = QImage::Format_ARGB32_Premultiplied;
break;
};
QImage image(geo.width() * dpr, geo.height() * dpr, format);
image.setDevicePixelRatio(dpr);
image.fill(Qt::transparent);
QPainter p(&image);
p.setRenderHint(QPainter::Antialiasing);
p.setWindow(QRect(geo.topLeft(), geo.size() * qPainterEffectiveDevicePixelRatio(&p)));
p.setClipRect(geo);
renderToPainter(&p, geo);
return image;
}
void DecorationRenderer::renderToPainter(QPainter *painter, const QRect &rect)
{
client()->decoration()->paint(painter, rect);
}
DecorationItem::DecorationItem(KDecoration2::Decoration *decoration, Scene::Window *window, Item *parent)
: Item(window, parent)
{
Toplevel *toplevel = window->window();
AbstractClient *client = qobject_cast<AbstractClient *>(window->window());
m_renderer.reset(Compositor::self()->scene()->createDecorationRenderer(client->decoratedClient()));
connect(toplevel, &Toplevel::frameGeometryChanged,
connect(client, &Toplevel::frameGeometryChanged,
this, &DecorationItem::handleFrameGeometryChanged);
connect(toplevel, &Toplevel::screenScaleChanged,
connect(client, &Toplevel::screenScaleChanged,
this, &DecorationItem::discardQuads);
connect(decoration, &KDecoration2::Decoration::bordersChanged,
this, &DecorationItem::discardQuads);
setSize(toplevel->size());
connect(renderer(), &DecorationRenderer::damaged,
this, &DecorationItem::scheduleRepaint);
// If the decoration is about to be destroyed, render the decoration for the last time.
connect(client, &Toplevel::markedAsZombie, this, &DecorationItem::preprocess);
setSize(client->size());
}
void DecorationItem::preprocess()
{
const QRegion damage = m_renderer->damage();
if (!damage.isEmpty()) {
m_renderer->render(damage);
m_renderer->resetDamage();
}
}
void DecorationItem::handleFrameGeometryChanged()
@ -32,4 +139,9 @@ void DecorationItem::handleFrameGeometryChanged()
setSize(window()->size());
}
DecorationRenderer *DecorationItem::renderer() const
{
return m_renderer.data();
}
} // namespace KWin

View file

@ -16,6 +16,47 @@ class Decoration;
namespace KWin
{
namespace Decoration
{
class DecoratedClientImpl;
}
class KWIN_EXPORT DecorationRenderer : public QObject
{
Q_OBJECT
public:
virtual void render(const QRegion &region) = 0;
void invalidate();
// TODO: Move damage tracking inside DecorationItem.
QRegion damage() const;
void addDamage(const QRegion &region);
void resetDamage();
Q_SIGNALS:
void damaged(const QRegion &region);
protected:
explicit DecorationRenderer(Decoration::DecoratedClientImpl *client);
Decoration::DecoratedClientImpl *client() const;
bool areImageSizesDirty() const {
return m_imageSizesDirty;
}
void resetImageSizesDirty() {
m_imageSizesDirty = false;
}
QImage renderToImage(const QRect &geo);
void renderToPainter(QPainter *painter, const QRect &rect);
private:
QPointer<Decoration::DecoratedClientImpl> m_client;
QRegion m_damage;
bool m_imageSizesDirty;
};
/**
* The DecorationItem class represents a server-side decoration.
*/
@ -26,11 +67,17 @@ class KWIN_EXPORT DecorationItem : public Item
public:
explicit DecorationItem(KDecoration2::Decoration *decoration, Scene::Window *window, Item *parent = nullptr);
DecorationRenderer *renderer() const;
private Q_SLOTS:
void handleFrameGeometryChanged();
protected:
void preprocess() override;
private:
QPointer<KDecoration2::Decoration> m_decoration;
QScopedPointer<DecorationRenderer> m_renderer;
};
} // namespace KWin

View file

@ -9,7 +9,6 @@
#include "decoratedclient.h"
#include "decorationbridge.h"
#include "decorationpalette.h"
#include "decorationrenderer.h"
#include "abstract_client.h"
#include "composite.h"
#include "cursor.h"
@ -33,9 +32,7 @@ DecoratedClientImpl::DecoratedClientImpl(AbstractClient *client, KDecoration2::D
, ApplicationMenuEnabledDecoratedClientPrivate(decoratedClient, decoration)
, m_client(client)
, m_clientSize(client->clientSize())
, m_renderer(nullptr)
{
createRenderer();
client->setDecoratedClient(QPointer<DecoratedClientImpl>(this));
connect(client, &AbstractClient::activeChanged, this,
[decoratedClient, client]() {
@ -77,19 +74,6 @@ DecoratedClientImpl::DecoratedClientImpl(AbstractClient *client, KDecoration2::D
&Decoration::DecoratedClientImpl::signalShadeChange);
connect(client, &AbstractClient::keepAboveChanged, decoratedClient, &KDecoration2::DecoratedClient::keepAboveChanged);
connect(client, &AbstractClient::keepBelowChanged, decoratedClient, &KDecoration2::DecoratedClient::keepBelowChanged);
connect(Compositor::self(), &Compositor::aboutToToggleCompositing, this, &DecoratedClientImpl::destroyRenderer);
m_compositorToggledConnection = connect(Compositor::self(), &Compositor::compositingToggled, this,
[this, decoration]() {
createRenderer();
decoration->update();
}
);
connect(Compositor::self(), &Compositor::aboutToDestroy, this,
[this] {
disconnect(m_compositorToggledConnection);
m_compositorToggledConnection = QMetaObject::Connection();
}
);
connect(client, &AbstractClient::quickTileModeChanged, decoratedClient,
[this, decoratedClient]() {
emit decoratedClient->adjacentScreenEdgesChanged(adjacentScreenEdges());
@ -318,16 +302,5 @@ bool DecoratedClientImpl::isApplicationMenuActive() const
return m_client->applicationMenuActive();
}
void DecoratedClientImpl::createRenderer()
{
m_renderer = kwinApp()->platform()->createDecorationRenderer(this);
}
void DecoratedClientImpl::destroyRenderer()
{
delete m_renderer;
m_renderer = nullptr;
}
}
}

View file

@ -24,8 +24,6 @@ class AbstractClient;
namespace Decoration
{
class Renderer;
class DecoratedClientImpl : public QObject, public KDecoration2::ApplicationMenuEnabledDecoratedClientPrivate
{
Q_OBJECT
@ -82,9 +80,6 @@ public:
AbstractClient *client() {
return m_client;
}
Renderer *renderer() {
return m_renderer;
}
KDecoration2::DecoratedClient *decoratedClient() {
return KDecoration2::DecoratedClientPrivate::client();
}
@ -95,12 +90,8 @@ private Q_SLOTS:
void delayedRequestToggleMaximization(Options::WindowOperation operation);
private:
void createRenderer();
void destroyRenderer();
AbstractClient *m_client;
QSize m_clientSize;
Renderer *m_renderer;
QMetaObject::Connection m_compositorToggledConnection;
QString m_toolTipText;
QTimer m_toolTipWakeUp;

View file

@ -8,7 +8,6 @@
*/
#include "decorationbridge.h"
#include "decoratedclient.h"
#include "decorationrenderer.h"
#include "decorations_logging.h"
#include "settings.h"
// KWin core

View file

@ -1,102 +0,0 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "decorationrenderer.h"
#include "decoratedclient.h"
#include "decorations/decorations_logging.h"
#include "deleted.h"
#include "abstract_client.h"
#include "screens.h"
#include <KDecoration2/Decoration>
#include <KDecoration2/DecoratedClient>
#include <QDebug>
#include <QPainter>
namespace KWin
{
namespace Decoration
{
Renderer::Renderer(DecoratedClientImpl *client)
: QObject(client)
, m_client(client)
, m_imageSizesDirty(true)
{
auto markImageSizesDirty = [this]{
schedule(m_client->client()->rect());
m_imageSizesDirty = true;
};
connect(client->decoration(), &KDecoration2::Decoration::damaged, this, &Renderer::schedule);
connect(client->client(), &AbstractClient::screenScaleChanged, this, markImageSizesDirty);
connect(client->decoration(), &KDecoration2::Decoration::bordersChanged, this, markImageSizesDirty);
connect(client->decoratedClient(), &KDecoration2::DecoratedClient::sizeChanged, this, markImageSizesDirty);
}
Renderer::~Renderer() = default;
void Renderer::schedule(const QRegion &rect)
{
m_scheduled = m_scheduled.united(rect);
emit renderScheduled(rect);
}
QRegion Renderer::getScheduled()
{
QRegion region = m_scheduled;
m_scheduled = QRegion();
return region;
}
QImage Renderer::renderToImage(const QRect &geo)
{
Q_ASSERT(m_client);
auto dpr = client()->client()->screenScale();
// Guess the pixel format of the X pixmap into which the QImage will be copied.
QImage::Format format;
const int depth = client()->client()->depth();
switch (depth) {
case 30:
format = QImage::Format_A2RGB30_Premultiplied;
break;
case 24:
case 32:
format = QImage::Format_ARGB32_Premultiplied;
break;
default:
qCCritical(KWIN_DECORATIONS) << "Unsupported client depth" << depth;
format = QImage::Format_ARGB32_Premultiplied;
break;
};
QImage image(geo.width() * dpr, geo.height() * dpr, format);
image.setDevicePixelRatio(dpr);
image.fill(Qt::transparent);
QPainter p(&image);
p.setRenderHint(QPainter::Antialiasing);
p.setWindow(QRect(geo.topLeft(), geo.size() * qPainterEffectiveDevicePixelRatio(&p)));
p.setClipRect(geo);
renderToPainter(&p, geo);
return image;
}
void Renderer::renderToPainter(QPainter *painter, const QRect &rect)
{
client()->decoration()->paint(painter, rect);
}
void Renderer::reparent(Deleted *deleted)
{
setParent(deleted);
m_client = nullptr;
}
}
}

View file

@ -1,76 +0,0 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef KWIN_DECORATION_RENDERER_H
#define KWIN_DECORATION_RENDERER_H
#include <QObject>
#include <QRegion>
#include <kwin_export.h>
namespace KWin
{
class Deleted;
namespace Decoration
{
class DecoratedClientImpl;
class KWIN_EXPORT Renderer : public QObject
{
Q_OBJECT
public:
~Renderer() override;
void schedule(const QRegion &region);
/**
* Reparents this Renderer to the @p deleted.
* After this call the Renderer is no longer able to render
* anything, client() returns a nullptr.
*/
virtual void reparent(Deleted *deleted);
Q_SIGNALS:
void renderScheduled(const QRegion &geo);
protected:
explicit Renderer(DecoratedClientImpl *client);
/**
* @returns the scheduled paint region and resets
*/
QRegion getScheduled();
virtual void render() = 0;
DecoratedClientImpl *client() {
return m_client;
}
bool areImageSizesDirty() const {
return m_imageSizesDirty;
}
void resetImageSizesDirty() {
m_imageSizesDirty = false;
}
QImage renderToImage(const QRect &geo);
void renderToPainter(QPainter *painter, const QRect &rect);
private:
DecoratedClientImpl *m_client;
QRegion m_scheduled;
bool m_imageSizesDirty;
};
}
}
#endif

View file

@ -14,8 +14,6 @@
#include "group.h"
#include "netinfo.h"
#include "shadow.h"
#include "decorations/decoratedclient.h"
#include "decorations/decorationrenderer.h"
#include <QDebug>
@ -30,13 +28,11 @@ Deleted::Deleted()
, m_minimized(false)
, m_modal(false)
, m_wasClient(false)
, m_decorationRenderer(nullptr)
, m_fullscreen(false)
, m_keepAbove(false)
, m_keepBelow(false)
, m_wasPopupWindow(false)
, m_wasOutline(false)
, m_wasDecorated(false)
, m_wasLockScreen(false)
{
}
@ -85,18 +81,11 @@ void Deleted::copyToDeleted(Toplevel* c)
if (WinInfo* cinfo = dynamic_cast< WinInfo* >(info))
cinfo->disable();
if (AbstractClient *client = dynamic_cast<AbstractClient*>(c)) {
m_wasDecorated = client->isDecorated();
if (m_wasDecorated) {
if (client->isDecorated()) {
client->layoutDecorationRects(decoration_left,
decoration_top,
decoration_right,
decoration_bottom);
if (client->isDecorated()) {
if (Decoration::Renderer *renderer = client->decoratedClient()->renderer()) {
m_decorationRenderer = renderer;
m_decorationRenderer->reparent(this);
}
}
}
m_wasClient = true;
m_minimized = client->isMinimized();
@ -163,11 +152,6 @@ QPoint Deleted::clientPos() const
return contentsRect.topLeft();
}
bool Deleted::wasDecorated() const
{
return m_wasDecorated;
}
void Deleted::layoutDecorationRects(QRect& left, QRect& top, QRect& right, QRect& bottom) const
{
left = decoration_left;

View file

@ -17,11 +17,6 @@ namespace KWin
class AbstractClient;
namespace Decoration
{
class Renderer;
}
class KWIN_EXPORT Deleted : public Toplevel
{
Q_OBJECT
@ -41,7 +36,6 @@ public:
QRect transparentRect() const override;
bool isDeleted() const override;
xcb_window_t frameId() const override;
bool wasDecorated() const;
void layoutDecorationRects(QRect &left, QRect &top, QRect &right, QRect &bottom) const;
Layer layer() const override {
return m_layer;
@ -61,10 +55,6 @@ public:
}
QByteArray windowRole() const override;
const Decoration::Renderer *decorationRenderer() const {
return m_decorationRenderer;
}
bool isFullScreen() const {
return m_fullscreen;
}
@ -127,7 +117,6 @@ private:
bool m_modal;
QList<AbstractClient*> m_mainClients;
bool m_wasClient;
Decoration::Renderer *m_decorationRenderer;
NET::WindowType m_type = NET::Unknown;
QByteArray m_windowRole;
bool m_fullscreen;
@ -136,7 +125,6 @@ private:
QString m_caption;
bool m_wasPopupWindow;
bool m_wasOutline;
bool m_wasDecorated;
bool m_wasLockScreen;
qreal m_bufferScale = 1;
};

View file

@ -595,14 +595,6 @@ OutlineVisual *Platform::createOutline(Outline *outline)
return nullptr;
}
Decoration::Renderer *Platform::createDecorationRenderer(Decoration::DecoratedClientImpl *client)
{
if (Compositor::self()->scene()) {
return Compositor::self()->scene()->createDecorationRenderer(client);
}
return nullptr;
}
void Platform::invertScreen()
{
if (effects) {

View file

@ -45,12 +45,6 @@ class Toplevel;
class XRenderBackend;
#endif
namespace Decoration
{
class Renderer;
class DecoratedClientImpl;
}
class KWIN_EXPORT Outputs : public QVector<AbstractOutput*>
{
public:
@ -359,13 +353,6 @@ public:
*/
virtual OutlineVisual *createOutline(Outline *outline);
/**
* Creates the Decoration::Renderer for the given @p client.
*
* The default implementation creates a Renderer suited for the Compositor, @c nullptr if there is no Compositor.
*/
virtual Decoration::Renderer *createDecorationRenderer(Decoration::DecoratedClientImpl *client);
/**
* Platform specific way to invert the screen.
* Default implementation invokes the invert effect

View file

@ -8,7 +8,6 @@ set(X11PLATFORM_SOURCES
overlaywindow_x11.cpp
screenedges_filter.cpp
windowselector.cpp
x11_decoration_renderer.cpp
x11_output.cpp
x11_platform.cpp
x11cursor.cpp

View file

@ -1,98 +0,0 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "x11_decoration_renderer.h"
#include "decorations/decoratedclient.h"
#include "x11client.h"
#include "deleted.h"
#include <kwinglobals.h>
#include <KDecoration2/Decoration>
#include <KDecoration2/DecoratedClient>
#include <QTimer>
namespace KWin
{
namespace Decoration
{
X11Renderer::X11Renderer(DecoratedClientImpl *client)
: Renderer(client)
, m_scheduleTimer(new QTimer(this))
, m_gc(XCB_NONE)
{
// delay any rendering to end of event cycle to catch multiple updates per cycle
m_scheduleTimer->setSingleShot(true);
m_scheduleTimer->setInterval(0);
connect(m_scheduleTimer, &QTimer::timeout, this, &X11Renderer::render);
connect(this, &Renderer::renderScheduled, m_scheduleTimer, static_cast<void (QTimer::*)()>(&QTimer::start));
}
X11Renderer::~X11Renderer()
{
if (m_gc != XCB_NONE) {
xcb_free_gc(connection(), m_gc);
}
}
void X11Renderer::reparent(Deleted *deleted)
{
if (m_scheduleTimer->isActive()) {
m_scheduleTimer->stop();
}
disconnect(m_scheduleTimer, &QTimer::timeout, this, &X11Renderer::render);
disconnect(this, &Renderer::renderScheduled, m_scheduleTimer, static_cast<void (QTimer::*)()>(&QTimer::start));
Renderer::reparent(deleted);
}
void X11Renderer::render()
{
if (!client()) {
return;
}
const QRegion scheduled = getScheduled();
if (scheduled.isEmpty()) {
return;
}
xcb_connection_t *c = connection();
if (m_gc == XCB_NONE) {
m_gc = xcb_generate_id(c);
xcb_create_gc(c, m_gc, client()->client()->frameId(), 0, nullptr);
}
QRect left, top, right, bottom;
client()->client()->layoutDecorationRects(left, top, right, bottom);
const QRect geometry = scheduled.boundingRect();
left = left.intersected(geometry);
top = top.intersected(geometry);
right = right.intersected(geometry);
bottom = bottom.intersected(geometry);
auto renderPart = [this, c](const QRect &geo) {
if (!geo.isValid()) {
return;
}
QImage image = renderToImage(geo);
xcb_put_image(c, XCB_IMAGE_FORMAT_Z_PIXMAP, client()->client()->frameId(), m_gc,
image.width(), image.height(), geo.x(), geo.y(), 0, client()->client()->depth(),
image.sizeInBytes(), image.constBits());
};
renderPart(left);
renderPart(top);
renderPart(right);
renderPart(bottom);
xcb_flush(c);
resetImageSizesDirty();
}
}
}

View file

@ -1,44 +0,0 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef KWIN_DECORATION_X11_RENDERER_H
#define KWIN_DECORATION_X11_RENDERER_H
#include "decorations/decorationrenderer.h"
#include <xcb/xcb.h>
class QTimer;
namespace KWin
{
namespace Decoration
{
class X11Renderer : public Renderer
{
Q_OBJECT
public:
explicit X11Renderer(DecoratedClientImpl *client);
~X11Renderer() override;
void reparent(Deleted *deleted) override;
protected:
void render() override;
private:
QTimer *m_scheduleTimer;
xcb_gcontext_t m_gc;
};
}
}
#endif

View file

@ -33,7 +33,6 @@
#include "overlaywindow_x11.h"
#include "non_composited_outline.h"
#include "workspace.h"
#include "x11_decoration_renderer.h"
#include "x11_output.h"
#include "xcbutils.h"
#include "renderloop.h"
@ -406,15 +405,6 @@ OutlineVisual *X11StandalonePlatform::createOutline(Outline *outline)
return ret;
}
Decoration::Renderer *X11StandalonePlatform::createDecorationRenderer(Decoration::DecoratedClientImpl *client)
{
auto renderer = Platform::createDecorationRenderer(client);
if (!renderer) {
renderer = new Decoration::X11Renderer(client);
}
return renderer;
}
void X11StandalonePlatform::invertScreen()
{
using namespace Xcb::RandR;

View file

@ -57,7 +57,6 @@ public:
OverlayWindow *createOverlayWindow() override;
OutlineVisual *createOutline(Outline *outline) override;
Decoration::Renderer *createDecorationRenderer(Decoration::DecoratedClientImpl *client) override;
void invertScreen() override;

View file

@ -39,6 +39,7 @@
#include "decorations/decoratedclient.h"
#include "shadowitem.h"
#include "surfaceitem.h"
#include "windowitem.h"
#include <logging.h>
#include <KWaylandServer/buffer_interface.h>
@ -901,7 +902,7 @@ Shadow *SceneOpenGL::createShadow(Toplevel *toplevel)
return new SceneOpenGLShadow(toplevel);
}
Decoration::Renderer *SceneOpenGL::createDecorationRenderer(Decoration::DecoratedClientImpl *impl)
DecorationRenderer *SceneOpenGL::createDecorationRenderer(Decoration::DecoratedClientImpl *impl)
{
return new SceneOpenGLDecorationRenderer(impl);
}
@ -1213,22 +1214,10 @@ void OpenGLWindow::endRenderWindow()
GLTexture *OpenGLWindow::getDecorationTexture() const
{
if (AbstractClient *client = dynamic_cast<AbstractClient *>(toplevel)) {
if (!client->isDecorated()) {
return nullptr;
}
if (SceneOpenGLDecorationRenderer *renderer = static_cast<SceneOpenGLDecorationRenderer*>(client->decoratedClient()->renderer())) {
renderer->render();
return renderer->texture();
}
} else if (toplevel->isDeleted()) {
Deleted *deleted = static_cast<Deleted *>(toplevel);
if (!deleted->wasDecorated()) {
return nullptr;
}
if (const SceneOpenGLDecorationRenderer *renderer = static_cast<const SceneOpenGLDecorationRenderer*>(deleted->decorationRenderer())) {
return renderer->texture();
}
const DecorationItem *decorationItem = windowItem()->decorationItem();
if (decorationItem) {
auto renderer = static_cast<const SceneOpenGLDecorationRenderer *>(decorationItem->renderer());
return renderer->texture();
}
return nullptr;
}
@ -2489,10 +2478,9 @@ bool SceneOpenGLShadow::prepareBackend()
}
SceneOpenGLDecorationRenderer::SceneOpenGLDecorationRenderer(Decoration::DecoratedClientImpl *client)
: Renderer(client)
: DecorationRenderer(client)
, m_texture()
{
connect(this, &Renderer::renderScheduled, client->client(), static_cast<void (AbstractClient::*)(const QRegion&)>(&AbstractClient::addRepaint));
}
SceneOpenGLDecorationRenderer::~SceneOpenGLDecorationRenderer()
@ -2573,12 +2561,8 @@ static void clamp(QImage &image, const QRect &viewport)
}
}
void SceneOpenGLDecorationRenderer::render()
void SceneOpenGLDecorationRenderer::render(const QRegion &region)
{
const QRegion scheduled = getScheduled();
if (scheduled.isEmpty()) {
return;
}
if (areImageSizesDirty()) {
resizeTexture();
resetImageSizesDirty();
@ -2645,7 +2629,7 @@ void SceneOpenGLDecorationRenderer::render()
m_texture->update(image, (position + dirtyOffset - viewport.topLeft()) * image.devicePixelRatio());
};
const QRect geometry = scheduled.boundingRect();
const QRect geometry = region.boundingRect();
const QPoint topPosition(padding, padding);
const QPoint bottomPosition(padding, topPosition.y() + top.height() + 2 * padding);
@ -2695,13 +2679,6 @@ void SceneOpenGLDecorationRenderer::resizeTexture()
}
}
void SceneOpenGLDecorationRenderer::reparent(Deleted *deleted)
{
render();
Renderer::reparent(deleted);
}
OpenGLFactory::OpenGLFactory(QObject *parent)
: SceneFactory(parent)
{

View file

@ -13,13 +13,12 @@
#include "openglbackend.h"
#include "decorationitem.h"
#include "scene.h"
#include "shadow.h"
#include "kwinglutils.h"
#include "decorations/decorationrenderer.h"
namespace KWin
{
class LanczosFilter;
@ -45,7 +44,7 @@ public:
void doneOpenGLContextCurrent() override;
bool supportsSurfacelessContext() const override;
bool supportsNativeFence() const override;
Decoration::Renderer *createDecorationRenderer(Decoration::DecoratedClientImpl *impl) override;
DecorationRenderer *createDecorationRenderer(Decoration::DecoratedClientImpl *impl) override;
void triggerFence() override;
virtual QMatrix4x4 projectionMatrix() const = 0;
bool animationsSupported() const override;
@ -256,7 +255,7 @@ private:
QSharedPointer<GLTexture> m_texture;
};
class SceneOpenGLDecorationRenderer : public Decoration::Renderer
class SceneOpenGLDecorationRenderer : public DecorationRenderer
{
Q_OBJECT
public:
@ -270,8 +269,7 @@ public:
explicit SceneOpenGLDecorationRenderer(Decoration::DecoratedClientImpl *client);
~SceneOpenGLDecorationRenderer() override;
void render() override;
void reparent(Deleted *deleted) override;
void render(const QRegion &region) override;
GLTexture *texture() {
return m_texture.data();

View file

@ -314,25 +314,18 @@ void SceneQPainter::Window::renderShadow(QPainter* painter)
void SceneQPainter::Window::renderWindowDecorations(QPainter *painter)
{
// TODO: custom decoration opacity
AbstractClient *client = dynamic_cast<AbstractClient*>(toplevel);
Deleted *deleted = dynamic_cast<Deleted*>(toplevel);
if (!client && !deleted) {
const DecorationItem *decorationItem = windowItem()->decorationItem();
if (!decorationItem) {
return;
}
const SceneQPainterDecorationRenderer *renderer = nullptr;
const auto renderer = static_cast<const SceneQPainterDecorationRenderer *>(decorationItem->renderer());
QRect dtr, dlr, drr, dbr;
if (client && client->isDecorated()) {
if (SceneQPainterDecorationRenderer *r = static_cast<SceneQPainterDecorationRenderer *>(client->decoratedClient()->renderer())) {
r->render();
renderer = r;
}
if (auto client = qobject_cast<AbstractClient *>(toplevel)) {
client->layoutDecorationRects(dlr, dtr, drr, dbr);
} else if (deleted && deleted->wasDecorated()) {
} else if (auto deleted = qobject_cast<Deleted *>(toplevel)) {
deleted->layoutDecorationRects(dlr, dtr, drr, dbr);
renderer = static_cast<const SceneQPainterDecorationRenderer *>(deleted->decorationRenderer());
}
if (!renderer) {
} else {
return;
}
@ -342,7 +335,7 @@ void SceneQPainter::Window::renderWindowDecorations(QPainter *painter)
painter->drawImage(dbr, renderer->image(SceneQPainterDecorationRenderer::DecorationPart::Bottom));
}
Decoration::Renderer *SceneQPainter::createDecorationRenderer(Decoration::DecoratedClientImpl *impl)
DecorationRenderer *SceneQPainter::createDecorationRenderer(Decoration::DecoratedClientImpl *impl)
{
return new SceneQPainterDecorationRenderer(impl);
}
@ -690,25 +683,18 @@ bool SceneQPainterShadow::prepareBackend()
// QPainterDecorationRenderer
//****************************************
SceneQPainterDecorationRenderer::SceneQPainterDecorationRenderer(Decoration::DecoratedClientImpl *client)
: Renderer(client)
: DecorationRenderer(client)
{
connect(this, &Renderer::renderScheduled, client->client(), static_cast<void (AbstractClient::*)(const QRegion&)>(&AbstractClient::addRepaint));
}
SceneQPainterDecorationRenderer::~SceneQPainterDecorationRenderer() = default;
QImage SceneQPainterDecorationRenderer::image(SceneQPainterDecorationRenderer::DecorationPart part) const
{
Q_ASSERT(part != DecorationPart::Count);
return m_images[int(part)];
}
void SceneQPainterDecorationRenderer::render()
void SceneQPainterDecorationRenderer::render(const QRegion &region)
{
const QRegion scheduled = getScheduled();
if (scheduled.isEmpty()) {
return;
}
if (areImageSizesDirty()) {
resizeImages();
resetImageSizesDirty();
@ -723,7 +709,7 @@ void SceneQPainterDecorationRenderer::render()
const QRect right(QPoint(top.width() - imageSize(DecorationPart::Right).width(), top.height()), imageSize(DecorationPart::Right));
const QRect bottom(QPoint(0, left.y() + left.height()), imageSize(DecorationPart::Bottom));
const QRect geometry = scheduled.boundingRect();
const QRect geometry = region.boundingRect();
auto renderPart = [this](const QRect &rect, const QRect &partRect, int index) {
if (rect.isEmpty()) {
return;
@ -767,13 +753,6 @@ void SceneQPainterDecorationRenderer::resizeImages()
checkAndCreate(int(DecorationPart::Bottom), bottom.size());
}
void SceneQPainterDecorationRenderer::reparent(Deleted *deleted)
{
render();
Renderer::reparent(deleted);
}
QPainterFactory::QPainterFactory(QObject *parent)
: SceneFactory(parent)
{

View file

@ -11,11 +11,10 @@
#include "qpainterbackend.h"
#include "decorationitem.h"
#include "scene.h"
#include "shadow.h"
#include "decorations/decorationrenderer.h"
namespace KWin {
class KWIN_EXPORT SceneQPainter : public Scene
@ -32,7 +31,7 @@ public:
bool initFailed() const override;
EffectFrame *createEffectFrame(EffectFrameImpl *frame) override;
Shadow *createShadow(Toplevel *toplevel) override;
Decoration::Renderer *createDecorationRenderer(Decoration::DecoratedClientImpl *impl) override;
DecorationRenderer *createDecorationRenderer(Decoration::DecoratedClientImpl *impl) override;
void screenGeometryChanged(const QSize &size) override;
PlatformSurfaceTexture *createPlatformSurfaceTextureInternal(SurfacePixmapInternal *pixmap) override;
PlatformSurfaceTexture *createPlatformSurfaceTextureWayland(SurfacePixmapWayland *pixmap) override;
@ -112,7 +111,7 @@ private:
QImage m_texture;
};
class SceneQPainterDecorationRenderer : public Decoration::Renderer
class SceneQPainterDecorationRenderer : public DecorationRenderer
{
Q_OBJECT
public:
@ -124,10 +123,8 @@ public:
Count
};
explicit SceneQPainterDecorationRenderer(Decoration::DecoratedClientImpl *client);
~SceneQPainterDecorationRenderer() override;
void render() override;
void reparent(Deleted *deleted) override;
void render(const QRegion &region) override;
QImage image(DecorationPart part) const;

View file

@ -28,6 +28,7 @@
#include "screens.h"
#include "shadowitem.h"
#include "surfaceitem_x11.h"
#include "windowitem.h"
#include "xcbutils.h"
#include "decorations/decoratedclient.h"
@ -135,7 +136,7 @@ Shadow *SceneXrender::createShadow(Toplevel *toplevel)
return new SceneXRenderShadow(toplevel);
}
Decoration::Renderer *SceneXrender::createDecorationRenderer(Decoration::DecoratedClientImpl* client)
DecorationRenderer *SceneXrender::createDecorationRenderer(Decoration::DecoratedClientImpl *client)
{
return new SceneXRenderDecorationRenderer(client);
}
@ -309,7 +310,8 @@ void SceneXrender::Window::performPaint(int mask, const QRegion &_region, const
X11Client *client = dynamic_cast<X11Client *>(toplevel);
Deleted *deleted = dynamic_cast<Deleted*>(toplevel);
const QRect decorationRect = toplevel->rect();
if ((client && client->isDecorated()) || (deleted && deleted->wasDecorated())) {
const DecorationItem *decorationItem = windowItem()->decorationItem();
if (decorationItem) {
// decorated client
transformed_shape = decorationRect;
if (toplevel->shape()) {
@ -378,7 +380,7 @@ void SceneXrender::Window::performPaint(int mask, const QRegion &_region, const
// This solves a number of glitches and on top of this
// it optimizes painting quite a bit
const bool blitInTempPixmap = xRenderOffscreen() || (data.crossFadeProgress() < 1.0 && !opaque) ||
(scaled && (wantShadow || (client && client->isDecorated()) || (deleted && deleted->wasDecorated())));
(scaled && (wantShadow || decorationItem));
xcb_render_picture_t renderTarget = m_scene->xrenderBufferPicture();
if (blitInTempPixmap) {
@ -423,18 +425,15 @@ void SceneXrender::Window::performPaint(int mask, const QRegion &_region, const
xcb_render_picture_t bottom = XCB_RENDER_PICTURE_NONE;
QRect dtr, dlr, drr, dbr;
const SceneXRenderDecorationRenderer *renderer = nullptr;
if (client && client->isDecorated()) {
SceneXRenderDecorationRenderer *r = static_cast<SceneXRenderDecorationRenderer*>(client->decoratedClient()->renderer());
if (r) {
r->render();
renderer = r;
if (decorationItem) {
renderer = static_cast<const SceneXRenderDecorationRenderer *>(decorationItem->renderer());
noBorder = false;
if (client) {
client->layoutDecorationRects(dlr, dtr, drr, dbr);
} else if (deleted) {
deleted->layoutDecorationRects(dlr, dtr, drr, dbr);
}
noBorder = false;
client->layoutDecorationRects(dlr, dtr, drr, dbr);
} else if (deleted && deleted->wasDecorated()) {
renderer = static_cast<const SceneXRenderDecorationRenderer*>(deleted->decorationRenderer());
noBorder = false;
deleted->layoutDecorationRects(dlr, dtr, drr, dbr);
}
if (renderer) {
left = renderer->picture(SceneXRenderDecorationRenderer::DecorationPart::Left);
@ -1004,10 +1003,9 @@ xcb_render_picture_t SceneXRenderShadow::picture(Shadow::ShadowElements element)
}
SceneXRenderDecorationRenderer::SceneXRenderDecorationRenderer(Decoration::DecoratedClientImpl *client)
: Renderer(client)
: DecorationRenderer(client)
, m_gc(XCB_NONE)
{
connect(this, &Renderer::renderScheduled, client->client(), static_cast<void (AbstractClient::*)(const QRegion&)>(&AbstractClient::addRepaint));
for (int i = 0; i < int(DecorationPart::Count); ++i) {
m_pixmaps[i] = XCB_PIXMAP_NONE;
m_pictures[i] = nullptr;
@ -1027,12 +1025,8 @@ SceneXRenderDecorationRenderer::~SceneXRenderDecorationRenderer()
}
}
void SceneXRenderDecorationRenderer::render()
void SceneXRenderDecorationRenderer::render(const QRegion &region)
{
QRegion scheduled = getScheduled();
if (scheduled.isEmpty()) {
return;
}
if (areImageSizesDirty()) {
resizePixmaps();
resetImageSizesDirty();
@ -1058,7 +1052,7 @@ void SceneXRenderDecorationRenderer::render()
image.width(), image.height(), geo.x() - offset.x(), geo.y() - offset.y(), 0, 32,
image.sizeInBytes(), image.constBits());
};
const QRect geometry = scheduled.boundingRect();
const QRect geometry = region.boundingRect();
renderPart(left.intersected(geometry), left.topLeft(), int(DecorationPart::Left));
renderPart(top.intersected(geometry), top.topLeft(), int(DecorationPart::Top));
renderPart(right.intersected(geometry), right.topLeft(), int(DecorationPart::Right));
@ -1113,12 +1107,6 @@ xcb_render_picture_t SceneXRenderDecorationRenderer::picture(SceneXRenderDecorat
return *picture;
}
void SceneXRenderDecorationRenderer::reparent(Deleted *deleted)
{
render();
Renderer::reparent(deleted);
}
#undef DOUBLE_TO_FIXED
#undef FIXED_TO_DOUBLE

View file

@ -10,9 +10,9 @@
#ifndef KWIN_SCENE_XRENDER_H
#define KWIN_SCENE_XRENDER_H
#include "decorationitem.h"
#include "scene.h"
#include "shadow.h"
#include "decorations/decorationrenderer.h"
#ifdef KWIN_HAVE_XRENDER_COMPOSITING
@ -39,7 +39,7 @@ public:
void screenGeometryChanged(const QSize &size) override;
xcb_render_picture_t xrenderBufferPicture() const override;
OverlayWindow *overlayWindow() const override;
Decoration::Renderer *createDecorationRenderer(Decoration::DecoratedClientImpl *client) override;
DecorationRenderer *createDecorationRenderer(Decoration::DecoratedClientImpl *client) override;
PlatformSurfaceTexture *createPlatformSurfaceTextureX11(SurfacePixmapX11 *pixmap) override;
bool animationsSupported() const override {
@ -162,7 +162,7 @@ private:
XRenderPicture* m_pictures[ShadowElementsCount];
};
class SceneXRenderDecorationRenderer : public Decoration::Renderer
class SceneXRenderDecorationRenderer : public DecorationRenderer
{
Q_OBJECT
public:
@ -176,8 +176,7 @@ public:
explicit SceneXRenderDecorationRenderer(Decoration::DecoratedClientImpl *client);
~SceneXRenderDecorationRenderer() override;
void render() override;
void reparent(Deleted *deleted) override;
void render(const QRegion &region) override;
xcb_render_picture_t picture(DecorationPart part) const;

View file

@ -57,7 +57,6 @@
#include "scene.h"
#include "abstract_output.h"
#include "decorationitem.h"
#include "internal_client.h"
#include "platform.h"
#include "shadowitem.h"

View file

@ -23,12 +23,11 @@ namespace KWin
namespace Decoration
{
class DecoratedClientImpl;
class Renderer;
}
class AbstractOutput;
class AbstractThumbnailItem;
class DecorationItem;
class DecorationRenderer;
class Deleted;
class EffectFrameImpl;
class EffectWindowImpl;
@ -156,7 +155,7 @@ public:
virtual void triggerFence();
virtual Decoration::Renderer *createDecorationRenderer(Decoration::DecoratedClientImpl *) = 0;
virtual DecorationRenderer *createDecorationRenderer(Decoration::DecoratedClientImpl *) = 0;
/**
* Whether the Scene is able to drive animations.

View file

@ -15,6 +15,7 @@ class Decoration;
namespace KWin
{
class DecorationItem;
/**
* The WindowItem class represents a window in the scene.

View file

@ -75,6 +75,71 @@ const NET::WindowTypes SUPPORTED_MANAGED_WINDOW_TYPES_MASK = NET::NormalMask | N
| NET::UtilityMask | NET::SplashMask | NET::NotificationMask | NET::OnScreenDisplayMask
| NET::CriticalNotificationMask;
X11DecorationRenderer::X11DecorationRenderer(Decoration::DecoratedClientImpl *client)
: DecorationRenderer(client)
, m_scheduleTimer(new QTimer(this))
, m_gc(XCB_NONE)
{
// Delay any rendering to end of event cycle to catch multiple updates per cycle.
m_scheduleTimer->setSingleShot(true);
m_scheduleTimer->setInterval(0);
connect(m_scheduleTimer, &QTimer::timeout, this, &X11DecorationRenderer::update);
connect(this, &X11DecorationRenderer::damaged, m_scheduleTimer, static_cast<void (QTimer::*)()>(&QTimer::start));
}
X11DecorationRenderer::~X11DecorationRenderer()
{
if (m_gc != XCB_NONE) {
xcb_free_gc(connection(), m_gc);
}
}
void X11DecorationRenderer::update()
{
if (!damage().isEmpty()) {
render(damage());
resetDamage();
}
}
void X11DecorationRenderer::render(const QRegion &region)
{
if (!client()) {
return;
}
xcb_connection_t *c = kwinApp()->x11Connection();
if (m_gc == XCB_NONE) {
m_gc = xcb_generate_id(c);
xcb_create_gc(c, m_gc, client()->client()->frameId(), 0, nullptr);
}
QRect left, top, right, bottom;
client()->client()->layoutDecorationRects(left, top, right, bottom);
const QRect geometry = region.boundingRect();
left = left.intersected(geometry);
top = top.intersected(geometry);
right = right.intersected(geometry);
bottom = bottom.intersected(geometry);
auto renderPart = [this, c](const QRect &geo) {
if (!geo.isValid()) {
return;
}
QImage image = renderToImage(geo);
xcb_put_image(c, XCB_IMAGE_FORMAT_Z_PIXMAP, client()->client()->frameId(), m_gc,
image.width(), image.height(), geo.x(), geo.y(), 0, client()->client()->depth(),
image.sizeInBytes(), image.constBits());
};
renderPart(left);
renderPart(top);
renderPart(right);
renderPart(bottom);
xcb_flush(c);
resetImageSizesDirty();
}
// Creating a client:
// - only by calling Workspace::createClient()
// - it creates a new client and calls manage() for it
@ -1054,6 +1119,7 @@ void X11Client::createDecoration(const QRect& oldgeom)
move(calculateGravitation(false));
resize(adjustedSize());
updateDecorationInputShape();
maybeCreateX11DecorationRenderer();
if (Compositor::compositing()) {
discardWindowPixmap();
}
@ -1066,6 +1132,7 @@ void X11Client::destroyDecoration()
if (isDecorated()) {
QPoint grav = calculateGravitation(true);
AbstractClient::destroyDecoration();
maybeDestroyX11DecorationRenderer();
resize(adjustedSize());
move(grav);
if (compositing())
@ -1077,6 +1144,22 @@ void X11Client::destroyDecoration()
m_decoInputExtent.reset();
}
void X11Client::maybeCreateX11DecorationRenderer()
{
if (kwinApp()->operationMode() != Application::OperationModeX11) {
return;
}
if (!compositing() && decoratedClient()) {
m_decorationRenderer.reset(new X11DecorationRenderer(decoratedClient()));
decoration()->update();
}
}
void X11Client::maybeDestroyX11DecorationRenderer()
{
m_decorationRenderer.reset();
}
void X11Client::layoutDecorationRects(QRect &left, QRect &top, QRect &right, QRect &bottom) const
{
if (!isDecorated()) {
@ -1353,6 +1436,8 @@ bool X11Client::setupCompositing()
if (!Toplevel::setupCompositing()){
return false;
}
// If compositing is back on, stop rendering decoration in the frame window.
maybeDestroyX11DecorationRenderer();
updateVisibility(); // for internalKeep()
return true;
}
@ -1363,6 +1448,8 @@ void X11Client::finishCompositing(ReleaseReason releaseReason)
updateVisibility();
// for safety in case KWin is just resizing the window
resetHaveResizeEffect();
// If compositing is off, render the decoration in the X11 frame window.
maybeCreateX11DecorationRenderer();
}
/**

View file

@ -11,6 +11,7 @@
#pragma once
// kwin
#include "decorationitem.h"
#include "options.h"
#include "rules.h"
#include "abstract_client.h"
@ -46,6 +47,28 @@ enum class Predicate {
InputIdMatch,
};
/**
* @todo Remove when the X11 platform support is dropped. This decoration renderer
* will be used if compositing is off.
*/
class X11DecorationRenderer : public DecorationRenderer
{
Q_OBJECT
public:
explicit X11DecorationRenderer(Decoration::DecoratedClientImpl *client);
~X11DecorationRenderer() override;
protected:
void render(const QRegion &region) override;
private:
void update();
QTimer *m_scheduleTimer;
xcb_gcontext_t m_gc;
};
class KWIN_EXPORT X11Client : public AbstractClient
{
Q_OBJECT
@ -424,6 +447,9 @@ private:
*/
void updateShowOnScreenEdge();
void maybeCreateX11DecorationRenderer();
void maybeDestroyX11DecorationRenderer();
Xcb::Window m_client;
Xcb::Window m_wrapper;
Xcb::Window m_frame;
@ -506,6 +532,7 @@ private:
QRect m_bufferGeometryBeforeUpdateBlocking;
QRect m_frameGeometryBeforeUpdateBlocking;
QRect m_clientGeometryBeforeUpdateBlocking;
QScopedPointer<X11DecorationRenderer> m_decorationRenderer;
};
inline xcb_window_t X11Client::wrapperId() const