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:
parent
1c43571fe1
commit
dbd574ec05
6 changed files with 368 additions and 91 deletions
|
@ -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 ®ion, 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;
|
||||
}
|
||||
EGLint error = eglGetError();
|
||||
if (error != EGL_SUCCESS) {
|
||||
qCWarning(KWIN_WAYLAND_BACKEND) << "Error occurred while creating context " << error;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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,12 +81,14 @@ void WaylandCursor::disable()
|
|||
|
||||
void WaylandCursor::update(KWayland::Client::Buffer::Ptr buffer, qreal scale, const QPoint &hotspot)
|
||||
{
|
||||
if (m_buffer != buffer || m_scale != scale || m_hotspot != hotspot) {
|
||||
m_buffer = buffer;
|
||||
m_scale = scale;
|
||||
m_hotspot = hotspot;
|
||||
|
||||
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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue