wayland: Rewrite wl-shm implementation
The main motivation for the rewrite is to properly port ShmClientBuffer to our GraphicsBuffer abstractions. As is, libwayland implementation is not suitable for our needs. We may need ShmClientBuffer to be alive when its corresponding wl_shm_buffer resource is destroyed, for example to play a window closing animation. With the existing api, we need to fight libwayland. Besides that, libwayland doesn't provide a way to get underlying pool file descriptor, which is needed to fill ShmAttributes.
This commit is contained in:
parent
f1d6366d4a
commit
6f3cb932df
13 changed files with 422 additions and 333 deletions
|
@ -8,6 +8,7 @@
|
||||||
*/
|
*/
|
||||||
#include "debug_console.h"
|
#include "debug_console.h"
|
||||||
#include "composite.h"
|
#include "composite.h"
|
||||||
|
#include "core/graphicsbufferview.h"
|
||||||
#include "core/inputdevice.h"
|
#include "core/inputdevice.h"
|
||||||
#include "input_event.h"
|
#include "input_event.h"
|
||||||
#include "internalwindow.h"
|
#include "internalwindow.h"
|
||||||
|
@ -25,7 +26,6 @@
|
||||||
#include "wayland/display.h"
|
#include "wayland/display.h"
|
||||||
#include "wayland/primaryselectionsource_v1_interface.h"
|
#include "wayland/primaryselectionsource_v1_interface.h"
|
||||||
#include "wayland/seat_interface.h"
|
#include "wayland/seat_interface.h"
|
||||||
#include "wayland/shmclientbuffer.h"
|
|
||||||
#include "wayland/subcompositor_interface.h"
|
#include "wayland/subcompositor_interface.h"
|
||||||
#include "wayland/surface_interface.h"
|
#include "wayland/surface_interface.h"
|
||||||
#include "wayland_server.h"
|
#include "wayland_server.h"
|
||||||
|
@ -1438,10 +1438,14 @@ QVariant SurfaceTreeModel::data(const QModelIndex &index, int role) const
|
||||||
if (role == Qt::DisplayRole || role == Qt::ToolTipRole) {
|
if (role == Qt::DisplayRole || role == Qt::ToolTipRole) {
|
||||||
return QStringLiteral("%1 (%2) - %3").arg(surface->client()->executablePath()).arg(surface->client()->processId()).arg(surface->id());
|
return QStringLiteral("%1 (%2) - %3").arg(surface->client()->executablePath()).arg(surface->client()->processId()).arg(surface->id());
|
||||||
} else if (role == Qt::DecorationRole) {
|
} else if (role == Qt::DecorationRole) {
|
||||||
if (auto buffer = qobject_cast<KWaylandServer::ShmClientBuffer *>(surface->buffer())) {
|
if (surface->buffer()) {
|
||||||
return buffer->data().scaled(QSize(64, 64), Qt::KeepAspectRatio);
|
const GraphicsBufferView view(surface->buffer());
|
||||||
|
if (const QImage *image = view.image()) {
|
||||||
|
return image->scaled(QSize(64, 64), Qt::KeepAspectRatio);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return QImage();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,11 +5,11 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "platformsupport/scenes/opengl/basiceglsurfacetexture_wayland.h"
|
#include "platformsupport/scenes/opengl/basiceglsurfacetexture_wayland.h"
|
||||||
|
#include "core/graphicsbufferview.h"
|
||||||
#include "libkwineffects/kwingltexture.h"
|
#include "libkwineffects/kwingltexture.h"
|
||||||
#include "platformsupport/scenes/opengl/abstract_egl_backend.h"
|
#include "platformsupport/scenes/opengl/abstract_egl_backend.h"
|
||||||
#include "scene/surfaceitem_wayland.h"
|
#include "scene/surfaceitem_wayland.h"
|
||||||
#include "utils/common.h"
|
#include "utils/common.h"
|
||||||
#include "wayland/shmclientbuffer.h"
|
|
||||||
|
|
||||||
#include <epoxy/egl.h>
|
#include <epoxy/egl.h>
|
||||||
|
|
||||||
|
@ -36,8 +36,8 @@ bool BasicEGLSurfaceTextureWayland::create()
|
||||||
{
|
{
|
||||||
if (m_pixmap->buffer()->dmabufAttributes()) {
|
if (m_pixmap->buffer()->dmabufAttributes()) {
|
||||||
return loadDmabufTexture(m_pixmap->buffer());
|
return loadDmabufTexture(m_pixmap->buffer());
|
||||||
} else if (auto buffer = qobject_cast<KWaylandServer::ShmClientBuffer *>(m_pixmap->buffer())) {
|
} else if (m_pixmap->buffer()->shmAttributes()) {
|
||||||
return loadShmTexture(buffer);
|
return loadShmTexture(m_pixmap->buffer());
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -53,17 +53,23 @@ void BasicEGLSurfaceTextureWayland::update(const QRegion ®ion)
|
||||||
{
|
{
|
||||||
if (m_pixmap->buffer()->dmabufAttributes()) {
|
if (m_pixmap->buffer()->dmabufAttributes()) {
|
||||||
updateDmabufTexture(m_pixmap->buffer());
|
updateDmabufTexture(m_pixmap->buffer());
|
||||||
} else if (auto buffer = qobject_cast<KWaylandServer::ShmClientBuffer *>(m_pixmap->buffer())) {
|
} else if (m_pixmap->buffer()->shmAttributes()) {
|
||||||
updateShmTexture(buffer, region);
|
updateShmTexture(m_pixmap->buffer(), region);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BasicEGLSurfaceTextureWayland::loadShmTexture(KWaylandServer::ShmClientBuffer *buffer)
|
bool BasicEGLSurfaceTextureWayland::loadShmTexture(GraphicsBuffer *buffer)
|
||||||
{
|
{
|
||||||
m_texture = GLTexture::upload(buffer->data());
|
const GraphicsBufferView view(buffer);
|
||||||
if (!m_texture) {
|
if (Q_UNLIKELY(!view.image())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_texture = GLTexture::upload(*view.image());
|
||||||
|
if (Q_UNLIKELY(!m_texture)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
m_texture->setFilter(GL_LINEAR);
|
m_texture->setFilter(GL_LINEAR);
|
||||||
m_texture->setWrapMode(GL_CLAMP_TO_EDGE);
|
m_texture->setWrapMode(GL_CLAMP_TO_EDGE);
|
||||||
m_texture->setContentTransform(TextureTransform::MirrorY);
|
m_texture->setContentTransform(TextureTransform::MirrorY);
|
||||||
|
@ -72,7 +78,7 @@ bool BasicEGLSurfaceTextureWayland::loadShmTexture(KWaylandServer::ShmClientBuff
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BasicEGLSurfaceTextureWayland::updateShmTexture(KWaylandServer::ShmClientBuffer *buffer, const QRegion ®ion)
|
void BasicEGLSurfaceTextureWayland::updateShmTexture(GraphicsBuffer *buffer, const QRegion ®ion)
|
||||||
{
|
{
|
||||||
if (Q_UNLIKELY(m_bufferType != BufferType::Shm)) {
|
if (Q_UNLIKELY(m_bufferType != BufferType::Shm)) {
|
||||||
destroy();
|
destroy();
|
||||||
|
@ -80,14 +86,14 @@ void BasicEGLSurfaceTextureWayland::updateShmTexture(KWaylandServer::ShmClientBu
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QImage &image = buffer->data();
|
const GraphicsBufferView view(buffer);
|
||||||
if (Q_UNLIKELY(image.isNull())) {
|
if (Q_UNLIKELY(!view.image())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QRegion damage = mapRegion(m_pixmap->item()->surfaceToBufferMatrix(), region);
|
const QRegion damage = mapRegion(m_pixmap->item()->surfaceToBufferMatrix(), region);
|
||||||
for (const QRect &rect : damage) {
|
for (const QRect &rect : damage) {
|
||||||
m_texture->update(image, rect.topLeft(), rect);
|
m_texture->update(*view.image(), rect.topLeft(), rect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,11 +8,6 @@
|
||||||
|
|
||||||
#include "openglsurfacetexture_wayland.h"
|
#include "openglsurfacetexture_wayland.h"
|
||||||
|
|
||||||
namespace KWaylandServer
|
|
||||||
{
|
|
||||||
class ShmClientBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace KWin
|
namespace KWin
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -31,8 +26,8 @@ public:
|
||||||
void update(const QRegion ®ion) override;
|
void update(const QRegion ®ion) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool loadShmTexture(KWaylandServer::ShmClientBuffer *buffer);
|
bool loadShmTexture(GraphicsBuffer *buffer);
|
||||||
void updateShmTexture(KWaylandServer::ShmClientBuffer *buffer, const QRegion ®ion);
|
void updateShmTexture(GraphicsBuffer *buffer, const QRegion ®ion);
|
||||||
bool loadDmabufTexture(GraphicsBuffer *buffer);
|
bool loadDmabufTexture(GraphicsBuffer *buffer);
|
||||||
void updateDmabufTexture(GraphicsBuffer *buffer);
|
void updateDmabufTexture(GraphicsBuffer *buffer);
|
||||||
void destroy();
|
void destroy();
|
||||||
|
|
|
@ -5,9 +5,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "qpaintersurfacetexture_wayland.h"
|
#include "qpaintersurfacetexture_wayland.h"
|
||||||
|
#include "core/graphicsbufferview.h"
|
||||||
#include "scene/surfaceitem_wayland.h"
|
#include "scene/surfaceitem_wayland.h"
|
||||||
#include "utils/common.h"
|
#include "utils/common.h"
|
||||||
#include "wayland/shmclientbuffer.h"
|
|
||||||
#include "wayland/surface_interface.h"
|
#include "wayland/surface_interface.h"
|
||||||
|
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
|
@ -24,23 +24,22 @@ QPainterSurfaceTextureWayland::QPainterSurfaceTextureWayland(QPainterBackend *ba
|
||||||
|
|
||||||
bool QPainterSurfaceTextureWayland::create()
|
bool QPainterSurfaceTextureWayland::create()
|
||||||
{
|
{
|
||||||
auto buffer = qobject_cast<KWaylandServer::ShmClientBuffer *>(m_pixmap->buffer());
|
const GraphicsBufferView view(m_pixmap->buffer());
|
||||||
if (Q_LIKELY(buffer)) {
|
if (Q_LIKELY(view.image())) {
|
||||||
// The buffer data is copied as the buffer interface returns a QImage
|
// The buffer data is copied as the buffer interface returns a QImage
|
||||||
// which doesn't own the data of the underlying wl_shm_buffer object.
|
// which doesn't own the data of the underlying wl_shm_buffer object.
|
||||||
m_image = buffer->data().copy();
|
m_image = view.image()->copy();
|
||||||
}
|
}
|
||||||
return !m_image.isNull();
|
return !m_image.isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
void QPainterSurfaceTextureWayland::update(const QRegion ®ion)
|
void QPainterSurfaceTextureWayland::update(const QRegion ®ion)
|
||||||
{
|
{
|
||||||
auto buffer = qobject_cast<KWaylandServer::ShmClientBuffer *>(m_pixmap->buffer());
|
const GraphicsBufferView view(m_pixmap->buffer());
|
||||||
if (Q_UNLIKELY(!buffer)) {
|
if (Q_UNLIKELY(!view.image())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QImage image = buffer->data();
|
|
||||||
const QRegion dirtyRegion = mapRegion(m_pixmap->item()->surfaceToBufferMatrix(), region);
|
const QRegion dirtyRegion = mapRegion(m_pixmap->item()->surfaceToBufferMatrix(), region);
|
||||||
QPainter painter(&m_image);
|
QPainter painter(&m_image);
|
||||||
painter.setCompositionMode(QPainter::CompositionMode_Source);
|
painter.setCompositionMode(QPainter::CompositionMode_Source);
|
||||||
|
@ -48,7 +47,7 @@ void QPainterSurfaceTextureWayland::update(const QRegion ®ion)
|
||||||
// The buffer data is copied as the buffer interface returns a QImage
|
// The buffer data is copied as the buffer interface returns a QImage
|
||||||
// which doesn't own the data of the underlying wl_shm_buffer object.
|
// which doesn't own the data of the underlying wl_shm_buffer object.
|
||||||
for (const QRect &rect : dirtyRegion) {
|
for (const QRect &rect : dirtyRegion) {
|
||||||
painter.drawImage(rect, image, rect);
|
painter.drawImage(rect, *view.image(), rect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,9 +10,9 @@
|
||||||
#include "shadow.h"
|
#include "shadow.h"
|
||||||
// kwin
|
// kwin
|
||||||
#include "atoms.h"
|
#include "atoms.h"
|
||||||
|
#include "core/graphicsbufferview.h"
|
||||||
#include "internalwindow.h"
|
#include "internalwindow.h"
|
||||||
#include "wayland/shadow_interface.h"
|
#include "wayland/shadow_interface.h"
|
||||||
#include "wayland/shmclientbuffer.h"
|
|
||||||
#include "wayland/surface_interface.h"
|
#include "wayland/surface_interface.h"
|
||||||
#include "wayland_server.h"
|
#include "wayland_server.h"
|
||||||
#include "x11window.h"
|
#include "x11window.h"
|
||||||
|
@ -201,9 +201,11 @@ bool Shadow::init(KDecoration2::Decoration *decoration)
|
||||||
|
|
||||||
static QImage shadowTileForBuffer(GraphicsBuffer *buffer)
|
static QImage shadowTileForBuffer(GraphicsBuffer *buffer)
|
||||||
{
|
{
|
||||||
auto shmBuffer = qobject_cast<KWaylandServer::ShmClientBuffer *>(buffer);
|
if (buffer) {
|
||||||
if (shmBuffer) {
|
const GraphicsBufferView view(buffer);
|
||||||
return shmBuffer->data().copy();
|
if (const QImage *image = view.image()) {
|
||||||
|
return image->copy();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return QImage();
|
return QImage();
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,10 +14,10 @@
|
||||||
#include "KWayland/Client/shm_pool.h"
|
#include "KWayland/Client/shm_pool.h"
|
||||||
#include "KWayland/Client/surface.h"
|
#include "KWayland/Client/surface.h"
|
||||||
// server
|
// server
|
||||||
|
#include "core/graphicsbufferview.h"
|
||||||
#include "wayland/compositor_interface.h"
|
#include "wayland/compositor_interface.h"
|
||||||
#include "wayland/display.h"
|
#include "wayland/display.h"
|
||||||
#include "wayland/shadow_interface.h"
|
#include "wayland/shadow_interface.h"
|
||||||
#include "wayland/shmclientbuffer.h"
|
|
||||||
|
|
||||||
using namespace KWaylandServer;
|
using namespace KWaylandServer;
|
||||||
|
|
||||||
|
@ -168,9 +168,11 @@ void ShadowTest::testCreateShadow()
|
||||||
|
|
||||||
static QImage bufferToImage(KWin::GraphicsBuffer *clientBuffer)
|
static QImage bufferToImage(KWin::GraphicsBuffer *clientBuffer)
|
||||||
{
|
{
|
||||||
auto shmBuffer = qobject_cast<ShmClientBuffer *>(clientBuffer);
|
if (clientBuffer) {
|
||||||
if (shmBuffer) {
|
KWin::GraphicsBufferView view(clientBuffer);
|
||||||
return shmBuffer->data();
|
if (QImage *image = view.image()) {
|
||||||
|
return image->copy();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return QImage();
|
return QImage();
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
#include "wayland/pointergestures_v1_interface.h"
|
#include "wayland/pointergestures_v1_interface.h"
|
||||||
#include "wayland/relativepointer_v1_interface.h"
|
#include "wayland/relativepointer_v1_interface.h"
|
||||||
#include "wayland/seat_interface.h"
|
#include "wayland/seat_interface.h"
|
||||||
#include "wayland/shmclientbuffer.h"
|
|
||||||
#include "wayland/subcompositor_interface.h"
|
#include "wayland/subcompositor_interface.h"
|
||||||
#include "wayland/surface_interface.h"
|
#include "wayland/surface_interface.h"
|
||||||
|
|
||||||
|
@ -1351,7 +1350,6 @@ void TestWaylandSeat::testCursor()
|
||||||
QCOMPARE(cursorChangedSpy.count(), 3);
|
QCOMPARE(cursorChangedSpy.count(), 3);
|
||||||
QCOMPARE(cursor->hotspot(), QPoint(1, 2));
|
QCOMPARE(cursor->hotspot(), QPoint(1, 2));
|
||||||
QVERIFY(cursor->surface());
|
QVERIFY(cursor->surface());
|
||||||
QCOMPARE(qobject_cast<ShmClientBuffer *>(cursor->surface()->buffer())->data(), img);
|
|
||||||
|
|
||||||
p->hideCursor();
|
p->hideCursor();
|
||||||
QVERIFY(cursorChangedSpy.wait());
|
QVERIFY(cursorChangedSpy.wait());
|
||||||
|
|
|
@ -8,11 +8,12 @@
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QtTest>
|
#include <QtTest>
|
||||||
// KWin
|
// KWin
|
||||||
|
#include "core/graphicsbuffer.h"
|
||||||
|
#include "core/graphicsbufferview.h"
|
||||||
#include "wayland/compositor_interface.h"
|
#include "wayland/compositor_interface.h"
|
||||||
#include "wayland/display.h"
|
#include "wayland/display.h"
|
||||||
#include "wayland/idleinhibit_v1_interface.h"
|
#include "wayland/idleinhibit_v1_interface.h"
|
||||||
#include "wayland/output_interface.h"
|
#include "wayland/output_interface.h"
|
||||||
#include "wayland/shmclientbuffer.h"
|
|
||||||
#include "wayland/surface_interface.h"
|
#include "wayland/surface_interface.h"
|
||||||
|
|
||||||
#include "KWayland/Client/compositor.h"
|
#include "KWayland/Client/compositor.h"
|
||||||
|
@ -43,7 +44,6 @@ private Q_SLOTS:
|
||||||
void testDamage();
|
void testDamage();
|
||||||
void testFrameCallback();
|
void testFrameCallback();
|
||||||
void testAttachBuffer();
|
void testAttachBuffer();
|
||||||
void testMultipleSurfaces();
|
|
||||||
void testOpaque();
|
void testOpaque();
|
||||||
void testInput();
|
void testInput();
|
||||||
void testScale();
|
void testScale();
|
||||||
|
@ -397,11 +397,13 @@ void TestWaylandSurface::testAttachBuffer()
|
||||||
// now the ServerSurface should have the black image attached as a buffer
|
// now the ServerSurface should have the black image attached as a buffer
|
||||||
KWin::GraphicsBuffer *buffer = serverSurface->buffer();
|
KWin::GraphicsBuffer *buffer = serverSurface->buffer();
|
||||||
buffer->ref();
|
buffer->ref();
|
||||||
auto shmBuffer = qobject_cast<KWaylandServer::ShmClientBuffer *>(buffer);
|
{
|
||||||
QVERIFY(shmBuffer);
|
KWin::GraphicsBufferView view(buffer);
|
||||||
QCOMPARE(shmBuffer->data(), black);
|
QVERIFY(view.image());
|
||||||
QCOMPARE(shmBuffer->data().format(), QImage::Format_RGB32);
|
QCOMPARE(*view.image(), black);
|
||||||
QCOMPARE(shmBuffer->size(), QSize(24, 24));
|
QCOMPARE(view.image()->format(), QImage::Format_RGB32);
|
||||||
|
QCOMPARE(view.image()->size(), QSize(24, 24));
|
||||||
|
}
|
||||||
|
|
||||||
// render another frame
|
// render another frame
|
||||||
s->attachBuffer(redBuffer);
|
s->attachBuffer(redBuffer);
|
||||||
|
@ -413,14 +415,16 @@ void TestWaylandSurface::testAttachBuffer()
|
||||||
QVERIFY(unmappedSpy.isEmpty());
|
QVERIFY(unmappedSpy.isEmpty());
|
||||||
KWin::GraphicsBuffer *buffer2 = serverSurface->buffer();
|
KWin::GraphicsBuffer *buffer2 = serverSurface->buffer();
|
||||||
buffer2->ref();
|
buffer2->ref();
|
||||||
auto shmBuffer2 = qobject_cast<KWaylandServer::ShmClientBuffer *>(buffer2);
|
{
|
||||||
QVERIFY(shmBuffer2);
|
KWin::GraphicsBufferView view(buffer2);
|
||||||
QCOMPARE(shmBuffer2->data().format(), QImage::Format_ARGB32_Premultiplied);
|
QVERIFY(view.image());
|
||||||
QCOMPARE(shmBuffer2->size(), QSize(24, 24));
|
QCOMPARE(view.image()->format(), QImage::Format_ARGB32_Premultiplied);
|
||||||
|
QCOMPARE(view.image()->size(), QSize(24, 24));
|
||||||
for (int i = 0; i < 24; ++i) {
|
for (int i = 0; i < 24; ++i) {
|
||||||
for (int j = 0; j < 24; ++j) {
|
for (int j = 0; j < 24; ++j) {
|
||||||
// it's premultiplied in the format
|
// it's premultiplied in the format
|
||||||
QCOMPARE(shmBuffer2->data().pixel(i, j), qRgba(128, 0, 0, 128));
|
QCOMPARE(view.image()->pixel(i, j), qRgba(128, 0, 0, 128));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
buffer2->unref();
|
buffer2->unref();
|
||||||
|
@ -448,14 +452,16 @@ void TestWaylandSurface::testAttachBuffer()
|
||||||
|
|
||||||
KWin::GraphicsBuffer *buffer3 = serverSurface->buffer();
|
KWin::GraphicsBuffer *buffer3 = serverSurface->buffer();
|
||||||
buffer3->ref();
|
buffer3->ref();
|
||||||
auto shmBuffer3 = qobject_cast<KWaylandServer::ShmClientBuffer *>(buffer3);
|
{
|
||||||
QVERIFY(shmBuffer3);
|
KWin::GraphicsBufferView view(buffer3);
|
||||||
QCOMPARE(shmBuffer3->data().format(), QImage::Format_ARGB32_Premultiplied);
|
QVERIFY(view.image());
|
||||||
QCOMPARE(shmBuffer3->size(), QSize(24, 24));
|
QCOMPARE(view.image()->format(), QImage::Format_ARGB32_Premultiplied);
|
||||||
|
QCOMPARE(view.image()->size(), QSize(24, 24));
|
||||||
for (int i = 0; i < 24; ++i) {
|
for (int i = 0; i < 24; ++i) {
|
||||||
for (int j = 0; j < 24; ++j) {
|
for (int j = 0; j < 24; ++j) {
|
||||||
// it's premultiplied in the format
|
// it's premultiplied in the format
|
||||||
QCOMPARE(shmBuffer3->data().pixel(i, j), qRgba(0, 0, 128, 128));
|
QCOMPARE(view.image()->pixel(i, j), qRgba(0, 0, 128, 128));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
buffer3->unref();
|
buffer3->unref();
|
||||||
|
@ -495,94 +501,6 @@ void TestWaylandSurface::testAttachBuffer()
|
||||||
buffer->unref();
|
buffer->unref();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestWaylandSurface::testMultipleSurfaces()
|
|
||||||
{
|
|
||||||
using namespace KWaylandServer;
|
|
||||||
KWayland::Client::Registry registry;
|
|
||||||
registry.setEventQueue(m_queue);
|
|
||||||
QSignalSpy shmSpy(®istry, &KWayland::Client::Registry::shmAnnounced);
|
|
||||||
registry.create(m_connection->display());
|
|
||||||
QVERIFY(registry.isValid());
|
|
||||||
registry.setup();
|
|
||||||
QVERIFY(shmSpy.wait());
|
|
||||||
|
|
||||||
KWayland::Client::ShmPool pool1;
|
|
||||||
KWayland::Client::ShmPool pool2;
|
|
||||||
pool1.setup(registry.bindShm(shmSpy.first().first().value<quint32>(), shmSpy.first().last().value<quint32>()));
|
|
||||||
pool2.setup(registry.bindShm(shmSpy.first().first().value<quint32>(), shmSpy.first().last().value<quint32>()));
|
|
||||||
QVERIFY(pool1.isValid());
|
|
||||||
QVERIFY(pool2.isValid());
|
|
||||||
|
|
||||||
// create the surfaces
|
|
||||||
QSignalSpy serverSurfaceCreated(m_compositorInterface, &KWaylandServer::CompositorInterface::surfaceCreated);
|
|
||||||
std::unique_ptr<KWayland::Client::Surface> s1(m_compositor->createSurface());
|
|
||||||
QVERIFY(serverSurfaceCreated.wait());
|
|
||||||
SurfaceInterface *serverSurface1 = serverSurfaceCreated.first().first().value<KWaylandServer::SurfaceInterface *>();
|
|
||||||
QVERIFY(serverSurface1);
|
|
||||||
// second surface
|
|
||||||
std::unique_ptr<KWayland::Client::Surface> s2(m_compositor->createSurface());
|
|
||||||
QVERIFY(serverSurfaceCreated.wait());
|
|
||||||
SurfaceInterface *serverSurface2 = serverSurfaceCreated.last().first().value<KWaylandServer::SurfaceInterface *>();
|
|
||||||
QVERIFY(serverSurface2);
|
|
||||||
QVERIFY(serverSurface1->resource() != serverSurface2->resource());
|
|
||||||
|
|
||||||
// create two images
|
|
||||||
QImage black(24, 24, QImage::Format_RGB32);
|
|
||||||
black.fill(Qt::black);
|
|
||||||
QImage red(24, 24, QImage::Format_ARGB32_Premultiplied);
|
|
||||||
red.fill(QColor(255, 0, 0, 128));
|
|
||||||
|
|
||||||
auto blackBuffer = pool1.createBuffer(black);
|
|
||||||
auto redBuffer = pool2.createBuffer(red);
|
|
||||||
|
|
||||||
s1->attachBuffer(blackBuffer);
|
|
||||||
s1->damage(QRect(0, 0, 24, 24));
|
|
||||||
s1->commit(KWayland::Client::Surface::CommitFlag::None);
|
|
||||||
QSignalSpy damageSpy1(serverSurface1, &KWaylandServer::SurfaceInterface::damaged);
|
|
||||||
QVERIFY(damageSpy1.wait());
|
|
||||||
|
|
||||||
// now the ServerSurface should have the black image attached as a buffer
|
|
||||||
KWin::GraphicsBuffer *buffer1 = serverSurface1->buffer();
|
|
||||||
QVERIFY(buffer1);
|
|
||||||
QImage buffer1Data = qobject_cast<ShmClientBuffer *>(buffer1)->data();
|
|
||||||
QCOMPARE(buffer1Data, black);
|
|
||||||
// accessing the same buffer is OK
|
|
||||||
QImage buffer1Data2 = qobject_cast<ShmClientBuffer *>(buffer1)->data();
|
|
||||||
QCOMPARE(buffer1Data2, buffer1Data);
|
|
||||||
buffer1Data = QImage();
|
|
||||||
QVERIFY(buffer1Data.isNull());
|
|
||||||
buffer1Data2 = QImage();
|
|
||||||
QVERIFY(buffer1Data2.isNull());
|
|
||||||
|
|
||||||
// attach a buffer for the other surface
|
|
||||||
s2->attachBuffer(redBuffer);
|
|
||||||
s2->damage(QRect(0, 0, 24, 24));
|
|
||||||
s2->commit(KWayland::Client::Surface::CommitFlag::None);
|
|
||||||
QSignalSpy damageSpy2(serverSurface2, &KWaylandServer::SurfaceInterface::damaged);
|
|
||||||
QVERIFY(damageSpy2.wait());
|
|
||||||
|
|
||||||
KWin::GraphicsBuffer *buffer2 = serverSurface2->buffer();
|
|
||||||
QVERIFY(buffer2);
|
|
||||||
QImage buffer2Data = qobject_cast<ShmClientBuffer *>(buffer2)->data();
|
|
||||||
QCOMPARE(buffer2Data, red);
|
|
||||||
|
|
||||||
// while buffer2 is accessed we cannot access buffer1
|
|
||||||
buffer1Data = qobject_cast<ShmClientBuffer *>(buffer1)->data();
|
|
||||||
QVERIFY(buffer1Data.isNull());
|
|
||||||
|
|
||||||
// a deep copy can be kept around
|
|
||||||
QImage deepCopy = buffer2Data.copy();
|
|
||||||
QCOMPARE(deepCopy, red);
|
|
||||||
buffer2Data = QImage();
|
|
||||||
QVERIFY(buffer2Data.isNull());
|
|
||||||
QCOMPARE(deepCopy, red);
|
|
||||||
|
|
||||||
// now that buffer2Data is destroyed we can access buffer1 again
|
|
||||||
buffer1Data = qobject_cast<ShmClientBuffer *>(buffer1)->data();
|
|
||||||
QVERIFY(!buffer1Data.isNull());
|
|
||||||
QCOMPARE(buffer1Data, black);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestWaylandSurface::testOpaque()
|
void TestWaylandSurface::testOpaque()
|
||||||
{
|
{
|
||||||
using namespace KWaylandServer;
|
using namespace KWaylandServer;
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
#include "display_p.h"
|
#include "display_p.h"
|
||||||
#include "linuxdmabufv1clientbuffer_p.h"
|
#include "linuxdmabufv1clientbuffer_p.h"
|
||||||
#include "output_interface.h"
|
#include "output_interface.h"
|
||||||
#include "shmclientbuffer.h"
|
#include "shmclientbuffer_p.h"
|
||||||
#include "utils/common.h"
|
#include "utils/common.h"
|
||||||
|
|
||||||
#include <QAbstractEventDispatcher>
|
#include <QAbstractEventDispatcher>
|
||||||
|
|
|
@ -4,207 +4,312 @@
|
||||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "shmclientbuffer.h"
|
#include "config-kwin.h"
|
||||||
#include "display.h"
|
|
||||||
|
|
||||||
#include <QHash>
|
#include "wayland/shmclientbuffer.h"
|
||||||
#include <QImage>
|
#include "wayland/display.h"
|
||||||
|
#include "wayland/shmclientbuffer_p.h"
|
||||||
|
|
||||||
#include <wayland-server-core.h>
|
#include <drm_fourcc.h>
|
||||||
#include <wayland-server-protocol.h>
|
#include <fcntl.h>
|
||||||
|
#include <mutex>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
using namespace KWin;
|
||||||
|
|
||||||
namespace KWaylandServer
|
namespace KWaylandServer
|
||||||
{
|
{
|
||||||
static const ShmClientBuffer *s_accessedBuffer = nullptr;
|
|
||||||
static int s_accessCounter = 0;
|
|
||||||
static QHash<wl_resource *, ShmClientBuffer *> s_buffers;
|
|
||||||
|
|
||||||
class ShmClientBufferPrivate
|
static constexpr int s_version = 1;
|
||||||
|
|
||||||
|
static constexpr uint32_t s_formats[] = {
|
||||||
|
WL_SHM_FORMAT_ARGB8888,
|
||||||
|
WL_SHM_FORMAT_XRGB8888,
|
||||||
|
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
|
||||||
|
WL_SHM_FORMAT_ARGB2101010,
|
||||||
|
WL_SHM_FORMAT_XRGB2101010,
|
||||||
|
WL_SHM_FORMAT_ABGR2101010,
|
||||||
|
WL_SHM_FORMAT_XBGR2101010,
|
||||||
|
WL_SHM_FORMAT_ABGR16161616,
|
||||||
|
WL_SHM_FORMAT_XBGR16161616,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
class ShmSigbusData
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ShmClientBufferPrivate(ShmClientBuffer *q);
|
ShmPool *pool = nullptr;
|
||||||
|
int accessCount = 0;
|
||||||
static void buffer_destroy_callback(wl_listener *listener, void *data);
|
|
||||||
|
|
||||||
ShmClientBuffer *q;
|
|
||||||
wl_resource *resource = nullptr;
|
|
||||||
QImage::Format format = QImage::Format_Invalid;
|
|
||||||
uint32_t width = 0;
|
|
||||||
uint32_t height = 0;
|
|
||||||
bool hasAlphaChannel = false;
|
|
||||||
QImage savedData;
|
|
||||||
|
|
||||||
struct DestroyListener
|
|
||||||
{
|
|
||||||
wl_listener listener;
|
|
||||||
ShmClientBufferPrivate *receiver;
|
|
||||||
};
|
|
||||||
DestroyListener destroyListener;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ShmClientBufferPrivate::ShmClientBufferPrivate(ShmClientBuffer *q)
|
static thread_local ShmSigbusData sigbusData;
|
||||||
: q(q)
|
static struct sigaction prevSigbusAction;
|
||||||
|
|
||||||
|
static uint32_t shmFormatToDrmFormat(uint32_t shmFormat)
|
||||||
{
|
{
|
||||||
}
|
switch (shmFormat) {
|
||||||
|
|
||||||
static void cleanupShmPool(void *poolHandle)
|
|
||||||
{
|
|
||||||
wl_shm_pool_unref(static_cast<wl_shm_pool *>(poolHandle));
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShmClientBufferPrivate::buffer_destroy_callback(wl_listener *listener, void *data)
|
|
||||||
{
|
|
||||||
auto bufferPrivate = reinterpret_cast<ShmClientBufferPrivate::DestroyListener *>(listener)->receiver;
|
|
||||||
wl_shm_buffer *buffer = wl_shm_buffer_get(bufferPrivate->resource);
|
|
||||||
wl_shm_pool *pool = wl_shm_buffer_ref_pool(buffer);
|
|
||||||
|
|
||||||
s_buffers.remove(bufferPrivate->resource);
|
|
||||||
|
|
||||||
wl_list_remove(&bufferPrivate->destroyListener.listener.link);
|
|
||||||
wl_list_init(&bufferPrivate->destroyListener.listener.link);
|
|
||||||
|
|
||||||
bufferPrivate->resource = nullptr;
|
|
||||||
bufferPrivate->savedData = QImage(static_cast<const uchar *>(wl_shm_buffer_get_data(buffer)),
|
|
||||||
bufferPrivate->width,
|
|
||||||
bufferPrivate->height,
|
|
||||||
wl_shm_buffer_get_stride(buffer),
|
|
||||||
bufferPrivate->format,
|
|
||||||
cleanupShmPool,
|
|
||||||
pool);
|
|
||||||
|
|
||||||
bufferPrivate->q->drop();
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool alphaChannelFromFormat(uint32_t format)
|
|
||||||
{
|
|
||||||
switch (format) {
|
|
||||||
case WL_SHM_FORMAT_ABGR16161616:
|
|
||||||
case WL_SHM_FORMAT_ABGR2101010:
|
|
||||||
case WL_SHM_FORMAT_ARGB2101010:
|
|
||||||
case WL_SHM_FORMAT_ARGB8888:
|
case WL_SHM_FORMAT_ARGB8888:
|
||||||
return true;
|
return DRM_FORMAT_ARGB8888;
|
||||||
case WL_SHM_FORMAT_XBGR16161616:
|
|
||||||
case WL_SHM_FORMAT_XBGR2101010:
|
|
||||||
case WL_SHM_FORMAT_XRGB2101010:
|
|
||||||
case WL_SHM_FORMAT_XRGB8888:
|
case WL_SHM_FORMAT_XRGB8888:
|
||||||
|
return DRM_FORMAT_XRGB8888;
|
||||||
default:
|
default:
|
||||||
return false;
|
return shmFormat; // other wl_shm formats match the drm formats
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static QImage::Format imageFormatForShmFormat(uint32_t format)
|
ShmPool::ShmPool(ShmClientBufferIntegration *integration, wl_client *client, int id, uint32_t version, FileDescriptor &&fd, MemoryMap &&mapping)
|
||||||
|
: QtWaylandServer::wl_shm_pool(client, id, version)
|
||||||
|
, integration(integration)
|
||||||
|
, mapping(std::move(mapping))
|
||||||
|
, fd(std::move(fd))
|
||||||
{
|
{
|
||||||
switch (format) {
|
#if HAVE_MEMFD
|
||||||
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
|
const int seals = fcntl(this->fd.get(), F_GET_SEALS);
|
||||||
case WL_SHM_FORMAT_ABGR16161616:
|
if (seals != -1) {
|
||||||
return QImage::Format_RGBA64_Premultiplied;
|
struct stat statbuf;
|
||||||
case WL_SHM_FORMAT_XBGR16161616:
|
if ((seals & F_SEAL_SHRINK) && fstat(this->fd.get(), &statbuf) >= 0) {
|
||||||
return QImage::Format_RGBX64;
|
sigbusImpossible = statbuf.st_size >= this->mapping.size();
|
||||||
case WL_SHM_FORMAT_ARGB2101010:
|
}
|
||||||
return QImage::Format_A2RGB30_Premultiplied;
|
}
|
||||||
case WL_SHM_FORMAT_XRGB2101010:
|
|
||||||
return QImage::Format_RGB30;
|
|
||||||
case WL_SHM_FORMAT_ABGR2101010:
|
|
||||||
return QImage::Format_A2BGR30_Premultiplied;
|
|
||||||
case WL_SHM_FORMAT_XBGR2101010:
|
|
||||||
return QImage::Format_BGR30;
|
|
||||||
#endif
|
#endif
|
||||||
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)
|
void ShmPool::ref()
|
||||||
: d(std::make_unique<ShmClientBufferPrivate>(this))
|
|
||||||
{
|
{
|
||||||
wl_shm_buffer *buffer = wl_shm_buffer_get(resource);
|
++refCount;
|
||||||
d->resource = 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));
|
|
||||||
|
|
||||||
d->destroyListener.receiver = d.get();
|
void ShmPool::unref()
|
||||||
d->destroyListener.listener.notify = ShmClientBufferPrivate::buffer_destroy_callback;
|
{
|
||||||
wl_resource_add_destroy_listener(resource, &d->destroyListener.listener);
|
--refCount;
|
||||||
|
if (refCount == 0) {
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShmPool::shm_pool_destroy_resource(Resource *resource)
|
||||||
|
{
|
||||||
|
unref();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShmPool::shm_pool_destroy(Resource *resource)
|
||||||
|
{
|
||||||
|
wl_resource_destroy(resource->handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShmPool::shm_pool_create_buffer(Resource *resource, uint32_t id, int32_t offset, int32_t width, int32_t height, int32_t stride, uint32_t format)
|
||||||
|
{
|
||||||
|
if (std::find(std::begin(s_formats), std::end(s_formats), format) == std::end(s_formats)) {
|
||||||
|
wl_resource_post_error(resource->handle,
|
||||||
|
WL_SHM_ERROR_INVALID_FORMAT,
|
||||||
|
"invalid format 0x%x",
|
||||||
|
format);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offset < 0 || width <= 0 || height <= 0 || stride < width
|
||||||
|
|| INT32_MAX / stride < height || offset > mapping.size() - stride * height) {
|
||||||
|
wl_resource_post_error(resource->handle,
|
||||||
|
WL_SHM_ERROR_INVALID_STRIDE,
|
||||||
|
"invalid width, height or stride (%dx%d, %u)",
|
||||||
|
width, height, stride);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ShmAttributes attributes{
|
||||||
|
.fd = fd.duplicate(),
|
||||||
|
.stride = stride,
|
||||||
|
.offset = offset,
|
||||||
|
.size = QSize(width, height),
|
||||||
|
.format = shmFormatToDrmFormat(format),
|
||||||
|
};
|
||||||
|
|
||||||
|
new ShmClientBuffer(this, std::move(attributes), resource->client(), id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShmPool::shm_pool_resize(Resource *resource, int32_t size)
|
||||||
|
{
|
||||||
|
if (size < mapping.size()) {
|
||||||
|
wl_resource_post_error(resource->handle, WL_SHM_ERROR_INVALID_FD, "shrinking pool invalid");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto remapping = MemoryMap(size, PROT_READ | PROT_WRITE, MAP_SHARED, fd.get(), 0);
|
||||||
|
if (remapping.isValid()) {
|
||||||
|
mapping = std::move(remapping);
|
||||||
|
} else {
|
||||||
|
wl_resource_post_error(resource->handle, WL_SHM_ERROR_INVALID_FD, "failed to map shm pool with the new size");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShmClientBuffer::buffer_destroy_resource(wl_resource *resource)
|
||||||
|
{
|
||||||
|
if (ShmClientBuffer *buffer = ShmClientBuffer::get(resource)) {
|
||||||
|
buffer->m_resource = nullptr;
|
||||||
|
buffer->drop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShmClientBuffer::buffer_destroy(wl_client *client, wl_resource *resource)
|
||||||
|
{
|
||||||
|
wl_resource_destroy(resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct wl_buffer_interface ShmClientBuffer::implementation = {
|
||||||
|
.destroy = buffer_destroy,
|
||||||
|
};
|
||||||
|
|
||||||
|
ShmClientBuffer::ShmClientBuffer(ShmPool *pool, ShmAttributes attributes, wl_client *client, uint32_t id)
|
||||||
|
: m_shmPool(pool)
|
||||||
|
, m_shmAttributes(std::move(attributes))
|
||||||
|
{
|
||||||
|
m_shmPool->ref();
|
||||||
|
|
||||||
connect(this, &GraphicsBuffer::released, [this]() {
|
connect(this, &GraphicsBuffer::released, [this]() {
|
||||||
wl_buffer_send_release(d->resource);
|
wl_buffer_send_release(m_resource);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
m_resource = wl_resource_create(client, &wl_buffer_interface, 1, id);
|
||||||
|
wl_resource_set_implementation(m_resource, &implementation, this, buffer_destroy_resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
ShmClientBuffer::~ShmClientBuffer()
|
ShmClientBuffer::~ShmClientBuffer()
|
||||||
{
|
{
|
||||||
|
m_shmPool->unref();
|
||||||
}
|
}
|
||||||
|
|
||||||
QSize ShmClientBuffer::size() const
|
QSize ShmClientBuffer::size() const
|
||||||
{
|
{
|
||||||
return QSize(d->width, d->height);
|
return m_shmAttributes.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShmClientBuffer::hasAlphaChannel() const
|
bool ShmClientBuffer::hasAlphaChannel() const
|
||||||
{
|
{
|
||||||
return d->hasAlphaChannel;
|
return alphaChannelFromDrmFormat(m_shmAttributes.format);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cleanupShmData(void *bufferHandle)
|
const ShmAttributes *ShmClientBuffer::shmAttributes() const
|
||||||
{
|
{
|
||||||
Q_ASSERT_X(s_accessCounter > 0, "cleanup", "access counter must be positive");
|
return &m_shmAttributes;
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wl_shm_buffer *buffer = wl_shm_buffer_get(d->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);
|
|
||||||
return QImage(data, d->width, d->height, stride, d->format, cleanupShmData, buffer);
|
|
||||||
}
|
|
||||||
return d->savedData;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ShmClientBuffer *ShmClientBuffer::get(wl_resource *resource)
|
ShmClientBuffer *ShmClientBuffer::get(wl_resource *resource)
|
||||||
{
|
{
|
||||||
if (auto buffer = s_buffers.value(resource)) {
|
if (wl_resource_instance_of(resource, &wl_buffer_interface, &implementation)) {
|
||||||
return buffer;
|
return static_cast<ShmClientBuffer *>(wl_resource_get_user_data(resource));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wl_shm_buffer_get(resource)) {
|
|
||||||
auto buffer = new ShmClientBuffer(resource);
|
|
||||||
s_buffers[resource] = buffer;
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void sigbusHandler(int signum, siginfo_t *info, void *context)
|
||||||
|
{
|
||||||
|
auto reraise = [&]() {
|
||||||
|
if (prevSigbusAction.sa_flags & SA_SIGINFO) {
|
||||||
|
prevSigbusAction.sa_sigaction(signum, info, context);
|
||||||
|
} else {
|
||||||
|
prevSigbusAction.sa_handler(signum);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const ShmPool *pool = sigbusData.pool;
|
||||||
|
if (!pool) {
|
||||||
|
reraise();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uchar *addr = static_cast<uchar *>(info->si_addr);
|
||||||
|
const uchar *mappingStart = static_cast<uchar *>(pool->mapping.data());
|
||||||
|
if (addr < mappingStart || addr >= mappingStart + pool->mapping.size()) {
|
||||||
|
reraise();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace the faulty mapping with a new one that's filled with zeros.
|
||||||
|
if (mmap(pool->mapping.data(), pool->mapping.size(), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, -1, 0) == MAP_FAILED) {
|
||||||
|
reraise();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void *ShmClientBuffer::map()
|
||||||
|
{
|
||||||
|
if (!m_shmPool->sigbusImpossible) {
|
||||||
|
// A SIGBUS signal may be emitted if the backing file is shrinked and we access now
|
||||||
|
// removed pages. Install a signal handler to handle this case. Note that if the
|
||||||
|
// backing file has F_SEAL_SHRINK seal, then we don't need to do anything.
|
||||||
|
|
||||||
|
static std::once_flag sigbusOnce;
|
||||||
|
std::call_once(sigbusOnce, []() {
|
||||||
|
struct sigaction action;
|
||||||
|
memset(&action, 0, sizeof(action));
|
||||||
|
sigemptyset(&action.sa_mask);
|
||||||
|
action.sa_sigaction = sigbusHandler;
|
||||||
|
action.sa_flags = SA_SIGINFO | SA_NODEFER;
|
||||||
|
sigaction(SIGBUS, &action, &prevSigbusAction);
|
||||||
|
});
|
||||||
|
|
||||||
|
Q_ASSERT(!sigbusData.pool || sigbusData.pool == m_shmPool);
|
||||||
|
sigbusData.pool = m_shmPool;
|
||||||
|
++sigbusData.accessCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
return reinterpret_cast<uchar *>(m_shmPool->mapping.data()) + m_shmAttributes.offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShmClientBuffer::unmap()
|
||||||
|
{
|
||||||
|
if (m_shmPool->sigbusImpossible) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_ASSERT(sigbusData.accessCount > 0);
|
||||||
|
--sigbusData.accessCount;
|
||||||
|
if (sigbusData.accessCount == 0) {
|
||||||
|
sigbusData.pool = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ShmClientBufferIntegrationPrivate::ShmClientBufferIntegrationPrivate(Display *display, ShmClientBufferIntegration *q)
|
||||||
|
: QtWaylandServer::wl_shm(*display, s_version)
|
||||||
|
, q(q)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShmClientBufferIntegrationPrivate::shm_bind_resource(Resource *resource)
|
||||||
|
{
|
||||||
|
for (const uint32_t &format : s_formats) {
|
||||||
|
send_format(resource->handle, format);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShmClientBufferIntegrationPrivate::shm_create_pool(Resource *resource, uint32_t id, int32_t fd, int32_t size)
|
||||||
|
{
|
||||||
|
FileDescriptor fileDescriptor{fd};
|
||||||
|
|
||||||
|
if (size <= 0) {
|
||||||
|
wl_resource_post_error(resource->handle, error_invalid_stride, "invalid size (%d)", size);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mapping = MemoryMap(size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||||
|
if (!mapping.isValid()) {
|
||||||
|
wl_resource_post_error(resource->handle, error_invalid_fd, "failed to map shm pool");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
new ShmPool(q, resource->client(), id, resource->version(), std::move(fileDescriptor), std::move(mapping));
|
||||||
|
}
|
||||||
|
|
||||||
ShmClientBufferIntegration::ShmClientBufferIntegration(Display *display)
|
ShmClientBufferIntegration::ShmClientBufferIntegration(Display *display)
|
||||||
: QObject(display)
|
: QObject(display)
|
||||||
|
, d(new ShmClientBufferIntegrationPrivate(display, this))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ShmClientBufferIntegration::~ShmClientBufferIntegration()
|
||||||
{
|
{
|
||||||
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
|
|
||||||
wl_display_add_shm_format(*display, WL_SHM_FORMAT_ARGB2101010);
|
|
||||||
wl_display_add_shm_format(*display, WL_SHM_FORMAT_XRGB2101010);
|
|
||||||
wl_display_add_shm_format(*display, WL_SHM_FORMAT_ABGR2101010);
|
|
||||||
wl_display_add_shm_format(*display, WL_SHM_FORMAT_XBGR2101010);
|
|
||||||
wl_display_add_shm_format(*display, WL_SHM_FORMAT_ABGR16161616);
|
|
||||||
wl_display_add_shm_format(*display, WL_SHM_FORMAT_XBGR16161616);
|
|
||||||
#endif
|
|
||||||
wl_display_init_shm(*display);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace KWaylandServer
|
} // namespace KWaylandServer
|
||||||
|
|
|
@ -6,50 +6,30 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "core/graphicsbuffer.h"
|
#include "kwin_export.h"
|
||||||
|
|
||||||
struct wl_resource;
|
#include <QObject>
|
||||||
|
|
||||||
namespace KWaylandServer
|
namespace KWaylandServer
|
||||||
{
|
{
|
||||||
|
|
||||||
class Display;
|
class Display;
|
||||||
class ShmClientBufferPrivate;
|
class ShmClientBufferIntegrationPrivate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The ShmClientBuffer class represents a wl_shm_buffer client buffer.
|
* The ShmClientBufferIntegration class provides support for shared memory client buffers.
|
||||||
*
|
|
||||||
* 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 KWIN_EXPORT ShmClientBuffer : public KWin::GraphicsBuffer
|
class KWIN_EXPORT ShmClientBufferIntegration : public QObject
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit ShmClientBuffer(wl_resource *resource);
|
|
||||||
~ShmClientBuffer() override;
|
|
||||||
|
|
||||||
QImage data() const;
|
|
||||||
|
|
||||||
QSize size() const override;
|
|
||||||
bool hasAlphaChannel() const override;
|
|
||||||
|
|
||||||
static ShmClientBuffer *get(wl_resource *resource);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::unique_ptr<ShmClientBufferPrivate> d;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The ShmClientBufferIntegration class provides support for wl_shm_buffer buffers.
|
|
||||||
*/
|
|
||||||
class ShmClientBufferIntegration : public QObject
|
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ShmClientBufferIntegration(Display *display);
|
explicit ShmClientBufferIntegration(Display *display);
|
||||||
|
~ShmClientBufferIntegration() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class ShmClientBufferIntegrationPrivate;
|
||||||
|
std::unique_ptr<ShmClientBufferIntegrationPrivate> d;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace KWaylandServer
|
} // namespace KWaylandServer
|
||||||
|
|
79
src/wayland/shmclientbuffer_p.h
Normal file
79
src/wayland/shmclientbuffer_p.h
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2023 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 "core/graphicsbuffer.h"
|
||||||
|
#include "wayland/shmclientbuffer.h"
|
||||||
|
#include "utils/filedescriptor.h"
|
||||||
|
#include "utils/memorymap.h"
|
||||||
|
|
||||||
|
#include "qwayland-server-wayland.h"
|
||||||
|
|
||||||
|
namespace KWaylandServer
|
||||||
|
{
|
||||||
|
|
||||||
|
class ShmClientBufferIntegrationPrivate : public QtWaylandServer::wl_shm
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ShmClientBufferIntegrationPrivate(Display *display, ShmClientBufferIntegration *q);
|
||||||
|
|
||||||
|
ShmClientBufferIntegration *q;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void shm_bind_resource(Resource *resource) override;
|
||||||
|
void shm_create_pool(Resource *resource, uint32_t id, int32_t fd, int32_t size) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ShmPool : public QtWaylandServer::wl_shm_pool
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ShmPool(ShmClientBufferIntegration *integration, wl_client *client, int id, uint32_t version, KWin::FileDescriptor &&fd, KWin::MemoryMap &&mapping);
|
||||||
|
|
||||||
|
void ref();
|
||||||
|
void unref();
|
||||||
|
|
||||||
|
ShmClientBufferIntegration *integration;
|
||||||
|
KWin::MemoryMap mapping;
|
||||||
|
KWin::FileDescriptor fd;
|
||||||
|
int refCount = 1;
|
||||||
|
bool sigbusImpossible = false;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void shm_pool_destroy_resource(Resource *resource) override;
|
||||||
|
void shm_pool_create_buffer(Resource *resource, uint32_t id, int32_t offset, int32_t width, int32_t height, int32_t stride, uint32_t format) override;
|
||||||
|
void shm_pool_destroy(Resource *resource) override;
|
||||||
|
void shm_pool_resize(Resource *resource, int32_t size) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KWIN_EXPORT ShmClientBuffer : public KWin::GraphicsBuffer
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
ShmClientBuffer(ShmPool *pool, KWin::ShmAttributes attributes, wl_client *client, uint32_t id);
|
||||||
|
~ShmClientBuffer() override;
|
||||||
|
|
||||||
|
void *map() override;
|
||||||
|
void unmap() override;
|
||||||
|
|
||||||
|
QSize size() const override;
|
||||||
|
bool hasAlphaChannel() const override;
|
||||||
|
const KWin::ShmAttributes *shmAttributes() const override;
|
||||||
|
|
||||||
|
static ShmClientBuffer *get(wl_resource *resource);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void buffer_destroy_resource(wl_resource *resource);
|
||||||
|
static void buffer_destroy(wl_client *client, wl_resource *resource);
|
||||||
|
static const struct wl_buffer_interface implementation;
|
||||||
|
|
||||||
|
wl_resource *m_resource = nullptr;
|
||||||
|
ShmPool *m_shmPool;
|
||||||
|
KWin::ShmAttributes m_shmAttributes;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace KWaylandServer
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "core/graphicsbufferview.h"
|
||||||
#include "../compositor_interface.h"
|
#include "../compositor_interface.h"
|
||||||
#include "../datadevicemanager_interface.h"
|
#include "../datadevicemanager_interface.h"
|
||||||
#include "../display.h"
|
#include "../display.h"
|
||||||
|
@ -10,7 +12,6 @@
|
||||||
#include "../output_interface.h"
|
#include "../output_interface.h"
|
||||||
#include "../pointer_interface.h"
|
#include "../pointer_interface.h"
|
||||||
#include "../seat_interface.h"
|
#include "../seat_interface.h"
|
||||||
#include "../shmclientbuffer.h"
|
|
||||||
#include "../xdgshell_interface.h"
|
#include "../xdgshell_interface.h"
|
||||||
|
|
||||||
#include "fakeoutput.h"
|
#include "fakeoutput.h"
|
||||||
|
@ -151,9 +152,9 @@ void CompositorWindow::paintEvent(QPaintEvent *event)
|
||||||
if (!surface || !surface->isMapped()) {
|
if (!surface || !surface->isMapped()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
auto clientBuffer = qobject_cast<KWaylandServer::ShmClientBuffer *>(surface->buffer());
|
KWin::GraphicsBufferView view(surface->buffer());
|
||||||
if (clientBuffer) {
|
if (view.image()) {
|
||||||
p.drawImage(QPoint(0, 0), clientBuffer->data());
|
p.drawImage(QPoint(0, 0), *view.image());
|
||||||
}
|
}
|
||||||
surface->frameRendered(QDateTime::currentMSecsSinceEpoch());
|
surface->frameRendered(QDateTime::currentMSecsSinceEpoch());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue