From 7fce72b7d11f0688318c075906548799cf6fd4ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Thu, 16 Oct 2014 13:04:51 +0200 Subject: [PATCH] Implement the wl_region interface Compositor::createRegion added which returns a Region. Also server side part is implemented. --- src/wayland/CMakeLists.txt | 2 + src/wayland/autotests/client/CMakeLists.txt | 11 + .../autotests/client/test_wayland_region.cpp | 285 ++++++++++++++++++ src/wayland/compositor_interface.cpp | 12 +- src/wayland/compositor_interface.h | 2 + src/wayland/region_interface.cpp | 165 ++++++++++ src/wayland/server/region_interface.h | 65 ++++ 7 files changed, 538 insertions(+), 4 deletions(-) create mode 100644 src/wayland/autotests/client/test_wayland_region.cpp create mode 100644 src/wayland/region_interface.cpp create mode 100644 src/wayland/server/region_interface.h diff --git a/src/wayland/CMakeLists.txt b/src/wayland/CMakeLists.txt index 53b1a91891..d565f3eca6 100644 --- a/src/wayland/CMakeLists.txt +++ b/src/wayland/CMakeLists.txt @@ -3,6 +3,7 @@ set(SERVER_LIB_SRCS compositor_interface.cpp display.cpp output_interface.cpp + region_interface.cpp seat_interface.cpp shell_interface.cpp surface_interface.cpp @@ -46,6 +47,7 @@ set_target_properties(KF5WaylandServer PROPERTIES VERSION ${KWAYLAND_VERSION_S # compositor_interface.h # display.h # output_interface.h +# region_interface.h # seat_interface.h # shell_interface.h # surface_interface.h diff --git a/src/wayland/autotests/client/CMakeLists.txt b/src/wayland/autotests/client/CMakeLists.txt index b593d17d12..e90e4dd54b 100644 --- a/src/wayland/autotests/client/CMakeLists.txt +++ b/src/wayland/autotests/client/CMakeLists.txt @@ -123,3 +123,14 @@ 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) + +######################################################## +# Test Region +######################################################## +set( testRegion_SRCS + test_wayland_region.cpp + ) +add_executable(testRegion ${testRegion_SRCS}) +target_link_libraries( testRegion Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer) +add_test(kwayland-testRegion testRegion) +ecm_mark_as_test(testRegion) diff --git a/src/wayland/autotests/client/test_wayland_region.cpp b/src/wayland/autotests/client/test_wayland_region.cpp new file mode 100644 index 0000000000..d12037bd51 --- /dev/null +++ b/src/wayland/autotests/client/test_wayland_region.cpp @@ -0,0 +1,285 @@ +/******************************************************************** +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/region.h" +#include "../../src/client/registry.h" +#include "../../src/server/display.h" +#include "../../src/server/compositor_interface.h" +#include "../../src/server/region_interface.h" +// Wayland +#include + +class TestRegion : public QObject +{ + Q_OBJECT +public: + explicit TestRegion(QObject *parent = nullptr); +private Q_SLOTS: + void init(); + void cleanup(); + + void testCreate(); + void testCreateWithRegion(); + void testCreateUniquePtr(); + void testAdd(); + void testRemove(); + void testDestroy(); + +private: + KWayland::Server::Display *m_display; + KWayland::Server::CompositorInterface *m_compositorInterface; + KWayland::Client::ConnectionThread *m_connection; + KWayland::Client::Compositor *m_compositor; + KWayland::Client::EventQueue *m_queue; + QThread *m_thread; +}; + +static const QString s_socketName = QStringLiteral("kwayland-test-wayland-region-0"); + +TestRegion::TestRegion(QObject *parent) + : QObject(parent) + , m_display(nullptr) + , m_compositorInterface(nullptr) + , m_connection(nullptr) + , m_compositor(nullptr) + , m_queue(nullptr) + , m_thread(nullptr) +{ +} + +void TestRegion::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()); + 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()); + + QVERIFY(compositorSpy.wait()); + m_compositor = registry.createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this); +} + +void TestRegion::cleanup() +{ + 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 TestRegion::testCreate() +{ + using namespace KWayland::Client; + using namespace KWayland::Server; + QSignalSpy regionCreatedSpy(m_compositorInterface, SIGNAL(regionCreated(KWayland::Server::RegionInterface*))); + QVERIFY(regionCreatedSpy.isValid()); + + QScopedPointer region(m_compositor->createRegion()); + QCOMPARE(region->region(), QRegion()); + + QVERIFY(regionCreatedSpy.wait()); + QCOMPARE(regionCreatedSpy.count(), 1); + auto serverRegion = regionCreatedSpy.first().first().value(); + QVERIFY(serverRegion); + QCOMPARE(serverRegion->region(), QRegion()); +} + +void TestRegion::testCreateWithRegion() +{ + using namespace KWayland::Client; + using namespace KWayland::Server; + QSignalSpy regionCreatedSpy(m_compositorInterface, SIGNAL(regionCreated(KWayland::Server::RegionInterface*))); + QVERIFY(regionCreatedSpy.isValid()); + + QScopedPointer region(m_compositor->createRegion(QRegion(0, 0, 10, 20), nullptr)); + QCOMPARE(region->region(), QRegion(0, 0, 10, 20)); + + QVERIFY(regionCreatedSpy.wait()); + QCOMPARE(regionCreatedSpy.count(), 1); + auto serverRegion = regionCreatedSpy.first().first().value(); + QVERIFY(serverRegion); + QCOMPARE(serverRegion->region(), QRegion(0, 0, 10, 20)); +} + +void TestRegion::testCreateUniquePtr() +{ + using namespace KWayland::Client; + using namespace KWayland::Server; + QSignalSpy regionCreatedSpy(m_compositorInterface, SIGNAL(regionCreated(KWayland::Server::RegionInterface*))); + QVERIFY(regionCreatedSpy.isValid()); + + std::unique_ptr region(m_compositor->createRegion(QRegion(0, 0, 10, 20))); + QCOMPARE(region->region(), QRegion(0, 0, 10, 20)); + + QVERIFY(regionCreatedSpy.wait()); + QCOMPARE(regionCreatedSpy.count(), 1); + auto serverRegion = regionCreatedSpy.first().first().value(); + QVERIFY(serverRegion); + QCOMPARE(serverRegion->region(), QRegion(0, 0, 10, 20)); +} + +void TestRegion::testAdd() +{ + using namespace KWayland::Client; + using namespace KWayland::Server; + QSignalSpy regionCreatedSpy(m_compositorInterface, SIGNAL(regionCreated(KWayland::Server::RegionInterface*))); + QVERIFY(regionCreatedSpy.isValid()); + + QScopedPointer region(m_compositor->createRegion()); + QVERIFY(regionCreatedSpy.wait()); + auto serverRegion = regionCreatedSpy.first().first().value(); + + QSignalSpy regionChangedSpy(serverRegion, SIGNAL(regionChanged(QRegion))); + QVERIFY(regionChangedSpy.isValid()); + + // adding a QRect + region->add(QRect(0, 0, 10, 20)); + QCOMPARE(region->region(), QRegion(0, 0, 10, 20)); + + QVERIFY(regionChangedSpy.wait()); + QCOMPARE(regionChangedSpy.count(), 1); + QCOMPARE(regionChangedSpy.last().first().value(), QRegion(0, 0, 10, 20)); + QCOMPARE(serverRegion->region(), QRegion(0, 0, 10, 20)); + + // adding a QRegion + region->add(QRegion(5, 5, 10, 50)); + QRegion compareRegion(0, 0, 10, 20); + compareRegion = compareRegion.united(QRect(5, 5, 10, 50)); + QCOMPARE(region->region(), compareRegion); + + QVERIFY(regionChangedSpy.wait()); + QCOMPARE(regionChangedSpy.count(), 2); + QCOMPARE(regionChangedSpy.last().first().value(), compareRegion); + QCOMPARE(serverRegion->region(), compareRegion); +} + +void TestRegion::testRemove() +{ + using namespace KWayland::Client; + using namespace KWayland::Server; + QSignalSpy regionCreatedSpy(m_compositorInterface, SIGNAL(regionCreated(KWayland::Server::RegionInterface*))); + QVERIFY(regionCreatedSpy.isValid()); + + std::unique_ptr region(m_compositor->createRegion(QRegion(0, 0, 100, 200))); + QVERIFY(regionCreatedSpy.wait()); + auto serverRegion = regionCreatedSpy.first().first().value(); + + QSignalSpy regionChangedSpy(serverRegion, SIGNAL(regionChanged(QRegion))); + QVERIFY(regionChangedSpy.isValid()); + + // subtract a QRect + region->subtract(QRect(0, 0, 10, 20)); + QRegion compareRegion(0, 0, 100, 200); + compareRegion = compareRegion.subtracted(QRect(0, 0, 10, 20)); + QCOMPARE(region->region(), compareRegion); + + QVERIFY(regionChangedSpy.wait()); + QCOMPARE(regionChangedSpy.count(), 1); + QCOMPARE(regionChangedSpy.last().first().value(), compareRegion); + QCOMPARE(serverRegion->region(), compareRegion); + + // subtracting a QRegion + region->subtract(QRegion(5, 5, 10, 50)); + compareRegion = compareRegion.subtracted(QRect(5, 5, 10, 50)); + QCOMPARE(region->region(), compareRegion); + + QVERIFY(regionChangedSpy.wait()); + QCOMPARE(regionChangedSpy.count(), 2); + QCOMPARE(regionChangedSpy.last().first().value(), compareRegion); + QCOMPARE(serverRegion->region(), compareRegion); +} + +void TestRegion::testDestroy() +{ + using namespace KWayland::Client; + QScopedPointer region(m_compositor->createRegion()); + + connect(m_connection, &ConnectionThread::connectionDied, region.data(), &Region::destroy); + connect(m_connection, &ConnectionThread::connectionDied, m_compositor, &Compositor::destroy); + connect(m_connection, &ConnectionThread::connectionDied, m_queue, &EventQueue::destroy); + QVERIFY(region->isValid()); + + QSignalSpy connectionDiedSpy(m_connection, SIGNAL(connectionDied())); + QVERIFY(connectionDiedSpy.isValid()); + delete m_display; + m_display = nullptr; + QVERIFY(connectionDiedSpy.wait()); + + // now the region should be destroyed; + QVERIFY(!region->isValid()); + + // calling destroy again should not fail + region->destroy(); +} + +QTEST_MAIN(TestRegion) +#include "test_wayland_region.moc" diff --git a/src/wayland/compositor_interface.cpp b/src/wayland/compositor_interface.cpp index 8338e395ff..9f61335d82 100644 --- a/src/wayland/compositor_interface.cpp +++ b/src/wayland/compositor_interface.cpp @@ -146,10 +146,14 @@ void CompositorInterface::Private::createRegionCallback(wl_client *client, wl_re void CompositorInterface::Private::createRegion(wl_client *client, wl_resource *resource, uint32_t id) { - Q_UNUSED(client) - Q_UNUSED(resource) - Q_UNUSED(id) - // TODO: implement + RegionInterface *region = new RegionInterface(q); + region->create(client, wl_resource_get_version(resource), id); + if (!region->resource()) { + wl_resource_post_no_memory(resource); + delete region; + return; + } + emit q->regionCreated(region); } bool CompositorInterface::isValid() const diff --git a/src/wayland/compositor_interface.h b/src/wayland/compositor_interface.h index 7e6b2384a6..6b972c90cc 100644 --- a/src/wayland/compositor_interface.h +++ b/src/wayland/compositor_interface.h @@ -20,6 +20,7 @@ License along with this library. If not, see . #ifndef WAYLAND_SERVER_COMPOSITOR_INTERFACE_H #define WAYLAND_SERVER_COMPOSITOR_INTERFACE_H +#include "region_interface.h" #include "surface_interface.h" #include @@ -46,6 +47,7 @@ public: Q_SIGNALS: void surfaceCreated(KWayland::Server::SurfaceInterface*); + void regionCreated(KWayland::Server::RegionInterface*); private: explicit CompositorInterface(Display *display, QObject *parent = nullptr); diff --git a/src/wayland/region_interface.cpp b/src/wayland/region_interface.cpp new file mode 100644 index 0000000000..4860d6441a --- /dev/null +++ b/src/wayland/region_interface.cpp @@ -0,0 +1,165 @@ +/******************************************************************** +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 "region_interface.h" +#include "compositor_interface.h" +// Wayland +#include + +namespace KWayland +{ +namespace Server +{ + +class RegionInterface::Private +{ +public: + Private(CompositorInterface *compositor, RegionInterface *q); + ~Private(); + void create(wl_client *client, quint32 version, quint32 id); + CompositorInterface *compositor; + wl_resource *region = nullptr; + QRegion qtRegion; + + static RegionInterface *get(wl_resource *native); + +private: + void add(const QRect &rect); + void subtract(const QRect &rect); + + static void unbind(wl_resource *r); + static void destroyCallback(wl_client *client, wl_resource *r); + static void addCallback(wl_client *client, wl_resource *r, int32_t x, int32_t y, int32_t width, int32_t height); + static void subtractCallback(wl_client *client, wl_resource *r, int32_t x, int32_t y, int32_t width, int32_t height); + + static Private *cast(wl_resource *r) { + return reinterpret_cast(wl_resource_get_user_data(r)); + } + + static const struct wl_region_interface s_interface; + RegionInterface *q; +}; + +const struct wl_region_interface RegionInterface::Private::s_interface = { + destroyCallback, + addCallback, + subtractCallback +}; + +RegionInterface::Private::Private(CompositorInterface *compositor, RegionInterface *q) + : compositor(compositor) + , q(q) +{ +} + +RegionInterface::Private::~Private() +{ + if (region) { + wl_resource_destroy(region); + } +} + +void RegionInterface::Private::add(const QRect &rect) +{ + qtRegion = qtRegion.united(rect); + emit q->regionChanged(qtRegion); +} + +void RegionInterface::Private::subtract(const QRect &rect) +{ + if (qtRegion.isEmpty()) { + return; + } + qtRegion = qtRegion.subtracted(rect); + emit q->regionChanged(qtRegion); +} + +void RegionInterface::Private::addCallback(wl_client *client, wl_resource *r, int32_t x, int32_t y, int32_t width, int32_t height) +{ + Q_UNUSED(client) + cast(r)->add(QRect(x, y, width, height)); +} + +void RegionInterface::Private::subtractCallback(wl_client *client, wl_resource *r, int32_t x, int32_t y, int32_t width, int32_t height) +{ + Q_UNUSED(client) + cast(r)->subtract(QRect(x, y, width, height)); +} + +void RegionInterface::Private::destroyCallback(wl_client *client, wl_resource *r) +{ + Q_UNUSED(client) + cast(r)->q->deleteLater(); +} + +void RegionInterface::Private::unbind(wl_resource *r) +{ + auto region = cast(r); + region->region = nullptr; + region->q->deleteLater(); +} + +void RegionInterface::Private::create(wl_client *client, quint32 version, quint32 id) +{ + Q_ASSERT(!region); + region = wl_resource_create(client, &wl_region_interface, version, id); + if (!region) { + return; + } + wl_resource_set_implementation(region, &s_interface, this, unbind); +} + +RegionInterface *RegionInterface::Private::get(wl_resource *native) +{ + if (!native) { + return nullptr; + } + return cast(native)->q; +} + +RegionInterface::RegionInterface(CompositorInterface *parent) + : QObject(/*parent*/) + , d(new Private(parent, this)) +{ +} + +RegionInterface::~RegionInterface() = default; + +void RegionInterface::create(wl_client *client, quint32 version, quint32 id) +{ + d->create(client, version, id); +} + +QRegion RegionInterface::region() const +{ + return d->qtRegion; +} + +wl_resource *RegionInterface::resource() const +{ + return d->region; +} + +RegionInterface *RegionInterface::get(wl_resource *native) +{ + return Private::get(native); +} + +} +} diff --git a/src/wayland/server/region_interface.h b/src/wayland/server/region_interface.h new file mode 100644 index 0000000000..106f116eb2 --- /dev/null +++ b/src/wayland/server/region_interface.h @@ -0,0 +1,65 @@ +/******************************************************************** +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_REGION_INTERFACE_H +#define WAYLAND_SERVER_REGION_INTERFACE_H + +#include +#include + +#include + +struct wl_client; +struct wl_resource; + +namespace KWayland +{ +namespace Server +{ +class CompositorInterface; + +class KWAYLANDSERVER_EXPORT RegionInterface : public QObject +{ + Q_OBJECT +public: + virtual ~RegionInterface(); + + void create(wl_client *client, quint32 version, quint32 id); + + wl_resource *resource() const; + + QRegion region() const; + + static RegionInterface *get(wl_resource *native); + +Q_SIGNALS: + void regionChanged(const QRegion&); + +private: + friend class CompositorInterface; + explicit RegionInterface(CompositorInterface *parent); + + class Private; + QScopedPointer d; +}; + +} +} + +#endif