diff --git a/autotests/wayland_client/CMakeLists.txt b/autotests/wayland_client/CMakeLists.txt index 6b50aa2494..d46327e4db 100644 --- a/autotests/wayland_client/CMakeLists.txt +++ b/autotests/wayland_client/CMakeLists.txt @@ -5,6 +5,7 @@ set_source_files_properties(${CMAKE_BINARY_DIR}/wayland_protocols/wayland-client ######################################################## set( testWaylandConnectionThread_SRCS test_wayland_connection_thread.cpp + ${KWIN_SOURCE_DIR}/wayland_server/buffer_interface.cpp ${KWIN_SOURCE_DIR}/wayland_client/connection_thread.cpp ${KWIN_SOURCE_DIR}/wayland_server/compositor_interface.cpp ${KWIN_SOURCE_DIR}/wayland_server/display.cpp @@ -59,6 +60,7 @@ set( testWaylandOutput_SRCS ${KWIN_SOURCE_DIR}/wayland_client/fullscreen_shell.cpp ${KWIN_SOURCE_DIR}/wayland_client/output.cpp ${CMAKE_BINARY_DIR}/wayland_protocols/wayland-client-fullscreen-shell.c + ${KWIN_SOURCE_DIR}/wayland_server/buffer_interface.cpp ${KWIN_SOURCE_DIR}/wayland_server/compositor_interface.cpp ${KWIN_SOURCE_DIR}/wayland_server/display.cpp ${KWIN_SOURCE_DIR}/wayland_server/output_interface.cpp @@ -94,12 +96,15 @@ ecm_mark_as_test(testWaylandShell) ######################################################## set( testWaylandSurface_SRCS test_wayland_surface.cpp + ${KWIN_SOURCE_DIR}/wayland_client/buffer.cpp ${KWIN_SOURCE_DIR}/wayland_client/compositor.cpp ${KWIN_SOURCE_DIR}/wayland_client/connection_thread.cpp ${KWIN_SOURCE_DIR}/wayland_client/registry.cpp ${KWIN_SOURCE_DIR}/wayland_client/fullscreen_shell.cpp + ${KWIN_SOURCE_DIR}/wayland_client/shm_pool.cpp ${KWIN_SOURCE_DIR}/wayland_client/surface.cpp ${CMAKE_BINARY_DIR}/wayland_protocols/wayland-client-fullscreen-shell.c + ${KWIN_SOURCE_DIR}/wayland_server/buffer_interface.cpp ${KWIN_SOURCE_DIR}/wayland_server/compositor_interface.cpp ${KWIN_SOURCE_DIR}/wayland_server/display.cpp ${KWIN_SOURCE_DIR}/wayland_server/output_interface.cpp diff --git a/autotests/wayland_client/test_wayland_surface.cpp b/autotests/wayland_client/test_wayland_surface.cpp index 1b480895f4..bb4a7291a3 100644 --- a/autotests/wayland_client/test_wayland_surface.cpp +++ b/autotests/wayland_client/test_wayland_surface.cpp @@ -19,11 +19,14 @@ along with this program. If not, see . *********************************************************************/ // Qt #include +#include // KWin #include "../../wayland_client/compositor.h" #include "../../wayland_client/connection_thread.h" #include "../../wayland_client/surface.h" #include "../../wayland_client/registry.h" +#include "../../wayland_client/shm_pool.h" +#include "../../wayland_server/buffer_interface.h" #include "../../wayland_server/compositor_interface.h" #include "../../wayland_server/display.h" #include "../../wayland_server/surface_interface.h" @@ -42,6 +45,7 @@ private Q_SLOTS: void testStaticAccessor(); void testDamage(); void testFrameCallback(); + void testAttachBuffer(); private: KWin::WaylandServer::Display *m_display; @@ -230,5 +234,69 @@ void TestWaylandSurface::testFrameCallback() QVERIFY(!frameRenderedSpy.isEmpty()); } +void TestWaylandSurface::testAttachBuffer() +{ + // here we need a shm pool + m_display->createShm(); + + KWin::Wayland::Registry registry; + QSignalSpy shmSpy(®istry, SIGNAL(shmAnnounced(quint32,quint32))); + registry.create(m_connection->display()); + QVERIFY(registry.isValid()); + registry.setup(); + QVERIFY(shmSpy.wait()); + + KWin::Wayland::ShmPool pool; + pool.setup(registry.bindShm(shmSpy.first().first().value(), shmSpy.first().last().value())); + QVERIFY(pool.isValid()); + + // create the surface + QSignalSpy serverSurfaceCreated(m_compositorInterface, SIGNAL(surfaceCreated(KWin::WaylandServer::SurfaceInterface*))); + QVERIFY(serverSurfaceCreated.isValid()); + KWin::Wayland::Surface *s = m_compositor->createSurface(); + QVERIFY(serverSurfaceCreated.wait()); + KWin::WaylandServer::SurfaceInterface *serverSurface = serverSurfaceCreated.first().first().value(); + QVERIFY(serverSurface); + + // create two images + // TODO: test RGB32 + QImage black(24, 24, QImage::Format_ARGB32); + black.fill(Qt::black); + QImage red(24, 24, QImage::Format_ARGB32); + red.fill(QColor(255, 0, 0, 128)); + + wl_buffer *blackBuffer = pool.createBuffer(black); + wl_buffer *redBuffer = pool.createBuffer(red); + + s->attachBuffer(redBuffer); + s->attachBuffer(blackBuffer); + s->damage(QRect(0, 0, 24, 24)); + s->commit(KWin::Wayland::Surface::CommitFlag::None); + QSignalSpy damageSpy(serverSurface, SIGNAL(damaged(QRegion))); + QVERIFY(damageSpy.isValid()); + QVERIFY(damageSpy.wait()); + + // now the ServerSurface should have the black image attached as a buffer + KWin::WaylandServer::BufferInterface *buffer = serverSurface->buffer(); + buffer->ref(); + QVERIFY(buffer->shmBuffer()); + QCOMPARE(buffer->data(), black); + + // render another frame + s->attachBuffer(redBuffer); + s->damage(QRect(0, 0, 24, 24)); + s->commit(KWin::Wayland::Surface::CommitFlag::None); + damageSpy.clear(); + QVERIFY(damageSpy.wait()); + KWin::WaylandServer::BufferInterface *buffer2 = serverSurface->buffer(); + buffer2->ref(); + QVERIFY(buffer2->shmBuffer()); + QCOMPARE(buffer2->data(), red); + buffer2->unref(); + + // TODO: add signal test on release + buffer->unref(); +} + QTEST_MAIN(TestWaylandSurface) #include "test_wayland_surface.moc" diff --git a/autotests/wayland_server/CMakeLists.txt b/autotests/wayland_server/CMakeLists.txt index bff5b51035..4589b27449 100644 --- a/autotests/wayland_server/CMakeLists.txt +++ b/autotests/wayland_server/CMakeLists.txt @@ -3,6 +3,7 @@ ######################################################## set( testWaylandServerDisplay_SRCS test_display.cpp + ${KWIN_SOURCE_DIR}/wayland_server/buffer_interface.cpp ${KWIN_SOURCE_DIR}/wayland_server/compositor_interface.cpp ${KWIN_SOURCE_DIR}/wayland_server/display.cpp ${KWIN_SOURCE_DIR}/wayland_server/output_interface.cpp diff --git a/wayland_server/buffer_interface.cpp b/wayland_server/buffer_interface.cpp new file mode 100644 index 0000000000..fb8e7501e2 --- /dev/null +++ b/wayland_server/buffer_interface.cpp @@ -0,0 +1,107 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2014 Martin Gräßlin + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ +#include "buffer_interface.h" +#include "surface_interface.h" + +namespace KWin +{ +namespace WaylandServer +{ + +BufferInterface::BufferInterface(wl_resource *resource, SurfaceInterface *parent) + : QObject(parent) + , m_buffer(resource) + , m_shmBuffer(wl_shm_buffer_get(m_buffer)) + , m_surface(parent) + , m_refCount(0) +{ +} + +BufferInterface::~BufferInterface() +{ + Q_ASSERT(m_refCount == 0); + releaseImage(); +} + +void BufferInterface::releaseImage() +{ + if (m_image.isNull()) { + return; + } + // first destroy it + m_image = QImage(); + wl_shm_buffer_end_access(m_shmBuffer); +} + +void BufferInterface::ref() +{ + m_refCount++; +} + +void BufferInterface::unref() +{ + Q_ASSERT(m_refCount > 0); + m_refCount--; + if (m_refCount == 0) { + releaseImage(); + wl_buffer_send_release(m_buffer); + deleteLater(); + } +} + +QImage::Format BufferInterface::format() const +{ + switch (wl_shm_buffer_get_format(m_shmBuffer)) { + case WL_SHM_FORMAT_ARGB8888: + return QImage::Format_ARGB32; + case WL_SHM_FORMAT_XRGB8888: + return QImage::Format_RGB32; + default: + return QImage::Format_Invalid; + } +} + +QImage BufferInterface::data() +{ + if (m_image.isNull()) { + createImage(); + } + return m_image; +} + +void BufferInterface::createImage() +{ + if (!m_shmBuffer) { + return; + } + const QImage::Format imageFormat = format(); + if (imageFormat == QImage::Format_Invalid) { + return; + } + wl_shm_buffer_begin_access(m_shmBuffer); + m_image = QImage((const uchar*)wl_shm_buffer_get_data(m_shmBuffer), + wl_shm_buffer_get_width(m_shmBuffer), + wl_shm_buffer_get_height(m_shmBuffer), + wl_shm_buffer_get_stride(m_shmBuffer), + imageFormat); +} + +} +} diff --git a/wayland_server/buffer_interface.h b/wayland_server/buffer_interface.h new file mode 100644 index 0000000000..41531c4c93 --- /dev/null +++ b/wayland_server/buffer_interface.h @@ -0,0 +1,72 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2014 Martin Gräßlin + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ +#ifndef KWIN_WAYLAND_SERVER_BUFFER_INTERFACE_H +#define KWIN_WAYLAND_SERVER_BUFFER_INTERFACE_H + +#include +#include + +struct wl_resource; +struct wl_shm_buffer; + +namespace KWin +{ +namespace WaylandServer +{ +class SurfaceInterface; + + +class BufferInterface : public QObject +{ + Q_OBJECT +public: + virtual ~BufferInterface(); + void ref(); + void unref(); + bool isReferenced() const { + return m_refCount > 0; + } + + SurfaceInterface *surface() const { + return m_surface; + } + wl_shm_buffer *shmBuffer() { + return m_shmBuffer; + } + + QImage data(); + +private: + friend class SurfaceInterface; + explicit BufferInterface(wl_resource *resource, SurfaceInterface *parent); + QImage::Format format() const; + void createImage(); + void releaseImage(); + wl_resource *m_buffer; + wl_shm_buffer *m_shmBuffer; + SurfaceInterface *m_surface; + int m_refCount; + QImage m_image; +}; + +} +} + +#endif diff --git a/wayland_server/display.cpp b/wayland_server/display.cpp index 06210ee33c..6778128704 100644 --- a/wayland_server/display.cpp +++ b/wayland_server/display.cpp @@ -131,6 +131,12 @@ CompositorInterface *Display::createCompositor(QObject *parent) return compositor; } +void Display::createShm() +{ + Q_ASSERT(m_running); + wl_display_init_shm(m_display); +} + void Display::removeOutput(OutputInterface *output) { m_outputs.removeAll(output); diff --git a/wayland_server/display.h b/wayland_server/display.h index d18d9c09ad..dc9633c7d0 100644 --- a/wayland_server/display.h +++ b/wayland_server/display.h @@ -66,6 +66,7 @@ public: } CompositorInterface *createCompositor(QObject *parent = nullptr); + void createShm(); Q_SIGNALS: void socketNameChanged(const QString&); diff --git a/wayland_server/surface_interface.cpp b/wayland_server/surface_interface.cpp index ea0343c95e..8ce1d2a93f 100644 --- a/wayland_server/surface_interface.cpp +++ b/wayland_server/surface_interface.cpp @@ -18,6 +18,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "surface_interface.h" +#include "buffer_interface.h" #include "compositor_interface.h" namespace KWin @@ -87,6 +88,9 @@ void SurfaceInterface::destroy() for (wl_resource *c : m_pending.callbacks) { wl_resource_destroy(c); } + if (m_current.buffer) { + m_current.buffer->unref(); + } if (m_surface) { wl_resource_destroy(m_surface); m_surface = nullptr; @@ -102,6 +106,12 @@ void SurfaceInterface::commit() const bool inputRegionChanged = m_current.input != m_pending.input; const bool scaleFactorChanged = m_current.scale != m_pending.scale; const bool transformFactorChanged = m_current.transform != m_pending.transform; + if (m_current.buffer) { + m_current.buffer->unref(); + } + if (m_pending.buffer) { + m_pending.buffer->ref(); + } // copy values m_current = m_pending; m_pending = State{}; @@ -149,6 +159,15 @@ void SurfaceInterface::addFrameCallback(uint32_t callback) m_pending.callbacks << r; } +void SurfaceInterface::attachBuffer(wl_resource *buffer, const QPoint &offset) +{ + m_pending.offset = offset; + if (m_pending.buffer) { + delete m_pending.buffer; + } + m_pending.buffer = new BufferInterface(buffer, this); +} + void SurfaceInterface::destroyFrameCallback(wl_resource *r) { SurfaceInterface *s = SurfaceInterface::cast(r); @@ -165,11 +184,7 @@ void SurfaceInterface::destroyCallback(wl_client *client, wl_resource *resource) void SurfaceInterface::attachCallback(wl_client *client, wl_resource *resource, wl_resource *buffer, int32_t sx, int32_t sy) { Q_UNUSED(client) - Q_UNUSED(resource) - Q_UNUSED(buffer) - Q_UNUSED(sx) - Q_UNUSED(sy) - // TODO: implement me + SurfaceInterface::cast(resource)->attachBuffer(buffer, QPoint(sx, sy)); } void SurfaceInterface::damageCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) diff --git a/wayland_server/surface_interface.h b/wayland_server/surface_interface.h index 659105e4e7..75e5cdc82c 100644 --- a/wayland_server/surface_interface.h +++ b/wayland_server/surface_interface.h @@ -31,6 +31,7 @@ namespace KWin { namespace WaylandServer { +class BufferInterface; class CompositorInterface; class SurfaceInterface : public QObject @@ -70,6 +71,12 @@ public: OutputInterface::Transform transform() const { return m_current.transform; } + BufferInterface *buffer() { + return m_current.buffer; + } + QPoint offset() const { + return m_current.offset; + } Q_SIGNALS: void damaged(const QRegion&); @@ -86,6 +93,8 @@ private: qint32 scale = 1; OutputInterface::Transform transform = OutputInterface::Transform::Normal; QList callbacks = QList(); + QPoint offset = QPoint(); + BufferInterface *buffer = nullptr; }; friend class CompositorInterface; explicit SurfaceInterface(CompositorInterface *parent); @@ -114,6 +123,7 @@ private: void setScale(qint32 scale); void setTransform(OutputInterface::Transform transform); void addFrameCallback(uint32_t callback); + void attachBuffer(wl_resource *buffer, const QPoint &offset); CompositorInterface *m_compositor; wl_resource *m_surface; wl_client *m_client;