backends/wayland: Introduce cursor layers

It's a necessary step to let kwin repaint the cursor from Compositor.

Unfortunately, it also means that we need to add more (temporary) code
to paint the cursor in backends.
This commit is contained in:
Vlad Zahorodnii 2022-12-07 12:31:39 +02:00
parent 1c43571fe1
commit dbd574ec05
6 changed files with 368 additions and 91 deletions

View file

@ -26,6 +26,7 @@
#include <kwinglutils.h>
// KDE
#include <KWayland/Client/shm_pool.h>
#include <KWayland/Client/surface.h>
// Qt
@ -61,13 +62,13 @@ static QVector<EGLint> regionToRects(const QRegion &region, Output *output)
return rects;
}
WaylandEglOutput::WaylandEglOutput(WaylandOutput *output, WaylandEglBackend *backend)
WaylandEglPrimaryLayer::WaylandEglPrimaryLayer(WaylandOutput *output, WaylandEglBackend *backend)
: m_waylandOutput(output)
, m_backend(backend)
{
}
bool WaylandEglOutput::init()
bool WaylandEglPrimaryLayer::init()
{
auto surface = m_waylandOutput->surface();
const QSize nativeSize = m_waylandOutput->geometry().size() * m_waylandOutput->scale();
@ -94,35 +95,23 @@ bool WaylandEglOutput::init()
return true;
}
WaylandEglOutput::~WaylandEglOutput()
WaylandEglPrimaryLayer::~WaylandEglPrimaryLayer()
{
wl_egl_window_destroy(m_overlay);
}
GLFramebuffer *WaylandEglOutput::fbo() const
GLFramebuffer *WaylandEglPrimaryLayer::fbo() const
{
return m_fbo.get();
}
bool WaylandEglOutput::makeContextCurrent() const
std::optional<OutputLayerBeginFrameInfo> WaylandEglPrimaryLayer::beginFrame()
{
if (m_eglSurface == EGL_NO_SURFACE) {
return false;
}
if (eglMakeCurrent(m_backend->eglDisplay(), m_eglSurface, m_eglSurface, m_backend->context()) == EGL_FALSE) {
qCCritical(KWIN_WAYLAND_BACKEND) << "Make Context Current failed";
return false;
return std::nullopt;
}
EGLint error = eglGetError();
if (error != EGL_SUCCESS) {
qCWarning(KWIN_WAYLAND_BACKEND) << "Error occurred while creating context " << error;
return false;
}
return true;
}
std::optional<OutputLayerBeginFrameInfo> WaylandEglOutput::beginFrame()
{
const QSize nativeSize = m_waylandOutput->pixelSize();
if (!m_fbo || m_fbo->size() != nativeSize) {
m_fbo = std::make_unique<GLFramebuffer>(0, nativeSize);
@ -130,9 +119,6 @@ std::optional<OutputLayerBeginFrameInfo> WaylandEglOutput::beginFrame()
wl_egl_window_resize(m_overlay, nativeSize.width(), nativeSize.height(), 0, 0);
}
eglWaitNative(EGL_CORE_NATIVE_ENGINE);
makeContextCurrent();
QRegion repair;
if (m_backend->supportsBufferAge()) {
repair = m_damageJournal.accumulate(m_bufferAge, infiniteRegion());
@ -145,14 +131,14 @@ std::optional<OutputLayerBeginFrameInfo> WaylandEglOutput::beginFrame()
};
}
bool WaylandEglOutput::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion)
bool WaylandEglPrimaryLayer::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion)
{
m_damageJournal.add(damagedRegion);
GLFramebuffer::popFramebuffer();
return true;
}
void WaylandEglOutput::aboutToStartPainting(const QRegion &damage)
void WaylandEglPrimaryLayer::aboutToStartPainting(const QRegion &damage)
{
if (m_bufferAge > 0 && !damage.isEmpty() && m_backend->supportsPartialUpdate()) {
QVector<EGLint> rects = regionToRects(damage, m_waylandOutput);
@ -164,7 +150,7 @@ void WaylandEglOutput::aboutToStartPainting(const QRegion &damage)
}
}
void WaylandEglOutput::present()
void WaylandEglPrimaryLayer::present()
{
m_waylandOutput->surface()->setupFrameCallback();
m_waylandOutput->surface()->setScale(std::ceil(m_waylandOutput->scale()));
@ -187,6 +173,81 @@ void WaylandEglOutput::present()
}
}
WaylandEglCursorLayer::WaylandEglCursorLayer(WaylandOutput *output, WaylandEglBackend *backend)
: m_output(output)
, m_backend(backend)
{
}
WaylandEglCursorLayer::~WaylandEglCursorLayer()
{
eglMakeCurrent(m_backend->eglDisplay(), EGL_NO_SURFACE, EGL_NO_SURFACE, m_backend->context());
m_framebuffer.reset();
m_texture.reset();
}
qreal WaylandEglCursorLayer::scale() const
{
return m_scale;
}
void WaylandEglCursorLayer::setScale(qreal scale)
{
m_scale = scale;
}
QPoint WaylandEglCursorLayer::hotspot() const
{
return m_hotspot;
}
void WaylandEglCursorLayer::setHotspot(const QPoint &hotspot)
{
m_hotspot = hotspot;
}
QSize WaylandEglCursorLayer::size() const
{
return m_size;
}
void WaylandEglCursorLayer::setSize(const QSize &size)
{
m_size = size;
}
std::optional<OutputLayerBeginFrameInfo> WaylandEglCursorLayer::beginFrame()
{
if (eglMakeCurrent(m_backend->eglDisplay(), EGL_NO_SURFACE, EGL_NO_SURFACE, m_backend->context()) == EGL_FALSE) {
qCCritical(KWIN_WAYLAND_BACKEND) << "Make Context Current failed";
return std::nullopt;
}
const QSize bufferSize = m_size.expandedTo(QSize(64, 64));
if (!m_texture || m_texture->size() != bufferSize) {
m_texture = std::make_unique<GLTexture>(GL_RGBA8, bufferSize);
m_framebuffer = std::make_unique<GLFramebuffer>(m_texture.get());
}
GLFramebuffer::pushFramebuffer(m_framebuffer.get());
return OutputLayerBeginFrameInfo{
.renderTarget = RenderTarget(m_framebuffer.get()),
.repaint = infiniteRegion(),
};
}
bool WaylandEglCursorLayer::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion)
{
GLFramebuffer::popFramebuffer();
// Technically, we could pass a linux-dmabuf buffer, but host kwin does not support that atm.
const QImage image = m_texture->toImage().mirrored(false, true);
KWayland::Client::Buffer::Ptr buffer = m_output->backend()->display()->shmPool()->createBuffer(image);
m_output->cursor()->update(buffer, m_scale, m_hotspot);
return true;
}
WaylandEglBackend::WaylandEglBackend(WaylandBackend *b)
: AbstractEglBackend()
, m_backend(b)
@ -196,13 +257,7 @@ WaylandEglBackend::WaylandEglBackend(WaylandBackend *b)
connect(m_backend, &WaylandBackend::outputAdded, this, &WaylandEglBackend::createEglWaylandOutput);
connect(m_backend, &WaylandBackend::outputRemoved, this, [this](Output *output) {
auto it = std::find_if(m_outputs.begin(), m_outputs.end(), [output](const auto &o) {
return o->m_waylandOutput == output;
});
if (it == m_outputs.end()) {
return;
}
m_outputs.erase(it);
m_outputs.erase(output);
});
b->setEglBackend(this);
@ -220,11 +275,15 @@ void WaylandEglBackend::cleanupSurfaces()
bool WaylandEglBackend::createEglWaylandOutput(Output *waylandOutput)
{
const auto output = std::make_shared<WaylandEglOutput>(static_cast<WaylandOutput *>(waylandOutput), this);
if (!output->init()) {
auto cursorLayer = std::make_unique<WaylandEglCursorLayer>(static_cast<WaylandOutput *>(waylandOutput), this);
auto primaryLayer = std::make_unique<WaylandEglPrimaryLayer>(static_cast<WaylandOutput *>(waylandOutput), this);
if (!primaryLayer->init()) {
return false;
}
m_outputs.insert(waylandOutput, output);
m_outputs[waylandOutput] = Layers{
.primaryLayer = std::move(primaryLayer),
.cursorLayer = std::move(cursorLayer),
};
return true;
}
@ -293,13 +352,12 @@ bool WaylandEglBackend::initRenderingContext()
}
}
if (m_outputs.isEmpty()) {
if (m_outputs.empty()) {
qCCritical(KWIN_WAYLAND_BACKEND) << "Create Window Surfaces failed";
return false;
}
const auto &firstOutput = m_outputs.first();
return firstOutput->makeContextCurrent();
return makeCurrent();
}
bool WaylandEglBackend::initBufferConfigs()
@ -340,7 +398,7 @@ bool WaylandEglBackend::initBufferConfigs()
std::shared_ptr<KWin::GLTexture> WaylandEglBackend::textureForOutput(KWin::Output *output) const
{
auto texture = std::make_unique<GLTexture>(GL_RGBA8, output->pixelSize());
GLFramebuffer::pushFramebuffer(m_outputs[output]->fbo());
GLFramebuffer::pushFramebuffer(m_outputs.at(output).primaryLayer->fbo());
GLFramebuffer renderTarget(texture.get());
renderTarget.blitFromFramebuffer(QRect(0, texture->height(), texture->width(), -texture->height()));
GLFramebuffer::popFramebuffer();
@ -359,12 +417,17 @@ std::unique_ptr<SurfaceTexture> WaylandEglBackend::createSurfaceTextureWayland(S
void WaylandEglBackend::present(Output *output)
{
m_outputs[output]->present();
m_outputs[output].primaryLayer->present();
}
OutputLayer *WaylandEglBackend::primaryLayer(Output *output)
{
return m_outputs[output].get();
return m_outputs[output].primaryLayer.get();
}
WaylandEglCursorLayer *WaylandEglBackend::cursorLayer(Output *output)
{
return m_outputs[output].cursorLayer.get();
}
}

View file

@ -17,6 +17,8 @@
#include <optional>
#include <wayland-egl.h>
#include <KWayland/Client/buffer.h>
#include <memory>
class QTemporaryFile;
@ -34,16 +36,15 @@ class WaylandBackend;
class WaylandOutput;
class WaylandEglBackend;
class WaylandEglOutput : public OutputLayer
class WaylandEglPrimaryLayer : public OutputLayer
{
public:
WaylandEglOutput(WaylandOutput *output, WaylandEglBackend *backend);
~WaylandEglOutput() override;
WaylandEglPrimaryLayer(WaylandOutput *output, WaylandEglBackend *backend);
~WaylandEglPrimaryLayer() override;
bool init();
GLFramebuffer *fbo() const;
bool makeContextCurrent() const;
void present();
std::optional<OutputLayerBeginFrameInfo> beginFrame() override;
@ -62,6 +63,36 @@ private:
friend class WaylandEglBackend;
};
class WaylandEglCursorLayer : public OutputLayer
{
Q_OBJECT
public:
WaylandEglCursorLayer(WaylandOutput *output, WaylandEglBackend *backend);
~WaylandEglCursorLayer() override;
qreal scale() const;
void setScale(qreal scale);
QPoint hotspot() const;
void setHotspot(const QPoint &hotspot);
QSize size() const;
void setSize(const QSize &size);
std::optional<OutputLayerBeginFrameInfo> beginFrame() override;
bool endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) override;
private:
WaylandOutput *m_output;
WaylandEglBackend *m_backend;
std::unique_ptr<GLFramebuffer> m_framebuffer;
std::unique_ptr<GLTexture> m_texture;
QPoint m_hotspot;
QSize m_size;
qreal m_scale = 1.0;
};
/**
* @brief OpenGL Backend using Egl on a Wayland surface.
*
@ -87,6 +118,7 @@ public:
void init() override;
void present(Output *output) override;
OutputLayer *primaryLayer(Output *output) override;
WaylandEglCursorLayer *cursorLayer(Output *output);
bool havePlatformBase() const
{
@ -105,10 +137,16 @@ private:
void cleanupSurfaces() override;
void presentOnSurface(WaylandEglOutput *output, const QRegion &damagedRegion);
void presentOnSurface(WaylandEglPrimaryLayer *output, const QRegion &damagedRegion);
struct Layers
{
std::unique_ptr<WaylandEglPrimaryLayer> primaryLayer;
std::unique_ptr<WaylandEglCursorLayer> cursorLayer;
};
WaylandBackend *m_backend;
QMap<Output *, std::shared_ptr<WaylandEglOutput>> m_outputs;
std::map<Output *, Layers> m_outputs;
bool m_havePlatformBase;
friend class EglWaylandTexture;
};

View file

@ -8,10 +8,16 @@
*/
#include "wayland_output.h"
#include "core/renderloop_p.h"
#include "composite.h"
#include "wayland_backend.h"
#include "wayland_display.h"
#include "wayland_egl_backend.h"
#include "wayland_qpainter_backend.h"
#include "wayland_server.h"
#include "kwingltexture.h"
#include "kwinglutils.h"
#include <KWayland/Client/compositor.h>
#include <KWayland/Client/pointer.h>
#include <KWayland/Client/pointerconstraints.h>
@ -21,6 +27,8 @@
#include <KLocalizedString>
#include <QPainter>
#include <cmath>
namespace KWin
@ -73,11 +81,13 @@ void WaylandCursor::disable()
void WaylandCursor::update(KWayland::Client::Buffer::Ptr buffer, qreal scale, const QPoint &hotspot)
{
m_buffer = buffer;
m_scale = scale;
m_hotspot = hotspot;
if (m_buffer != buffer || m_scale != scale || m_hotspot != hotspot) {
m_buffer = buffer;
m_scale = scale;
m_hotspot = hotspot;
sync();
sync();
}
}
void WaylandCursor::sync()
@ -157,6 +167,11 @@ WaylandCursor *WaylandOutput::cursor() const
return m_cursor.get();
}
WaylandBackend *WaylandOutput::backend() const
{
return m_backend;
}
RenderLoop *WaylandOutput::renderLoop() const
{
return m_renderLoop.get();
@ -164,12 +179,17 @@ RenderLoop *WaylandOutput::renderLoop() const
bool WaylandOutput::setCursor(const QImage &image, const QPoint &hotspot)
{
KWayland::Client::Buffer::Ptr buffer;
if (!image.isNull()) {
buffer = m_backend->display()->shmPool()->createBuffer(image);
if (m_hasPointerLock) {
return false;
}
m_cursor->update(buffer, image.devicePixelRatio(), hotspot);
return !m_hasPointerLock;
if (WaylandEglBackend *backend = qobject_cast<WaylandEglBackend *>(Compositor::self()->backend())) {
renderCursorOpengl(backend, image, hotspot);
} else if (WaylandQPainterBackend *backend = qobject_cast<WaylandQPainterBackend *>(Compositor::self()->backend())) {
renderCursorQPainter(backend, image, hotspot);
}
return true;
}
bool WaylandOutput::moveCursor(const QPoint &position)
@ -178,6 +198,68 @@ bool WaylandOutput::moveCursor(const QPoint &position)
return !m_hasPointerLock;
}
void WaylandOutput::renderCursorOpengl(WaylandEglBackend *backend, const QImage &image, const QPoint &hotspot)
{
WaylandEglCursorLayer *cursorLayer = backend->cursorLayer(this);
cursorLayer->setSize(image.size());
cursorLayer->setScale(image.devicePixelRatio());
cursorLayer->setHotspot(hotspot);
std::optional<OutputLayerBeginFrameInfo> beginInfo = cursorLayer->beginFrame();
if (!beginInfo) {
return;
}
const QRect cursorRect(QPoint(0, 0), image.size() / image.devicePixelRatio());
QMatrix4x4 mvp;
mvp.ortho(QRect(QPoint(), beginInfo->renderTarget.size()));
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
GLTexture texture(image);
texture.bind();
ShaderBinder binder(ShaderTrait::MapTexture);
binder.shader()->setUniform(GLShader::ModelViewProjectionMatrix, mvp);
texture.render(cursorRect, beginInfo->renderTarget.devicePixelRatio());
texture.unbind();
glDisable(GL_BLEND);
cursorLayer->endFrame(infiniteRegion(), infiniteRegion());
}
void WaylandOutput::renderCursorQPainter(WaylandQPainterBackend *backend, const QImage &image, const QPoint &hotspot)
{
WaylandQPainterCursorLayer *cursorLayer = backend->cursorLayer(this);
cursorLayer->setSize(image.size());
cursorLayer->setScale(image.devicePixelRatio());
cursorLayer->setHotspot(hotspot);
std::optional<OutputLayerBeginFrameInfo> beginInfo = cursorLayer->beginFrame();
if (!beginInfo) {
return;
}
const QRect cursorRect(QPoint(0, 0), image.size() / image.devicePixelRatio());
QImage *c = std::get<QImage *>(beginInfo->renderTarget.nativeHandle());
c->setDevicePixelRatio(scale());
c->fill(Qt::transparent);
QPainter p;
p.begin(c);
p.setWorldTransform(logicalToNativeMatrix(cursorRect, 1, transform()).toTransform());
p.setRenderHint(QPainter::SmoothPixmapTransform);
p.drawImage(QPoint(0, 0), image);
p.end();
cursorLayer->endFrame(infiniteRegion(), infiniteRegion());
}
void WaylandOutput::init(const QSize &pixelSize, qreal scale)
{
m_renderLoop->setRefreshRate(s_refreshRate);

View file

@ -32,6 +32,8 @@ namespace KWin
namespace Wayland
{
class WaylandBackend;
class WaylandEglBackend;
class WaylandQPainterBackend;
class WaylandCursor
{
@ -74,6 +76,7 @@ public:
bool isReady() const;
KWayland::Client::Surface *surface() const;
WaylandCursor *cursor() const;
WaylandBackend *backend() const;
void lockPointer(KWayland::Client::Pointer *pointer, bool lock);
void resize(const QSize &pixelSize);
@ -84,6 +87,8 @@ public:
private:
void handleConfigure(const QSize &size, KWayland::Client::XdgShellSurface::States states, quint32 serial);
void updateWindowTitle();
void renderCursorOpengl(WaylandEglBackend *backend, const QImage &image, const QPoint &hotspot);
void renderCursorQPainter(WaylandQPainterBackend *backend, const QImage &image, const QPoint &hotspot);
std::unique_ptr<RenderLoop> m_renderLoop;
std::unique_ptr<KWayland::Client::Surface> m_surface;

View file

@ -36,26 +36,19 @@ WaylandQPainterBufferSlot::~WaylandQPainterBufferSlot()
buffer->setUsed(false);
}
WaylandQPainterOutput::WaylandQPainterOutput(WaylandOutput *output)
WaylandQPainterPrimaryLayer::WaylandQPainterPrimaryLayer(WaylandOutput *output)
: m_waylandOutput(output)
, m_pool(output->backend()->display()->shmPool())
{
connect(m_pool, &KWayland::Client::ShmPool::poolResized, this, &WaylandQPainterPrimaryLayer::remapBuffer);
}
WaylandQPainterOutput::~WaylandQPainterOutput()
WaylandQPainterPrimaryLayer::~WaylandQPainterPrimaryLayer()
{
m_slots.clear();
}
bool WaylandQPainterOutput::init(KWayland::Client::ShmPool *pool)
{
m_pool = pool;
connect(pool, &KWayland::Client::ShmPool::poolResized, this, &WaylandQPainterOutput::remapBuffer);
return true;
}
void WaylandQPainterOutput::remapBuffer()
void WaylandQPainterPrimaryLayer::remapBuffer()
{
qCDebug(KWIN_WAYLAND_BACKEND) << "Remapped back buffer of surface" << m_waylandOutput->surface();
@ -65,7 +58,7 @@ void WaylandQPainterOutput::remapBuffer()
}
}
void WaylandQPainterOutput::present()
void WaylandQPainterPrimaryLayer::present()
{
for (const auto &slot : m_slots) {
if (slot.get() == m_back) {
@ -82,12 +75,12 @@ void WaylandQPainterOutput::present()
s->commit();
}
WaylandQPainterBufferSlot *WaylandQPainterOutput::back() const
WaylandQPainterBufferSlot *WaylandQPainterPrimaryLayer::back() const
{
return m_back;
}
WaylandQPainterBufferSlot *WaylandQPainterOutput::acquire()
WaylandQPainterBufferSlot *WaylandQPainterPrimaryLayer::acquire()
{
const QSize nativeSize(m_waylandOutput->pixelSize());
if (m_swapchainSize != nativeSize) {
@ -116,12 +109,12 @@ WaylandQPainterBufferSlot *WaylandQPainterOutput::acquire()
return m_back;
}
QRegion WaylandQPainterOutput::accumulateDamage(int bufferAge) const
QRegion WaylandQPainterPrimaryLayer::accumulateDamage(int bufferAge) const
{
return m_damageJournal.accumulate(bufferAge, infiniteRegion());
}
std::optional<OutputLayerBeginFrameInfo> WaylandQPainterOutput::beginFrame()
std::optional<OutputLayerBeginFrameInfo> WaylandQPainterPrimaryLayer::beginFrame()
{
WaylandQPainterBufferSlot *slot = acquire();
return OutputLayerBeginFrameInfo{
@ -130,12 +123,71 @@ std::optional<OutputLayerBeginFrameInfo> WaylandQPainterOutput::beginFrame()
};
}
bool WaylandQPainterOutput::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion)
bool WaylandQPainterPrimaryLayer::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion)
{
m_damageJournal.add(damagedRegion);
return true;
}
WaylandQPainterCursorLayer::WaylandQPainterCursorLayer(WaylandOutput *output)
: m_output(output)
{
}
WaylandQPainterCursorLayer::~WaylandQPainterCursorLayer()
{
}
qreal WaylandQPainterCursorLayer::scale() const
{
return m_scale;
}
void WaylandQPainterCursorLayer::setScale(qreal scale)
{
m_scale = scale;
}
QPoint WaylandQPainterCursorLayer::hotspot() const
{
return m_hotspot;
}
void WaylandQPainterCursorLayer::setHotspot(const QPoint &hotspot)
{
m_hotspot = hotspot;
}
QSize WaylandQPainterCursorLayer::size() const
{
return m_size;
}
void WaylandQPainterCursorLayer::setSize(const QSize &size)
{
m_size = size;
}
std::optional<OutputLayerBeginFrameInfo> WaylandQPainterCursorLayer::beginFrame()
{
const QSize bufferSize = m_size.expandedTo(QSize(64, 64));
if (m_backingStore.size() != bufferSize) {
m_backingStore = QImage(bufferSize, QImage::Format_ARGB32_Premultiplied);
}
return OutputLayerBeginFrameInfo{
.renderTarget = RenderTarget(&m_backingStore),
.repaint = infiniteRegion(),
};
}
bool WaylandQPainterCursorLayer::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion)
{
KWayland::Client::Buffer::Ptr buffer = m_output->backend()->display()->shmPool()->createBuffer(m_backingStore);
m_output->cursor()->update(buffer, m_scale, m_hotspot);
return true;
}
WaylandQPainterBackend::WaylandQPainterBackend(Wayland::WaylandBackend *b)
: QPainterBackend()
, m_backend(b)
@ -147,13 +199,7 @@ WaylandQPainterBackend::WaylandQPainterBackend(Wayland::WaylandBackend *b)
}
connect(m_backend, &WaylandBackend::outputAdded, this, &WaylandQPainterBackend::createOutput);
connect(m_backend, &WaylandBackend::outputRemoved, this, [this](Output *waylandOutput) {
auto it = std::find_if(m_outputs.begin(), m_outputs.end(), [waylandOutput](const auto &output) {
return output->m_waylandOutput == waylandOutput;
});
if (it == m_outputs.end()) {
return;
}
m_outputs.erase(it);
m_outputs.erase(waylandOutput);
});
}
@ -163,19 +209,26 @@ WaylandQPainterBackend::~WaylandQPainterBackend()
void WaylandQPainterBackend::createOutput(Output *waylandOutput)
{
const auto output = std::make_shared<WaylandQPainterOutput>(static_cast<WaylandOutput *>(waylandOutput));
output->init(m_backend->display()->shmPool());
m_outputs.insert(waylandOutput, output);
m_outputs[waylandOutput] = Layers{
.primaryLayer = std::make_unique<WaylandQPainterPrimaryLayer>(static_cast<WaylandOutput *>(waylandOutput)),
.cursorLayer = std::make_unique<WaylandQPainterCursorLayer>(static_cast<WaylandOutput *>(waylandOutput)),
};
}
void WaylandQPainterBackend::present(Output *output)
{
m_outputs[output]->present();
m_outputs[output].primaryLayer->present();
}
OutputLayer *WaylandQPainterBackend::primaryLayer(Output *output)
{
return m_outputs[output].get();
}
return m_outputs[output].primaryLayer.get();
}
WaylandQPainterCursorLayer *WaylandQPainterBackend::cursorLayer(Output *output)
{
return m_outputs[output].cursorLayer.get();
}
}
}

