Split BufferInterface

Currently, the BufferInterface encapsulates all the kinds of client
buffers. This has become a somewhat annoying issue as we want to
reference the shm pool if a shm buffer is destroyed, or have custom
buffer readiness logic for linux dma-buf client buffers.

Implementing all of that with the current abstractions will be
challenging as there's no good separation between different client
buffer types.

This change splits the BufferInterface class in three sub-classes -
DrmClientBuffer, LinuxDmaBufV1ClientBuffer, and ShmClientBuffer.

In addition to that, this change fixes the broken buffer ref'ing api.
master
Vlad Zahorodnii 3 years ago
parent a244769a3f
commit 0d2879c62d

@ -2,7 +2,8 @@ set(SERVER_LIB_SRCS
abstract_data_source.cpp
appmenu_interface.cpp
blur_interface.cpp
buffer_interface.cpp
clientbuffer.cpp
clientbufferintegration.cpp
clientconnection.cpp
compositor_interface.cpp
contrast_interface.cpp
@ -16,6 +17,7 @@ set(SERVER_LIB_SRCS
datasource_interface.cpp
display.cpp
dpms_interface.cpp
drmclientbuffer.cpp
eglstream_controller_interface.cpp
fakeinput_interface.cpp
filtered_display.cpp
@ -27,7 +29,7 @@ set(SERVER_LIB_SRCS
keyboard_shortcuts_inhibit_v1_interface.cpp
keystate_interface.cpp
layershell_v1_interface.cpp
linuxdmabuf_v1_interface.cpp
linuxdmabufv1clientbuffer.cpp
output_interface.cpp
outputchangeset.cpp
outputconfiguration_interface.cpp
@ -51,6 +53,7 @@ set(SERVER_LIB_SRCS
server_decoration_interface.cpp
server_decoration_palette_interface.cpp
shadow_interface.cpp
shmclientbuffer.cpp
slide_interface.cpp
subcompositor_interface.cpp
surface_interface.cpp
@ -226,7 +229,7 @@ ecm_add_qtwayland_server_protocol_kde(SERVER_LIB_SRCS
BASENAME keystate
)
ecm_add_wayland_server_protocol(SERVER_LIB_SRCS
ecm_add_qtwayland_server_protocol_kde(SERVER_LIB_SRCS
PROTOCOL ${WaylandProtocols_DATADIR}/unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml
BASENAME linux-dmabuf-unstable-v1
)
@ -320,7 +323,8 @@ set(SERVER_LIB_HEADERS
abstract_data_source.h
appmenu_interface.h
blur_interface.h
buffer_interface.h
clientbuffer.h
clientbufferintegration.h
clientconnection.h
compositor_interface.h
contrast_interface.h
@ -334,6 +338,7 @@ set(SERVER_LIB_HEADERS
datasource_interface.h
display.h
dpms_interface.h
drmclientbuffer.h
eglstream_controller_interface.h
fakeinput_interface.h
filtered_display.h
@ -345,7 +350,7 @@ set(SERVER_LIB_HEADERS
keyboard_shortcuts_inhibit_v1_interface.h
keystate_interface.h
layershell_v1_interface.h
linuxdmabuf_v1_interface.h
linuxdmabufv1clientbuffer.h
output_interface.h
outputchangeset.h
outputconfiguration_interface.h
@ -365,6 +370,7 @@ set(SERVER_LIB_HEADERS
server_decoration_interface.h
server_decoration_palette_interface.h
shadow_interface.h
shmclientbuffer.h
slide_interface.h
subcompositor_interface.h
surface_interface.h

@ -11,7 +11,6 @@
#include "KWayland/Client/surface.h"
#include "KWayland/Client/registry.h"
#include "KWayland/Client/shm_pool.h"
#include "../../src/server/buffer_interface.h"
#include "../../src/server/compositor_interface.h"
#include "../../src/server/display.h"
#include "../../src/server/surface_interface.h"

@ -14,7 +14,7 @@
#include "KWayland/Client/shm_pool.h"
#include "KWayland/Client/surface.h"
// server
#include "../../src/server/buffer_interface.h"
#include "../../src/server/shmclientbuffer.h"
#include "../../src/server/display.h"
#include "../../src/server/compositor_interface.h"
#include "../../src/server/shadow_interface.h"
@ -175,6 +175,15 @@ void ShadowTest::testCreateShadow()
QVERIFY(!serverSurface->shadow());
}
static QImage bufferToImage(ClientBuffer *clientBuffer)
{
auto shmBuffer = qobject_cast<ShmClientBuffer *>(clientBuffer);
if (shmBuffer) {
return shmBuffer->data();
}
return QImage();
}
void ShadowTest::testShadowElements()
{
// this test verifies that all shadow elements are correctly passed to the server
@ -222,35 +231,14 @@ void ShadowTest::testShadowElements()
auto serverShadow = serverSurface->shadow();
QVERIFY(serverShadow);
QCOMPARE(serverShadow->offset(), QMarginsF(1, 2, 3, 4));
QCOMPARE(serverShadow->topLeft()->data(), topLeftImage);
QCOMPARE(serverShadow->top()->data(), topImage);
QCOMPARE(serverShadow->topRight()->data(), topRightImage);
QCOMPARE(serverShadow->right()->data(), rightImage);
QCOMPARE(serverShadow->bottomRight()->data(), bottomRightImage);
QCOMPARE(serverShadow->bottom()->data(), bottomImage);
QCOMPARE(serverShadow->bottomLeft()->data(), bottomLeftImage);
QCOMPARE(serverShadow->left()->data(), leftImage);
// try to destroy the buffer
// first attach one buffer
shadow->attachTopLeft(m_shm->createBuffer(topLeftImage));
// create a destroyed signal
QSignalSpy destroyedSpy(serverShadow->topLeft(), &BufferInterface::aboutToBeDestroyed);
QVERIFY(destroyedSpy.isValid());
delete m_shm;
m_shm = nullptr;
QVERIFY(destroyedSpy.wait());
// now all buffers should be gone
// TODO: does that need a signal?
QVERIFY(!serverShadow->topLeft());
QVERIFY(!serverShadow->top());
QVERIFY(!serverShadow->topRight());
QVERIFY(!serverShadow->right());
QVERIFY(!serverShadow->bottomRight());
QVERIFY(!serverShadow->bottom());
QVERIFY(!serverShadow->bottomLeft());
QVERIFY(!serverShadow->left());
QCOMPARE(bufferToImage(serverShadow->topLeft()), topLeftImage);
QCOMPARE(bufferToImage(serverShadow->top()), topImage);
QCOMPARE(bufferToImage(serverShadow->topRight()), topRightImage);
QCOMPARE(bufferToImage(serverShadow->right()), rightImage);
QCOMPARE(bufferToImage(serverShadow->bottomRight()), bottomRightImage);
QCOMPARE(bufferToImage(serverShadow->bottom()), bottomImage);
QCOMPARE(bufferToImage(serverShadow->bottomLeft()), bottomLeftImage);
QCOMPARE(bufferToImage(serverShadow->left()), leftImage);
}
void ShadowTest::testSurfaceDestroy()

@ -12,7 +12,6 @@
#include "KWayland/Client/surface.h"
#include "KWayland/Client/registry.h"
#include "KWayland/Client/shm_pool.h"
#include "../../src/server/buffer_interface.h"
#include "../../src/server/compositor_interface.h"
#include "../../src/server/display.h"
#include "../../src/server/surface_interface.h"

@ -23,7 +23,6 @@
#include "KWayland/Client/subcompositor.h"
#include "KWayland/Client/subsurface.h"
#include "KWayland/Client/touch.h"
#include "../../src/server/buffer_interface.h"
#include "../../src/server/compositor_interface.h"
#include "../../src/server/datadevicemanager_interface.h"
#include "../../src/server/datasource_interface.h"
@ -33,6 +32,7 @@
#include "../../src/server/pointergestures_v1_interface.h"
#include "../../src/server/relativepointer_v1_interface.h"
#include "../../src/server/seat_interface.h"
#include "../../src/server/shmclientbuffer.h"
#include "../../src/server/subcompositor_interface.h"
#include "../../src/server/surface_interface.h"
// Wayland
@ -1284,7 +1284,7 @@ void TestWaylandSeat::testCursor()
QCOMPARE(changedSpy.count(), 3);
QCOMPARE(cursorChangedSpy.count(), 4);
QCOMPARE(surfaceChangedSpy.count(), 1);
QCOMPARE(cursor->surface()->buffer()->data(), img);
QCOMPARE(qobject_cast<ShmClientBuffer *>(cursor->surface()->buffer())->data(), img);
// and add another image to the surface
QImage blue(QSize(10, 20), QImage::Format_ARGB32_Premultiplied);
@ -1295,7 +1295,7 @@ void TestWaylandSeat::testCursor()
QVERIFY(changedSpy.wait());
QCOMPARE(changedSpy.count(), 4);
QCOMPARE(cursorChangedSpy.count(), 5);
QCOMPARE(cursor->surface()->buffer()->data(), blue);
QCOMPARE(qobject_cast<ShmClientBuffer *>(cursor->surface()->buffer())->data(), blue);
p->hideCursor();
QVERIFY(surfaceChangedSpy.wait());
@ -1356,7 +1356,7 @@ void TestWaylandSeat::testCursorDamage()
cursorSurface->commit(Surface::CommitFlag::None);
p->setCursor(cursorSurface, QPoint(0, 0));
QVERIFY(cursorChangedSpy.wait());
QCOMPARE(pointer->cursor()->surface()->buffer()->data(), red);
QCOMPARE(qobject_cast<ShmClientBuffer *>(pointer->cursor()->surface()->buffer())->data(), red);
// and damage the surface
QImage blue(QSize(10, 10), QImage::Format_ARGB32_Premultiplied);
@ -1365,7 +1365,7 @@ void TestWaylandSeat::testCursorDamage()
cursorSurface->damage(QRect(0, 0, 10, 10));
cursorSurface->commit(Surface::CommitFlag::None);
QVERIFY(cursorChangedSpy.wait());
QCOMPARE(pointer->cursor()->surface()->buffer()->data(), blue);
QCOMPARE(qobject_cast<ShmClientBuffer *>(pointer->cursor()->surface()->buffer())->data(), blue);
}
void TestWaylandSeat::testKeyboard()

@ -15,7 +15,6 @@
#include "KWayland/Client/subcompositor.h"
#include "KWayland/Client/subsurface.h"
#include "KWayland/Client/surface.h"
#include "../../src/server/buffer_interface.h"
#include "../../src/server/display.h"
#include "../../src/server/compositor_interface.h"
#include "../../src/server/subcompositor_interface.h"
@ -641,8 +640,6 @@ void TestSubSurface::testSyncMode()
parent->commit();
QVERIFY(childDamagedSpy.wait());
QCOMPARE(childDamagedSpy.count(), 1);
QCOMPARE(childSurface->buffer()->data(), image);
QCOMPARE(parentSurface->buffer()->data(), image2);
QVERIFY(childSurface->isMapped());
QVERIFY(parentSurface->isMapped());
@ -692,7 +689,6 @@ void TestSubSurface::testDeSyncMode()
// setting to desync should apply the state directly
subSurface->setMode(SubSurface::Mode::Desynchronized);
QVERIFY(childDamagedSpy.wait());
QCOMPARE(childSurface->buffer()->data(), image);
QVERIFY(!childSurface->isMapped());
QVERIFY(!parentSurface->isMapped());
@ -702,7 +698,6 @@ void TestSubSurface::testDeSyncMode()
surface->damage(QRect(0, 0, 200, 200));
surface->commit(Surface::CommitFlag::None);
QVERIFY(childDamagedSpy.wait());
QCOMPARE(childSurface->buffer()->data(), image);
}

@ -17,11 +17,12 @@
#include "KWayland/Client/region.h"
#include "KWayland/Client/registry.h"
#include "KWayland/Client/shm_pool.h"
#include "../../src/server/buffer_interface.h"
#include "../../src/server/clientbuffer.h"
#include "../../src/server/compositor_interface.h"
#include "../../src/server/display.h"
#include "../../src/server/idleinhibit_v1_interface.h"
#include "../../src/server/surface_interface.h"
#include "../../src/server/shmclientbuffer.h"
// Wayland
#include <wayland-client-protocol.h>
@ -405,11 +406,13 @@ void TestWaylandSurface::testAttachBuffer()
QVERIFY(unmappedSpy.isEmpty());
// now the ServerSurface should have the black image attached as a buffer
KWaylandServer::BufferInterface *buffer = serverSurface->buffer();
KWaylandServer::ClientBuffer *buffer = serverSurface->buffer();
buffer->ref();
QVERIFY(buffer->shmBuffer());
QCOMPARE(buffer->data(), black);
QCOMPARE(buffer->data().format(), QImage::Format_RGB32);
auto shmBuffer = qobject_cast<KWaylandServer::ShmClientBuffer *>(buffer);
QVERIFY(shmBuffer);
QCOMPARE(shmBuffer->data(), black);
QCOMPARE(shmBuffer->data().format(), QImage::Format_RGB32);
QCOMPARE(shmBuffer->size(), QSize(24, 24));
// render another frame
s->attachBuffer(redBuffer);
@ -419,16 +422,16 @@ void TestWaylandSurface::testAttachBuffer()
QVERIFY(damageSpy.wait());
QCOMPARE(mappedSpy.count(), 1);
QVERIFY(unmappedSpy.isEmpty());
KWaylandServer::BufferInterface *buffer2 = serverSurface->buffer();
KWaylandServer::ClientBuffer *buffer2 = serverSurface->buffer();
buffer2->ref();
QVERIFY(buffer2->shmBuffer());
QCOMPARE(buffer2->data().format(), QImage::Format_ARGB32_Premultiplied);
QCOMPARE(buffer2->data().width(), 24);
QCOMPARE(buffer2->data().height(), 24);
auto shmBuffer2 = qobject_cast<KWaylandServer::ShmClientBuffer *>(buffer2);
QVERIFY(shmBuffer2);
QCOMPARE(shmBuffer2->data().format(), QImage::Format_ARGB32_Premultiplied);
QCOMPARE(shmBuffer2->size(), QSize(24, 24));
for (int i = 0; i < 24; ++i) {
for (int j = 0; j < 24; ++j) {
// it's premultiplied in the format
QCOMPARE(buffer2->data().pixel(i, j), qRgba(128, 0, 0, 128));
QCOMPARE(shmBuffer2->data().pixel(i, j), qRgba(128, 0, 0, 128));
}
}
buffer2->unref();
@ -448,7 +451,6 @@ void TestWaylandSurface::testAttachBuffer()
QCOMPARE(mappedSpy.count(), 1);
QVERIFY(unmappedSpy.isEmpty());
QVERIFY(!buffer2->isReferenced());
delete buffer2;
// TODO: we should have a signal on when the Buffer gets released
QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents);
if (!redBuffer.data()->isReleased()) {
@ -456,16 +458,16 @@ void TestWaylandSurface::testAttachBuffer()
}
QVERIFY(redBuffer.data()->isReleased());
KWaylandServer::BufferInterface *buffer3 = serverSurface->buffer();
KWaylandServer::ClientBuffer *buffer3 = serverSurface->buffer();
buffer3->ref();
QVERIFY(buffer3->shmBuffer());
QCOMPARE(buffer3->data().format(), QImage::Format_ARGB32_Premultiplied);
QCOMPARE(buffer3->data().width(), 24);
QCOMPARE(buffer3->data().height(), 24);
auto shmBuffer3 = qobject_cast<KWaylandServer::ShmClientBuffer *>(buffer3);
QVERIFY(shmBuffer3);
QCOMPARE(shmBuffer3->data().format(), QImage::Format_ARGB32_Premultiplied);
QCOMPARE(shmBuffer3->size(), QSize(24, 24));
for (int i = 0; i < 24; ++i) {
for (int j = 0; j < 24; ++j) {
// it's premultiplied in the format
QCOMPARE(buffer3->data().pixel(i, j), qRgba(0, 0, 128, 128));
QCOMPARE(shmBuffer3->data().pixel(i, j), qRgba(0, 0, 128, 128));
}
}
buffer3->unref();
@ -555,12 +557,12 @@ void TestWaylandSurface::testMultipleSurfaces()
QVERIFY(damageSpy1.wait());
// now the ServerSurface should have the black image attached as a buffer
BufferInterface *buffer1 = serverSurface1->buffer();
ClientBuffer *buffer1 = serverSurface1->buffer();
QVERIFY(buffer1);
QImage buffer1Data = buffer1->data();
QImage buffer1Data = qobject_cast<ShmClientBuffer *>(buffer1)->data();
QCOMPARE(buffer1Data, black);
// accessing the same buffer is OK
QImage buffer1Data2 = buffer1->data();
QImage buffer1Data2 = qobject_cast<ShmClientBuffer *>(buffer1)->data();
QCOMPARE(buffer1Data2, buffer1Data);
buffer1Data = QImage();
QVERIFY(buffer1Data.isNull());
@ -575,13 +577,13 @@ void TestWaylandSurface::testMultipleSurfaces()
QVERIFY(damageSpy2.isValid());
QVERIFY(damageSpy2.wait());
BufferInterface *buffer2 = serverSurface2->buffer();
ClientBuffer *buffer2 = serverSurface2->buffer();
QVERIFY(buffer2);
QImage buffer2Data = buffer2->data();
QImage buffer2Data = qobject_cast<ShmClientBuffer *>(buffer2)->data();
QCOMPARE(buffer2Data, red);
// while buffer2 is accessed we cannot access buffer1
buffer1Data = buffer1->data();
buffer1Data = qobject_cast<ShmClientBuffer *>(buffer1)->data();
QVERIFY(buffer1Data.isNull());
// a deep copy can be kept around
@ -592,7 +594,7 @@ void TestWaylandSurface::testMultipleSurfaces()
QCOMPARE(deepCopy, red);
// now that buffer2Data is destroyed we can access buffer1 again
buffer1Data = buffer1->data();
buffer1Data = qobject_cast<ShmClientBuffer *>(buffer1)->data();
QVERIFY(!buffer1Data.isNull());
QCOMPARE(buffer1Data, black);
}
@ -931,14 +933,9 @@ void TestWaylandSurface::testDestroyAttachedBuffer()
m_connection->flush();
// Let's try to destroy it
QSignalSpy destroySpy(serverSurface->buffer(), &BufferInterface::aboutToBeDestroyed);
QVERIFY(destroySpy.isValid());
delete m_shm;
m_shm = nullptr;
QVERIFY(destroySpy.wait());
// TODO: should this emit unmapped?
QVERIFY(!serverSurface->buffer());
QTRY_VERIFY(serverSurface->buffer()->isDestroyed());
}

@ -0,0 +1,85 @@
/*
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "clientbuffer.h"
#include "clientbuffer_p.h"
#include "qwayland-server-wayland.h"
namespace KWaylandServer
{
ClientBuffer::ClientBuffer(ClientBufferPrivate &dd)
: d_ptr(&dd)
{
}
ClientBuffer::ClientBuffer(wl_resource *resource, ClientBufferPrivate &dd)
: d_ptr(&dd)
{
initialize(resource);
}
ClientBuffer::~ClientBuffer()
{
}
void ClientBuffer::initialize(wl_resource *resource)
{
Q_D(ClientBuffer);
d->resource = resource;
}
wl_resource *ClientBuffer::resource() const
{
Q_D(const ClientBuffer);
return d->resource;
}
bool ClientBuffer::isReferenced() const
{
Q_D(const ClientBuffer);
return d->refCount > 0;
}
bool ClientBuffer::isDestroyed() const
{
Q_D(const ClientBuffer);
return d->isDestroyed;
}
void ClientBuffer::ref()
{
Q_D(ClientBuffer);
d->refCount++;
}
void ClientBuffer::unref()
{
Q_D(ClientBuffer);
Q_ASSERT(d->refCount > 0);
--d->refCount;
if (!isReferenced()) {
if (isDestroyed()) {
delete this;
} else {
wl_buffer_send_release(d->resource);
}
}
}
void ClientBuffer::markAsDestroyed()
{
Q_D(ClientBuffer);
if (!isReferenced()) {
delete this;
} else {
d->resource = nullptr;
d->isDestroyed = true;
}
}
} // namespace KWaylandServer

@ -0,0 +1,77 @@
/*
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#pragma once
#include <QImage>
#include <QObject>
#include <QSize>
#include <KWaylandServer/kwaylandserver_export.h>
struct wl_resource;
namespace KWaylandServer
{
class ClientBufferPrivate;
/**
* The ClientBuffer class represents a client buffer.
*
* While the ClientBuffer is referenced, it won't be destroyed. Note that the client can
* still destroy the wl_buffer object while the ClientBuffer is referenced by the compositor.
* You can use the isDestroyed() function to check whether the wl_buffer object has been
* destroyed.
*/
class KWAYLANDSERVER_EXPORT ClientBuffer : public QObject
{
Q_OBJECT
Q_DECLARE_PRIVATE(ClientBuffer)
public:
~ClientBuffer() override;
/**
* This enum type is used to specify the corner where the origin is. That's it, the
* buffer corner where 0,0 is located.
*/
enum class Origin {
TopLeft,
BottomLeft,
};
bool isReferenced() const;
bool isDestroyed() const;
void ref();
void unref();
/**
* Returns the wl_resource for this ClientBuffer. If the buffer is destroyed, @c null
* will be returned.
*/
wl_resource *resource() const;
/**
* Returns the size in the native pixels. The returned size is unaffected by buffer
* scale or other surface transforms, e.g. @c wp_viewport.
*/
virtual QSize size() const = 0;
virtual bool hasAlphaChannel() const = 0;
virtual Origin origin() const = 0;
void markAsDestroyed(); ///< @internal
protected:
ClientBuffer(ClientBufferPrivate &dd);
ClientBuffer(wl_resource *resource, ClientBufferPrivate &dd);
void initialize(wl_resource *resource);
QScopedPointer<ClientBufferPrivate> d_ptr;
};
} // namespace KWaylandServer

@ -0,0 +1,24 @@
/*
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#pragma once
#include "clientbuffer.h"
namespace KWaylandServer
{
class ClientBufferPrivate
{
public:
virtual ~ClientBufferPrivate() {}
int refCount = 0;
wl_resource *resource = nullptr;
bool isDestroyed = false;
};
} // namespace KWaylandServer

@ -0,0 +1,41 @@
/*
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "clientbufferintegration.h"
#include "display.h"
#include "display_p.h"
namespace KWaylandServer
{
ClientBufferIntegration::ClientBufferIntegration(Display *display)
: QObject(display)
, m_display(display)
{
DisplayPrivate *displayPrivate = DisplayPrivate::get(display);
displayPrivate->bufferIntegrations.append(this);
}
ClientBufferIntegration::~ClientBufferIntegration()
{
if (m_display) {
DisplayPrivate *displayPrivate = DisplayPrivate::get(m_display);
displayPrivate->bufferIntegrations.removeOne(this);
}
}
Display *ClientBufferIntegration::display() const
{
return m_display;
}
ClientBuffer *ClientBufferIntegration::createBuffer(wl_resource *resource)
{
Q_UNUSED(resource)
return nullptr;
}
} // namespace KWaylandServer

@ -0,0 +1,34 @@
/*
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#pragma once
#include "clientbuffer.h"
#include <QPointer>
namespace KWaylandServer
{
class Display;
class KWAYLANDSERVER_EXPORT ClientBufferIntegration : public QObject
{
Q_OBJECT
public:
explicit ClientBufferIntegration(Display *display);
~ClientBufferIntegration() override;
Display *display() const;
virtual ClientBuffer *createBuffer(wl_resource *resource);
private:
QPointer<Display> m_display;
};
} // namespace KWaylandServer

@ -5,9 +5,12 @@
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "display.h"
#include "clientbufferintegration.h"
#include "display_p.h"
#include "drmclientbuffer.h"
#include "logging.h"
#include "output_interface.h"
#include "shmclientbuffer.h"
#include <QAbstractEventDispatcher>
#include <QCoreApplication>
@ -119,7 +122,7 @@ void Display::flush()
void Display::createShm()
{
Q_ASSERT(d->display);
wl_display_init_shm(d->display);
new ShmClientBufferIntegration(this);
}
quint32 Display::nextSerial()
@ -227,6 +230,7 @@ void Display::setEglDisplay(void *display)
return;
}
d->eglDisplay = (EGLDisplay)display;
new DrmClientBufferIntegration(this);
}
void *Display::eglDisplay() const
@ -234,4 +238,69 @@ void *Display::eglDisplay() const
return d->eglDisplay;
}
struct ClientBufferDestroyListener : wl_listener
{
ClientBufferDestroyListener(Display *display, ClientBuffer *buffer);
~ClientBufferDestroyListener();
Display *display;
};
void bufferDestroyCallback(wl_listener *listener, void *data)
{
ClientBufferDestroyListener *destroyListener = static_cast<ClientBufferDestroyListener *>(listener);
DisplayPrivate *displayPrivate = DisplayPrivate::get(destroyListener->display);
ClientBuffer *buffer = displayPrivate->q->clientBufferForResource(static_cast<wl_resource *>(data));
displayPrivate->unregisterClientBuffer(buffer);
buffer->markAsDestroyed();
}
ClientBufferDestroyListener::ClientBufferDestroyListener(Display *display, ClientBuffer *buffer)
: display(display)
{
notify = bufferDestroyCallback;
link.prev = nullptr;
link.next = nullptr;
wl_resource_add_destroy_listener(buffer->resource(), this);
}
ClientBufferDestroyListener::~ClientBufferDestroyListener()
{
wl_list_remove(&link);
}
ClientBuffer *Display::clientBufferForResource(wl_resource *resource) const
{
ClientBuffer *buffer = d->resourceToBuffer.value(resource);
if (buffer) {
return buffer;
}
for (ClientBufferIntegration *integration : qAsConst(d->bufferIntegrations)) {
ClientBuffer *buffer = integration->createBuffer(resource);
if (buffer) {
d->registerClientBuffer(buffer);
return buffer;
}
}
return nullptr;
}
void DisplayPrivate::registerClientBuffer(ClientBuffer *buffer)
{
resourceToBuffer.insert(buffer->resource(), buffer);
bufferToListener.insert(buffer, new ClientBufferDestroyListener(q, buffer));
}
void DisplayPrivate::unregisterClientBuffer(ClientBuffer *buffer)
{
Q_ASSERT_X(buffer->resource(), "unregisterClientBuffer", "buffer must have valid resource");
resourceToBuffer.remove(buffer->resource());
delete bufferToListener.take(buffer);
}
}

@ -33,6 +33,7 @@ namespace KWaylandServer
* @see Display
*/
class ClientBuffer;
class ClientConnection;
class DisplayPrivate;
class OutputInterface;
@ -139,6 +140,12 @@ public:
*/
void *eglDisplay() const;
/**
* Returns the client buffer with the specified @a resource. Returns @c null if there's
* no such a buffer.
*/
ClientBuffer *clientBufferForResource(wl_resource *resource) const;
private Q_SLOTS:
void flush();

@ -9,6 +9,7 @@
#include <wayland-server-core.h>
#include <QHash>
#include <QList>
#include <QSocketNotifier>
#include <QString>
@ -16,14 +17,19 @@
#include <EGL/egl.h>
struct wl_resource;
namespace KWaylandServer
{
class ClientBufferIntegration;
class ClientBuffer;
class ClientConnection;
class Display;
class OutputInterface;
class OutputDeviceInterface;
class SeatInterface;
struct ClientBufferDestroyListener;
class DisplayPrivate
{
@ -33,6 +39,9 @@ public:
void registerSocketName(const QString &socketName);
void registerClientBuffer(ClientBuffer *clientBuffer);
void unregisterClientBuffer(ClientBuffer *clientBuffer);
Display *q;
QSocketNotifier *socketNotifier = nullptr;
wl_display *display = nullptr;
@ -44,6 +53,9 @@ public:
QVector<ClientConnection *> clients;
QStringList socketNames;
EGLDisplay eglDisplay = EGL_NO_DISPLAY;
QHash<::wl_resource *, ClientBuffer *> resourceToBuffer;
QHash<ClientBuffer *, ClientBufferDestroyListener *> bufferToListener;
QList<ClientBufferIntegration *> bufferIntegrations;
};
} // namespace KWaylandServer

@ -0,0 +1,96 @@
/*
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "drmclientbuffer.h"
#include "clientbuffer_p.h"
#include "display.h"
#include <EGL/egl.h>
#include <QtGui/qopengl.h>
#ifndef EGL_WL_bind_wayland_display
#define EGL_WAYLAND_Y_INVERTED_WL 0x31DB
#endif
namespace KWaylandServer
{
typedef GLboolean(*eglQueryWaylandBufferWL_func)(EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value);
static eglQueryWaylandBufferWL_func eglQueryWaylandBufferWL = nullptr;
class DrmClientBufferPrivate : public ClientBufferPrivate
{
public:
int textureFormat = 0;
int width = 0;
int height = 0;
int yInverted = 0;
bool hasAlphaChannel = false;
};
DrmClientBuffer::DrmClientBuffer(wl_resource *resource, DrmClientBufferIntegration *integration)
: ClientBuffer(resource, *new DrmClientBufferPrivate)
{
Q_D(DrmClientBuffer);
EGLDisplay eglDisplay = integration->display()->eglDisplay();
eglQueryWaylandBufferWL(eglDisplay, resource, EGL_TEXTURE_FORMAT, &d->textureFormat);
eglQueryWaylandBufferWL(eglDisplay, resource, EGL_WIDTH, &d->width);
eglQueryWaylandBufferWL(eglDisplay, resource, EGL_HEIGHT, &d->height);
bool ok = eglQueryWaylandBufferWL(eglDisplay, resource, EGL_WAYLAND_Y_INVERTED_WL, &d->yInverted);
if (!ok) {
// If EGL_WAYLAND_Y_INVERTED_WL is unsupported, we must assume that the buffer is inverted.
d->yInverted = true;
}
}
int DrmClientBuffer::textureFormat() const
{
Q_D(const DrmClientBuffer);
return d->textureFormat;
}
QSize DrmClientBuffer::size() const
{
Q_D(const DrmClientBuffer);
return QSize(d->width, d->height);
}
bool DrmClientBuffer::hasAlphaChannel() const
{
Q_D(const DrmClientBuffer);
return d->textureFormat == EGL_TEXTURE_RGBA;
}
ClientBuffer::Origin DrmClientBuffer::origin() const
{
Q_D(const DrmClientBuffer);
return d->yInverted ? Origin::TopLeft : Origin::BottomLeft;
}
DrmClientBufferIntegration::DrmClientBufferIntegration(Display *display)
: ClientBufferIntegration(display)
{
}
ClientBuffer *DrmClientBufferIntegration::createBuffer(::wl_resource *resource)
{
EGLDisplay eglDisplay = display()->eglDisplay();
static bool resolved = false;
if (!resolved && eglDisplay != EGL_NO_DISPLAY) {
eglQueryWaylandBufferWL = (eglQueryWaylandBufferWL_func)eglGetProcAddress("eglQueryWaylandBufferWL");
resolved = true;
}
EGLint format;
if (eglQueryWaylandBufferWL(eglDisplay, resource, EGL_TEXTURE_FORMAT, &format)) {
return new DrmClientBuffer(resource, this);
}
return nullptr;
}
} // namespace KWaylandServer

@ -0,0 +1,52 @@
/*
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#pragma once
#include "clientbuffer.h"
#include "clientbufferintegration.h"
namespace KWaylandServer
{
class DrmClientBufferPrivate;
/**
* The DrmClientBufferIntegration class provides support for wl_drm client buffers.
*/
class KWAYLANDSERVER_EXPORT DrmClientBufferIntegration : public ClientBufferIntegration
{
Q_OBJECT
public:
explicit DrmClientBufferIntegration(Display *display);
ClientBuffer *createBuffer(::wl_resource *resource) override;
};
/**
* The DrmClientBuffer class represents a wl_drm client buffer.
*
* Nowadays, the wl_drm protocol is de-facto deprecated with the introduction of the
* linux-dmabuf-v1 protocol. Note that Vulkan WSI in Mesa still prefers wl_drm, but
* that's about to change, https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/4942/
*/
class KWAYLANDSERVER_EXPORT DrmClientBuffer : public ClientBuffer
{
Q_OBJECT
Q_DECLARE_PRIVATE(DrmClientBuffer)
public:
explicit DrmClientBuffer(wl_resource *resource, DrmClientBufferIntegration *integration);
int textureFormat() const;
QSize size() const override;
bool hasAlphaChannel() const override;
Origin origin() const override;
};
} // namespace KWaylandServer

@ -0,0 +1,470 @@
/*
SPDX-FileCopyrightText: 2018 Fredrik Höglund <fredrik@kde.org>
SPDX-FileCopyrightText: 2019 Roman Gilg <subdiff@gmail.com>
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
Based on the libweston implementation,
SPDX-FileCopyrightText: 2014, 2015 Collabora, Ltd.
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "linuxdmabufv1clientbuffer.h"
#include "clientbuffer_p.h"
#include "display.h"
#include "display_p.h"
#include "drm_fourcc.h"
#include "qwayland-server-linux-dmabuf-unstable-v1.h"
#include "qwayland-server-wayland.h"
#include <QDebug>
#include <QVector>
#include <unistd.h>
namespace KWaylandServer
{
static const int s_version = 3;
class LinuxDmaBufV1ClientBufferIntegrationPrivate : public QtWaylandServer::zwp_linux_dmabuf_v1
{
public:
LinuxDmaBufV1ClientBufferIntegrationPrivate(LinuxDmaBufV1ClientBufferIntegration *q, Display *display);
LinuxDmaBufV1ClientBufferIntegration *q;
LinuxDmaBufV1ClientBufferIntegration::RendererInterface *rendererInterface = nullptr;
QHash<uint32_t, QSet<uint64_t>> supportedModifiers;
protected:
void zwp_linux_dmabuf_v1_bind_resource(Resource *resource) override;
void zwp_linux_dmabuf_v1_destroy(Resource *resource) override;
void zwp_linux_dmabuf_v1_create_params(Resource *resource, uint32_t params_id) override;
};
class LinuxDmaBufV1ClientBufferPrivate : public ClientBufferPrivate, public QtWaylandServer::wl_buffer
{
public:
QSize size;
quint32 format;
quint32 flags;
QVector<LinuxDmaBufV1Plane> planes;
bool hasAlphaChannel = false;
protected:
void buffer_destroy(Resource *resource) override;
};
class LinuxDmaBufParamsV1 : public QtWaylandServer::zwp_linux_buffer_params_v1
{
public:
LinuxDmaBufParamsV1(LinuxDmaBufV1ClientBufferIntegration *integration, ::wl_resource *resource);
~LinuxDmaBufParamsV1() override;
protected:
void zwp_linux_buffer_params_v1_destroy_resource(Resource *resource) override;
void zwp_linux_buffer_params_v1_destroy(Resource *resource) override;
void zwp_linux_buffer_params_v1_add(Resource *resource, int32_t fd, uint32_t plane_idx, uint32_t offset, uint32_t stride, uint32_t modifier_hi, uint32_t modifier_lo) override;
void zwp_linux_buffer_params_v1_create(Resource *resource, int32_t width, int32_t height, uint32_t format, uint32_t flags) override;
void zwp_linux_buffer_params_v1_create_immed(Resource *resource, uint32_t buffer_id, int32_t width, int32_t height, uint32_t format, uint32_t flags) override;
private:
bool test(Resource *resource, uint32_t width, uint32_t height);
LinuxDmaBufV1ClientBufferIntegration *m_integration;
QVector<LinuxDmaBufV1Plane> m_planes;
int m_planeCount = 0;
bool m_isUsed = false;
};
LinuxDmaBufV1ClientBufferIntegrationPrivate::LinuxDmaBufV1ClientBufferIntegrationPrivate(LinuxDmaBufV1ClientBufferIntegration *q, Display *display)
: QtWaylandServer::zwp_linux_dmabuf_v1(*display, s_version)
, q(q)
{
}
void LinuxDmaBufV1ClientBufferIntegrationPrivate::zwp_linux_dmabuf_v1_bind_resource(Resource *resource)
{
for (auto it = supportedModifiers.constBegin(); it != supportedModifiers.constEnd(); ++it) {
const uint32_t format = it.key();
QSet<uint64_t> modifiers = it.value();
if (modifiers.isEmpty()) {
modifiers.insert(DRM_FORMAT_MOD_INVALID);
}
for (const uint64_t &modifier : qAsConst(modifiers)) {
if (resource->version() >= ZWP_LINUX_DMABUF_V1_MODIFIER_SINCE_VERSION) {
const uint32_t modifier_lo = modifier & 0xffffffff;
const uint32_t modifier_hi = modifier >> 32;
send_modifier(resource->handle, format, modifier_hi, modifier_lo);
} else if (modifier == DRM_FORMAT_MOD_LINEAR || modifier == DRM_FORMAT_MOD_INVALID) {
send_format(resource->handle, format);
}
}
}
}
void LinuxDmaBufV1ClientBufferIntegrationPrivate::zwp_linux_dmabuf_v1_destroy(Resource *resource)
{
wl_resource_destroy(resource->handle);
}
void LinuxDmaBufV1ClientBufferIntegrationPrivate::zwp_linux_dmabuf_v1_create_params(Resource *resource, uint32_t params_id)
{
wl_resource *paramsResource = wl_resource_create(resource->client(),
&zwp_linux_buffer_params_v1_interface,
resource->version(), params_id);
if (!paramsResource) {
wl_resource_post_no_memory(resource->handle);
return;
}
new LinuxDmaBufParamsV1(q, paramsResource);
}
LinuxDmaBufParamsV1::LinuxDmaBufParamsV1(LinuxDmaBufV1ClientBufferIntegration *integration, ::wl_resource *resource)
: QtWaylandServer::zwp_linux_buffer_params_v1(resource)
, m_integration(integration)
, m_planes(4)
{
}
LinuxDmaBufParamsV1::~LinuxDmaBufParamsV1()
{
for (const LinuxDmaBufV1Plane &plane : m_planes) {
if (plane.fd != -1) {
close(plane.fd);
}
}
}
void LinuxDmaBufParamsV1::zwp_linux_buffer_params_v1_destroy_resource(Resource *resource)
{
Q_UNUSED(resource)
delete this;
}
void LinuxDmaBufParamsV1::zwp_linux_buffer_params_v1_destroy(Resource *resource)
{
wl_resource_destroy(resource->handle);
}
void LinuxDmaBufParamsV1::zwp_linux_buffer_params_v1_add(Resource *resource, int32_t fd, uint32_t plane_idx, uint32_t offset, uint32_t stride, uint32_t modifier_hi, uint32_t modifier_lo)
{
if (Q_UNLIKELY(m_isUsed)) {
wl_resource_post_error(resource->handle, error_already_used,
"the params object has already been used to create a wl_buffer");
close(fd);
return;
}
if (Q_UNLIKELY(plane_idx >= uint(m_planes.size()))) {
wl_resource_post_error(resource->handle, error_plane_idx,
"plane index %d is out of bounds", plane_idx);
close(fd);
return;
}
LinuxDmaBufV1Plane &plane = m_planes[plane_idx];
if (Q_UNLIKELY(plane.fd != -1)) {
wl_resource_post_error(resource->handle, error_plane_set,
"the plane index %d was already set", plane_idx);
close(fd);
return;
}
plane.fd = fd;
plane.modifier = (quint64(modifier_hi) << 32) | modifier_lo;
plane.offset = offset;
plane.stride = stride;
m_planeCount++;
}
void LinuxDmaBufParamsV1::zwp_linux_buffer_params_v1_create(Resource *resource, int32_t width, int32_t height, uint32_t format, uint32_t flags)
{
if (Q_UNLIKELY(m_isUsed)) {
wl_resource_post_error(resource->handle, error_already_used,
"the params object has already been used to create a wl_buffer");
return;
}
if (Q_UNLIKELY(!test(resource, width, height))) {
return;
}
m_isUsed = true;
m_planes.resize(m_planeCount);
LinuxDmaBufV1ClientBuffer *clientBuffer = m_integration->rendererInterface()->importBuffer(
m_planes, format, QSize(width, height), flags);
if (!clientBuffer) {
send_failed(resource->handle);
return;
}
m_planes.clear(); // the ownership of file descriptors has been moved to the buffer
wl_resource *bufferResource = wl_resource_create(resource->client(), &wl_buffer_interface, 1, 0);
if (!bufferResource) {
delete clientBuffer;
wl_resource_post_no_memory(resource->handle);
return;
}
clientBuffer->initialize(bufferResource);
send_created(resource->handle, bufferResource);
DisplayPrivate *displayPrivate = DisplayPrivate::get(m_integration->display());
displayPrivate->registerClientBuffer(clientBuffer);
}
void LinuxDmaBufParamsV1::zwp_linux_buffer_params_v1_create_immed(Resource *resource, uint32_t buffer_id, int32_t width, int32_t height, uint32_t format, uint32_t flags)
{
if (Q_UNLIKELY(m_isUsed)) {
wl_resource_post_error(resource->handle, error_already_used,
"the params object has already been used to create a wl_buffer");
return;
}
if (Q_UNLIKELY(!test(resource, width, height))) {
return;
}
m_isUsed = true;
m_planes.resize(m_planeCount);
LinuxDmaBufV1ClientBuffer *clientBuffer = m_integration->rendererInterface()->importBuffer(
m_planes, format, QSize(width, height), flags);
if (!clientBuffer) {
wl_resource_post_error(resource->handle, error_invalid_wl_buffer,
"importing the supplied dmabufs failed");
return;
}
m_planes.clear(); // the ownership of file descriptors has been moved to the buffer
wl_resource *bufferResource = wl_resource_create(resource->client(), &wl_buffer_interface, 1, buffer_id);
if (!bufferResource) {
delete clientBuffer;
wl_resource_post_no_memory(resource->handle);
return;
}
clientBuffer->initialize(bufferResource);
DisplayPrivate *displayPrivate = DisplayPrivate::get(m_integration->display());
displayPrivate->registerClientBuffer(clientBuffer);
}
bool LinuxDmaBufParamsV1::test(Resource *resource, uint32_t width, uint32_t height)
{
if (Q_UNLIKELY(!m_planeCount)) {
wl_resource_post_error(resource->handle, error_incomplete, "no planes have been specified");
return false;
}
// Check for holes in the dmabuf set (e.g. [0, 1, 3]).
for (int i = 0; i < m_planeCount; ++i) {
if (m_planes[i].fd == -1) {
wl_resource_post_error(resource->handle, error_incomplete,
"no dmabuf has been added for plane %d", i);
return false;
}
}
if (Q_UNLIKELY(width == 0 || height == 0)) {
wl_resource_post_error(resource->handle, error_invalid_dimensions,
"invalid width %d or height %d", width, height);
return false;
}
for (int i = 0; i < m_planeCount; ++i) {
const LinuxDmaBufV1Plane &plane = m_planes.at(i);
// Check for overflows.
if (Q_UNLIKELY(uint64_t(plane.offset) + plane.stride > UINT32_MAX)) {
wl_resource_post_error(resource->handle, error_out_of_bounds,
"size overflow for plane %d", i);
return false;
}
if (Q_UNLIKELY(i == 0 &&
uint64_t(plane.offset) + uint64_t(plane.stride) * height > UINT32_MAX)) {
wl_resource_post_error(resource->handle, error_out_of_bounds,
"size overflow for plane %d", i);
return false;
}
// Don't report an error as it might be caused by the kernel not supporting
// seeking on dmabuf.
const off_t size = lseek(plane.fd, 0, SEEK_END);
if (size == -1) {
continue;
}
if (Q_UNLIKELY(plane.offset >= size)) {
wl_resource_post_error(resource->handle, error_out_of_bounds,
"invalid offset %i for plane %d", plane.offset, i);
return false;
}
if (Q_UNLIKELY(plane.offset + plane.stride > size)) {
wl_resource_post_error(resource->handle, error_out_of_bounds,
"invalid stride %i for plane %d", plane.stride, i);
return false;
}
// Only valid for first plane as other planes might be sub-sampled according to
// fourcc format.
if (Q_UNLIKELY(i == 0 && plane.offset + plane.stride * height > size)) {
wl_resource_post_error(resource->handle, error_out_of_bounds,
"invalid buffer stride of height for plane %d", i);
return false;
}
}
return true;
}
LinuxDmaBufV1ClientBufferIntegration::LinuxDmaBufV1ClientBufferIntegration(Display *display)
: ClientBufferIntegration(display)
, d(new LinuxDmaBufV1ClientBufferIntegrationPrivate(this, display))
{
}
LinuxDmaBufV1ClientBufferIntegration::~LinuxDmaBufV1ClientBufferIntegration()
{
}
LinuxDmaBufV1ClientBufferIntegration::RendererInterface *LinuxDmaBufV1ClientBufferIntegration::rendererInterface() const
{
return d->rendererInterface;
}
void LinuxDmaBufV1ClientBufferIntegration::setRendererInterface(RendererInterface *rendererInterface)
{
d->rendererInterface = rendererInterface;
}
void LinuxDmaBufV1ClientBufferIntegration::setSupportedFormatsWithModifiers(const QHash<uint32_t, QSet<uint64_t>> &set)
{
d->supportedModifiers = set;
}
static bool testAlphaChannel(uint32_t drmFormat)
{
switch (drmFormat) {
case DRM_FORMAT_ARGB4444:
case DRM_FORMAT_ABGR4444:
case DRM_FORMAT_RGBA4444:
case DRM_FORMAT_BGRA4444:
case DRM_FORMAT_ARGB1555:
case DRM_FORMAT_ABGR1555:
case DRM_FORMAT_RGBA5551:
case DRM_FORMAT_BGRA5551:
case DRM_FORMAT_ARGB8888:
case DRM_FORMAT_ABGR8888:
case DRM_FORMAT_RGBA8888:
case DRM_FORMAT_BGRA8888:
case DRM_FORMAT_ARGB2101010:
case DRM_FORMAT_ABGR2101010:
case DRM_FORMAT_RGBA1010102:
case DRM_FORMAT_BGRA1010102:
case DRM_FORMAT_XRGB8888_A8:
case DRM_FORMAT_XBGR8888_A8:
case DRM_FORMAT_RGBX8888_A8:
case DRM_FORMAT_BGRX8888_A8:
case DRM_FORMAT_RGB888_A8:
case DRM_FORMAT_BGR888_A8:
case DRM_FORMAT_RGB565_A8:
case DRM_FORMAT_BGR565_A8:
return true;
default:
return false;
}
}
void LinuxDmaBufV1ClientBufferPrivate::buffer_destroy(Resource *resource)
{
wl_resource_destroy(resource->handle);
}
LinuxDmaBufV1ClientBuffer::LinuxDmaBufV1ClientBuffer(const QSize &size,
quint32 format,
quint32 flags,
const QVector<LinuxDmaBufV1Plane> &planes)
: ClientBuffer(*new LinuxDmaBufV1ClientBufferPrivate)
{
Q_D(LinuxDmaBufV1ClientBuffer);
d->size = size;
d->format = format;
d->flags = flags;
d->planes = planes;
d->hasAlphaChannel = testAlphaChannel(format);
}
LinuxDmaBufV1ClientBuffer::~LinuxDmaBufV1ClientBuffer()
{
Q_D(LinuxDmaBufV1ClientBuffer);
for (int i = 0; i < d->planes.count(); ++i) {
if (d->planes[i].fd != -1) {
close(d->planes[i].fd);
d->planes[i].fd = -1;
}
}
}
void LinuxDmaBufV1ClientBuffer::initialize(wl_resource *resource)
{
Q_D(LinuxDmaBufV1ClientBuffer);
d->init(resource);
ClientBuffer::initialize(resource);
}
quint32 LinuxDmaBufV1ClientBuffer::format() const
{
Q_D(const LinuxDmaBufV1ClientBuffer);
return d->format;
}
quint32 LinuxDmaBufV1ClientBuffer::flags() const
{
Q_D(const LinuxDmaBufV1ClientBuffer);
return d->flags;
}
QVector<LinuxDmaBufV1Plane> LinuxDmaBufV1ClientBuffer::planes() const
{
Q_D(const LinuxDmaBufV1ClientBuffer);
return d->planes;
}
QSize LinuxDmaBufV1ClientBuffer::size() const
{
Q_D(const LinuxDmaBufV1ClientBuffer);
return d->size;
}
bool LinuxDmaBufV1ClientBuffer::hasAlphaChannel() const
{
Q_D(const LinuxDmaBufV1ClientBuffer);
return d->hasAlphaChannel;
}
ClientBuffer::Origin LinuxDmaBufV1ClientBuffer::origin() const
{
Q_D(const LinuxDmaBufV1ClientBuffer);
if (d->flags & QtWaylandServer::zwp_linux_buffer_params_v1::flags_y_invert) {
return ClientBuffer::Origin::BottomLeft;
} else {
return ClientBuffer::Origin::TopLeft;
}
}
} // namespace KWaylandServer

@ -0,0 +1,117 @@
/*
SPDX-FileCopyrightText: 2018 Fredrik Höglund <fredrik@kde.org>
SPDX-FileCopyrightText: 2019 Roman Gilg <subdiff@gmail.com>
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#pragma once
#include "clientbuffer.h"
#include "clientbufferintegration.h"
#include <QHash>
#include <QSet>
namespace KWaylandServer
{
class LinuxDmaBufV1ClientBufferPrivate;
class LinuxDmaBufV1ClientBufferIntegrationPrivate;
/**
* The LinuxDmaBufV1Plane type represents a plane in a client buffer.
*/
struct LinuxDmaBufV1Plane
{
int fd = -1; ///< The dmabuf file descriptor
quint32 offset = 0; ///< The offset from the start of buffer
quint32 stride = 0; ///< The distance from the start of a row to the next row in bytes
quint64 modifier = 0; ///< The layout modifier
};
/**
* The LinuxDmaBufV1ClientBuffer class represents a linux dma-buf client buffer.
*
* The LinuxDmaBufV1ClientBuffer can be used even after the underlying wl_buffer object
* is destroyed by the client.
*/
class KWAYLANDSERVER_EXPORT LinuxDmaBufV1ClientBuffer : public ClientBuffer
{
Q_OBJECT
Q_DECLARE_PRIVATE(LinuxDmaBufV1ClientBuffer)
public:
LinuxDmaBufV1ClientBuffer(const QSize &size,
quint32 format,
quint32 flags,
const QVector<LinuxDmaBufV1Plane> &planes);
~LinuxDmaBufV1ClientBuffer() override;
quint32 format() const;
quint32 flags() const;
QVector<LinuxDmaBufV1Plane> planes() const;
QSize size() const override;
bool hasAlphaChannel() const override;
Origin origin() const override;
private:
void initialize(wl_resource *resource);
friend class LinuxDmaBufParamsV1;
};
/**
* The LinuxDmaBufV1ClientBufferIntegration class provides support for linux dma-buf buffers.
*/
class KWAYLANDSERVER_EXPORT LinuxDmaBufV1ClientBufferIntegration : public ClientBufferIntegration
{
Q_OBJECT
public:
explicit LinuxDmaBufV1ClientBufferIntegration(Display *display);
~LinuxDmaBufV1ClientBufferIntegration() override;
/**
* The Iface class provides an interface from the LinuxDmabufInterface into the compositor
*/
class RendererInterface {
public:
virtual ~RendererInterface() = default;
/**
* Imports a linux-dmabuf buffer into the compositor.
*
* The parent LinuxDmabufUnstableV1Interface class takes ownership of returned
* buffer objects.
*
* In return the returned buffer takes ownership of the file descriptor for each
* plane.
*
* Note that it is the responsibility of the caller to close the file descriptors
* when the import fails.
*
* @return The imported buffer on success, and nullptr otherwise.
*/
virtual LinuxDmaBufV1ClientBuffer *importBuffer(const QVector<LinuxDmaBufV1Plane> &planes,
quint32 format,
const QSize &size,
quint32 flags) = 0;
};
RendererInterface *rendererInterface() const;
/**
* Sets the compositor implementation for the dmabuf interface.
*
* The ownership is not transferred by this call.
*/
void setRendererInterface(RendererInterface *rendererInterface);
void setSupportedFormatsWithModifiers(const QHash<uint32_t, QSet<uint64_t>> &set);
private:
QScopedPointer<LinuxDmaBufV1ClientBufferIntegrationPrivate> d;
};
} // namespace KWaylandServer

@ -1,337 +0,0 @@
/*
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "buffer_interface.h"
#include "compositor_interface.h"
#include "display.h"
#include "logging.h"
#include "linuxdmabuf_v1_interface.h"
// Wayland
#include <wayland-server.h>
// EGL
#include <EGL/egl.h>
#include <QtGui/qopengl.h>
#include "drm_fourcc.h"
namespace KWaylandServer
{
namespace EGL
{
typedef GLboolean(*eglQueryWaylandBufferWL_func)(EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value);
eglQueryWaylandBufferWL_func eglQueryWaylandBufferWL = nullptr;
}
class BufferInterface::Private
{
public:
Private(BufferInterface *q, Display *display, wl_resource *resource);
~Private();
QImage::Format format() const;
QImage createImage();
wl_resource *buffer;
wl_shm_buffer *shmBuffer;
LinuxDmabufBuffer *dmabufBuffer;
int refCount;
QSize size;
bool alpha;
static BufferInterface *get(wl_resource *r);
private:
static void destroyListenerCallback(wl_listener *listener, void *data);
static Private *cast(wl_resource *r);
static void imageBufferCleanupHandler(void *info);
static QList<Private*> s_buffers;
static Private *s_accessedBuffer;
static int s_accessCounter;
BufferInterface *q;
wl_listener listener;
};
QList<BufferInterface::Private*> BufferInterface::Private::s_buffers;
BufferInterface::Private *BufferInterface::Private::s_accessedBuffer = nullptr;
int BufferInterface::Private::s_accessCounter = 0;
BufferInterface::Private *BufferInterface::Private::cast(wl_resource *r)
{
auto it = std::find_if(s_buffers.constBegin(), s_buffers.constEnd(), [r](Private *d) { return d->buffer == r; });
if (it == s_buffers.constEnd()) {
return nullptr;
}
return *it;
}
BufferInterface *BufferInterface::Private::get(wl_resource *r)
{
Private *p = cast(r);
if (!p) {
return nullptr;
}
return p->q;
}
void BufferInterface::Private::imageBufferCleanupHandler(void *info)
{
Private *p = reinterpret_cast<Private*>(info);
Q_ASSERT(p == s_accessedBuffer);
Q_ASSERT(s_accessCounter > 0);
s_accessCounter--;
if (s_accessCounter == 0) {
s_accessedBuffer = nullptr;
}
wl_shm_buffer_end_access(p->shmBuffer);
}
BufferInterface::Private::Private(BufferInterface *q, Display *display, wl_resource *resource)
: buffer(resource)
, shmBuffer(wl_shm_buffer_get(resource))
, dmabufBuffer(nullptr)
, refCount(0)
, alpha(false)
, q(q)
{
if (!shmBuffer && wl_resource_instance_of(resource, &wl_buffer_interface, LinuxDmabufUnstableV1Interface::bufferImplementation())) {
dmabufBuffer = static_cast<LinuxDmabufBuffer *>(wl_resource_get_user_data(resource));
}
s_buffers << this;
listener.notify = destroyListenerCallback;
listener.link.prev = nullptr;
listener.link.next = nullptr;
wl_resource_add_destroy_listener(resource, &listener);
if (shmBuffer) {
size = QSize(wl_shm_buffer_get_width(shmBuffer), wl_shm_buffer_get_height(shmBuffer));
// check alpha
switch (wl_shm_buffer_get_format(shmBuffer)) {
case WL_SHM_FORMAT_ARGB8888:
alpha = true;
break;
case WL_SHM_FORMAT_XRGB8888:
default:
alpha = false;
break;
}
} else if (dmabufBuffer) {
switch (dmabufBuffer->format()) {
case DRM_FORMAT_ARGB4444:
case DRM_FORMAT_ABGR4444:
case DRM_FORMAT_RGBA4444:
case DRM_FORMAT_BGRA4444:
case DRM_FORMAT_ARGB1555:
case DRM_FORMAT_ABGR1555:
case DRM_FORMAT_RGBA5551:
case DRM_FORMAT_BGRA5551:
case DRM_FORMAT_ARGB8888:
case DRM_FORMAT_ABGR8888:
case DRM_FORMAT_RGBA8888:
case DRM_FORMAT_BGRA8888:
case DRM_FORMAT_ARGB2101010:
case DRM_FORMAT_ABGR2101010:
case DRM_FORMAT_RGBA1010102:
case DRM_FORMAT_BGRA1010102:
case DRM_FORMAT_XRGB8888_A8:
case DRM_FORMAT_XBGR8888_A8:
case DRM_FORMAT_RGBX8888_A8:
case DRM_FORMAT_BGRX8888_A8:
case DRM_FORMAT_RGB888_A8:
case DRM_FORMAT_BGR888_A8:
case DRM_FORMAT_RGB565_A8:
case DRM_FORMAT_BGR565_A8:
alpha = true;
break;
default:
alpha = false;
break;
}
size = dmabufBuffer->size();
} else {
EGLDisplay eglDisplay = display->eglDisplay();
static bool resolved = false;
using namespace EGL;
if (!resolved && eglDisplay != EGL_NO_DISPLAY) {
eglQueryWaylandBufferWL = (eglQueryWaylandBufferWL_func)eglGetProcAddress("eglQueryWaylandBufferWL");
resolved = true;
}
if (eglQueryWaylandBufferWL) {
EGLint width, height;
bool valid = false;
valid = eglQueryWaylandBufferWL(eglDisplay, buffer, EGL_WIDTH, &width);
valid = valid && eglQueryWaylandBufferWL(eglDisplay, buffer, EGL_HEIGHT, &height);
if (valid) {
size = QSize(width, height);
}
// check alpha
EGLint format;
if (eglQueryWaylandBufferWL(eglDisplay, buffer, EGL_TEXTURE_FORMAT, &format)) {
switch (format) {
case EGL_TEXTURE_RGBA:
alpha = true;
break;
case EGL_TEXTURE_RGB:
default:
alpha = false;
break;
}
}
}
}
}
BufferInterface::Private::~Private()
{
wl_list_remove(&listener.link);
s_buffers.removeAll(this);
}
BufferInterface *BufferInterface::get(Display *display, wl_resource *r)
{
if (!r) {
return nullptr;
}
// TODO: verify it's a buffer
BufferInterface *b = Private::get(r);
if (b) {
return b;
}
return new BufferInterface(display, r);
}
BufferInterface::BufferInterface(Display *display, wl_resource *resource)
: QObject()
, d(new Private(this, display, resource))
{
}
BufferInterface::~BufferInterface()
{
if (d->refCount != 0) {
qCWarning(KWAYLAND_SERVER) << "Buffer destroyed while still being referenced, ref count:" << d->refCount;
}
}
void BufferInterface::Private::destroyListenerCallback(wl_listener *listener, void *data)
{
Q_UNUSED(listener);
auto b = cast(reinterpret_cast<wl_resource*>(data));
b->buffer = nullptr;
Q_EMIT b->q->aboutToBeDestroyed(b->q);
delete b->q;
}
void BufferInterface::ref()
{
d->refCount++;
}
void BufferInterface::unref()
{
Q_ASSERT(d->refCount > 0);
d->refCount--;
if (d->refCount == 0) {
if (d->buffer) {
wl_buffer_send_release(d->buffer);
}
}
}
QImage::Format BufferInterface::Private::format() const
{
if (!shmBuffer) {
return QImage::Format_Invalid;
}
switch (wl_shm_buffer_get_format(shmBuffer)) {
case WL_SHM_FORMAT_ARGB8888:
return QImage::Format_ARGB32_Premultiplied;
case WL_SHM_FORMAT_XRGB8888:
return QImage::Format_RGB32;
default:
return QImage::Format_Invalid;
}
}
QImage BufferInterface::data()
{
return d->createImage();
}
QImage BufferInterface::Private::createImage()
{
if (!shmBuffer) {
return QImage();
}
if (s_accessedBuffer != nullptr && s_accessedBuffer != this) {
return QImage();
}
const QImage::Format imageFormat = format();
if (imageFormat == QImage::Format_Invalid) {
return QImage();
}
s_accessedBuffer = this;
s_accessCounter++;
wl_shm_buffer_begin_access(shmBuffer);
return QImage((const uchar*)wl_shm_buffer_get_data(shmBuffer),
size.width(),
size.height(),
wl_shm_buffer_get_stride(shmBuffer),
imageFormat,
&imageBufferCleanupHandler, this);
}
bool BufferInterface::isReferenced() const
{
return d->refCount > 0;
}
wl_shm_buffer *BufferInterface::shmBuffer()
{
return d->shmBuffer;
}
LinuxDmabufBuffer *BufferInterface::linuxDmabufBuffer()
{
return d->dmabufBuffer;
}
wl_resource *BufferInterface::resource() const
{
return d->buffer;
}
int BufferInterface::width() const
{
return d->size.width();
}
int BufferInterface::height() const
{
return d->size.height();
}
QSize BufferInterface::size() const
{
return d->size;
}
void BufferInterface::setSize(const QSize &size)
{
if (d->shmBuffer || d->size == size) {
return;
}
d->size = size;
Q_EMIT sizeChanged();
}
bool BufferInterface::hasAlphaChannel() const
{
return d->alpha;
}
}

@ -1,172 +0,0 @@
/*
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#pragma once
#include <QImage>
#include <QObject>
#include <KWaylandServer/kwaylandserver_export.h>
struct wl_resource;
struct wl_shm_buffer;
namespace KWaylandServer
{
class Display;
class LinuxDmabufBuffer;
/**
* @brief Reference counted representation of a Wayland buffer on Server side.
*
* This class encapsulates a rendering buffer which is normally attached to a SurfaceInterface.
* A client should not render to a Wayland buffer as long as the buffer gets used by the server.
* The server signals whether it's still used. This class provides a convenience access for this
* functionality by performing reference counting and deleting the BufferInterface instance
* automatically once it is no longer accessed.
*
* The BufferInterface is referenced as long as it is attached to a SurfaceInterface. If one wants
* to keep access to the BufferInterface for a longer time ensure to call ref on first usage and
* unref again once access to it is no longer needed.
*
* In Wayland the buffer is an abstract concept and a buffer might represent multiple different
* concrete buffer techniques. This class has direct support for shared memory buffers built and
* provides access to the native buffer for different (e.g. EGL/drm) buffers.
*
* If the EGL display has been registered in the Display the BufferInterface can also provide
* some information about an EGL/drm buffer.
*
* For shared memory buffers a direct conversion to a memory-mapped QImage possible using the
* data method. Please refer to the documentation for notes on the restrictions when using the
* shared memory-mapped QImages.
*
* @see Display
* @see SurfaceInterace
*/
class KWAYLANDSERVER_EXPORT BufferInterface : public QObject
{
Q_OBJECT
public:
BufferInterface(Display *display, wl_resource *resource);
virtual ~BufferInterface();
/**
* Reference the BufferInterface.
*
* As long as the reference counting has not reached @c 0 the BufferInterface is valid
* and blocked for usage by the client.
*
* @see unref
* @see isReferenced
*/
void ref();
/**
* Unreference the BufferInterface.
*
* If the reference counting reached @c 0 the BufferInterface is released, so that the
* client can use it again.
*
* @see ref
* @see isReferenced
*/
void unref();
/**
* @returns whether the BufferInterface is currently referenced
*
* @see ref
* @see unref
*/
bool isReferenced() const;
/**
* @returns The native wl_shm_buffer if the BufferInterface represents a shared memory buffer, otherwise @c nullptr.
*/
wl_shm_buffer *shmBuffer();
/**
* Returns a pointer to the LinuxDmabufBuffer when the buffer is a dmabuf buffer, and nullptr otherwise.
*/
LinuxDmabufBuffer *linuxDmabufBuffer();
/**
* @returns the native wl_resource wrapped by this BufferInterface.
*/
wl_resource *resource() const;
/**
* Creates a QImage for the shared memory buffer.
*
* If the BufferInterface does not reference a shared memory buffer a null QImage is returned.
*
* The QImage shares the memory with the buffer and this constraints how the returned
* QImage can be used and when this method can be invoked.
*
* It is not safe to have two shared memory QImages for different BufferInterfaces at
* the same time. This method ensures that this does not happen and returns a null
* QImage if a different BufferInterface's data is still mapped to a QImage. Please note
* that this also applies to all implicitly data shared copies.
*
* In case it is needed to keep a copy, a deep copy has to be performed by using QImage::copy.
*
* As the underlying shared memory buffer is owned by a different client it is not safe to
* write to the returned QImage. The image is a read-only buffer. If there is need to modify
* the image, perform a deep copy.
*
*/
QImage data();
/**
* Returns the width of the buffer in device pixels.
*/
int width() const;
/**
* Returns the height of the buffer in device pixels.
*/
int height() const;
/**
* Returns the size of this BufferInterface.
* Note: only for shared memory buffers (shmBuffer) the size can be derived,
* for other buffers it might be possible to derive the size if an EGL display
* is set on Display otherwise the user of the BufferInterface has to use setSize to
* provide the proper size.
* @see setSize
* @see Display::setEglDisplay
*/
QSize size() const;
/**
* Sets the @p size for non shared memory buffers.
* @see size
* @see sizeChanged
*/
void setSize(const QSize &size);
/**
* Returns whether the format of the BufferInterface has an alpha channel.
* For shared memory buffers returns @c true for format @c WL_SHM_FORMAT_ARGB8888,
* for all other formats returns @c false.
*
* For EGL buffers returns @c true for format @c EGL_TEXTURE_RGBA, for all other formats
* returns @c false.
*
* If the format cannot be queried the default value (@c false) is returned.
*
*/
bool hasAlphaChannel() const;
static BufferInterface *get(Display *display, wl_resource *r);
Q_SIGNALS:
void aboutToBeDestroyed(KWaylandServer::BufferInterface*);
/**
* Emitted when the size of the Buffer changes.
*/
void sizeChanged();
private:
class Private;
QScopedPointer<Private> d;
};
}
Q_DECLARE_METATYPE(KWaylandServer::BufferInterface*)

@ -1,497 +0,0 @@
/*
SPDX-FileCopyrightText: 2019 Roman Gilg <subdiff@gmail.com>
SPDX-FileCopyrightText: 2018 Fredrik Höglund <fredrik@kde.org>
Based on the libweston implementation,
SPDX-FileCopyrightText: 2014, 2015 Collabora, Ltd.
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "linuxdmabuf_v1_interface.h"
#include "drm_fourcc.h"
#include "global_p.h"
#include "wayland-linux-dmabuf-unstable-v1-server-protocol.h"
#include "wayland-server-protocol.h"
#include <KWaylandServer/kwaylandserver_export.h>
#include <QVector>
#include <array>
#include <assert.h>
#include <unistd.h>
namespace KWaylandServer
{
class LinuxDmabufBuffer::Private
{
public:
Private(LinuxDmabufBuffer *_q) : q(_q) {
q->d = this;
}
virtual ~Private() = default;
virtual uint32_t format() const = 0;
virtual QSize size() const = 0;
LinuxDmabufBuffer *q;
};
LinuxDmabufBuffer::LinuxDmabufBuffer()
{
}
uint32_t LinuxDmabufBuffer::format() const
{
return d->format();
}
QSize LinuxDmabufBuffer::size() const
{
return d->size();
}
class LinuxDmabufUnstableV1Buffer::Private : LinuxDmabufBuffer::Private
{
public:
Private(LinuxDmabufUnstableV1Buffer *_q)
: LinuxDmabufBuffer::Private(_q)
, q(_q)
{
}
~Private() override = default;
uint32_t format() const override {
return m_format;
}
QSize size() const override {
return m_size;
}
uint32_t m_format;
QSize m_size;
LinuxDmabufUnstableV1Buffer *q;
};
LinuxDmabufUnstableV1Buffer::LinuxDmabufUnstableV1Buffer(uint32_t format, const QSize &size)
: LinuxDmabufBuffer()
, d(new LinuxDmabufUnstableV1Buffer::Private(this))
{
d->m_format = format;
d->m_size = size;
}
LinuxDmabufUnstableV1Buffer::~LinuxDmabufUnstableV1Buffer()
{
}
typedef LinuxDmabufUnstableV1Interface V1Iface;
class V1Iface::Private : public Global::Private
{
public:
Private(V1Iface *q, Display *display);
~Private();
static const struct wl_buffer_interface *bufferImplementation() { return &s_bufferImplementation; }
V1Iface::Impl *impl;
QHash<uint32_t, QSet<uint64_t> > supportedFormatsWithModifiers;
V1Iface * const q;
static const uint32_t s_version;
void bind(wl_client *client, uint32_t version, uint32_t id) override final;
void createParams(wl_client *client, wl_resource *resource, uint32_t id);
static void unbind(wl_client *client, wl_resource *resource);
static void createParamsCallback(wl_client *client, wl_resource *resource, uint32_t id);
private:
class Params
{
public:
Params(V1Iface::Private *dmabufInterface, wl_client *client, uint32_t version, uint32_t id);
~Params();
void postNoMemory() { wl_resource_post_no_memory(m_resource); }
wl_resource *resource() const { return m_resource; }
void add(int fd, uint32_t plane_idx, uint32_t offset, uint32_t stride, uint64_t modifier);
void create(wl_client *client, uint32_t bufferId, const QSize &size, uint32_t format, uint32_t flags);
static void destroy(wl_client *client, wl_resource *resource);
static void add(wl_client *client, wl_resource *resource, int fd, uint32_t plane_idx, uint32_t offset, uint32_t stride, uint32_t modifier_hi, uint32_t modifier_lo);
static void create(wl_client *client, wl_resource *resource, int width, int height, uint32_t format, uint32_t flags);
static void createImmed(wl_client *client, wl_resource *resource, uint32_t new_id, int width, int height, uint32_t format, uint32_t flags);
private:
static const struct zwp_linux_buffer_params_v1_interface s_interface;
wl_resource *m_resource;
V1Iface::Private *m_dmabufInterface;
std::array<V1Iface::Plane, 4> m_planes;
size_t m_planeCount = 0;
bool m_createRequested = false;
};
static const struct zwp_linux_dmabuf_v1_interface s_implementation;
static const struct wl_buffer_interface s_bufferImplementation;
};
void V1Iface::Private::Params::create(wl_client *client, wl_resource *resource,
int width, int height, uint32_t format, uint32_t flags)
{
Q_UNUSED(client)
V1Iface::Private::Params *params = static_cast<V1Iface::Private::Params *>(wl_resource_get_user_data(resource));
assert(params->m_resource == resource);
params->create(client, 0, QSize(width, height), format, flags);
}
void V1Iface::Private::Params::createImmed(wl_client *client, wl_resource *resource,
uint32_t new_id, int width, int height,
uint32_t format, uint32_t flags)
{
Q_UNUSED(client)
V1Iface::Private::Params *params = static_cast<V1Iface::Private::Params *>(wl_resource_get_user_data(resource));
assert(params->m_resource == resource);
params->create(client, new_id, QSize(width, height), format, flags);
}
#ifndef K_DOXYGEN
const struct zwp_linux_dmabuf_v1_interface V1Iface::Private::s_implementation = {
[](wl_client *, wl_resource *resource) { wl_resource_destroy(resource); }, // unbind
createParamsCallback
};
const struct wl_buffer_interface V1Iface::Private::s_bufferImplementation = {
[](wl_client *, wl_resource *resource) { wl_resource_destroy(resource); } // destroy
};
#ifndef K_DOXYGEN
const struct zwp_linux_buffer_params_v1_interface V1Iface::Private::Params::s_interface = {
destroy,
add,
create,
createImmed
};
#endif
V1Iface::Private::Params::Params(V1Iface::Private *dmabufInterface, wl_client *client, uint32_t version, uint32_t id)
: m_dmabufInterface(dmabufInterface)
{
m_resource = wl_resource_create(client, &zwp_linux_buffer_params_v1_interface, version, id);
if (!m_resource) {
return;
}
wl_resource_set_implementation(m_resource, &s_interface, this,
[](wl_resource *resource) {
delete static_cast<V1Iface::Private::Params *>(wl_resource_get_user_data(resource));
});
for (auto &plane : m_planes) {
plane.fd = -1;
plane.offset = 0;
plane.stride = 0;
plane.modifier = 0;
}
}
V1Iface::Private::Params::~Params()
{
// Close the file descriptors
for (auto &plane : m_planes) {
if (plane.fd != -1) {
::close(plane.fd);
}
}
}
void V1Iface::Private::Params::create(wl_client *client, uint32_t bufferId, const QSize &size, uint32_t format, uint32_t flags)
{
// Validate the parameters
// -----------------------
const uint32_t width = size.width();
const uint32_t height = size.height();
if (m_createRequested) {
wl_resource_post_error(m_resource,
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED,
"params was already used to create a wl_buffer");
return;
}
m_createRequested = true;
if (m_planeCount == 0) {
wl_resource_post_error(m_resource,
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
"no dmabuf has been added to the params");
return;
}
// Check for holes in the dmabufs set (e.g. [0, 1, 3])
for (uint32_t i = 0; i < m_planeCount; i++) {
if (m_planes[i].fd != -1)
continue;
wl_resource_post_error(m_resource,
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
"no dmabuf has been added for plane %i", i);
return;
}
if (width < 1 || height < 1) {
wl_resource_post_error(m_resource,
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_DIMENSIONS,
"invalid width %d or height %d", width, height);
return;
}
for (uint32_t i = 0; i < m_planeCount; i++) {
auto &plane = m_planes[i];
if (uint64_t(plane.offset) + plane.stride > UINT32_MAX) {
wl_resource_post_error(m_resource,
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
"size overflow for plane %i", i);
return;
}
if (i == 0 && uint64_t(plane.offset) + plane.stride * height > UINT32_MAX) {
wl_resource_post_error(m_resource,
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
"size overflow for plane %i", i);
return;
}
// Don't report an error as it might be caused by the kernel not supporting seeking on dmabuf
off_t size = ::lseek(plane.fd, 0, SEEK_END);
if (size == -1)
continue;
if (plane.offset >= size) {
wl_resource_post_error(m_resource,
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
"invalid offset %i for plane %i",
plane.offset, i);
return;
}
if (plane.offset + plane.stride > size) {
wl_resource_post_error(m_resource,
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
"invalid stride %i for plane %i",
plane.stride, i);
return;
}
// Only valid for first plane as other planes might be
// sub-sampled according to fourcc format
if (i == 0 && plane.offset + plane.stride * height > size) {
wl_resource_post_error(m_resource,
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
"invalid buffer stride or height for plane %i", i);
return;
}
}
// Import the buffer
// -----------------
QVector<V1Iface::Plane> planes;
planes.reserve(m_planeCount);
for (uint32_t i = 0; i < m_planeCount; i++)
planes << m_planes[i];
LinuxDmabufUnstableV1Buffer *buffer = m_dmabufInterface->impl->importBuffer(planes,
format,
size,
(V1Iface::Flags) flags);
if (buffer) {
// The buffer has ownership of the file descriptors now
for (auto &plane : m_planes) {
plane.fd = -1;
}
wl_resource *resource = wl_resource_create(client, &wl_buffer_interface, 1, bufferId);
if (!resource ) {
postNoMemory();
delete buffer;
return;
}
wl_resource_set_implementation(resource, m_dmabufInterface->q->bufferImplementation(), buffer,
[](wl_resource *resource) { // Destructor
delete static_cast<LinuxDmabufUnstableV1Buffer *>(wl_resource_get_user_data(resource));
});
// XXX Do we need this?
//buffer->setResource(resource);
// Send a 'created' event when the request is not for an immediate import, i.e. bufferId is zero
if (bufferId == 0) {
zwp_linux_buffer_params_v1_send_created(m_resource, resource);
}
} else {
if (bufferId == 0) {
zwp_linux_buffer_params_v1_send_failed(m_resource);
} else {
// since the behavior is left implementation defined by the
// protocol in case of create_immed failure due to an unknown cause,
// we choose to treat it as a fatal error and immediately kill the
// client instead of creating an invalid handle and waiting for it
// to be used.
wl_resource_post_error(m_resource,
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_WL_BUFFER,
"importing the supplied dmabufs failed");
}
}
}
void V1Iface::Private::Params::add(int fd, uint32_t plane_idx, uint32_t offset, uint32_t stride, uint64_t modifier)
{
if (m_createRequested) {
wl_resource_post_error(m_resource,
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED,
"params was already used to create a wl_buffer");
::close(fd);
return;
}
if (plane_idx >= m_planes.size()) {
wl_resource_post_error(m_resource,
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_IDX,
"plane index %u is too high", plane_idx);
::close(fd);
return;
}
auto &plane = m_planes[plane_idx];
if (plane.fd != -1) {
wl_resource_post_error(m_resource,
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_SET,
"a dmabuf has already been added for plane %u",
plane_idx);
::close(fd);
return;
}
plane.fd = fd;
plane.offset = offset;
plane.stride = stride;
plane.modifier = modifier;
m_planeCount++;
}
void V1Iface::Private::Params::destroy(wl_client *client, wl_resource *resource)
{
Q_UNUSED(client)
wl_resource_destroy(resource);
}
void V1Iface::Private::Params::add(wl_client *client, wl_resource *resource,
int fd, uint32_t plane_idx,
uint32_t offset, uint32_t stride,
uint32_t modifier_hi, uint32_t modifier_lo)
{
Q_UNUSED(client)
V1Iface::Private::Params *params = static_cast<V1Iface::Private::Params *>(wl_resource_get_user_data(resource));
assert(params->m_resource == resource);
params->add(fd, plane_idx, offset, stride, (uint64_t(modifier_hi) << 32) | modifier_lo);
}
const uint32_t V1Iface::Private::s_version = 3;
#endif
V1Iface::Private::Private(V1Iface *q, Display *display)
: Global::Private(display, &zwp_linux_dmabuf_v1_interface, s_version),
q(q)
{
}
V1Iface::Private::~Private() = default;
void V1Iface::Private::bind(wl_client *client, uint32_t version, uint32_t id)
{
wl_resource *resource = wl_resource_create(client, &zwp_linux_dmabuf_v1_interface, std::min(s_version, version), id);
if (!resource) {
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(resource, &s_implementation, this, nullptr);
// Send formats & modifiers
// ------------------------
QHash<uint32_t, QSet<uint64_t>>::const_iterator it = supportedFormatsWithModifiers.constBegin();
while (it != supportedFormatsWithModifiers.constEnd()) {
QSet<uint64_t> modifiers = it.value();
if (modifiers.isEmpty()) {
modifiers << DRM_FORMAT_MOD_INVALID;
}
for (uint64_t modifier : qAsConst(modifiers)) {
if (version >= ZWP_LINUX_DMABUF_V1_MODIFIER_SINCE_VERSION) {
const uint32_t modifier_lo = modifier & 0xFFFFFFFF;
const uint32_t modifier_hi = modifier >> 32;
zwp_linux_dmabuf_v1_send_modifier(resource, it.key(), modifier_hi, modifier_lo);
} else if (modifier == DRM_FORMAT_MOD_LINEAR || modifier == DRM_FORMAT_MOD_INVALID) {
zwp_linux_dmabuf_v1_send_format(resource, it.key());
}
}
it++;
}
}
void V1Iface::Private::createParams(wl_client *client, wl_resource *resource, uint32_t id)
{
Params *params = new Params(this, client, wl_resource_get_version(resource), id);
if (!params->resource()) {
wl_resource_post_no_memory(resource);
delete params;
}
}
void V1Iface::Private::createParamsCallback(wl_client *client, wl_resource *resource, uint32_t id)
{
V1Iface::Private *global = static_cast<V1Iface::Private *>(wl_resource_get_user_data(resource));
global->createParams(client, resource, id);
}
V1Iface::LinuxDmabufUnstableV1Interface(Display *display, QObject *parent)
: Global(new Private(this, display), parent)
{
}
V1Iface::~LinuxDmabufUnstableV1Interface() = default;
void V1Iface::setImpl(V1Iface::Impl *impl)
{
d_func()->impl = impl;
}
void V1Iface::setSupportedFormatsWithModifiers(QHash<uint32_t, QSet<uint64_t> > set)
{
d_func()->supportedFormatsWithModifiers = set;
}
const struct wl_buffer_interface *V1Iface::bufferImplementation()
{
return V1Iface::Private::bufferImplementation();
}
V1Iface::Private *V1Iface::d_func() const
{
return reinterpret_cast<Private*>(d.data());
}
}

@ -1,161 +0,0 @@
/*
SPDX-FileCopyrightText: 2019 Roman Gilg <subdiff@gmail.com>
SPDX-FileCopyrightText: 2018 Fredrik Höglund <fredrik@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#pragma once
#include "global.h"
#include "resource.h"
#include <KWaylandServer/kwaylandserver_export.h>
#include <QHash>
#include <QSet>
#include <QSize>
struct wl_buffer_interface;
namespace KWaylandServer
{
class BufferInterface;
/**
* The base class for linux-dmabuf buffers
*
* Compositors should reimplement this class to store objects specific
* to the underlying graphics stack.
*/
class KWAYLANDSERVER_EXPORT LinuxDmabufBuffer
{
public:
LinuxDmabufBuffer();
virtual ~LinuxDmabufBuffer() = default;
/**
* Returns the DRM format code for the buffer.
*/
uint32_t format() const;
/**
* Returns the size of the buffer.
*/
QSize size() const;
private:
class Private;
Private *d;
friend class LinuxDmabufUnstableV1Buffer;
};
class KWAYLANDSERVER_EXPORT LinuxDmabufUnstableV1Buffer : public LinuxDmabufBuffer
{
public:
/**
* Creates a new Buffer.
*/
LinuxDmabufUnstableV1Buffer(uint32_t format, const QSize &size);
/**
* Destroys the Buffer.
*/
virtual ~LinuxDmabufUnstableV1Buffer();
private:
class Private;
QScopedPointer<Private> d;
};
/**
* Represents the global zpw_linux_dmabuf_v1 interface.
*
* This interface provides a way for clients to create generic dmabuf based wl_buffers.
*/
class KWAYLANDSERVER_EXPORT LinuxDmabufUnstableV1Interface : public Global
{
Q_OBJECT
public:
explicit LinuxDmabufUnstableV1Interface(Display *display, QObject *parent = nullptr);
enum Flag {
YInverted = (1 << 0), /// Contents are y-inverted
Interlaced = (1 << 1), /// Content is interlaced
BottomFieldFirst = (1 << 2), /// Bottom field first
};
Q_DECLARE_FLAGS(Flags, Flag)
/**
* Represents a plane in a buffer
*/
struct Plane {
int fd; /// The dmabuf file descriptor
uint32_t offset; /// The offset from the start of buffer
uint32_t stride; /// The distance from the start of a row to the next row in bytes
uint64_t modifier; /// The layout modifier
};
/**
* The Iface class provides an interface from the LinuxDmabufInterface into the compositor
*/
class Impl {
public:
Impl() = default;
virtual ~Impl() = default;
/**
* Imports a linux-dmabuf buffer into the compositor.
*
* The parent LinuxDmabufUnstableV1Interface class takes ownership of returned
* buffer objects.
*
* In return the returned buffer takes ownership of the file descriptor for each
* plane.
*
* Note that it is the responsibility of the caller to close the file descriptors
* when the import fails.
*
* @return The imported buffer on success, and nullptr otherwise.
*/
virtual LinuxDmabufUnstableV1Buffer *importBuffer(const QVector<Plane> &planes,
uint32_t format,
const QSize &size,
Flags flags) = 0;
};
/**
* Destroys the LinuxDmabufUnstableV1Interface.
*/
virtual ~LinuxDmabufUnstableV1Interface();
/**
* Sets the compositor implementation for the dmabuf interface.
*
* The ownership is not transferred by this call.
*/
void setImpl(Impl *impl);
void setSupportedFormatsWithModifiers(QHash<uint32_t, QSet<uint64_t> > set);
/**
* Returns the LinuxDmabufInterface for the given resource.
*/
static LinuxDmabufUnstableV1Interface *get(wl_resource *native);
private:
/**
* Returns a pointer to the wl_buffer implementation for imported dmabufs.
*/
static const struct wl_buffer_interface *bufferImplementation();
friend class BufferInterface;
class Private;
Private *d_func() const;
};
}
Q_DECLARE_METATYPE(KWaylandServer::LinuxDmabufUnstableV1Interface*)
Q_DECLARE_OPERATORS_FOR_FLAGS(KWaylandServer::LinuxDmabufUnstableV1Interface::Flags)

@ -4,7 +4,7 @@
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "shadow_interface.h"
#include "buffer_interface.h"
#include "clientbuffer.h"
#include "display.h"
#include "surface_interface_p.h"
@ -105,14 +105,14 @@ public:
BottomLeftBuffer = 1 << 7,
Offset = 1 << 8,
};
BufferInterface *left = nullptr;
BufferInterface *topLeft = nullptr;
BufferInterface *top = nullptr;
BufferInterface *topRight = nullptr;
BufferInterface *right = nullptr;
BufferInterface *bottomRight = nullptr;
BufferInterface *bottom = nullptr;
BufferInterface *bottomLeft = nullptr;
QPointer<ClientBuffer> left;
QPointer<ClientBuffer> topLeft;
QPointer<ClientBuffer> top;
QPointer<ClientBuffer> topRight;
QPointer<ClientBuffer> right;
QPointer<ClientBuffer> bottomRight;
QPointer<ClientBuffer> bottom;
QPointer<ClientBuffer> bottomLeft;
QMarginsF offset;
Flags flags = Flags::None;
};
@ -175,41 +175,7 @@ void ShadowInterfacePrivate::org_kde_kwin_shadow_commit(Resource *resource)
void ShadowInterfacePrivate::attach(ShadowInterfacePrivate::State::Flags flag, wl_resource *buffer)
{
BufferInterface *b = BufferInterface::get(manager->display(), buffer);
if (b) {
QObject::connect(b, &BufferInterface::aboutToBeDestroyed, q,
[this](BufferInterface *buffer) {
#define PENDING( __PART__ ) \
if (pending.__PART__ == buffer) { \
pending.__PART__ = nullptr; \
}
PENDING(left)
PENDING(topLeft)
PENDING(top)
PENDING(topRight)
PENDING(right)
PENDING(bottomRight)
PENDING(bottom)
PENDING(bottomLeft)
#undef PENDING
#define CURRENT( __PART__ ) \
if (current.__PART__ == buffer) { \
current.__PART__->unref(); \
current.__PART__ = nullptr; \
}
CURRENT(left)
CURRENT(topLeft)
CURRENT(top)
CURRENT(topRight)
CURRENT(right)
CURRENT(bottomRight)
CURRENT(bottom)
CURRENT(bottomLeft)
#undef CURRENT
}
);
}
ClientBuffer *b = manager->display()->clientBufferForResource(buffer);
switch (flag) {
case State::LeftBuffer:
pending.left = b;
@ -367,7 +333,7 @@ QMarginsF ShadowInterface::offset() const
}
#define BUFFER( __PART__ ) \
BufferInterface *ShadowInterface::__PART__() const \
ClientBuffer *ShadowInterface::__PART__() const \
{ \
return d->current.__PART__; \
}

@ -15,7 +15,7 @@ struct wl_resource;
namespace KWaylandServer
{
class BufferInterface;
class ClientBuffer;
class Display;
class ShadowManagerInterfacePrivate;
class ShadowInterfacePrivate;
@ -40,14 +40,14 @@ class KWAYLANDSERVER_EXPORT ShadowInterface : public QObject
public:
~ShadowInterface() override;
BufferInterface *left() const;
BufferInterface *topLeft() const;
BufferInterface *top() const;
BufferInterface *topRight() const;
BufferInterface *right() const;
BufferInterface *bottomRight() const;
BufferInterface *bottom() const;
BufferInterface *bottomLeft() const;
ClientBuffer *left() const;
ClientBuffer *topLeft() const;
ClientBuffer *top() const;
ClientBuffer *topRight() const;
ClientBuffer *right() const;
ClientBuffer *bottomRight() const;
ClientBuffer *bottom() const;
ClientBuffer *bottomLeft() const;
QMarginsF offset() const;

@ -0,0 +1,124 @@
/*
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "shmclientbuffer.h"
#include "clientbuffer_p.h"
#include "display.h"
#include <wayland-server-core.h>
#include <wayland-server-protocol.h>
namespace KWaylandServer
{
static const ShmClientBuffer *s_accessedBuffer = nullptr;
static int s_accessCounter = 0;
class ShmClientBufferPrivate : public ClientBufferPrivate
{
public:
QImage::Format format = QImage::Format_Invalid;
uint32_t width = 0;
uint32_t height = 0;
bool hasAlphaChannel = false;
};
static bool alphaChannelFromFormat(uint32_t format)
{
switch (format) {
case WL_SHM_FORMAT_ARGB8888:
return true;
case WL_SHM_FORMAT_XRGB8888:
default:
return false;
}
}
static QImage::Format imageFormatForShmFormat(uint32_t format)
{
switch (format) {
case WL_SHM_FORMAT_ARGB8888:
return QImage::Format_ARGB32_Premultiplied;
case WL_SHM_FORMAT_XRGB8888:
return QImage::Format_RGB32;
default:
return QImage::Format_Invalid;
}
}
ShmClientBuffer::ShmClientBuffer(wl_resource *resource)
: ClientBuffer(resource, *new ShmClientBufferPrivate)
{
Q_D(ShmClientBuffer);
wl_shm_buffer *buffer = wl_shm_buffer_get(resource);
d->width = wl_shm_buffer_get_width(buffer);
d->height = wl_shm_buffer_get_height(buffer);
d->hasAlphaChannel = alphaChannelFromFormat(wl_shm_buffer_get_format(buffer));
d->format = imageFormatForShmFormat(wl_shm_buffer_get_format(buffer));
}
QSize ShmClientBuffer::size() const
{
Q_D(const ShmClientBuffer);
return QSize(d->width, d->height);
}
bool ShmClientBuffer::hasAlphaChannel() const
{
Q_D(const ShmClientBuffer);
return d->hasAlphaChannel;
}
ClientBuffer::Origin ShmClientBuffer::origin() const
{
return Origin::TopLeft;
}
static void cleanupShmData(void *bufferHandle)
{
Q_ASSERT_X(s_accessCounter > 0, "cleanup", "access counter must be positive");
s_accessCounter--;
if (s_accessCounter == 0) {
s_accessedBuffer = nullptr;
}
wl_shm_buffer_end_access(static_cast<wl_shm_buffer *>(bufferHandle));
}
QImage ShmClientBuffer::data() const
{
if (s_accessedBuffer && s_accessedBuffer != this) {
return QImage();
}
Q_D(const ShmClientBuffer);
if (wl_shm_buffer *buffer = wl_shm_buffer_get(resource())) {
s_accessedBuffer = this;
s_accessCounter++;
wl_shm_buffer_begin_access(buffer);
const uchar *data = static_cast<const uchar *>(wl_shm_buffer_get_data(buffer));
const uint32_t stride = wl_shm_buffer_get_stride(buffer);
const uint32_t format = wl_shm_buffer_get_format(buffer);
return QImage(data, d->width, d->height, stride, d->format, cleanupShmData, buffer);
}
return QImage();
}
ShmClientBufferIntegration::ShmClientBufferIntegration(Display *display)
: ClientBufferIntegration(display)
{
wl_display_init_shm(*display);
}
ClientBuffer *ShmClientBufferIntegration::createBuffer(::wl_resource *resource)
{
if (wl_shm_buffer_get(resource)) {
return new ShmClientBuffer(resource);
}
return nullptr;
}
} // namespace KWaylandServer

@ -0,0 +1,51 @@
/*
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#pragma once
#include "clientbuffer.h"
#include "clientbufferintegration.h"
namespace KWaylandServer
{
class ShmClientBufferPrivate;
/**
* The ShmClientBuffer class represents a wl_shm_buffer client buffer.
*
* The buffer's data can be accessed using the data() function. Note that it is not allowed
* to access data of several shared memory buffers simultaneously.
*/
class KWAYLANDSERVER_EXPORT ShmClientBuffer : public ClientBuffer
{
Q_OBJECT
Q_DECLARE_PRIVATE(ShmClientBuffer)
public:
explicit ShmClientBuffer(wl_resource *resource);
QImage data() const;
QSize size() const override;
bool hasAlphaChannel() const override;
Origin origin() const override;
};
/**
* The ShmClientBufferIntegration class provides support for wl_shm_buffer buffers.
*/
class ShmClientBufferIntegration : public ClientBufferIntegration
{
Q_OBJECT
public:
explicit ShmClientBufferIntegration(Display *display);
ClientBuffer *createBuffer(::wl_resource *resource) override;
};
} // namespace KWaylandServer

@ -6,7 +6,7 @@
*/
#include "surface_interface.h"
#include "surface_interface_p.h"
#include "buffer_interface.h"
#include "clientbuffer.h"
#include "clientconnection.h"
#include "compositor_interface.h"
#include "display.h"
@ -278,7 +278,7 @@ void SurfaceInterfacePrivate::surface_attach(Resource *resource, struct ::wl_res
pending.bufferDamage = QRegion();
return;
}
pending.buffer = BufferInterface::get(compositor->display(), buffer);
pending.buffer = compositor->display()->clientBufferForResource(buffer);
}
void SurfaceInterfacePrivate::surface_damage(Resource *, int32_t x, int32_t y, int32_t width, int32_t height)
@ -429,18 +429,18 @@ QMatrix4x4 SurfaceInterfacePrivate::buildSurfaceToBufferMatrix()
break;
case OutputInterface::Transform::Rotated90:
case OutputInterface::Transform::Flipped90:
surfaceToBufferMatrix.translate(0, current.buffer->height() / current.bufferScale);
surfaceToBufferMatrix.translate(0, bufferSize.height() / current.bufferScale);
surfaceToBufferMatrix.rotate(-90, 0, 0, 1);
break;
case OutputInterface::Transform::Rotated180:
case OutputInterface::Transform::Flipped180:
surfaceToBufferMatrix.translate(current.buffer->width() / current.bufferScale,
current.buffer->height() / current.bufferScale);
surfaceToBufferMatrix.translate(bufferSize.width() / current.bufferScale,
bufferSize.height() / current.bufferScale);
surfaceToBufferMatrix.rotate(-180, 0, 0, 1);
break;
case OutputInterface::Transform::Rotated270:
case OutputInterface::Transform::Flipped270:
surfaceToBufferMatrix.translate(current.buffer->width() / current.bufferScale, 0);
surfaceToBufferMatrix.translate(bufferSize.width() / current.bufferScale, 0);
surfaceToBufferMatrix.rotate(-270, 0, 0, 1);
break;
}
@ -448,12 +448,12 @@ QMatrix4x4 SurfaceInterfacePrivate::buildSurfaceToBufferMatrix()
switch (current.bufferTransform) {
case OutputInterface::Transform::Flipped:
case OutputInterface::Transform::Flipped180:
surfaceToBufferMatrix.translate(current.buffer->width() / current.bufferScale, 0);
surfaceToBufferMatrix.translate(bufferSize.width() / current.bufferScale, 0);
surfaceToBufferMatrix.scale(-1, 1);
break;
case OutputInterface::Transform::Flipped90:
case OutputInterface::Transform::Flipped270:
surfaceToBufferMatrix.translate(current.buffer->height() / current.bufferScale, 0);
surfaceToBufferMatrix.translate(bufferSize.height() / current.bufferScale, 0);
surfaceToBufferMatrix.scale(-1, 1);
break;
default:
@ -562,13 +562,11 @@ void SurfaceInterfacePrivate::applyState(SurfaceState *next)
if (bufferRef != current.buffer) {
if (bufferRef) {
QObject::disconnect(bufferRef, &BufferInterface::aboutToBeDestroyed, q, &SurfaceInterface::handleBufferRemoved);
bufferRef->unref();
}
bufferRef = current.buffer;
if (bufferRef) {
bufferRef->ref();
QObject::connect(bufferRef, &BufferInterface::aboutToBeDestroyed, q, &SurfaceInterface::handleBufferRemoved);
}
}
@ -722,7 +720,7 @@ OutputInterface::Transform SurfaceInterface::bufferTransform() const
return d->current.bufferTransform;
}
BufferInterface *SurfaceInterface::buffer()
ClientBuffer *SurfaceInterface::buffer()
{
return d->bufferRef;
}
@ -993,14 +991,6 @@ QMatrix4x4 SurfaceInterface::surfaceToBufferMatrix() const
return d->surfaceToBufferMatrix;
}
void SurfaceInterface::handleBufferRemoved()
{
if (d->bufferRef) {
d->bufferRef->unref();
d->bufferRef = nullptr;
}
}
QPointF SurfaceInterface::mapToChild(SurfaceInterface *child, const QPointF &point) const
{
QPointF local = point;

@ -18,7 +18,7 @@
namespace KWaylandServer
{
class BlurInterface;
class BufferInterface;
class ClientBuffer;
class ConfinedPointerV1Interface;
class ContrastInterface;
class CompositorInterface;
@ -40,13 +40,13 @@ class SurfaceInterfacePrivate;
* This should make interacting from the server easier, it only needs to monitor the SurfaceInterface
* and does not need to track each specific interface.
*
* The SurfaceInterface takes care of reference/unreferencing the BufferInterface attached to it.
* As long as a BufferInterface is attached, the released signal won't be sent. If the BufferInterface
* The SurfaceInterface takes care of reference/unreferencing the ClientBuffer attached to it.
* As long as a ClientBuffer is attached, the released signal won't be sent. If the ClientBuffer
* is no longer needed by the SurfaceInterface, it will get unreferenced and might be automatically
* deleted (if it's no longer referenced).
*
* @see CompositorInterface
* @see BufferInterface
* @see ClientBuffer
* @see SubSurfaceInterface
* @see BlurInterface
* @see ContrastInterface
@ -176,9 +176,9 @@ public:
*/
OutputInterface::Transform bufferTransform() const;
/**
* @returns the current BufferInterface, might be @c nullptr.
* @returns the current ClientBuffer, might be @c nullptr.
*/
BufferInterface *buffer();
ClientBuffer *buffer();
QPoint offset() const;
/**
* Returns the current size of the surface, in surface coordinates.
@ -237,9 +237,9 @@ public:
/**
* Whether the SurfaceInterface is currently considered to be mapped.
* A SurfaceInterface is mapped if it has a non-null BufferInterface attached.
* A SurfaceInterface is mapped if it has a non-null ClientBuffer attached.
* If the SurfaceInterface references a SubSurfaceInterface it is only considered
* mapped if it has a BufferInterface attached and the parent SurfaceInterface is mapped.
* mapped if it has a ClientBuffer attached and the parent SurfaceInterface is mapped.
*
* @returns Whether the SurfaceInterface is currently mapped
*/
@ -351,7 +351,7 @@ Q_SIGNALS:
/**
* Emitted whenever the SurfaceInterface got damaged.
* The signal is only emitted during the commit of state.
* A damage means that a new BufferInterface got attached.
* A damage means that a new ClientBuffer got attached.
*
* @see buffer
* @see damage
@ -426,8 +426,6 @@ Q_SIGNALS:
void committed();
private:
void handleBufferRemoved();
QScopedPointer<SurfaceInterfacePrivate> d;
friend class SurfaceInterfacePrivate;
};

@ -56,7 +56,7 @@ struct SurfaceState
OutputInterface::Transform bufferTransform = OutputInterface::Transform::Normal;
QList<KWaylandFrameCallback *> frameCallbacks;
QPoint offset = QPoint();
QPointer<BufferInterface> buffer;
QPointer<ClientBuffer> buffer;
QPointer<ShadowInterface> shadow;
QPointer<BlurInterface> blur;
QPointer<ContrastInterface> contrast;
@ -115,7 +115,7 @@ public:
QSize bufferSize;
QSize surfaceSize;
QRegion inputRegion;
BufferInterface *bufferRef = nullptr;
ClientBuffer *bufferRef = nullptr;
bool hasCacheState = false;
// workaround for https://bugreports.qt.io/browse/QTBUG-52192

@ -3,7 +3,7 @@
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "../src/server/buffer_interface.h"
#include "../src/server/shmclientbuffer.h"
#include "../src/server/compositor_interface.h"
#include "../src/server/datadevicemanager_interface.h"
#include "../src/server/display.h"
@ -154,8 +154,10 @@ void CompositorWindow::paintEvent(QPaintEvent *event)
if (!surface || !surface->isMapped()) {
continue;
}
KWaylandServer::BufferInterface *buffer = surface->buffer();
p.drawImage(QPoint(0, 0), buffer->data());
auto clientBuffer = qobject_cast<KWaylandServer::ShmClientBuffer *>(surface->buffer());
if (clientBuffer) {
p.drawImage(QPoint(0, 0), clientBuffer->data());
}
surface->frameRendered(QDateTime::currentMSecsSinceEpoch());
}
}

Loading…
Cancel
Save