diff --git a/src/wayland/CMakeLists.txt b/src/wayland/CMakeLists.txt
index ab21e99e5b..53b1a91891 100644
--- a/src/wayland/CMakeLists.txt
+++ b/src/wayland/CMakeLists.txt
@@ -6,6 +6,7 @@ set(SERVER_LIB_SRCS
seat_interface.cpp
shell_interface.cpp
surface_interface.cpp
+ subcompositor_interface.cpp
)
add_library(KF5WaylandServer ${SERVER_LIB_SRCS})
diff --git a/src/wayland/autotests/client/CMakeLists.txt b/src/wayland/autotests/client/CMakeLists.txt
index c7dc316003..b593d17d12 100644
--- a/src/wayland/autotests/client/CMakeLists.txt
+++ b/src/wayland/autotests/client/CMakeLists.txt
@@ -100,3 +100,26 @@ add_executable(testCompositor ${testCompositor_SRCS})
target_link_libraries( testCompositor Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer)
add_test(kwayland-testCompositor testCompositor)
ecm_mark_as_test(testCompositor)
+
+########################################################
+# Test SubCompositor
+########################################################
+set( testSubCompositor_SRCS
+ test_wayland_subcompositor.cpp
+ )
+add_executable(testSubCompositor ${testSubCompositor_SRCS})
+target_link_libraries( testSubCompositor Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer)
+add_test(kwayland-testSubCompositor testSubCompositor)
+ecm_mark_as_test(testSubCompositor)
+
+
+########################################################
+# Test SubSurface
+########################################################
+set( testSubSurface_SRCS
+ test_wayland_subsurface.cpp
+ )
+add_executable(testSubSurface ${testSubSurface_SRCS})
+target_link_libraries( testSubSurface Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer Wayland::Client)
+add_test(kwayland-testSubSurface testSubSurface)
+ecm_mark_as_test(testSubSurface)
diff --git a/src/wayland/autotests/client/test_wayland_registry.cpp b/src/wayland/autotests/client/test_wayland_registry.cpp
index 77658734b9..1bc07b82a1 100644
--- a/src/wayland/autotests/client/test_wayland_registry.cpp
+++ b/src/wayland/autotests/client/test_wayland_registry.cpp
@@ -28,6 +28,7 @@ License along with this library. If not, see .
#include "../../src/server/output_interface.h"
#include "../../src/server/seat_interface.h"
#include "../../src/server/shell_interface.h"
+#include "../../src/server/subcompositor_interface.h"
// Wayland
#include
@@ -46,6 +47,7 @@ private Q_SLOTS:
void testBindOutput();
void testBindShm();
void testBindSeat();
+ void testBindSubCompositor();
void testGlobalSync();
void testGlobalSyncThreaded();
void testRemoval();
@@ -57,6 +59,7 @@ private:
KWayland::Server::OutputInterface *m_output;
KWayland::Server::SeatInterface *m_seat;
KWayland::Server::ShellInterface *m_shell;
+ KWayland::Server::SubCompositorInterface *m_subcompositor;
};
static const QString s_socketName = QStringLiteral("kwin-test-wayland-registry-0");
@@ -68,6 +71,7 @@ TestWaylandRegistry::TestWaylandRegistry(QObject *parent)
, m_output(nullptr)
, m_seat(nullptr)
, m_shell(nullptr)
+ , m_subcompositor(nullptr)
{
}
@@ -85,6 +89,8 @@ void TestWaylandRegistry::init()
m_seat->create();
m_shell = m_display->createShell();
m_shell->create();
+ m_subcompositor = m_display->createSubCompositor();
+ m_subcompositor->create();
}
void TestWaylandRegistry::cleanup()
@@ -168,6 +174,11 @@ void TestWaylandRegistry::testBindShm()
TEST_BIND(KWayland::Client::Registry::Interface::Shm, SIGNAL(shmAnnounced(quint32,quint32)), bindShm, wl_shm_destroy)
}
+void TestWaylandRegistry::testBindSubCompositor()
+{
+ TEST_BIND(KWayland::Client::Registry::Interface::SubCompositor, SIGNAL(subCompositorAnnounced(quint32,quint32)), bindSubCompositor, wl_subcompositor_destroy)
+}
+
#undef TEST_BIND
void TestWaylandRegistry::testRemoval()
@@ -194,6 +205,8 @@ void TestWaylandRegistry::testRemoval()
QVERIFY(shellAnnouncedSpy.isValid());
QSignalSpy seatAnnouncedSpy(®istry, SIGNAL(seatAnnounced(quint32,quint32)));
QVERIFY(seatAnnouncedSpy.isValid());
+ QSignalSpy subCompositorAnnouncedSpy(®istry, SIGNAL(subCompositorAnnounced(quint32,quint32)));
+ QVERIFY(subCompositorAnnouncedSpy.isValid());
QVERIFY(!registry.isValid());
registry.create(connection.display());
@@ -204,12 +217,14 @@ void TestWaylandRegistry::testRemoval()
QVERIFY(!outputAnnouncedSpy.isEmpty());
QVERIFY(!shellAnnouncedSpy.isEmpty());
QVERIFY(!seatAnnouncedSpy.isEmpty());
+ QVERIFY(!subCompositorAnnouncedSpy.isEmpty());
QVERIFY(registry.hasInterface(KWayland::Client::Registry::Interface::Compositor));
QVERIFY(registry.hasInterface(KWayland::Client::Registry::Interface::Output));
QVERIFY(registry.hasInterface(KWayland::Client::Registry::Interface::Seat));
QVERIFY(registry.hasInterface(KWayland::Client::Registry::Interface::Shell));
QVERIFY(registry.hasInterface(KWayland::Client::Registry::Interface::Shm));
+ QVERIFY(registry.hasInterface(KWayland::Client::Registry::Interface::SubCompositor));
QVERIFY(!registry.hasInterface(KWayland::Client::Registry::Interface::FullscreenShell));
QSignalSpy seatRemovedSpy(®istry, SIGNAL(seatRemoved(quint32)));
@@ -244,6 +259,14 @@ void TestWaylandRegistry::testRemoval()
QCOMPARE(compositorRemovedSpy.first().first(), compositorAnnouncedSpy.first().first());
QVERIFY(!registry.hasInterface(KWayland::Client::Registry::Interface::Compositor));
+ QSignalSpy subCompositorRemovedSpy(®istry, SIGNAL(subCompositorRemoved(quint32)));
+ QVERIFY(subCompositorRemovedSpy.isValid());
+
+ delete m_subcompositor;
+ QVERIFY(subCompositorRemovedSpy.wait());
+ QCOMPARE(subCompositorRemovedSpy.first().first(), subCompositorAnnouncedSpy.first().first());
+ QVERIFY(!registry.hasInterface(KWayland::Client::Registry::Interface::SubCompositor));
+
// cannot test shmRemoved as there is no functionality for it
}
diff --git a/src/wayland/autotests/client/test_wayland_subcompositor.cpp b/src/wayland/autotests/client/test_wayland_subcompositor.cpp
new file mode 100644
index 0000000000..2e848f8fb2
--- /dev/null
+++ b/src/wayland/autotests/client/test_wayland_subcompositor.cpp
@@ -0,0 +1,173 @@
+/********************************************************************
+Copyright 2014 Martin Gräßlin
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) version 3, or any
+later version accepted by the membership of KDE e.V. (or its
+successor approved by the membership of KDE e.V.), which shall
+act as a proxy defined in Section 6 of version 3 of the license.
+
+This library 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
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library. If not, see .
+*********************************************************************/
+// Qt
+#include
+// KWin
+#include "../../src/client/connection_thread.h"
+#include "../../src/client/event_queue.h"
+#include "../../src/client/registry.h"
+#include "../../src/client/subcompositor.h"
+#include "../../src/server/display.h"
+#include "../../src/server/subcompositor_interface.h"
+
+class TestSubCompositor : public QObject
+{
+ Q_OBJECT
+public:
+ explicit TestSubCompositor(QObject *parent = nullptr);
+private Q_SLOTS:
+ void init();
+ void cleanup();
+
+ void testDestroy();
+ void testCast();
+
+private:
+ KWayland::Server::Display *m_display;
+ KWayland::Server::SubCompositorInterface *m_subcompositorInterface;
+ KWayland::Client::ConnectionThread *m_connection;
+ KWayland::Client::SubCompositor *m_subCompositor;
+ KWayland::Client::EventQueue *m_queue;
+ QThread *m_thread;
+};
+
+static const QString s_socketName = QStringLiteral("kwayland-test-wayland-subcompositor-0");
+
+TestSubCompositor::TestSubCompositor(QObject *parent)
+ : QObject(parent)
+ , m_display(nullptr)
+ , m_subcompositorInterface(nullptr)
+ , m_connection(nullptr)
+ , m_subCompositor(nullptr)
+ , m_queue(nullptr)
+ , m_thread(nullptr)
+{
+}
+
+void TestSubCompositor::init()
+{
+ using namespace KWayland::Server;
+ delete m_display;
+ m_display = new Display(this);
+ m_display->setSocketName(s_socketName);
+ m_display->start();
+ QVERIFY(m_display->isRunning());
+
+ // setup connection
+ m_connection = new KWayland::Client::ConnectionThread;
+ QSignalSpy connectedSpy(m_connection, SIGNAL(connected()));
+ m_connection->setSocketName(s_socketName);
+
+ m_thread = new QThread(this);
+ m_connection->moveToThread(m_thread);
+ m_thread->start();
+
+ m_connection->initConnection();
+ QVERIFY(connectedSpy.wait());
+
+ m_queue = new KWayland::Client::EventQueue(this);
+ QVERIFY(!m_queue->isValid());
+ m_queue->setup(m_connection);
+ QVERIFY(m_queue->isValid());
+
+ KWayland::Client::Registry registry;
+ QSignalSpy subCompositorSpy(®istry, SIGNAL(subCompositorAnnounced(quint32,quint32)));
+ QVERIFY(subCompositorSpy.isValid());
+ QVERIFY(!registry.eventQueue());
+ registry.setEventQueue(m_queue);
+ QCOMPARE(registry.eventQueue(), m_queue);
+ registry.create(m_connection->display());
+ QVERIFY(registry.isValid());
+ registry.setup();
+
+ m_subcompositorInterface = m_display->createSubCompositor(m_display);
+ QVERIFY(m_subcompositorInterface);
+ m_subcompositorInterface->create();
+ QVERIFY(m_subcompositorInterface->isValid());
+
+ QVERIFY(subCompositorSpy.wait());
+ m_subCompositor = registry.createSubCompositor(subCompositorSpy.first().first().value(), subCompositorSpy.first().last().value(), this);
+}
+
+void TestSubCompositor::cleanup()
+{
+ if (m_subCompositor) {
+ delete m_subCompositor;
+ m_subCompositor = nullptr;
+ }
+ if (m_queue) {
+ delete m_queue;
+ m_queue = nullptr;
+ }
+ if (m_thread) {
+ m_thread->quit();
+ m_thread->wait();
+ delete m_thread;
+ m_thread = nullptr;
+ }
+ delete m_connection;
+ m_connection = nullptr;
+
+ delete m_display;
+ m_display = nullptr;
+}
+
+void TestSubCompositor::testDestroy()
+{
+ using namespace KWayland::Client;
+ connect(m_connection, &ConnectionThread::connectionDied, m_subCompositor, &SubCompositor::destroy);
+ connect(m_connection, &ConnectionThread::connectionDied, m_queue, &EventQueue::destroy);
+ QVERIFY(m_subCompositor->isValid());
+
+ QSignalSpy connectionDiedSpy(m_connection, SIGNAL(connectionDied()));
+ QVERIFY(connectionDiedSpy.isValid());
+ delete m_display;
+ m_display = nullptr;
+ QVERIFY(connectionDiedSpy.wait());
+
+ // now the pool should be destroyed;
+ QVERIFY(!m_subCompositor->isValid());
+
+ // calling destroy again should not fail
+ m_subCompositor->destroy();
+}
+
+void TestSubCompositor::testCast()
+{
+ using namespace KWayland::Client;
+ Registry registry;
+ QSignalSpy subCompositorSpy(®istry, SIGNAL(subCompositorAnnounced(quint32,quint32)));
+ registry.create(m_connection->display());
+ QVERIFY(registry.isValid());
+ registry.setup();
+
+ QVERIFY(subCompositorSpy.wait());
+
+ SubCompositor c;
+ auto wlSubComp = registry.bindSubCompositor(subCompositorSpy.first().first().value(), subCompositorSpy.first().last().value());
+ c.setup(wlSubComp);
+ QCOMPARE((wl_subcompositor*)c, wlSubComp);
+
+ const SubCompositor &c2(c);
+ QCOMPARE((wl_subcompositor*)c2, wlSubComp);
+}
+
+QTEST_MAIN(TestSubCompositor)
+#include "test_wayland_subcompositor.moc"
diff --git a/src/wayland/autotests/client/test_wayland_subsurface.cpp b/src/wayland/autotests/client/test_wayland_subsurface.cpp
new file mode 100644
index 0000000000..c94ebfba6f
--- /dev/null
+++ b/src/wayland/autotests/client/test_wayland_subsurface.cpp
@@ -0,0 +1,560 @@
+/********************************************************************
+Copyright 2014 Martin Gräßlin
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) version 3, or any
+later version accepted by the membership of KDE e.V. (or its
+successor approved by the membership of KDE e.V.), which shall
+act as a proxy defined in Section 6 of version 3 of the license.
+
+This library 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
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library. If not, see .
+*********************************************************************/
+// Qt
+#include
+// KWin
+#include "../../src/client/compositor.h"
+#include "../../src/client/connection_thread.h"
+#include "../../src/client/event_queue.h"
+#include "../../src/client/registry.h"
+#include "../../src/client/subcompositor.h"
+#include "../../src/client/subsurface.h"
+#include "../../src/client/surface.h"
+#include "../../src/server/display.h"
+#include "../../src/server/compositor_interface.h"
+#include "../../src/server/subcompositor_interface.h"
+#include "../../src/server/surface_interface.h"
+// Wayland
+#include
+
+class TestSubSurface : public QObject
+{
+ Q_OBJECT
+public:
+ explicit TestSubSurface(QObject *parent = nullptr);
+private Q_SLOTS:
+ void init();
+ void cleanup();
+
+ void testCreate();
+ void testMode();
+ void testPosition();
+ void testPlaceAbove();
+ void testPlaceBelow();
+ void testDestroy();
+ void testCast();
+
+private:
+ KWayland::Server::Display *m_display;
+ KWayland::Server::CompositorInterface *m_compositorInterface;
+ KWayland::Server::SubCompositorInterface *m_subcompositorInterface;
+ KWayland::Client::ConnectionThread *m_connection;
+ KWayland::Client::Compositor *m_compositor;
+ KWayland::Client::SubCompositor *m_subCompositor;
+ KWayland::Client::EventQueue *m_queue;
+ QThread *m_thread;
+};
+
+static const QString s_socketName = QStringLiteral("kwayland-test-wayland-subsurface-0");
+
+TestSubSurface::TestSubSurface(QObject *parent)
+ : QObject(parent)
+ , m_display(nullptr)
+ , m_compositorInterface(nullptr)
+ , m_subcompositorInterface(nullptr)
+ , m_connection(nullptr)
+ , m_compositor(nullptr)
+ , m_subCompositor(nullptr)
+ , m_queue(nullptr)
+ , m_thread(nullptr)
+{
+}
+
+void TestSubSurface::init()
+{
+ using namespace KWayland::Server;
+ delete m_display;
+ m_display = new Display(this);
+ m_display->setSocketName(s_socketName);
+ m_display->start();
+ QVERIFY(m_display->isRunning());
+
+ // setup connection
+ m_connection = new KWayland::Client::ConnectionThread;
+ QSignalSpy connectedSpy(m_connection, SIGNAL(connected()));
+ m_connection->setSocketName(s_socketName);
+
+ m_thread = new QThread(this);
+ m_connection->moveToThread(m_thread);
+ m_thread->start();
+
+ m_connection->initConnection();
+ QVERIFY(connectedSpy.wait());
+
+ m_queue = new KWayland::Client::EventQueue(this);
+ QVERIFY(!m_queue->isValid());
+ m_queue->setup(m_connection);
+ QVERIFY(m_queue->isValid());
+
+ KWayland::Client::Registry registry;
+ QSignalSpy compositorSpy(®istry, SIGNAL(compositorAnnounced(quint32,quint32)));
+ QVERIFY(compositorSpy.isValid());
+ QSignalSpy subCompositorSpy(®istry, SIGNAL(subCompositorAnnounced(quint32,quint32)));
+ QVERIFY(subCompositorSpy.isValid());
+ QVERIFY(!registry.eventQueue());
+ registry.setEventQueue(m_queue);
+ QCOMPARE(registry.eventQueue(), m_queue);
+ registry.create(m_connection->display());
+ QVERIFY(registry.isValid());
+ registry.setup();
+
+ m_compositorInterface = m_display->createCompositor(m_display);
+ m_compositorInterface->create();
+ QVERIFY(m_compositorInterface->isValid());
+
+ m_subcompositorInterface = m_display->createSubCompositor(m_display);
+ QVERIFY(m_subcompositorInterface);
+ m_subcompositorInterface->create();
+ QVERIFY(m_subcompositorInterface->isValid());
+
+ QVERIFY(subCompositorSpy.wait());
+ m_subCompositor = registry.createSubCompositor(subCompositorSpy.first().first().value(), subCompositorSpy.first().last().value(), this);
+
+ if (compositorSpy.isEmpty()) {
+ QVERIFY(compositorSpy.wait());
+ }
+ m_compositor = registry.createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this);
+}
+
+void TestSubSurface::cleanup()
+{
+ if (m_subCompositor) {
+ delete m_subCompositor;
+ m_subCompositor = nullptr;
+ }
+ if (m_compositor) {
+ delete m_compositor;
+ m_compositor = nullptr;
+ }
+ if (m_queue) {
+ delete m_queue;
+ m_queue = nullptr;
+ }
+ if (m_thread) {
+ m_thread->quit();
+ m_thread->wait();
+ delete m_thread;
+ m_thread = nullptr;
+ }
+ delete m_connection;
+ m_connection = nullptr;
+
+ delete m_display;
+ m_display = nullptr;
+}
+
+void TestSubSurface::testCreate()
+{
+ using namespace KWayland::Client;
+ using namespace KWayland::Server;
+ QSignalSpy surfaceCreatedSpy(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*)));
+ QVERIFY(surfaceCreatedSpy.isValid());
+
+ // create two Surfaces
+ QScopedPointer surface(m_compositor->createSurface());
+ QVERIFY(surfaceCreatedSpy.wait());
+ SurfaceInterface *serverSurface = surfaceCreatedSpy.first().first().value();
+ QVERIFY(serverSurface);
+
+ surfaceCreatedSpy.clear();
+ QScopedPointer parent(m_compositor->createSurface());
+ QVERIFY(surfaceCreatedSpy.wait());
+ SurfaceInterface *serverParentSurface = surfaceCreatedSpy.first().first().value();
+ QVERIFY(serverParentSurface);
+
+ QSignalSpy subSurfaceCreatedSpy(m_subcompositorInterface, SIGNAL(subSurfaceCreated(KWayland::Server::SubSurfaceInterface*)));
+ QVERIFY(subSurfaceCreatedSpy.isValid());
+
+ // create subSurface for surface of parent
+ QScopedPointer subSurface(m_subCompositor->createSubSurface(QPointer(surface.data()), QPointer(parent.data())));
+
+ QVERIFY(subSurfaceCreatedSpy.wait());
+ SubSurfaceInterface *serverSubSurface = subSurfaceCreatedSpy.first().first().value();
+ QVERIFY(serverSubSurface);
+ QCOMPARE(serverSubSurface->parentSurface().data(), serverParentSurface);
+ QCOMPARE(serverSubSurface->surface().data(), serverSurface);
+ QCOMPARE(serverSurface->subSurface().data(), serverSubSurface);
+ // children are only added after committing the surface
+ QCOMPARE(serverParentSurface->childSubSurfaces().count(), 0);
+ // so let's commit the surface, to apply the stacking change
+ parent->commit(Surface::CommitFlag::None);
+ wl_display_flush(m_connection->display());
+ QCoreApplication::processEvents();
+ QCOMPARE(serverParentSurface->childSubSurfaces().count(), 1);
+ QCOMPARE(serverParentSurface->childSubSurfaces().first().data(), serverSubSurface);
+
+ // and let's destroy it again
+ QSignalSpy destroyedSpy(serverSubSurface, SIGNAL(destroyed(QObject*)));
+ QVERIFY(destroyedSpy.isValid());
+ subSurface.reset();
+ QVERIFY(destroyedSpy.wait());
+ QCOMPARE(serverSurface->subSurface(), QPointer());
+ // only applied after next commit
+ QCOMPARE(serverParentSurface->childSubSurfaces().count(), 1);
+ // but the surface should be invalid
+ QVERIFY(serverParentSurface->childSubSurfaces().first().isNull());
+ // committing the state should solve it
+ parent->commit(Surface::CommitFlag::None);
+ wl_display_flush(m_connection->display());
+ QCoreApplication::processEvents();
+ QCOMPARE(serverParentSurface->childSubSurfaces().count(), 0);
+}
+
+void TestSubSurface::testMode()
+{
+ using namespace KWayland::Client;
+ using namespace KWayland::Server;
+ // create two Surface
+ QScopedPointer surface(m_compositor->createSurface());
+ QScopedPointer parent(m_compositor->createSurface());
+
+ QSignalSpy subSurfaceCreatedSpy(m_subcompositorInterface, SIGNAL(subSurfaceCreated(KWayland::Server::SubSurfaceInterface*)));
+ QVERIFY(subSurfaceCreatedSpy.isValid());
+
+ // create the SubSurface for surface of parent
+ QScopedPointer subSurface(m_subCompositor->createSubSurface(QPointer(surface.data()), QPointer(parent.data())));
+ QVERIFY(subSurfaceCreatedSpy.wait());
+ SubSurfaceInterface *serverSubSurface = subSurfaceCreatedSpy.first().first().value();
+ QVERIFY(serverSubSurface);
+
+ // both client and server subsurface should be in synchronized mode
+ QCOMPARE(subSurface->mode(), SubSurface::Mode::Synchronized);
+ QCOMPARE(serverSubSurface->mode(), SubSurfaceInterface::Mode::Synchronized);
+
+ // verify that we can change to desynchronized
+ QSignalSpy modeChangedSpy(serverSubSurface, SIGNAL(modeChanged(KWayland::Server::SubSurfaceInterface::Mode)));
+ QVERIFY(modeChangedSpy.isValid());
+
+ subSurface->setMode(SubSurface::Mode::Desynchronized);
+ QCOMPARE(subSurface->mode(), SubSurface::Mode::Desynchronized);
+
+ QVERIFY(modeChangedSpy.wait());
+ QCOMPARE(modeChangedSpy.first().first().value(), SubSurfaceInterface::Mode::Desynchronized);
+ QCOMPARE(serverSubSurface->mode(), SubSurfaceInterface::Mode::Desynchronized);
+
+ // setting the same again won't change
+ subSurface->setMode(SubSurface::Mode::Desynchronized);
+ QCOMPARE(subSurface->mode(), SubSurface::Mode::Desynchronized);
+ // not testing the signal, we do that after changing to synchronized
+
+ // and change back to synchronized
+ subSurface->setMode(SubSurface::Mode::Synchronized);
+ QCOMPARE(subSurface->mode(), SubSurface::Mode::Synchronized);
+
+ QVERIFY(modeChangedSpy.wait());
+ QCOMPARE(modeChangedSpy.count(), 2);
+ QCOMPARE(modeChangedSpy.first().first().value(), SubSurfaceInterface::Mode::Desynchronized);
+ QCOMPARE(modeChangedSpy.last().first().value(), SubSurfaceInterface::Mode::Synchronized);
+ QCOMPARE(serverSubSurface->mode(), SubSurfaceInterface::Mode::Synchronized);
+}
+
+void TestSubSurface::testPosition()
+{
+ using namespace KWayland::Client;
+ using namespace KWayland::Server;
+ // create two Surface
+ QScopedPointer surface(m_compositor->createSurface());
+ QScopedPointer parent(m_compositor->createSurface());
+
+ QSignalSpy subSurfaceCreatedSpy(m_subcompositorInterface, SIGNAL(subSurfaceCreated(KWayland::Server::SubSurfaceInterface*)));
+ QVERIFY(subSurfaceCreatedSpy.isValid());
+
+ // create the SubSurface for surface of parent
+ QScopedPointer subSurface(m_subCompositor->createSubSurface(QPointer(surface.data()), QPointer(parent.data())));
+ QVERIFY(subSurfaceCreatedSpy.wait());
+ SubSurfaceInterface *serverSubSurface = subSurfaceCreatedSpy.first().first().value();
+ QVERIFY(serverSubSurface);
+
+ // both client and server should have a default position
+ QCOMPARE(subSurface->position(), QPoint());
+ QCOMPARE(serverSubSurface->position(), QPoint());
+
+ QSignalSpy positionChangedSpy(serverSubSurface, SIGNAL(positionChanged(QPoint)));
+ QVERIFY(positionChangedSpy.isValid());
+
+ // changing the position should not trigger a direct update on server side
+ subSurface->setPosition(QPoint(10, 20));
+ QCOMPARE(subSurface->position(), QPoint(10, 20));
+ // ensure it's processed on server side
+ wl_display_flush(m_connection->display());
+ QCoreApplication::processEvents();
+ QCOMPARE(serverSubSurface->position(), QPoint());
+ // changing once more
+ subSurface->setPosition(QPoint(20, 30));
+ QCOMPARE(subSurface->position(), QPoint(20, 30));
+ // ensure it's processed on server side
+ wl_display_flush(m_connection->display());
+ QCoreApplication::processEvents();
+ QCOMPARE(serverSubSurface->position(), QPoint());
+
+ // committing the parent surface should update the position
+ parent->commit(Surface::CommitFlag::None);
+ QVERIFY(positionChangedSpy.wait());
+ QCOMPARE(positionChangedSpy.count(), 1);
+ QCOMPARE(positionChangedSpy.first().first().toPoint(), QPoint(20, 30));
+ QCOMPARE(serverSubSurface->position(), QPoint(20, 30));
+}
+
+void TestSubSurface::testPlaceAbove()
+{
+ using namespace KWayland::Client;
+ using namespace KWayland::Server;
+ // create needed Surfaces (one parent, three client
+ QScopedPointer surface1(m_compositor->createSurface());
+ QScopedPointer surface2(m_compositor->createSurface());
+ QScopedPointer surface3(m_compositor->createSurface());
+ QScopedPointer parent(m_compositor->createSurface());
+
+ QSignalSpy subSurfaceCreatedSpy(m_subcompositorInterface, SIGNAL(subSurfaceCreated(KWayland::Server::SubSurfaceInterface*)));
+ QVERIFY(subSurfaceCreatedSpy.isValid());
+
+ // create the SubSurfaces for surface of parent
+ QScopedPointer subSurface1(m_subCompositor->createSubSurface(QPointer(surface1.data()), QPointer(parent.data())));
+ QVERIFY(subSurfaceCreatedSpy.wait());
+ SubSurfaceInterface *serverSubSurface1 = subSurfaceCreatedSpy.first().first().value();
+ QVERIFY(serverSubSurface1);
+ subSurfaceCreatedSpy.clear();
+ QScopedPointer subSurface2(m_subCompositor->createSubSurface(QPointer(surface2.data()), QPointer(parent.data())));
+ QVERIFY(subSurfaceCreatedSpy.wait());
+ SubSurfaceInterface *serverSubSurface2 = subSurfaceCreatedSpy.first().first().value();
+ QVERIFY(serverSubSurface2);
+ subSurfaceCreatedSpy.clear();
+ QScopedPointer subSurface3(m_subCompositor->createSubSurface(QPointer(surface3.data()), QPointer(parent.data())));
+ QVERIFY(subSurfaceCreatedSpy.wait());
+ SubSurfaceInterface *serverSubSurface3 = subSurfaceCreatedSpy.first().first().value();
+ QVERIFY(serverSubSurface3);
+ subSurfaceCreatedSpy.clear();
+
+ // so far the stacking order should still be empty
+ QVERIFY(serverSubSurface1->parentSurface()->childSubSurfaces().isEmpty());
+
+ // commiting the parent should create the stacking order
+ parent->commit(Surface::CommitFlag::None);
+ // ensure it's processed on server side
+ wl_display_flush(m_connection->display());
+ QCoreApplication::processEvents();
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().count(), 3);
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(0).data(), serverSubSurface1);
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(1).data(), serverSubSurface2);
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(2).data(), serverSubSurface3);
+
+ // raising subsurface1 should place it to top of stack
+ subSurface1->raise();
+ // ensure it's processed on server side
+ wl_display_flush(m_connection->display());
+ QCoreApplication::processEvents();
+ // but as long as parent is not committed it shouldn't change on server side
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(0).data(), serverSubSurface1);
+ // after commit it's changed
+ parent->commit(Surface::CommitFlag::None);
+ wl_display_flush(m_connection->display());
+ QCoreApplication::processEvents();
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().count(), 3);
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(0).data(), serverSubSurface2);
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(1).data(), serverSubSurface3);
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(2).data(), serverSubSurface1);
+
+ // try placing 3 above 1, should result in 2, 1, 3
+ subSurface3->placeAbove(QPointer(subSurface1.data()));
+ parent->commit(Surface::CommitFlag::None);
+ wl_display_flush(m_connection->display());
+ QCoreApplication::processEvents();
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().count(), 3);
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(0).data(), serverSubSurface2);
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(1).data(), serverSubSurface1);
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(2).data(), serverSubSurface3);
+
+ // try placing 3 above 2, should result in 2, 3, 1
+ subSurface3->placeAbove(QPointer(subSurface2.data()));
+ parent->commit(Surface::CommitFlag::None);
+ wl_display_flush(m_connection->display());
+ QCoreApplication::processEvents();
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().count(), 3);
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(0).data(), serverSubSurface2);
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(1).data(), serverSubSurface3);
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(2).data(), serverSubSurface1);
+
+ // try placing 1 above 3 - shouldn't change
+ subSurface1->placeAbove(QPointer(subSurface3.data()));
+ parent->commit(Surface::CommitFlag::None);
+ wl_display_flush(m_connection->display());
+ QCoreApplication::processEvents();
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().count(), 3);
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(0).data(), serverSubSurface2);
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(1).data(), serverSubSurface3);
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(2).data(), serverSubSurface1);
+
+ // and 2 above 3 - > 3, 2, 1
+ subSurface2->placeAbove(QPointer(subSurface3.data()));
+ parent->commit(Surface::CommitFlag::None);
+ wl_display_flush(m_connection->display());
+ QCoreApplication::processEvents();
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().count(), 3);
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(0).data(), serverSubSurface3);
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(1).data(), serverSubSurface2);
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(2).data(), serverSubSurface1);
+}
+
+void TestSubSurface::testPlaceBelow()
+{
+ using namespace KWayland::Client;
+ using namespace KWayland::Server;
+ // create needed Surfaces (one parent, three client
+ QScopedPointer surface1(m_compositor->createSurface());
+ QScopedPointer surface2(m_compositor->createSurface());
+ QScopedPointer surface3(m_compositor->createSurface());
+ QScopedPointer parent(m_compositor->createSurface());
+
+ QSignalSpy subSurfaceCreatedSpy(m_subcompositorInterface, SIGNAL(subSurfaceCreated(KWayland::Server::SubSurfaceInterface*)));
+ QVERIFY(subSurfaceCreatedSpy.isValid());
+
+ // create the SubSurfaces for surface of parent
+ QScopedPointer subSurface1(m_subCompositor->createSubSurface(QPointer(surface1.data()), QPointer(parent.data())));
+ QVERIFY(subSurfaceCreatedSpy.wait());
+ SubSurfaceInterface *serverSubSurface1 = subSurfaceCreatedSpy.first().first().value();
+ QVERIFY(serverSubSurface1);
+ subSurfaceCreatedSpy.clear();
+ QScopedPointer subSurface2(m_subCompositor->createSubSurface(QPointer(surface2.data()), QPointer(parent.data())));
+ QVERIFY(subSurfaceCreatedSpy.wait());
+ SubSurfaceInterface *serverSubSurface2 = subSurfaceCreatedSpy.first().first().value();
+ QVERIFY(serverSubSurface2);
+ subSurfaceCreatedSpy.clear();
+ QScopedPointer subSurface3(m_subCompositor->createSubSurface(QPointer(surface3.data()), QPointer(parent.data())));
+ QVERIFY(subSurfaceCreatedSpy.wait());
+ SubSurfaceInterface *serverSubSurface3 = subSurfaceCreatedSpy.first().first().value();
+ QVERIFY(serverSubSurface3);
+ subSurfaceCreatedSpy.clear();
+
+ // so far the stacking order should still be empty
+ QVERIFY(serverSubSurface1->parentSurface()->childSubSurfaces().isEmpty());
+
+ // commiting the parent should create the stacking order
+ parent->commit(Surface::CommitFlag::None);
+ // ensure it's processed on server side
+ wl_display_flush(m_connection->display());
+ QCoreApplication::processEvents();
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().count(), 3);
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(0).data(), serverSubSurface1);
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(1).data(), serverSubSurface2);
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(2).data(), serverSubSurface3);
+
+ // lowering subsurface3 should place it to the bottom of stack
+ subSurface3->lower();
+ // ensure it's processed on server side
+ wl_display_flush(m_connection->display());
+ QCoreApplication::processEvents();
+ // but as long as parent is not committed it shouldn't change on server side
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(0).data(), serverSubSurface1);
+ // after commit it's changed
+ parent->commit(Surface::CommitFlag::None);
+ wl_display_flush(m_connection->display());
+ QCoreApplication::processEvents();
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().count(), 3);
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(0).data(), serverSubSurface3);
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(1).data(), serverSubSurface1);
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(2).data(), serverSubSurface2);
+
+ // place 1 below 3 -> 1, 3, 2
+ subSurface1->placeBelow(QPointer(subSurface3.data()));
+ parent->commit(Surface::CommitFlag::None);
+ wl_display_flush(m_connection->display());
+ QCoreApplication::processEvents();
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().count(), 3);
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(0).data(), serverSubSurface1);
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(1).data(), serverSubSurface3);
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(2).data(), serverSubSurface2);
+
+ // 2 below 3 -> 1, 2, 3
+ subSurface2->placeBelow(QPointer(subSurface3.data()));
+ parent->commit(Surface::CommitFlag::None);
+ wl_display_flush(m_connection->display());
+ QCoreApplication::processEvents();
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().count(), 3);
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(0).data(), serverSubSurface1);
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(1).data(), serverSubSurface2);
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(2).data(), serverSubSurface3);
+
+ // 1 below 2 -> shouldn't change
+ subSurface1->placeBelow(QPointer(subSurface2.data()));
+ parent->commit(Surface::CommitFlag::None);
+ wl_display_flush(m_connection->display());
+ QCoreApplication::processEvents();
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().count(), 3);
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(0).data(), serverSubSurface1);
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(1).data(), serverSubSurface2);
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(2).data(), serverSubSurface3);
+
+ // and 3 below 1 -> 3, 1, 2
+ subSurface3->placeBelow(QPointer(subSurface1.data()));
+ parent->commit(Surface::CommitFlag::None);
+ wl_display_flush(m_connection->display());
+ QCoreApplication::processEvents();
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().count(), 3);
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(0).data(), serverSubSurface3);
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(1).data(), serverSubSurface1);
+ QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(2).data(), serverSubSurface2);
+}
+
+void TestSubSurface::testDestroy()
+{
+ using namespace KWayland::Client;
+
+ // create two Surfaces
+ QScopedPointer surface(m_compositor->createSurface());
+ QScopedPointer parent(m_compositor->createSurface());
+ // create subSurface for surface of parent
+ QScopedPointer subSurface(m_subCompositor->createSubSurface(QPointer(surface.data()), QPointer(parent.data())));
+
+ connect(m_connection, &ConnectionThread::connectionDied, m_compositor, &Compositor::destroy);
+ connect(m_connection, &ConnectionThread::connectionDied, m_subCompositor, &SubCompositor::destroy);
+ connect(m_connection, &ConnectionThread::connectionDied, m_queue, &EventQueue::destroy);
+ connect(m_connection, &ConnectionThread::connectionDied, surface.data(), &Surface::destroy);
+ connect(m_connection, &ConnectionThread::connectionDied, parent.data(), &Surface::destroy);
+ connect(m_connection, &ConnectionThread::connectionDied, subSurface.data(), &SubSurface::destroy);
+ QVERIFY(subSurface->isValid());
+
+ QSignalSpy connectionDiedSpy(m_connection, SIGNAL(connectionDied()));
+ QVERIFY(connectionDiedSpy.isValid());
+ delete m_display;
+ m_display = nullptr;
+ QVERIFY(connectionDiedSpy.wait());
+
+ // now the pool should be destroyed;
+ QVERIFY(!subSurface->isValid());
+
+ // calling destroy again should not fail
+ subSurface->destroy();
+}
+
+void TestSubSurface::testCast()
+{
+ using namespace KWayland::Client;
+
+ // create two Surfaces
+ QScopedPointer surface(m_compositor->createSurface());
+ QScopedPointer parent(m_compositor->createSurface());
+ // create subSurface for surface of parent
+ QScopedPointer subSurface(m_subCompositor->createSubSurface(QPointer(surface.data()), QPointer(parent.data())));
+
+ QCOMPARE(SubSurface::get(*(subSurface.data())), QPointer(subSurface.data()));
+}
+
+QTEST_MAIN(TestSubSurface)
+#include "test_wayland_subsurface.moc"
diff --git a/src/wayland/display.cpp b/src/wayland/display.cpp
index 07a08b68da..fe71df50b9 100644
--- a/src/wayland/display.cpp
+++ b/src/wayland/display.cpp
@@ -22,6 +22,7 @@ License along with this library. If not, see .
#include "output_interface.h"
#include "seat_interface.h"
#include "shell_interface.h"
+#include "subcompositor_interface.h"
#include
#include
@@ -164,6 +165,13 @@ SeatInterface *Display::createSeat(QObject *parent)
return seat;
}
+SubCompositorInterface *Display::createSubCompositor(QObject *parent)
+{
+ auto c = new SubCompositorInterface(this, parent);
+ connect(this, &Display::aboutToTerminate, c, [this,c] { delete c; });
+ return c;
+}
+
void Display::createShm()
{
Q_ASSERT(d->running);
diff --git a/src/wayland/display.h b/src/wayland/display.h
index a47ece87eb..553ed14c22 100644
--- a/src/wayland/display.h
+++ b/src/wayland/display.h
@@ -37,6 +37,7 @@ class CompositorInterface;
class OutputInterface;
class SeatInterface;
class ShellInterface;
+class SubCompositorInterface;
class KWAYLANDSERVER_EXPORT Display : public QObject
{
@@ -68,6 +69,7 @@ public:
void createShm();
ShellInterface *createShell(QObject *parent = nullptr);
SeatInterface *createSeat(QObject *parent = nullptr);
+ SubCompositorInterface *createSubCompositor(QObject *parent = nullptr);
Q_SIGNALS:
void socketNameChanged(const QString&);
diff --git a/src/wayland/subcompositor_interface.cpp b/src/wayland/subcompositor_interface.cpp
new file mode 100644
index 0000000000..a7bcacb258
--- /dev/null
+++ b/src/wayland/subcompositor_interface.cpp
@@ -0,0 +1,337 @@
+/********************************************************************
+Copyright 2014 Martin Gräßlin
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) version 3, or any
+later version accepted by the membership of KDE e.V. (or its
+successor approved by the membership of KDE e.V.), which shall
+act as a proxy defined in Section 6 of version 3 of the license.
+
+This library 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
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library. If not, see .
+*********************************************************************/
+#include "subcompositor_interface.h"
+#include "subsurface_interface_p.h"
+#include "display.h"
+#include "surface_interface_p.h"
+// Wayland
+#include
+
+namespace KWayland
+{
+namespace Server
+{
+
+static const quint32 s_version = 1;
+
+class SubCompositorInterface::Private
+{
+public:
+ Private(SubCompositorInterface *q, Display *d);
+ void create();
+
+ Display *display;
+ wl_global *compositor;
+
+private:
+ void bind(wl_client *client, uint32_t version, uint32_t id);
+ void subsurface(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface, wl_resource *parent);
+
+ static void bind(wl_client *client, void *data, uint32_t version, uint32_t id);
+ static void unbind(wl_resource *resource);
+ static void destroyCallback(wl_client *client, wl_resource *resource);
+ static void subsurfaceCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface, wl_resource *parent);
+
+ static Private *cast(wl_resource *r) {
+ return reinterpret_cast(wl_resource_get_user_data(r));
+ }
+
+ SubCompositorInterface *q;
+ static const struct wl_subcompositor_interface s_interface;
+};
+
+const struct wl_subcompositor_interface SubCompositorInterface::Private::s_interface = {
+ destroyCallback,
+ subsurfaceCallback
+};
+
+SubCompositorInterface::Private::Private(SubCompositorInterface *q, Display *d)
+ : display(d)
+ , compositor(nullptr)
+ , q(q)
+{
+}
+
+void SubCompositorInterface::Private::create()
+{
+ Q_ASSERT(!compositor);
+ compositor = wl_global_create(*display, &wl_subcompositor_interface, s_version, this, bind);
+}
+
+void SubCompositorInterface::Private::bind(wl_client *client, void *data, uint32_t version, uint32_t id)
+{
+ reinterpret_cast(data)->bind(client, version, id);
+}
+
+void SubCompositorInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id)
+{
+ wl_resource *resource = wl_resource_create(client, &wl_subcompositor_interface, qMin(version, s_version), id);
+ if (!resource) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+ wl_resource_set_implementation(resource, &s_interface, this, unbind);
+}
+
+void SubCompositorInterface::Private::unbind(wl_resource *resource)
+{
+ Q_UNUSED(resource)
+}
+
+void SubCompositorInterface::Private::destroyCallback(wl_client *client, wl_resource *resource)
+{
+ Q_UNUSED(client)
+ Q_UNUSED(resource)
+}
+
+void SubCompositorInterface::Private::subsurfaceCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface, wl_resource *sparent)
+{
+ cast(resource)->subsurface(client, resource, id, surface, sparent);
+}
+
+void SubCompositorInterface::Private::subsurface(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *nativeSurface, wl_resource *nativeParentSurface)
+{
+ Q_UNUSED(client)
+ SurfaceInterface *surface = SurfaceInterface::get(nativeSurface);
+ SurfaceInterface *parentSurface = SurfaceInterface::get(nativeParentSurface);
+ if (!surface || !parentSurface) {
+ wl_resource_post_error(resource, WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, "Surface or parent surface not found");
+ return;
+ }
+ if (surface == parentSurface) {
+ wl_resource_post_error(resource, WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, "Cannot become sub composite to same surface");
+ return;
+ }
+ // TODO: add check that surface is not already used in an interface (e.g. Shell)
+ // TODO: add check that parentSurface is not a child of surface
+ SubSurfaceInterface *s = new SubSurfaceInterface(q);
+ s->d->create(client, wl_resource_get_version(resource), id, surface, parentSurface);
+ if (!s->subSurface()) {
+ wl_resource_post_no_memory(resource);
+ delete s;
+ return;
+ }
+ emit q->subSurfaceCreated(s);
+}
+
+SubCompositorInterface::SubCompositorInterface(Display *display, QObject *parent)
+ : QObject(parent)
+ , d(new Private(this, display))
+{
+}
+
+SubCompositorInterface::~SubCompositorInterface()
+{
+ destroy();
+}
+
+void SubCompositorInterface::destroy()
+{
+ if (!d->compositor) {
+ return;
+ }
+ wl_global_destroy(d->compositor);
+ d->compositor = nullptr;
+}
+
+void SubCompositorInterface::create()
+{
+ d->create();
+}
+
+bool SubCompositorInterface::isValid() const
+{
+ return d->compositor != nullptr;
+}
+
+const struct wl_subsurface_interface SubSurfaceInterface::Private::s_interface = {
+ destroyCallback,
+ setPositionCallback,
+ placeAboveCallback,
+ placeBelowCallback,
+ setSyncCallback,
+ setDeSyncCallback
+};
+
+SubSurfaceInterface::Private *SubSurfaceInterface::Private::cast(wl_resource *r)
+{
+ return reinterpret_cast(wl_resource_get_user_data(r));
+}
+
+SubSurfaceInterface::Private::Private(SubSurfaceInterface *q)
+ : q(q)
+{
+}
+
+SubSurfaceInterface::Private::~Private()
+{
+ // no need to notify the surface as it's tracking a QPointer which will be reset automatically
+ if (parent) {
+ parent->d->removeChild(QPointer(q));
+ }
+ if (subSurface) {
+ wl_resource_destroy(subSurface);
+ }
+}
+
+void SubSurfaceInterface::Private::create(wl_client *client, quint32 version, quint32 id, SurfaceInterface *s, SurfaceInterface *p)
+{
+ Q_ASSERT(!subSurface);
+ subSurface = wl_resource_create(client, &wl_subsurface_interface, version, id);
+ if (!subSurface) {
+ return;
+ }
+ surface = s;
+ parent = p;
+ surface->d->subSurface = QPointer(q);
+ parent->d->addChild(QPointer(q));
+ wl_resource_set_implementation(subSurface, &s_interface, this, unbind);
+}
+
+void SubSurfaceInterface::Private::commit()
+{
+ if (scheduledPosChange) {
+ scheduledPosChange = false;
+ pos = scheduledPos;
+ scheduledPos = QPoint();
+ emit q->positionChanged(pos);
+ }
+}
+
+void SubSurfaceInterface::Private::unbind(wl_resource *r)
+{
+ auto s = cast(r);
+ s->subSurface = nullptr;
+ s->q->deleteLater();
+}
+
+void SubSurfaceInterface::Private::destroyCallback(wl_client *client, wl_resource *resource)
+{
+ Q_UNUSED(client)
+ cast(resource)->q->deleteLater();
+}
+
+void SubSurfaceInterface::Private::setPositionCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y)
+{
+ Q_UNUSED(client)
+ // TODO: is this a fixed position?
+ cast(resource)->setPosition(QPoint(x, y));
+}
+
+void SubSurfaceInterface::Private::setPosition(const QPoint &p)
+{
+ if (scheduledPos == p) {
+ return;
+ }
+ scheduledPos = p;
+ scheduledPosChange = true;
+}
+
+void SubSurfaceInterface::Private::placeAboveCallback(wl_client *client, wl_resource *resource, wl_resource *sibling)
+{
+ Q_UNUSED(client)
+ cast(resource)->placeAbove(SurfaceInterface::get(sibling));
+}
+
+void SubSurfaceInterface::Private::placeAbove(SurfaceInterface *sibling)
+{
+ if (parent.isNull()) {
+ // TODO: raise error
+ return;
+ }
+ if (!parent->d->raiseChild(QPointer(q), sibling)) {
+ wl_resource_post_error(subSurface, WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, "Incorrect sibling");
+ }
+}
+
+void SubSurfaceInterface::Private::placeBelowCallback(wl_client *client, wl_resource *resource, wl_resource *sibling)
+{
+ Q_UNUSED(client)
+ cast(resource)->placeBelow(SurfaceInterface::get(sibling));
+}
+
+void SubSurfaceInterface::Private::placeBelow(SurfaceInterface *sibling)
+{
+ if (parent.isNull()) {
+ // TODO: raise error
+ return;
+ }
+ if (!parent->d->lowerChild(QPointer(q), sibling)) {
+ wl_resource_post_error(subSurface, WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, "Incorrect sibling");
+ }
+}
+
+void SubSurfaceInterface::Private::setSyncCallback(wl_client *client, wl_resource *resource)
+{
+ Q_UNUSED(client)
+ cast(resource)->setMode(Mode::Synchronized);
+}
+
+void SubSurfaceInterface::Private::setDeSyncCallback(wl_client *client, wl_resource *resource)
+{
+ Q_UNUSED(client)
+ cast(resource)->setMode(Mode::Desynchronized);
+}
+
+void SubSurfaceInterface::Private::setMode(Mode m)
+{
+ if (mode == m) {
+ return;
+ }
+ mode = m;
+ emit q->modeChanged(m);
+}
+
+SubSurfaceInterface::SubSurfaceInterface(SubCompositorInterface *parent)
+ : QObject(/*parent*/)
+ , d(new Private(this))
+{
+ Q_UNUSED(parent)
+}
+
+SubSurfaceInterface::~SubSurfaceInterface() = default;
+
+QPoint SubSurfaceInterface::position() const
+{
+ return d->pos;
+}
+
+wl_resource *SubSurfaceInterface::subSurface()
+{
+ return d->subSurface;
+}
+
+QPointer SubSurfaceInterface::surface()
+{
+ return d->surface;
+}
+
+QPointer SubSurfaceInterface::parentSurface()
+{
+ return d->parent;
+}
+
+SubSurfaceInterface::Mode SubSurfaceInterface::mode() const
+{
+ return d->mode;
+}
+
+}
+}
diff --git a/src/wayland/subcompositor_interface.h b/src/wayland/subcompositor_interface.h
new file mode 100644
index 0000000000..7ff142cf9c
--- /dev/null
+++ b/src/wayland/subcompositor_interface.h
@@ -0,0 +1,97 @@
+/********************************************************************
+Copyright 2014 Martin Gräßlin
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) version 3, or any
+later version accepted by the membership of KDE e.V. (or its
+successor approved by the membership of KDE e.V.), which shall
+act as a proxy defined in Section 6 of version 3 of the license.
+
+This library 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
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library. If not, see .
+*********************************************************************/
+#ifndef WAYLAND_SERVER_SUBCOMPOSITOR_INTERFACE_H
+#define WAYLAND_SERVER_SUBCOMPOSITOR_INTERFACE_H
+
+#include
+#include
+
+#include
+
+struct wl_resource;
+
+namespace KWayland
+{
+namespace Server
+{
+
+class Display;
+class SurfaceInterface;
+class SubSurfaceInterface;
+
+class KWAYLANDSERVER_EXPORT SubCompositorInterface : public QObject
+{
+ Q_OBJECT
+public:
+ virtual ~SubCompositorInterface();
+
+ void create();
+ void destroy();
+ bool isValid() const;
+
+Q_SIGNALS:
+ void subSurfaceCreated(KWayland::Server::SubSurfaceInterface*);
+
+private:
+ explicit SubCompositorInterface(Display *display, QObject *parent = nullptr);
+ friend class Display;
+ class Private;
+ QScopedPointer d;
+};
+
+class KWAYLANDSERVER_EXPORT SubSurfaceInterface : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QPoint position READ position NOTIFY positionChanged)
+ Q_PROPERTY(KWayland::Server::SubSurfaceInterface::Mode mode READ mode NOTIFY modeChanged)
+public:
+ virtual ~SubSurfaceInterface();
+
+ QPoint position() const;
+
+ enum class Mode {
+ Synchronized,
+ Desynchronized
+ };
+ Mode mode() const;
+
+ wl_resource *subSurface();
+ QPointer surface();
+ QPointer parentSurface();
+
+Q_SIGNALS:
+ void positionChanged(const QPoint&);
+ void modeChanged(KWayland::Server::SubSurfaceInterface::Mode);
+
+private:
+ friend class SubCompositorInterface;
+ friend class SurfaceInterface;
+ explicit SubSurfaceInterface(SubCompositorInterface *parent);
+
+ class Private;
+ QScopedPointer d;
+};
+
+}
+}
+
+Q_DECLARE_METATYPE(KWayland::Server::SubSurfaceInterface::Mode)
+
+#endif
diff --git a/src/wayland/subsurface_interface_p.h b/src/wayland/subsurface_interface_p.h
new file mode 100644
index 0000000000..82bf57337f
--- /dev/null
+++ b/src/wayland/subsurface_interface_p.h
@@ -0,0 +1,75 @@
+/********************************************************************
+Copyright 2014 Martin Gräßlin
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) version 3, or any
+later version accepted by the membership of KDE e.V. (or its
+successor approved by the membership of KDE e.V.), which shall
+act as a proxy defined in Section 6 of version 3 of the license.
+
+This library 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
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library. If not, see .
+*********************************************************************/
+#ifndef WAYLAND_SERVER_SUBSURFACE_INTERFACE_P_H
+#define WAYLAND_SERVER_SUBSURFACE_INTERFACE_P_H
+
+#include "subcompositor_interface.h"
+// Qt
+#include
+// Wayland
+#include
+
+namespace KWayland
+{
+namespace Server
+{
+
+class SubSurfaceInterface::Private
+{
+public:
+ Private(SubSurfaceInterface *q);
+ ~Private();
+
+ void create(wl_client *client, quint32 version, quint32 id, SurfaceInterface *surface, SurfaceInterface *parent);
+ void commit();
+
+ QPoint pos = QPoint(0, 0);
+ QPoint scheduledPos = QPoint();
+ bool scheduledPosChange = false;
+ Mode mode = Mode::Synchronized;
+ wl_resource *subSurface = nullptr;
+ QPointer surface;
+ QPointer parent;
+
+private:
+ void setMode(Mode mode);
+ void setPosition(const QPoint &pos);
+ void placeAbove(SurfaceInterface *sibling);
+ void placeBelow(SurfaceInterface *sibling);
+
+ static void unbind(wl_resource *r);
+ static void destroyCallback(wl_client *client, wl_resource *resource);
+ static void setPositionCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y);
+ static void placeAboveCallback(wl_client *client, wl_resource *resource, wl_resource *sibling);
+ static void placeBelowCallback(wl_client *client, wl_resource *resource, wl_resource *sibling);
+ static void setSyncCallback(wl_client *client, wl_resource *resource);
+ static void setDeSyncCallback(wl_client *client, wl_resource *resource);
+
+ static Private *cast(wl_resource *r);
+
+ SubSurfaceInterface *q;
+
+ static const struct wl_subsurface_interface s_interface;
+};
+
+}
+}
+
+#endif
diff --git a/src/wayland/surface_interface.cpp b/src/wayland/surface_interface.cpp
index a983f4d620..79a74e1073 100644
--- a/src/wayland/surface_interface.cpp
+++ b/src/wayland/surface_interface.cpp
@@ -18,75 +18,21 @@ You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see .
*********************************************************************/
#include "surface_interface.h"
+#include "surface_interface_p.h"
#include "buffer_interface.h"
#include "compositor_interface.h"
+#include "subcompositor_interface.h"
+#include "subsurface_interface_p.h"
// Wayland
#include
+// std
+#include
namespace KWayland
{
namespace Server
{
-class SurfaceInterface::Private
-{
-public:
- struct State {
- QRegion damage = QRegion();
- QRegion opaque = QRegion();
- QRegion input = QRegion();
- qint32 scale = 1;
- OutputInterface::Transform transform = OutputInterface::Transform::Normal;
- QList callbacks = QList();
- QPoint offset = QPoint();
- BufferInterface *buffer = nullptr;
- };
- Private(SurfaceInterface *q, CompositorInterface *c);
- ~Private();
-
- void create(wl_client *client, quint32 version, quint32 id);
- void destroy();
-
- static SurfaceInterface *get(wl_resource *native);
-
- CompositorInterface *compositor;
- wl_resource *surface = nullptr;
- wl_client *client = nullptr;
- State current;
- State pending;
-
-private:
- void commit();
- void damage(const QRect &rect);
- void setScale(qint32 scale);
- void setTransform(OutputInterface::Transform transform);
- void addFrameCallback(uint32_t callback);
- void attachBuffer(wl_resource *buffer, const QPoint &offset);
-
- static Private *cast(wl_resource *r) {
- return reinterpret_cast(wl_resource_get_user_data(r));
- }
-
- static void unbind(wl_resource *r);
- static void destroyFrameCallback(wl_resource *r);
-
- static void destroyCallback(wl_client *client, wl_resource *resource);
- static void attachCallback(wl_client *client, wl_resource *resource, wl_resource *buffer, int32_t sx, int32_t sy);
- static void damageCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height);
- static void frameCallaback(wl_client *client, wl_resource *resource, uint32_t callback);
- static void opaqueRegionCallback(wl_client *client, wl_resource *resource, wl_resource *region);
- static void inputRegionCallback(wl_client *client, wl_resource *resource, wl_resource *region);
- static void commitCallback(wl_client *client, wl_resource *resource);
- // since version 2
- static void bufferTransformCallback(wl_client *client, wl_resource *resource, int32_t transform);
- // since version 3
- static void bufferScaleCallback(wl_client *client, wl_resource *resource, int32_t scale);
-
- SurfaceInterface *q;
- static const struct wl_surface_interface s_interface;
- static QList s_allSurfaces;
-};
-
QList SurfaceInterface::Private::s_allSurfaces;
SurfaceInterface::Private::Private(SurfaceInterface *q, CompositorInterface *c)
@@ -102,6 +48,88 @@ SurfaceInterface::Private::~Private()
s_allSurfaces.removeAll(this);
}
+SurfaceInterface::Private *SurfaceInterface::Private::cast(wl_resource *r)
+{
+ return reinterpret_cast(wl_resource_get_user_data(r));
+}
+
+void SurfaceInterface::Private::addChild(QPointer< SubSurfaceInterface > subSurface)
+{
+ pending.children.append(subSurface);
+}
+
+void SurfaceInterface::Private::removeChild(QPointer< SubSurfaceInterface > subSurface)
+{
+ pending.children.removeAll(subSurface);
+}
+
+bool SurfaceInterface::Private::raiseChild(QPointer subsurface, SurfaceInterface *sibling)
+{
+ auto it = std::find(pending.children.begin(), pending.children.end(), subsurface);
+ if (it == pending.children.end()) {
+ return false;
+ }
+ if (pending.children.count() == 1) {
+ // nothing to do
+ return true;
+ }
+ if (sibling == q) {
+ // it's to the parent, so needs to become last item
+ pending.children.append(*it);
+ pending.children.erase(it);
+ return true;
+ }
+ if (!sibling->subSurface()) {
+ // not a sub surface
+ return false;
+ }
+ auto siblingIt = std::find(pending.children.begin(), pending.children.end(), sibling->subSurface());
+ if (siblingIt == pending.children.end() || siblingIt == it) {
+ // not a sibling
+ return false;
+ }
+ auto value = (*it);
+ pending.children.erase(it);
+ // find the iterator again
+ siblingIt = std::find(pending.children.begin(), pending.children.end(), sibling->subSurface());
+ pending.children.insert(++siblingIt, value);
+ return true;
+}
+
+bool SurfaceInterface::Private::lowerChild(QPointer subsurface, SurfaceInterface *sibling)
+{
+ auto it = std::find(pending.children.begin(), pending.children.end(), subsurface);
+ if (it == pending.children.end()) {
+ return false;
+ }
+ if (pending.children.count() == 1) {
+ // nothing to do
+ return true;
+ }
+ if (sibling == q) {
+ // it's to the parent, so needs to become first item
+ auto value = *it;
+ pending.children.erase(it);
+ pending.children.prepend(value);
+ return true;
+ }
+ if (!sibling->subSurface()) {
+ // not a sub surface
+ return false;
+ }
+ auto siblingIt = std::find(pending.children.begin(), pending.children.end(), sibling->subSurface());
+ if (siblingIt == pending.children.end() || siblingIt == it) {
+ // not a sibling
+ return false;
+ }
+ auto value = (*it);
+ pending.children.erase(it);
+ // find the iterator again
+ siblingIt = std::find(pending.children.begin(), pending.children.end(), sibling->subSurface());
+ pending.children.insert(siblingIt, value);
+ return true;
+}
+
SurfaceInterface *SurfaceInterface::Private::get(wl_resource *native)
{
auto it = std::find_if(s_allSurfaces.constBegin(), s_allSurfaces.constEnd(), [native](Private *s) {
@@ -202,6 +230,14 @@ void SurfaceInterface::Private::commit()
// copy values
current = pending;
pending = State{};
+ pending.children = current.children;
+ // commit all subSurfaces to apply position changes
+ for (auto it = current.children.constBegin(); it != current.children.constEnd(); ++it) {
+ if (!(*it)) {
+ continue;
+ }
+ (*it)->d->commit();
+ }
if (opaqueRegionChanged) {
emit q->opaqueChanged(current.opaque);
}
@@ -371,5 +407,15 @@ SurfaceInterface *SurfaceInterface::get(wl_resource *native)
return Private::get(native);
}
+QList< QPointer< SubSurfaceInterface > > SurfaceInterface::childSubSurfaces() const
+{
+ return d->current.children;
+}
+
+QPointer< SubSurfaceInterface > SurfaceInterface::subSurface() const
+{
+ return d->subSurface;
+}
+
}
}
diff --git a/src/wayland/surface_interface.h b/src/wayland/surface_interface.h
index c98cc93f8f..92f0629615 100644
--- a/src/wayland/surface_interface.h
+++ b/src/wayland/surface_interface.h
@@ -23,6 +23,7 @@ License along with this library. If not, see .
#include "output_interface.h"
#include
+#include
#include
#include
@@ -33,6 +34,7 @@ namespace Server
{
class BufferInterface;
class CompositorInterface;
+class SubSurfaceInterface;
class KWAYLANDSERVER_EXPORT SurfaceInterface : public QObject
{
@@ -60,6 +62,15 @@ public:
BufferInterface *buffer();
QPoint offset() const;
+ /**
+ * @returns The SubSurface for this Surface in case there is one.
+ **/
+ QPointer subSurface() const;
+ /**
+ * @returns Children in stacking order from bottom (first) to top (last).
+ **/
+ QList> childSubSurfaces() const;
+
static SurfaceInterface *get(wl_resource *native);
Q_SIGNALS:
@@ -71,6 +82,7 @@ Q_SIGNALS:
private:
friend class CompositorInterface;
+ friend class SubSurfaceInterface;
explicit SurfaceInterface(CompositorInterface *parent);
class Private;
diff --git a/src/wayland/surface_interface_p.h b/src/wayland/surface_interface_p.h
new file mode 100644
index 0000000000..530cd17aba
--- /dev/null
+++ b/src/wayland/surface_interface_p.h
@@ -0,0 +1,100 @@
+/********************************************************************
+Copyright 2014 Martin Gräßlin
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) version 3, or any
+later version accepted by the membership of KDE e.V. (or its
+successor approved by the membership of KDE e.V.), which shall
+act as a proxy defined in Section 6 of version 3 of the license.
+
+This library 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
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library. If not, see .
+*********************************************************************/
+#ifndef WAYLAND_SERVER_SURFACE_INTERFACE_P_H
+#define WAYLAND_SERVER_SURFACE_INTERFACE_P_H
+
+#include "surface_interface.h"
+// Wayland
+#include
+
+namespace KWayland
+{
+namespace Server
+{
+
+class SurfaceInterface::Private
+{
+public:
+ struct State {
+ QRegion damage = QRegion();
+ QRegion opaque = QRegion();
+ QRegion input = QRegion();
+ qint32 scale = 1;
+ OutputInterface::Transform transform = OutputInterface::Transform::Normal;
+ QList callbacks = QList();
+ QPoint offset = QPoint();
+ BufferInterface *buffer = nullptr;
+ // stacking order: bottom (first) -> top (last)
+ QList> children;
+ };
+ Private(SurfaceInterface *q, CompositorInterface *c);
+ ~Private();
+
+ void create(wl_client *client, quint32 version, quint32 id);
+ void destroy();
+
+ void addChild(QPointer subsurface);
+ void removeChild(QPointer subsurface);
+ bool raiseChild(QPointer subsurface, SurfaceInterface *sibling);
+ bool lowerChild(QPointer subsurface, SurfaceInterface *sibling);
+
+ static SurfaceInterface *get(wl_resource *native);
+
+ CompositorInterface *compositor;
+ wl_resource *surface = nullptr;
+ wl_client *client = nullptr;
+ State current;
+ State pending;
+ QPointer subSurface;
+
+private:
+ void commit();
+ void damage(const QRect &rect);
+ void setScale(qint32 scale);
+ void setTransform(OutputInterface::Transform transform);
+ void addFrameCallback(uint32_t callback);
+ void attachBuffer(wl_resource *buffer, const QPoint &offset);
+
+ static Private *cast(wl_resource *r);
+
+ static void unbind(wl_resource *r);
+ static void destroyFrameCallback(wl_resource *r);
+
+ static void destroyCallback(wl_client *client, wl_resource *resource);
+ static void attachCallback(wl_client *client, wl_resource *resource, wl_resource *buffer, int32_t sx, int32_t sy);
+ static void damageCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height);
+ static void frameCallaback(wl_client *client, wl_resource *resource, uint32_t callback);
+ static void opaqueRegionCallback(wl_client *client, wl_resource *resource, wl_resource *region);
+ static void inputRegionCallback(wl_client *client, wl_resource *resource, wl_resource *region);
+ static void commitCallback(wl_client *client, wl_resource *resource);
+ // since version 2
+ static void bufferTransformCallback(wl_client *client, wl_resource *resource, int32_t transform);
+ // since version 3
+ static void bufferScaleCallback(wl_client *client, wl_resource *resource, int32_t scale);
+
+ SurfaceInterface *q;
+ static const struct wl_surface_interface s_interface;
+ static QList s_allSurfaces;
+};
+
+}
+}
+
+#endif