View file

@ -13,6 +13,8 @@
#include "qpainterbackend.h"
#include "utils/damagejournal.h"
#include <KWayland/Client/buffer.h>
#include <QImage>
#include <QObject>
#include <QWeakPointer>
@ -46,16 +48,15 @@ public:
int age = 0;
};
class WaylandQPainterOutput : public OutputLayer
class WaylandQPainterPrimaryLayer : public OutputLayer
{
public:
WaylandQPainterOutput(WaylandOutput *output);
~WaylandQPainterOutput() override;
WaylandQPainterPrimaryLayer(WaylandOutput *output);
~WaylandQPainterPrimaryLayer() override;
std::optional<OutputLayerBeginFrameInfo> beginFrame() override;
bool endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) override;
bool init(KWayland::Client::ShmPool *pool);
void remapBuffer();
WaylandQPainterBufferSlot *back() const;
@ -77,6 +78,34 @@ private:
friend class WaylandQPainterBackend;
};
class WaylandQPainterCursorLayer : public OutputLayer
{
Q_OBJECT
public:
explicit WaylandQPainterCursorLayer(WaylandOutput *output);
~WaylandQPainterCursorLayer() override;
qreal scale() const;
void setScale(qreal scale);
QPoint hotspot() const;
void setHotspot(const QPoint &hotspot);
QSize size() const;
void setSize(const QSize &size);
std::optional<OutputLayerBeginFrameInfo> beginFrame() override;
bool endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) override;
private:
WaylandOutput *m_output;
QImage m_backingStore;
QPoint m_hotspot;
QSize m_size;
qreal m_scale = 1.0;
};
class WaylandQPainterBackend : public QPainterBackend
{
Q_OBJECT
@ -86,13 +115,20 @@ public:
void present(Output *output) override;
OutputLayer *primaryLayer(Output *output) override;
WaylandQPainterCursorLayer *cursorLayer(Output *output);
private:
void createOutput(Output *waylandOutput);
void frameRendered();
struct Layers
{
std::unique_ptr<WaylandQPainterPrimaryLayer> primaryLayer;
std::unique_ptr<WaylandQPainterCursorLayer> cursorLayer;
};
WaylandBackend *m_backend;
QMap<Output *, std::shared_ptr<WaylandQPainterOutput>> m_outputs;
std::map<Output *, Layers> m_outputs;
};
} // namespace Wayland