diff --git a/autotests/client/CMakeLists.txt b/autotests/client/CMakeLists.txt --- a/autotests/client/CMakeLists.txt +++ b/autotests/client/CMakeLists.txt @@ -349,6 +349,17 @@ ecm_mark_as_test(testXdgShellV5) ######################################################## +# Test XdgForeign +######################################################## +set( testXdgForeign_SRCS + test_xdg_foreign.cpp + ) +add_executable(testXdgForeign ${testXdgForeign_SRCS}) +target_link_libraries( testXdgForeign Qt5::Test Qt5::Gui KF5::WaylandServer KF5::WaylandClient Wayland::Client) +add_test(NAME kwayland-testXdgForeign COMMAND testXdgForeign) +ecm_mark_as_test(testXdgForeign) + +######################################################## # Test Pointer Constraints ######################################################## add_executable(testPointerConstraints test_pointer_constraints.cpp) diff --git a/autotests/client/test_xdg_foreign.cpp b/autotests/client/test_xdg_foreign.cpp new file mode 100644 --- /dev/null +++ b/autotests/client/test_xdg_foreign.cpp @@ -0,0 +1,401 @@ +/******************************************************************** +Copyright 2014 Martin Gräßlin +Copyright 2017 Marco Martin + +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/client/surface.h" +#include "../../src/client/xdgforeign.h" +#include "../../src/server/display.h" +#include "../../src/server/compositor_interface.h" +#include "../../src/server/surface_interface.h" +#include "../../src/server/xdgforeign_interface.h" + +using namespace KWayland::Client; + +class TestForeign : public QObject +{ + Q_OBJECT +public: + explicit TestForeign(QObject *parent = nullptr); +private Q_SLOTS: + void init(); + void cleanup(); + + void testExport(); + void testDeleteImported(); + void testDeleteChildSurface(); + void testDeleteParentSurface(); + void testDeleteExported(); + void testExportTwoTimes(); + void testImportTwoTimes(); + +private: + void doExport(); + + KWayland::Server::Display *m_display; + KWayland::Server::CompositorInterface *m_compositorInterface; + KWayland::Server::XdgForeignInterface *m_foreignInterface; + KWayland::Client::ConnectionThread *m_connection; + KWayland::Client::Compositor *m_compositor; + KWayland::Client::EventQueue *m_queue; + KWayland::Client::XdgExporter *m_exporter; + KWayland::Client::XdgImporter *m_importer; + + QPointer m_exportedSurface; + QPointer m_exportedSurfaceInterface; + + QPointer m_exported; + QPointer m_imported; + + QPointer m_childSurface; + QPointer m_childSurfaceInterface; + + QThread *m_thread; +}; + +static const QString s_socketName = QStringLiteral("kwayland-test-xdg-foreign-0"); + +TestForeign::TestForeign(QObject *parent) + : QObject(parent) + , m_display(nullptr) + , m_compositorInterface(nullptr) + , m_connection(nullptr) + , m_compositor(nullptr) + , m_queue(nullptr) + , m_exporter(nullptr) + , m_importer(nullptr) + , m_thread(nullptr) +{ +} + +void TestForeign::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()); + + qRegisterMetaType("KWayland::Server::SurfaceInterface"); + + // setup connection + m_connection = new KWayland::Client::ConnectionThread; + QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); + QVERIFY(connectedSpy.isValid()); + 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()); + + Registry registry; + QSignalSpy compositorSpy(®istry, &Registry::compositorAnnounced); + QVERIFY(compositorSpy.isValid()); + + QSignalSpy exporterSpy(®istry, &Registry::exporterUnstableV2Announced); + QVERIFY(exporterSpy.isValid()); + + QSignalSpy importerSpy(®istry, &Registry::importerUnstableV2Announced); + QVERIFY(importerSpy.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); + + m_foreignInterface = m_display->createXdgForeignUnstableInterface(m_display); + m_foreignInterface->create(); + QVERIFY(m_foreignInterface->isValid()); + + QVERIFY(exporterSpy.wait()); + //Both importer and exporter should have been triggered by now + QCOMPARE(exporterSpy.count(), 1); + QCOMPARE(importerSpy.count(), 1); + + m_exporter = registry.createXdgExporterUnstable(exporterSpy.first().first().value(), exporterSpy.first().last().value(), this); + m_importer = registry.createXdgImporterUnstable(importerSpy.first().first().value(), importerSpy.first().last().value(), this); +} + +void TestForeign::cleanup() +{ +#define CLEANUP(variable) \ + if (variable) { \ + delete variable; \ + variable = nullptr; \ + } + + //some tests delete it beforehand + if (m_exportedSurfaceInterface) { + QSignalSpy exportedSurfaceDestroyedSpy(m_exportedSurfaceInterface.data(), &QObject::destroyed); + QVERIFY(exportedSurfaceDestroyedSpy.isValid()); + CLEANUP(m_exportedSurface) + exportedSurfaceDestroyedSpy.wait(); + } + + if (m_childSurfaceInterface) { + QSignalSpy childSurfaceDestroyedSpy(m_childSurfaceInterface.data(), &QObject::destroyed); + QVERIFY(childSurfaceDestroyedSpy.isValid()); + CLEANUP(m_childSurface) + childSurfaceDestroyedSpy.wait(); + } + + + CLEANUP(m_compositor) + CLEANUP(m_exporter) + CLEANUP(m_importer) + CLEANUP(m_queue) + if (m_connection) { + m_connection->deleteLater(); + m_connection = nullptr; + } + if (m_thread) { + m_thread->quit(); + m_thread->wait(); + delete m_thread; + m_thread = nullptr; + } + CLEANUP(m_compositorInterface) + CLEANUP(m_foreignInterface) + CLEANUP(m_display) +#undef CLEANUP +} + +void TestForeign::doExport() +{ + QSignalSpy serverSurfaceCreated(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); + QVERIFY(serverSurfaceCreated.isValid()); + + m_exportedSurface = m_compositor->createSurface(); + QVERIFY(serverSurfaceCreated.wait()); + + m_exportedSurfaceInterface = serverSurfaceCreated.first().first().value(); + + //Export a window + m_exported = m_exporter->exportSurface(m_exportedSurface, this); + QVERIFY(m_exported->handle().isEmpty()); + QSignalSpy doneSpy(m_exported.data(), &XdgExported::done); + QVERIFY(doneSpy.wait()); + QVERIFY(!m_exported->handle().isEmpty()); + + QSignalSpy transientSpy(m_foreignInterface, &KWayland::Server::XdgForeignInterface::transientChanged); + QVERIFY(transientSpy.isValid()); + + //Import the just exported window + m_imported = m_importer->import(m_exported->handle(), this); + QVERIFY(m_imported->isValid()); + + QSignalSpy childSurfaceInterfaceCreated(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); + QVERIFY(serverSurfaceCreated.isValid()); + m_childSurface = m_compositor->createSurface(); + QVERIFY(childSurfaceInterfaceCreated.wait()); + m_childSurfaceInterface = childSurfaceInterfaceCreated.first().first().value(); + m_childSurface->commit(Surface::CommitFlag::None); + + m_imported->setParentOf(m_childSurface); + QVERIFY(transientSpy.wait()); + + QCOMPARE(transientSpy.first().first().value(), m_childSurfaceInterface.data()); + QCOMPARE(transientSpy.first().at(1).value(), m_exportedSurfaceInterface.data()); + + //transientFor api + QCOMPARE(m_foreignInterface->transientFor(m_childSurfaceInterface), m_exportedSurfaceInterface.data()); +} + +void TestForeign::testExport() +{ + doExport(); +} + +void TestForeign::testDeleteImported() +{ + doExport(); + + QSignalSpy transientSpy(m_foreignInterface, &KWayland::Server::XdgForeignInterface::transientChanged); + + QVERIFY(transientSpy.isValid()); + m_imported->deleteLater(); + m_imported = nullptr; + + QVERIFY(transientSpy.wait()); + + QCOMPARE(transientSpy.first().first().value(), m_childSurfaceInterface.data()); + QCOMPARE(transientSpy.first().at(1).value(), nullptr); + QCOMPARE(m_foreignInterface->transientFor(m_childSurfaceInterface), nullptr); +} + +void TestForeign::testDeleteChildSurface() +{ + doExport(); + + QSignalSpy transientSpy(m_foreignInterface, &KWayland::Server::XdgForeignInterface::transientChanged); + + QVERIFY(transientSpy.isValid()); + m_childSurface->deleteLater(); + + QVERIFY(transientSpy.wait()); + + //when the client surface dies, the server one will eventually die too + QSignalSpy surfaceDestroyedSpy(m_childSurfaceInterface, SIGNAL(destroyed())); + QVERIFY(surfaceDestroyedSpy.wait()); + + QCOMPARE(transientSpy.first().at(0).value(), nullptr); + QCOMPARE(transientSpy.first().at(1).value(), m_exportedSurfaceInterface.data()); +} + +void TestForeign::testDeleteParentSurface() +{ + doExport(); + + QSignalSpy transientSpy(m_foreignInterface, &KWayland::Server::XdgForeignInterface::transientChanged); + + QVERIFY(transientSpy.isValid()); + m_exportedSurface->deleteLater(); + + QSignalSpy exportedSurfaceDestroyedSpy(m_exportedSurfaceInterface.data(), &QObject::destroyed); + QVERIFY(exportedSurfaceDestroyedSpy.isValid()); + exportedSurfaceDestroyedSpy.wait(); + + QVERIFY(transientSpy.wait()); + + QCOMPARE(transientSpy.first().first().value(), m_childSurfaceInterface.data()); + QCOMPARE(transientSpy.first().at(1).value(), nullptr); + QCOMPARE(m_foreignInterface->transientFor(m_childSurfaceInterface), nullptr); +} + +void TestForeign::testDeleteExported() +{ + doExport(); + + QSignalSpy transientSpy(m_foreignInterface, &KWayland::Server::XdgForeignInterface::transientChanged); + QSignalSpy destroyedSpy(m_imported.data(), &KWayland::Client::XdgImported::importedDestroyed); + + QVERIFY(transientSpy.isValid()); + m_exported->deleteLater(); + m_exported = nullptr; + + QVERIFY(transientSpy.wait()); + QVERIFY(destroyedSpy.wait()); + + QCOMPARE(transientSpy.first().first().value(), m_childSurfaceInterface.data()); + QCOMPARE(transientSpy.first().at(1).value(), nullptr); + QCOMPARE(m_foreignInterface->transientFor(m_childSurfaceInterface), nullptr); + + QVERIFY(!m_imported->isValid()); +} + +void TestForeign::testExportTwoTimes() +{ + doExport(); + + //Export second window + KWayland::Client::XdgExported *exported2 = m_exporter->exportSurface(m_exportedSurface, this); + QVERIFY(exported2->handle().isEmpty()); + QSignalSpy doneSpy(exported2, &XdgExported::done); + QVERIFY(doneSpy.wait()); + QVERIFY(!exported2->handle().isEmpty()); + + QSignalSpy transientSpy(m_foreignInterface, &KWayland::Server::XdgForeignInterface::transientChanged); + QVERIFY(transientSpy.isValid()); + + //Import the just exported window + KWayland::Client::XdgImported *imported2 = m_importer->import(exported2->handle(), this); + QVERIFY(imported2->isValid()); + + //create a second child surface + QSignalSpy serverSurfaceCreated(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); + QVERIFY(serverSurfaceCreated.isValid()); + + KWayland::Client::Surface *childSurface2 = m_compositor->createSurface(); + QVERIFY(serverSurfaceCreated.wait()); + + KWayland::Server::SurfaceInterface *childSurface2Interface = serverSurfaceCreated.first().first().value(); + + imported2->setParentOf(childSurface2); + QVERIFY(transientSpy.wait()); + + QCOMPARE(transientSpy.first().first().value(), childSurface2Interface); + QCOMPARE(transientSpy.first().at(1).value(), m_exportedSurfaceInterface.data()); + + //transientFor api + //check the old relationship is still here + QCOMPARE(m_foreignInterface->transientFor(m_childSurfaceInterface), m_exportedSurfaceInterface.data()); + //check the new relationship + QCOMPARE(m_foreignInterface->transientFor(childSurface2Interface), m_exportedSurfaceInterface.data()); +} + +void TestForeign::testImportTwoTimes() +{ + doExport(); + + QSignalSpy transientSpy(m_foreignInterface, &KWayland::Server::XdgForeignInterface::transientChanged); + QVERIFY(transientSpy.isValid()); + + //Import another time the exported window + KWayland::Client::XdgImported *imported2 = m_importer->import(m_exported->handle(), this); + QVERIFY(imported2->isValid()); + + //create a second child surface + QSignalSpy serverSurfaceCreated(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); + QVERIFY(serverSurfaceCreated.isValid()); + + KWayland::Client::Surface *childSurface2 = m_compositor->createSurface(); + QVERIFY(serverSurfaceCreated.wait()); + + KWayland::Server::SurfaceInterface *childSurface2Interface = serverSurfaceCreated.first().first().value(); + + imported2->setParentOf(childSurface2); + QVERIFY(transientSpy.wait()); + + QCOMPARE(transientSpy.first().first().value(), childSurface2Interface); + QCOMPARE(transientSpy.first().at(1).value(), m_exportedSurfaceInterface.data()); + + //transientFor api + //check the old relationship is still here + QCOMPARE(m_foreignInterface->transientFor(m_childSurfaceInterface), m_exportedSurfaceInterface.data()); + //check the new relationship + QCOMPARE(m_foreignInterface->transientFor(childSurface2Interface), m_exportedSurfaceInterface.data()); +} + +QTEST_GUILESS_MAIN(TestForeign) +#include "test_xdg_foreign.moc" diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt --- a/src/client/CMakeLists.txt +++ b/src/client/CMakeLists.txt @@ -50,6 +50,8 @@ textinput_v2.cpp xdgshell.cpp xdgshell_v5.cpp + xdgforeign_v2.cpp + xdgforeign.cpp ) ecm_add_wayland_client_protocol(CLIENT_LIB_SRCS @@ -138,6 +140,10 @@ PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/pointer-constraints-unstable-v1.xml BASENAME pointer-constraints-unstable-v1 ) +ecm_add_wayland_client_protocol(CLIENT_LIB_SRCS + PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/xdg-foreign-unstable-v2.xml + BASENAME xdg-foreign-unstable-v2 +) add_library(KF5WaylandClient ${CLIENT_LIB_SRCS}) generate_export_header(KF5WaylandClient @@ -205,6 +211,7 @@ touch.h textinput.h xdgshell.h + xdgforeign_v2.h ) install(FILES diff --git a/src/client/protocols/xdg-foreign-unstable-v1.xml b/src/client/protocols/xdg-foreign-unstable-v1.xml new file mode 100644 --- /dev/null +++ b/src/client/protocols/xdg-foreign-unstable-v1.xml @@ -0,0 +1,182 @@ + + + + + Copyright © 2015-2016 Red Hat Inc. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + + This protocol specifies a way for making it possible to reference a surface + of a different client. With such a reference, a client can, by using the + interfaces provided by this protocol, manipulate the relationship between + its own surfaces and the surface of some other client. For example, stack + some of its own surface above the other clients surface. + + In order for a client A to get a reference of a surface of client B, client + B must first export its surface using xdg_exporter.export. Upon doing this, + client B will receive a handle (a unique string) that it may share with + client A in some way (for example D-Bus). After client A has received the + handle from client B, it may use xdg_importer.import to create a reference + to the surface client B just exported. See the corresponding requests for + details. + + A possible use case for this is out-of-process dialogs. For example when a + sandboxed client without file system access needs the user to select a file + on the file system, given sandbox environment support, it can export its + surface, passing the exported surface handle to an unsandboxed process that + can show a file browser dialog and stack it above the sandboxed client's + surface. + + Warning! The protocol described in this file is experimental and backward + incompatible changes may be made. Backward compatible changes may be added + together with the corresponding interface version bump. Backward + incompatible changes are done by bumping the version number in the protocol + and interface names and resetting the interface version. Once the protocol + is to be declared stable, the 'z' prefix and the version number in the + protocol and interface names are removed and the interface version number is + reset. + + + + + A global interface used for exporting surfaces that can later be imported + using xdg_importer. + + + + + Notify the compositor that the xdg_exporter object will no longer be + used. + + + + + + The export request exports the passed surface so that it can later be + imported via xdg_importer. When called, a new xdg_exported object will + be created and xdg_exported.handle will be sent immediately. See the + corresponding interface and event for details. + + A surface may be exported multiple times, and each exported handle may + be used to create a xdg_imported multiple times. Only xdg_surface + surfaces may be exported. + + + + + + + + + A global interface used for importing surfaces exported by xdg_exporter. + With this interface, a client can create a reference to a surface of + another client. + + + + + Notify the compositor that the xdg_importer object will no longer be + used. + + + + + + The import request imports a surface from any client given a handle + retrieved by exporting said surface using xdg_exporter.export. When + called, a new xdg_imported object will be created. This new object + represents the imported surface, and the importing client can + manipulate its relationship using it. See xdg_imported for details. + + + + + + + + + A xdg_exported object represents an exported reference to a surface. The + exported surface may be referenced as long as the xdg_exported object not + destroyed. Destroying the xdg_exported invalidates any relationship the + importer may have established using xdg_imported. + + + + + Revoke the previously exported surface. This invalidates any + relationship the importer may have set up using the xdg_imported created + given the handle sent via xdg_exported.handle. + + + + + + The handle event contains the unique handle of this exported surface + reference. It may be shared with any client, which then can use it to + import the surface by calling xdg_importer.import. A handle may be + used to import the surface multiple times. + + + + + + + + A xdg_imported object represents an imported reference to surface exported + by some client. A client can use this interface to manipulate + relationships between its own surfaces and the imported surface. + + + + + Notify the compositor that it will no longer use the xdg_imported + object. Any relationship that may have been set up will at this point + be invalidated. + + + + + + Set the imported surface as the parent of some surface of the client. + The passed surface must be a toplevel xdg_surface. Calling this function + sets up a surface to surface relation with the same stacking and positioning + semantics as xdg_surface.set_parent. + + + + + + + The imported surface handle has been destroyed and any relationship set + up has been invalidated. This may happen for various reasons, for + example if the exported surface or the exported surface handle has been + destroyed, if the handle used for importing was invalid. + + + + + diff --git a/src/client/protocols/xdg-foreign-unstable-v2.xml b/src/client/protocols/xdg-foreign-unstable-v2.xml new file mode 100644 --- /dev/null +++ b/src/client/protocols/xdg-foreign-unstable-v2.xml @@ -0,0 +1,182 @@ + + + + + Copyright © 2015-2016 Red Hat Inc. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + + This protocol specifies a way for making it possible to reference a surface + of a different client. With such a reference, a client can, by using the + interfaces provided by this protocol, manipulate the relationship between + its own surfaces and the surface of some other client. For example, stack + some of its own surface above the other clients surface. + + In order for a client A to get a reference of a surface of client B, client + B must first export its surface using xdg_exporter.export. Upon doing this, + client B will receive a handle (a unique string) that it may share with + client A in some way (for example D-Bus). After client A has received the + handle from client B, it may use xdg_importer.import to create a reference + to the surface client B just exported. See the corresponding requests for + details. + + A possible use case for this is out-of-process dialogs. For example when a + sandboxed client without file system access needs the user to select a file + on the file system, given sandbox environment support, it can export its + surface, passing the exported surface handle to an unsandboxed process that + can show a file browser dialog and stack it above the sandboxed client's + surface. + + Warning! The protocol described in this file is experimental and backward + incompatible changes may be made. Backward compatible changes may be added + together with the corresponding interface version bump. Backward + incompatible changes are done by bumping the version number in the protocol + and interface names and resetting the interface version. Once the protocol + is to be declared stable, the 'z' prefix and the version number in the + protocol and interface names are removed and the interface version number is + reset. + + + + + A global interface used for exporting surfaces that can later be imported + using xdg_importer. + + + + + Notify the compositor that the xdg_exporter object will no longer be + used. + + + + + + The export request exports the passed surface so that it can later be + imported via xdg_importer. When called, a new xdg_exported object will + be created and xdg_exported.handle will be sent immediately. See the + corresponding interface and event for details. + + A surface may be exported multiple times, and each exported handle may + be used to create a xdg_imported multiple times. Only xdg_surface + surfaces may be exported. + + + + + + + + + A global interface used for importing surfaces exported by xdg_exporter. + With this interface, a client can create a reference to a surface of + another client. + + + + + Notify the compositor that the xdg_importer object will no longer be + used. + + + + + + The import request imports a surface from any client given a handle + retrieved by exporting said surface using xdg_exporter.export. When + called, a new xdg_imported object will be created. This new object + represents the imported surface, and the importing client can + manipulate its relationship using it. See xdg_imported for details. + + + + + + + + + A xdg_exported object represents an exported reference to a surface. The + exported surface may be referenced as long as the xdg_exported object not + destroyed. Destroying the xdg_exported invalidates any relationship the + importer may have established using xdg_imported. + + + + + Revoke the previously exported surface. This invalidates any + relationship the importer may have set up using the xdg_imported created + given the handle sent via xdg_exported.handle. + + + + + + The handle event contains the unique handle of this exported surface + reference. It may be shared with any client, which then can use it to + import the surface by calling xdg_importer.import. A handle may be + used to import the surface multiple times. + + + + + + + + A xdg_imported object represents an imported reference to surface exported + by some client. A client can use this interface to manipulate + relationships between its own surfaces and the imported surface. + + + + + Notify the compositor that it will no longer use the xdg_imported + object. Any relationship that may have been set up will at this point + be invalidated. + + + + + + Set the imported surface as the parent of some surface of the client. + The passed surface must be a toplevel xdg_surface. Calling this function + sets up a surface to surface relation with the same stacking and positioning + semantics as xdg_surface.set_parent. + + + + + + + The imported surface handle has been destroyed and any relationship set + up has been invalidated. This may happen for various reasons, for + example if the exported surface or the exported surface handle has been + destroyed, if the handle used for importing was invalid. + + + + + diff --git a/src/client/registry.h b/src/client/registry.h --- a/src/client/registry.h +++ b/src/client/registry.h @@ -53,6 +53,8 @@ struct zwp_relative_pointer_manager_v1; struct zwp_pointer_gestures_v1; struct zwp_pointer_constraints_v1; +struct zxdg_exporter_v2; +struct zxdg_importer_v2; namespace KWayland { @@ -88,6 +90,10 @@ class TextInputManagerUnstableV2; class XdgShell; class RelativePointerManager; +class XdgExporterUnstableV2; +class XdgImporterUnstableV2; +class XdgExporter; +class XdgImporter; /** * @short Wrapper for the wl_registry interface. @@ -150,7 +156,9 @@ XdgShellUnstableV5, ///< Refers to xdg_shell (unstable version 5), @since 5.25 RelativePointerManagerUnstableV1, ///< Refers to zwp_relative_pointer_manager_v1, @since 5.28 PointerGesturesUnstableV1, ///< Refers to zwp_pointer_gestures_v1, @since 5.29 - PointerConstraintsUnstableV1 ///< Refers to zwp_pointer_constraints_v1, @since 5.29 + PointerConstraintsUnstableV1, ///< Refers to zwp_pointer_constraints_v1, @since 5.29 + XdgExporterUnstableV2, ///< refers to zxdg_exporter_v2, @since 5.39 + XdgImporterUnstableV2 ///< refers to zxdg_importer_v2, @since 5.39 }; explicit Registry(QObject *parent = nullptr); virtual ~Registry(); @@ -515,6 +523,9 @@ * @since 5.29 **/ zwp_pointer_constraints_v1 *bindPointerConstraintsUnstableV1(uint32_t name, uint32_t version) const; + + zxdg_exporter_v2 *bindXdgExporterUnstableV2(uint32_t name, uint32_t version) const; + zxdg_importer_v2 *bindXdgImporterUnstableV2(uint32_t name, uint32_t version) const; ///@} /** @@ -925,6 +936,9 @@ * @since 5.29 **/ PointerConstraints *createPointerConstraints(quint32 name, quint32 version, QObject *parent = nullptr); + + XdgExporter *createXdgExporterUnstable(quint32 name, quint32 version, QObject *parent = nullptr); + XdgImporter *createXdgImporterUnstable(quint32 name, quint32 version, QObject *parent = nullptr); ///@} /** @@ -1114,6 +1128,9 @@ * @since 5.29 **/ void pointerConstraintsUnstableV1Announced(quint32 name, quint32 version); + + void exporterUnstableV2Announced(quint32 name, quint32 version); + void importerUnstableV2Announced(quint32 name, quint32 version); ///@} /** * @name Interface removed signals. @@ -1267,6 +1284,9 @@ * @since 5.29 **/ void pointerConstraintsUnstableV1Removed(quint32 name); + + void exporterUnstableV2Removed(quint32 name); + void importerUnstableV2Removed(quint32 name); ///@} /** * Generic announced signal which gets emitted whenever an interface gets diff --git a/src/client/registry.cpp b/src/client/registry.cpp --- a/src/client/registry.cpp +++ b/src/client/registry.cpp @@ -49,6 +49,7 @@ #include "xdgshell.h" #include "xdgshell_p.h" #include "wayland_pointer_p.h" +#include "xdgforeign_v2.h" // Qt #include // wayland @@ -72,6 +73,7 @@ #include #include #include +#include /***** * How to add another interface: @@ -281,6 +283,20 @@ &zwp_pointer_constraints_v1_interface, &Registry::pointerConstraintsUnstableV1Announced, &Registry::pointerConstraintsUnstableV1Removed + }}, + {Registry::Interface::XdgExporterUnstableV2, { + 1, + QByteArrayLiteral("zxdg_exporter_v2"), + &zxdg_exporter_v2_interface, + &Registry::exporterUnstableV2Announced, + &Registry::exporterUnstableV2Removed + }}, + {Registry::Interface::XdgImporterUnstableV2, { + 1, + QByteArrayLiteral("zxdg_importer_v2"), + &zxdg_importer_v2_interface, + &Registry::importerUnstableV2Announced, + &Registry::importerUnstableV2Removed }} }; @@ -581,6 +597,8 @@ BIND(RelativePointerManagerUnstableV1, zwp_relative_pointer_manager_v1) BIND(PointerGesturesUnstableV1, zwp_pointer_gestures_v1) BIND(PointerConstraintsUnstableV1, zwp_pointer_constraints_v1) +BIND(XdgExporterUnstableV2, zxdg_exporter_v2) +BIND(XdgImporterUnstableV2, zxdg_importer_v2) BIND2(ShadowManager, Shadow, org_kde_kwin_shadow_manager) BIND2(BlurManager, Blur, org_kde_kwin_blur_manager) BIND2(ContrastManager, Contrast, org_kde_kwin_contrast_manager) @@ -639,6 +657,18 @@ #undef CREATE #undef CREATE2 +XdgExporter *Registry::createXdgExporterUnstable(quint32 name, quint32 version, QObject *parent) +{ + //only V1 supported for now + return d->create(name, version, parent, &Registry::bindXdgExporterUnstableV2); +} + +XdgImporter *Registry::createXdgImporterUnstable(quint32 name, quint32 version, QObject *parent) +{ + //only V1 supported for now + return d->create(name, version, parent, &Registry::bindXdgImporterUnstableV2); +} + TextInputManager *Registry::createTextInputManager(quint32 name, quint32 version, QObject *parent) { switch (d->interfaceForName(name)) { diff --git a/src/client/xdgforeign.h b/src/client/xdgforeign.h new file mode 100644 --- /dev/null +++ b/src/client/xdgforeign.h @@ -0,0 +1,350 @@ +/**************************************************************************** +Copyright 2017 Marco Martin + +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 KWAYLAND_CLIENT_XDGFOREIGN_H +#define KWAYLAND_CLIENT_XDGFOREIGN_H + +#include "surface.h" + +#include + +#include + +struct zxdg_exporter_v2; +struct zxdg_importer_v2; +struct zxdg_exported_v2; +struct zxdg_imported_v2; + +namespace KWayland +{ +namespace Client +{ + +class EventQueue; +class Surface; +class XdgExported; +class XdgImported; + +/** + * @short Wrapper for the zxdg_exporter_v2 interface. + * + * This class provides a convenient wrapper for the zxdg_exporter_v2 interface. + * + * To use this class one needs to interact with the Registry. There are two + * possible ways to create the interface: + * @code + * *c = registry->create(name, version); + * @endcode + * + * This creates the and sets it up directly. As an alternative this + * can also be done in a more low level way: + * @code + * *c = new ; + * c->setup(registry->bind(name, version)); + * @endcode + * + * The can be used as a drop-in replacement for any zxdg_exporter_v2 + * pointer as it provides matching cast operators. + * + * @see Registry + **/ +class KWAYLANDCLIENT_EXPORT XdgExporter : public QObject +{ + Q_OBJECT +public: + virtual ~XdgExporter(); + + /** + * Setup this to manage the @p . + * When using Registry::create there is no need to call this + * method. + **/ + void setup(zxdg_exporter_v2 *); + /** + * @returns @c true if managing a zxdg_exporter_v2. + **/ + bool isValid() const; + /** + * Releases the zxdg_exporter_v2 interface. + * After the interface has been released the instance is no + * longer valid and can be setup with another zxdg_exporter_v2 interface. + **/ + void release(); + /** + * Destroys the data held by this . + * This method is supposed to be used when the connection to the Wayland + * server goes away. If the connection is not valid anymore, it's not + * possible to call release anymore as that calls into the Wayland + * connection and the call would fail. This method cleans up the data, so + * that the instance can be deleted or set up to a new zxdg_exporter_v2 interface + * once there is a new connection available. + * + * It is suggested to connect this method to ConnectionThread::connectionDied: + * @code + * connect(connection, &ConnectionThread::connectionDied, , &::destroy); + * @endcode + * + * @see release + **/ + void destroy(); + + /** + * Sets the @p queue to use for creating objects with this . + **/ + void setEventQueue(EventQueue *queue); + /** + * @returns The event queue to use for creating objects with this . + **/ + EventQueue *eventQueue(); + + XdgExported *exportSurface(Surface *surface, QObject *parent = nullptr); + + operator zxdg_exporter_v2*(); + operator zxdg_exporter_v2*() const; + +Q_SIGNALS: + /** + * The corresponding global for this interface on the Registry got removed. + * + * This signal gets only emitted if the got created by + * Registry::create + **/ + void removed(); + +protected: + class Private; + explicit XdgExporter(Private *p, QObject *parent = nullptr); + QScopedPointer d; +}; + +/** + * @short Wrapper for the zxdg_importer_v2 interface. + * + * This class provides a convenient wrapper for the zxdg_importer_v2 interface. + * + * To use this class one needs to interact with the Registry. There are two + * possible ways to create the interface: + * @code + * *c = registry->create(name, version); + * @endcode + * + * This creates the and sets it up directly. As an alternative this + * can also be done in a more low level way: + * @code + * *c = new ; + * c->setup(registry->bind(name, version)); + * @endcode + * + * The can be used as a drop-in replacement for any zxdg_importer_v2 + * pointer as it provides matching cast operators. + * + * @see Registry + **/ +class KWAYLANDCLIENT_EXPORT XdgImporter : public QObject +{ + Q_OBJECT +public: + virtual ~XdgImporter(); + + /** + * Setup this to manage the @p . + * When using Registry::create there is no need to call this + * method. + **/ + void setup(zxdg_importer_v2 *); + /** + * @returns @c true if managing a zxdg_importer_v2. + **/ + bool isValid() const; + /** + * Releases the zxdg_importer_v2 interface. + * After the interface has been released the instance is no + * longer valid and can be setup with another zxdg_importer_v2 interface. + **/ + void release(); + /** + * Destroys the data held by this . + * This method is supposed to be used when the connection to the Wayland + * server goes away. If the connection is not valid anymore, it's not + * possible to call release anymore as that calls into the Wayland + * connection and the call would fail. This method cleans up the data, so + * that the instance can be deleted or set up to a new zxdg_importer_v2 interface + * once there is a new connection available. + * + * It is suggested to connect this method to ConnectionThread::connectionDied: + * @code + * connect(connection, &ConnectionThread::connectionDied, , &::destroy); + * @endcode + * + * @see release + **/ + void destroy(); + + /** + * Sets the @p queue to use for creating objects with this . + **/ + void setEventQueue(EventQueue *queue); + /** + * @returns The event queue to use for creating objects with this . + **/ + EventQueue *eventQueue(); + + XdgImported *import(const QString & handle, QObject *parent = nullptr); + + operator zxdg_importer_v2*(); + operator zxdg_importer_v2*() const; + +Q_SIGNALS: + /** + * The corresponding global for this interface on the Registry got removed. + * + * This signal gets only emitted if the got created by + * Registry::create + **/ + void removed(); + +protected: + class Private; + explicit XdgImporter(Private *p, QObject *parent = nullptr); + QScopedPointer d; +}; + +class KWAYLANDCLIENT_EXPORT XdgExported : public QObject +{ + Q_OBJECT +public: + virtual ~XdgExported(); + + /** + * Setup this to manage the @p . + * When using ::create there is no need to call this + * method. + **/ + void setup(zxdg_exported_v2 *); + /** + * @returns @c true if managing a zxdg_exported_v2. + **/ + bool isValid() const; + /** + * Releases the zxdg_exported_v2 interface. + * After the interface has been released the instance is no + * longer valid and can be setup with another zxdg_exported_v2 interface. + **/ + void release(); + /** + * Destroys the data held by this . + * This method is supposed to be used when the connection to the Wayland + * server goes away. If the connection is not valid anymore, it's not + * possible to call release anymore as that calls into the Wayland + * connection and the call would fail. This method cleans up the data, so + * that the instance can be deleted or set up to a new zxdg_exported_v2 interface + * once there is a new connection available. + * + * It is suggested to connect this method to ConnectionThread::connectionDied: + * @code + * connect(connection, &ConnectionThread::connectionDied, , &::destroy); + * @endcode + * + * @see release + **/ + void destroy(); + + QString handle() const; + + operator zxdg_exported_v2*(); + operator zxdg_exported_v2*() const; + +Q_SIGNALS: + /** + * Emitted when the exported window is fully initialized. + * the handle will be valid at this point + **/ + void done(); + +protected: + friend class XdgExporter; + class Private; + explicit XdgExported(Private *p, QObject *parent = nullptr); + QScopedPointer d; +}; + +class KWAYLANDCLIENT_EXPORT XdgImported : public QObject +{ + Q_OBJECT +public: + virtual ~XdgImported(); + + /** + * Setup this to manage the @p . + * When using ::create there is no need to call this + * method. + **/ + void setup(zxdg_imported_v2 *); + /** + * @returns @c true if managing a zxdg_imported_v2. + **/ + bool isValid() const; + /** + * Releases the zxdg_imported_v2 interface. + * After the interface has been released the instance is no + * longer valid and can be setup with another zxdg_imported_v2 interface. + **/ + void release(); + /** + * Destroys the data held by this . + * This method is supposed to be used when the connection to the Wayland + * server goes away. If the connection is not valid anymore, it's not + * possible to call release anymore as that calls into the Wayland + * connection and the call would fail. This method cleans up the data, so + * that the instance can be deleted or set up to a new zxdg_imported_v2 interface + * once there is a new connection available. + * + * It is suggested to connect this method to ConnectionThread::connectionDied: + * @code + * connect(connection, &ConnectionThread::connectionDied, , &::destroy); + * @endcode + * + * @see release + **/ + void destroy(); + + void setParentOf(Surface *surface); + + operator zxdg_imported_v2*(); + operator zxdg_imported_v2*() const; + +Q_SIGNALS: + /** + * Emitted when the imported surface is not valid anymore, + * for instance because it's no longer exported on the other end + */ + void importedDestroyed(); + +protected: + friend class XdgImporter; + class Private; + explicit XdgImported(Private *p, QObject *parent = nullptr); + QScopedPointer d; +}; + + +} +} + +#endif diff --git a/src/client/xdgforeign.cpp b/src/client/xdgforeign.cpp new file mode 100644 --- /dev/null +++ b/src/client/xdgforeign.cpp @@ -0,0 +1,271 @@ +/**************************************************************************** +Copyright 2017 Marco Martin + +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 "xdgforeign.h" +#include "xdgforeign_p.h" +#include "event_queue.h" +#include "wayland_pointer_p.h" + +#include + +#include + +namespace KWayland +{ +namespace Client +{ + +XdgExporter::Private::Private() +{ +} + +XdgExporter::Private::~Private() +{ +} + +XdgExporter::XdgExporter(Private *p, QObject *parent) + : QObject(parent) + , d(p) +{ +} + +XdgExporter::~XdgExporter() +{ + release(); +} + +void XdgExporter::setup(zxdg_exporter_v2 *exporter) +{ + d->setupV2(exporter); +} + +void XdgExporter::release() +{ + d->release(); +} + +void XdgExporter::destroy() +{ + d->destroy(); +} + +XdgExporter::operator zxdg_exporter_v2*() { + return d->exporterV2(); +} + +XdgExporter::operator zxdg_exporter_v2*() const { + return d->exporterV2(); +} + +bool XdgExporter::isValid() const +{ + return d->isValid(); +} + +void XdgExporter::setEventQueue(EventQueue *queue) +{ + d->queue = queue; +} + +EventQueue *XdgExporter::eventQueue() +{ + return d->queue; +} + +XdgExported *XdgExporter::exportSurface(Surface *surface, QObject *parent) +{ + return d->exportTopLevelV2(surface, parent); +} + + + +XdgImporter::Private::Private() +{ +} + +XdgImporter::Private::~Private() +{ +} + +XdgImporter::XdgImporter(Private *p, QObject *parent) + : QObject(parent) + , d(p) +{ +} + +XdgImporter::~XdgImporter() +{ + release(); +} + +void XdgImporter::setup(zxdg_importer_v2 *importer) +{ + d->setupV2(importer); +} + +void XdgImporter::release() +{ + d->release(); +} + +void XdgImporter::destroy() +{ + d->destroy(); +} + +XdgImporter::operator zxdg_importer_v2*() { + return d->importerV2(); +} + +XdgImporter::operator zxdg_importer_v2*() const { + return d->importerV2(); +} + +bool XdgImporter::isValid() const +{ + return d->isValid(); +} + +void XdgImporter::setEventQueue(EventQueue *queue) +{ + d->queue = queue; +} + +EventQueue *XdgImporter::eventQueue() +{ + return d->queue; +} + +XdgImported *XdgImporter::import(const QString & handle, QObject *parent) +{ + Q_ASSERT(isValid()); + return d->importTopLevelV2(handle, parent); +} + +XdgExported::XdgExported(Private *p, QObject *parent) + : QObject(parent) + , d(p) +{ +} + +XdgExported::Private::Private(XdgExported *q) + : q(q) +{ +} + +XdgExported::Private::~Private() +{ +} + +XdgExported::~XdgExported() +{ + release(); +} + +void XdgExported::setup(zxdg_exported_v2 *exported) +{ + d->setupV2(exported); +} + +void XdgExported::release() +{ + d->release(); +} + +void XdgExported::destroy() +{ + d->destroy(); +} + +QString XdgExported::handle() const +{ + return d->handle; +} + +XdgExported::operator zxdg_exported_v2*() { + return d->exportedV2(); +} + +XdgExported::operator zxdg_exported_v2*() const { + return d->exportedV2(); +} + +bool XdgExported::isValid() const +{ + return d->isValid(); +} + +XdgImported::Private::Private(XdgImported *q) + : q(q) +{ +} + +XdgImported::Private::~Private() +{ +} + +XdgImported::XdgImported(Private *p, QObject *parent) + : QObject(parent) + , d(p) +{ +} + +XdgImported::~XdgImported() +{ + release(); +} + +void XdgImported::setup(zxdg_imported_v2 *imported) +{ + d->setupV2(imported); +} + +void XdgImported::release() +{ + d->release(); +} + +void XdgImported::destroy() +{ + d->destroy(); +} + +XdgImported::operator zxdg_imported_v2*() { + return d->importedV2(); +} + +XdgImported::operator zxdg_imported_v2*() const { + return d->importedV2(); +} + +bool XdgImported::isValid() const +{ + return d->isValid(); +} + +void XdgImported::setParentOf(Surface *surface) +{ + Q_ASSERT(isValid()); + d->setParentOf(surface); +} + + +} +} + diff --git a/src/client/xdgforeign_p.h b/src/client/xdgforeign_p.h new file mode 100644 --- /dev/null +++ b/src/client/xdgforeign_p.h @@ -0,0 +1,113 @@ +/**************************************************************************** +Copyright 2017 Marco Martin + +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 KWAYLAND_CLIENT_XDGFOREIGN_P_H +#define KWAYLAND_CLIENT_XDGFOREIGN_P_H + +#include "xdgforeign.h" +#include + +namespace KWayland +{ +namespace Client +{ + +class XdgExportedUnstableV2; +class XdgImportedUnstableV2; + +class Q_DECL_HIDDEN XdgExporter::Private +{ +public: + Private(); + virtual ~Private(); + + virtual XdgExported *exportTopLevelV2(Surface *surface, QObject *parent) = 0; + + virtual void setupV2(zxdg_exporter_v2 *arg) = 0; + virtual zxdg_exporter_v2 *exporterV2() = 0; + + virtual void release() = 0; + virtual void destroy() = 0; + virtual bool isValid() = 0; + + EventQueue *queue = nullptr; +}; + +class Q_DECL_HIDDEN XdgImporter::Private +{ +public: + Private(); + virtual ~Private(); + + virtual XdgImported *importTopLevelV2(const QString & handle, QObject *parent) = 0; + + virtual void setupV2(zxdg_importer_v2 *arg) = 0; + virtual zxdg_importer_v2 *importerV2() = 0; + + virtual void release() = 0; + virtual void destroy() = 0; + virtual bool isValid() = 0; + + EventQueue *queue = nullptr; +}; + + +class Q_DECL_HIDDEN XdgExported::Private +{ +public: + Private(XdgExported *q); + virtual ~Private(); + + virtual void setupV2(zxdg_exported_v2 *) = 0; + virtual zxdg_exported_v2 *exportedV2() = 0; + + virtual void release() = 0; + virtual void destroy() = 0; + virtual bool isValid() = 0; + + QString handle; + +protected: + XdgExported *q; + +}; + + +class Q_DECL_HIDDEN XdgImported::Private +{ +public: + Private(XdgImported *q); + virtual ~Private(); + + virtual void setupV2(zxdg_imported_v2 *) = 0; + virtual zxdg_imported_v2 *importedV2() = 0; + + virtual void setParentOf(Surface *surface) = 0; + virtual void release() = 0; + virtual void destroy() = 0; + virtual bool isValid() = 0; + +protected: + XdgImported *q; +}; + +} +} + +#endif diff --git a/src/client/xdgforeign_v1.h b/src/client/xdgforeign_v1.h new file mode 100644 --- /dev/null +++ b/src/client/xdgforeign_v1.h @@ -0,0 +1,153 @@ +/**************************************************************************** +Copyright 2017 Marco Martin + +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 KWAYLAND_CLIENT_XDGFOREIGN_V1_H +#define KWAYLAND_CLIENT_XDGFOREIGN_V1_H + +#include "xdgforeign.h" +#include "surface.h" + +#include + +#include + +struct zxdg_exporter_v1; +struct zxdg_importer_v1; +struct zxdg_exported_v1; +struct zxdg_imported_v1; + +namespace KWayland +{ +namespace Client +{ + +class EventQueue; +class Surface; +class XdgExportedUnstableV1; +class XdgImportedUnstableV1; + +/** + * @short Wrapper for the zxdg_exporter_v1 interface. + * + * This class provides a convenient wrapper for the zxdg_exporter_v1 interface. + * + * To use this class one needs to interact with the Registry. There are two + * possible ways to create the interface: + * @code + * *c = registry->create(name, version); + * @endcode + * + * This creates the and sets it up directly. As an alternative this + * can also be done in a more low level way: + * @code + * *c = new ; + * c->setup(registry->bind(name, version)); + * @endcode + * + * The can be used as a drop-in replacement for any zxdg_exporter_v1 + * pointer as it provides matching cast operators. + * + * @see Registry + **/ +class Q_DECL_HIDDEN XdgExporterUnstableV1 : public XdgExporter +{ + Q_OBJECT +public: + /** + * Creates a new . + * Note: after constructing the it is not yet valid and one needs + * to call setup. In order to get a ready to use prefer using + * Registry::create. + **/ + explicit XdgExporterUnstableV1(QObject *parent = nullptr); + virtual ~XdgExporterUnstableV1(); + +private: + class Private; +}; + +/** + * @short Wrapper for the zxdg_importer_v1 interface. + * + * This class provides a convenient wrapper for the zxdg_importer_v1 interface. + * + * To use this class one needs to interact with the Registry. There are two + * possible ways to create the interface: + * @code + * *c = registry->create(name, version); + * @endcode + * + * This creates the and sets it up directly. As an alternative this + * can also be done in a more low level way: + * @code + * *c = new ; + * c->setup(registry->bind(name, version)); + * @endcode + * + * The can be used as a drop-in replacement for any zxdg_importer_v1 + * pointer as it provides matching cast operators. + * + * @see Registry + **/ +class Q_DECL_HIDDEN XdgImporterUnstableV1 : public XdgImporter +{ + Q_OBJECT +public: + /** + * Creates a new . + * Note: after constructing the it is not yet valid and one needs + * to call setup. In order to get a ready to use prefer using + * Registry::create. + **/ + explicit XdgImporterUnstableV1(QObject *parent = nullptr); + virtual ~XdgImporterUnstableV1(); + +private: + class Private; +}; + +class Q_DECL_HIDDEN XdgExportedUnstableV1 : public XdgExported +{ + Q_OBJECT +public: + virtual ~XdgExportedUnstableV1(); + +private: + friend class XdgExporterUnstableV1; + explicit XdgExportedUnstableV1(QObject *parent = nullptr); + class Private; +}; + +class Q_DECL_HIDDEN XdgImportedUnstableV1 : public XdgImported +{ + Q_OBJECT +public: + virtual ~XdgImportedUnstableV1(); + +private: + friend class XdgImporterUnstableV1; + explicit XdgImportedUnstableV1(QObject *parent = nullptr); + class Private; +}; + + +} +} + +#endif diff --git a/src/client/xdgforeign_v1.cpp b/src/client/xdgforeign_v1.cpp new file mode 100644 --- /dev/null +++ b/src/client/xdgforeign_v1.cpp @@ -0,0 +1,337 @@ +/**************************************************************************** +Copyright 2017 Marco Martin + +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 "xdgforeign_v1.h" +#include "xdgforeign_p.h" +#include "event_queue.h" +#include "wayland_pointer_p.h" + +#include + +#include + +namespace KWayland +{ +namespace Client +{ + +class Q_DECL_HIDDEN XdgExporterUnstableV1::Private : public XdgExporter::Private +{ +public: + Private(); + + XdgExported *exportTopLevelV1(Surface *surface, QObject *parent) override; + void setupV1(zxdg_exporter_v1 *arg) override; + zxdg_exporter_v1 *exporterV1() override; + + void release() override; + void destroy() override; + bool isValid() override; + + WaylandPointer exporter; +}; + +XdgExporterUnstableV1::Private::Private() + : XdgExporter::Private() +{} + +zxdg_exporter_v1 *XdgExporterUnstableV1::Private::exporterV1() +{ + return exporter; +} + +void XdgExporterUnstableV1::Private::release() +{ + exporter.release(); +} + +void XdgExporterUnstableV1::Private::destroy() +{ + exporter.destroy(); +} + +bool XdgExporterUnstableV1::Private::isValid() +{ + return exporter.isValid(); +} + +XdgExported *XdgExporterUnstableV1::Private::exportTopLevelV1(Surface *surface, QObject *parent) +{ + Q_ASSERT(isValid()); + auto p = new XdgExportedUnstableV1(parent); + auto w = zxdg_exporter_v1_export_toplevel(exporter, *surface); + if (queue) { + queue->addProxy(w); + } + p->setup(w); + return p; +} + + +XdgExporterUnstableV1::XdgExporterUnstableV1(QObject *parent) + : XdgExporter(new Private, parent) +{ +} + +void XdgExporterUnstableV1::Private::setupV1(zxdg_exporter_v1 *arg) +{ + Q_ASSERT(arg); + Q_ASSERT(!exporter); + exporter.setup(arg); +} + +XdgExporterUnstableV1::~XdgExporterUnstableV1() +{ +} + +class Q_DECL_HIDDEN XdgImporterUnstableV1::Private : public XdgImporter::Private +{ +public: + Private(); + + XdgImported *importTopLevelV1(const QString & handle, QObject *parent) override; + void setupV1(zxdg_importer_v1 *arg) override; + zxdg_importer_v1 *importerV1() override; + + void release() override; + void destroy() override; + bool isValid() override; + + WaylandPointer importer; + EventQueue *queue = nullptr; +}; + +XdgImporterUnstableV1::Private::Private() + : XdgImporter::Private() +{} + +zxdg_importer_v1 *XdgImporterUnstableV1::Private::importerV1() +{ + return importer; +} + +void XdgImporterUnstableV1::Private::release() +{ + importer.release(); +} + +void XdgImporterUnstableV1::Private::destroy() +{ + importer.destroy(); +} + +bool XdgImporterUnstableV1::Private::isValid() +{ + return importer.isValid(); +} + +XdgImported *XdgImporterUnstableV1::Private::importTopLevelV1(const QString & handle, QObject *parent) +{ + Q_ASSERT(isValid()); + auto p = new XdgImportedUnstableV1(parent); + auto w = zxdg_importer_v1_import_toplevel(importer, handle.toUtf8()); + if (queue) { + queue->addProxy(w); + } + p->setup(w); + return p; +} + + +XdgImporterUnstableV1::XdgImporterUnstableV1(QObject *parent) + : XdgImporter(new Private, parent) +{ +} + +void XdgImporterUnstableV1::Private::setupV1(zxdg_importer_v1 *arg) +{ + Q_ASSERT(arg); + Q_ASSERT(!importer); + importer.setup(arg); +} + +XdgImporterUnstableV1::~XdgImporterUnstableV1() +{ +} + + +class Q_DECL_HIDDEN XdgExportedUnstableV1::Private : public XdgExported::Private +{ +public: + Private(XdgExportedUnstableV1 *q); + + void setupV1(zxdg_exported_v1 *arg) override; + zxdg_exported_v1 *exportedV1() override; + + void release() override; + void destroy() override; + bool isValid() override; + + WaylandPointer exported; + +private: + static void handleCallback(void *data, zxdg_exported_v1 *zxdg_exported_v1, const char * handle); + + static const zxdg_exported_v1_listener s_listener; +}; + +zxdg_exported_v1 *XdgExportedUnstableV1::Private::exportedV1() +{ + return exported; +} + +void XdgExportedUnstableV1::Private::release() +{ + exported.release(); +} + +void XdgExportedUnstableV1::Private::destroy() +{ + exported.destroy(); +} + +bool XdgExportedUnstableV1::Private::isValid() +{ + return exported.isValid(); +} + + +const zxdg_exported_v1_listener XdgExportedUnstableV1::Private::s_listener = { + handleCallback +}; + +void XdgExportedUnstableV1::Private::handleCallback(void *data, zxdg_exported_v1 *zxdg_exported_v1, const char * handle) +{ + auto p = reinterpret_cast(data); + Q_ASSERT(p->exported == zxdg_exported_v1); + + p->handle = handle; + emit p->q->done(); +} + +XdgExportedUnstableV1::XdgExportedUnstableV1(QObject *parent) + : XdgExported(new Private(this), parent) +{ +} + +void XdgExportedUnstableV1::Private::setupV1(zxdg_exported_v1 *arg) +{ + Q_ASSERT(arg); + Q_ASSERT(!exported); + exported.setup(arg); + zxdg_exported_v1_add_listener(exported, &s_listener, this); +} + +XdgExportedUnstableV1::Private::Private(XdgExportedUnstableV1 *q) + : XdgExported::Private::Private(q) +{ +} + +XdgExportedUnstableV1::~XdgExportedUnstableV1() +{ +} + +class Q_DECL_HIDDEN XdgImportedUnstableV1::Private : public XdgImported::Private +{ +public: + Private(XdgImportedUnstableV1 *q); + + void setupV1(zxdg_imported_v1 *arg) override; + zxdg_imported_v1 *importedV1() override; + + void setParentOf(Surface *surface) override; + void release() override; + void destroy() override; + bool isValid() override; + + WaylandPointer imported; + +private: + static void destroyedCallback(void *data, zxdg_imported_v1 *zxdg_imported_v1); + + static const zxdg_imported_v1_listener s_listener; +}; + +XdgImportedUnstableV1::Private::Private(XdgImportedUnstableV1 *q) + : XdgImported::Private::Private(q) +{ +} + +zxdg_imported_v1 *XdgImportedUnstableV1::Private::importedV1() +{ + return imported; +} + +void XdgImportedUnstableV1::Private::release() +{ + imported.release(); +} + +void XdgImportedUnstableV1::Private::destroy() +{ + imported.destroy(); +} + +bool XdgImportedUnstableV1::Private::isValid() +{ + return imported.isValid(); +} + +void XdgImportedUnstableV1::Private::setParentOf(Surface *surface) +{ + Q_ASSERT(isValid()); + zxdg_imported_v1_set_parent_of(imported, *surface); +} + +const zxdg_imported_v1_listener XdgImportedUnstableV1::Private::s_listener = { + destroyedCallback +}; + +void XdgImportedUnstableV1::Private::destroyedCallback(void *data, zxdg_imported_v1 *zxdg_imported_v1) +{ + auto p = reinterpret_cast(data); + Q_ASSERT(p->imported == zxdg_imported_v1); + + p->q->release(); + emit p->q->importedDestroyed(); +} + + + +XdgImportedUnstableV1::XdgImportedUnstableV1(QObject *parent) + : XdgImported(new Private(this), parent) +{ +} + +void XdgImportedUnstableV1::Private::setupV1(zxdg_imported_v1 *arg) +{ + Q_ASSERT(arg); + Q_ASSERT(!imported); + imported.setup(arg); + zxdg_imported_v1_add_listener(imported, &s_listener, this); +} + +XdgImportedUnstableV1::~XdgImportedUnstableV1() +{ +} + + +} +} + diff --git a/src/client/xdgforeign_v2.h b/src/client/xdgforeign_v2.h new file mode 100644 --- /dev/null +++ b/src/client/xdgforeign_v2.h @@ -0,0 +1,153 @@ +/**************************************************************************** +Copyright 2017 Marco Martin + +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 KWAYLAND_CLIENT_XDGFOREIGN_V2_H +#define KWAYLAND_CLIENT_XDGFOREIGN_V2_H + +#include "xdgforeign.h" +#include "surface.h" + +#include + +#include + +struct zxdg_exporter_v2; +struct zxdg_importer_v2; +struct zxdg_exported_v2; +struct zxdg_imported_v2; + +namespace KWayland +{ +namespace Client +{ + +class EventQueue; +class Surface; +class XdgExportedUnstableV2; +class XdgImportedUnstableV2; + +/** + * @short Wrapper for the zxdg_exporter_v2 interface. + * + * This class provides a convenient wrapper for the zxdg_exporter_v2 interface. + * + * To use this class one needs to interact with the Registry. There are two + * possible ways to create the interface: + * @code + * *c = registry->create(name, version); + * @endcode + * + * This creates the and sets it up directly. As an alternative this + * can also be done in a more low level way: + * @code + * *c = new ; + * c->setup(registry->bind(name, version)); + * @endcode + * + * The can be used as a drop-in replacement for any zxdg_exporter_v2 + * pointer as it provides matching cast operators. + * + * @see Registry + **/ +class Q_DECL_HIDDEN XdgExporterUnstableV2 : public XdgExporter +{ + Q_OBJECT +public: + /** + * Creates a new . + * Note: after constructing the it is not yet valid and one needs + * to call setup. In order to get a ready to use prefer using + * Registry::create. + **/ + explicit XdgExporterUnstableV2(QObject *parent = nullptr); + virtual ~XdgExporterUnstableV2(); + +private: + class Private; +}; + +/** + * @short Wrapper for the zxdg_importer_v2 interface. + * + * This class provides a convenient wrapper for the zxdg_importer_v2 interface. + * + * To use this class one needs to interact with the Registry. There are two + * possible ways to create the interface: + * @code + * *c = registry->create(name, version); + * @endcode + * + * This creates the and sets it up directly. As an alternative this + * can also be done in a more low level way: + * @code + * *c = new ; + * c->setup(registry->bind(name, version)); + * @endcode + * + * The can be used as a drop-in replacement for any zxdg_importer_v2 + * pointer as it provides matching cast operators. + * + * @see Registry + **/ +class Q_DECL_HIDDEN XdgImporterUnstableV2 : public XdgImporter +{ + Q_OBJECT +public: + /** + * Creates a new . + * Note: after constructing the it is not yet valid and one needs + * to call setup. In order to get a ready to use prefer using + * Registry::create. + **/ + explicit XdgImporterUnstableV2(QObject *parent = nullptr); + virtual ~XdgImporterUnstableV2(); + +private: + class Private; +}; + +class Q_DECL_HIDDEN XdgExportedUnstableV2 : public XdgExported +{ + Q_OBJECT +public: + virtual ~XdgExportedUnstableV2(); + +private: + friend class XdgExporterUnstableV2; + explicit XdgExportedUnstableV2(QObject *parent = nullptr); + class Private; +}; + +class Q_DECL_HIDDEN XdgImportedUnstableV2 : public XdgImported +{ + Q_OBJECT +public: + virtual ~XdgImportedUnstableV2(); + +private: + friend class XdgImporterUnstableV2; + explicit XdgImportedUnstableV2(QObject *parent = nullptr); + class Private; +}; + + +} +} + +#endif diff --git a/src/client/xdgforeign_v2.cpp b/src/client/xdgforeign_v2.cpp new file mode 100644 --- /dev/null +++ b/src/client/xdgforeign_v2.cpp @@ -0,0 +1,337 @@ +/**************************************************************************** +Copyright 2017 Marco Martin + +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 "xdgforeign_v2.h" +#include "xdgforeign_p.h" +#include "event_queue.h" +#include "wayland_pointer_p.h" + +#include + +#include + +namespace KWayland +{ +namespace Client +{ + +class Q_DECL_HIDDEN XdgExporterUnstableV2::Private : public XdgExporter::Private +{ +public: + Private(); + + XdgExported *exportTopLevelV2(Surface *surface, QObject *parent) override; + void setupV2(zxdg_exporter_v2 *arg) override; + zxdg_exporter_v2 *exporterV2() override; + + void release() override; + void destroy() override; + bool isValid() override; + + WaylandPointer exporter; +}; + +XdgExporterUnstableV2::Private::Private() + : XdgExporter::Private() +{} + +zxdg_exporter_v2 *XdgExporterUnstableV2::Private::exporterV2() +{ + return exporter; +} + +void XdgExporterUnstableV2::Private::release() +{ + exporter.release(); +} + +void XdgExporterUnstableV2::Private::destroy() +{ + exporter.destroy(); +} + +bool XdgExporterUnstableV2::Private::isValid() +{ + return exporter.isValid(); +} + +XdgExported *XdgExporterUnstableV2::Private::exportTopLevelV2(Surface *surface, QObject *parent) +{ + Q_ASSERT(isValid()); + auto p = new XdgExportedUnstableV2(parent); + auto w = zxdg_exporter_v2_export_toplevel(exporter, *surface); + if (queue) { + queue->addProxy(w); + } + p->setup(w); + return p; +} + + +XdgExporterUnstableV2::XdgExporterUnstableV2(QObject *parent) + : XdgExporter(new Private, parent) +{ +} + +void XdgExporterUnstableV2::Private::setupV2(zxdg_exporter_v2 *arg) +{ + Q_ASSERT(arg); + Q_ASSERT(!exporter); + exporter.setup(arg); +} + +XdgExporterUnstableV2::~XdgExporterUnstableV2() +{ +} + +class Q_DECL_HIDDEN XdgImporterUnstableV2::Private : public XdgImporter::Private +{ +public: + Private(); + + XdgImported *importTopLevelV2(const QString & handle, QObject *parent) override; + void setupV2(zxdg_importer_v2 *arg) override; + zxdg_importer_v2 *importerV2() override; + + void release() override; + void destroy() override; + bool isValid() override; + + WaylandPointer importer; + EventQueue *queue = nullptr; +}; + +XdgImporterUnstableV2::Private::Private() + : XdgImporter::Private() +{} + +zxdg_importer_v2 *XdgImporterUnstableV2::Private::importerV2() +{ + return importer; +} + +void XdgImporterUnstableV2::Private::release() +{ + importer.release(); +} + +void XdgImporterUnstableV2::Private::destroy() +{ + importer.destroy(); +} + +bool XdgImporterUnstableV2::Private::isValid() +{ + return importer.isValid(); +} + +XdgImported *XdgImporterUnstableV2::Private::importTopLevelV2(const QString & handle, QObject *parent) +{ + Q_ASSERT(isValid()); + auto p = new XdgImportedUnstableV2(parent); + auto w = zxdg_importer_v2_import_toplevel(importer, handle.toUtf8()); + if (queue) { + queue->addProxy(w); + } + p->setup(w); + return p; +} + + +XdgImporterUnstableV2::XdgImporterUnstableV2(QObject *parent) + : XdgImporter(new Private, parent) +{ +} + +void XdgImporterUnstableV2::Private::setupV2(zxdg_importer_v2 *arg) +{ + Q_ASSERT(arg); + Q_ASSERT(!importer); + importer.setup(arg); +} + +XdgImporterUnstableV2::~XdgImporterUnstableV2() +{ +} + + +class Q_DECL_HIDDEN XdgExportedUnstableV2::Private : public XdgExported::Private +{ +public: + Private(XdgExportedUnstableV2 *q); + + void setupV2(zxdg_exported_v2 *arg) override; + zxdg_exported_v2 *exportedV2() override; + + void release() override; + void destroy() override; + bool isValid() override; + + WaylandPointer exported; + +private: + static void handleCallback(void *data, zxdg_exported_v2 *zxdg_exported_v2, const char * handle); + + static const zxdg_exported_v2_listener s_listener; +}; + +zxdg_exported_v2 *XdgExportedUnstableV2::Private::exportedV2() +{ + return exported; +} + +void XdgExportedUnstableV2::Private::release() +{ + exported.release(); +} + +void XdgExportedUnstableV2::Private::destroy() +{ + exported.destroy(); +} + +bool XdgExportedUnstableV2::Private::isValid() +{ + return exported.isValid(); +} + + +const zxdg_exported_v2_listener XdgExportedUnstableV2::Private::s_listener = { + handleCallback +}; + +void XdgExportedUnstableV2::Private::handleCallback(void *data, zxdg_exported_v2 *zxdg_exported_v2, const char * handle) +{ + auto p = reinterpret_cast(data); + Q_ASSERT(p->exported == zxdg_exported_v2); + + p->handle = handle; + emit p->q->done(); +} + +XdgExportedUnstableV2::XdgExportedUnstableV2(QObject *parent) + : XdgExported(new Private(this), parent) +{ +} + +void XdgExportedUnstableV2::Private::setupV2(zxdg_exported_v2 *arg) +{ + Q_ASSERT(arg); + Q_ASSERT(!exported); + exported.setup(arg); + zxdg_exported_v2_add_listener(exported, &s_listener, this); +} + +XdgExportedUnstableV2::Private::Private(XdgExportedUnstableV2 *q) + : XdgExported::Private::Private(q) +{ +} + +XdgExportedUnstableV2::~XdgExportedUnstableV2() +{ +} + +class Q_DECL_HIDDEN XdgImportedUnstableV2::Private : public XdgImported::Private +{ +public: + Private(XdgImportedUnstableV2 *q); + + void setupV2(zxdg_imported_v2 *arg) override; + zxdg_imported_v2 *importedV2() override; + + void setParentOf(Surface *surface) override; + void release() override; + void destroy() override; + bool isValid() override; + + WaylandPointer imported; + +private: + static void destroyedCallback(void *data, zxdg_imported_v2 *zxdg_imported_v2); + + static const zxdg_imported_v2_listener s_listener; +}; + +XdgImportedUnstableV2::Private::Private(XdgImportedUnstableV2 *q) + : XdgImported::Private::Private(q) +{ +} + +zxdg_imported_v2 *XdgImportedUnstableV2::Private::importedV2() +{ + return imported; +} + +void XdgImportedUnstableV2::Private::release() +{ + imported.release(); +} + +void XdgImportedUnstableV2::Private::destroy() +{ + imported.destroy(); +} + +bool XdgImportedUnstableV2::Private::isValid() +{ + return imported.isValid(); +} + +void XdgImportedUnstableV2::Private::setParentOf(Surface *surface) +{ + Q_ASSERT(isValid()); + zxdg_imported_v2_set_parent_of(imported, *surface); +} + +const zxdg_imported_v2_listener XdgImportedUnstableV2::Private::s_listener = { + destroyedCallback +}; + +void XdgImportedUnstableV2::Private::destroyedCallback(void *data, zxdg_imported_v2 *zxdg_imported_v2) +{ + auto p = reinterpret_cast(data); + Q_ASSERT(p->imported == zxdg_imported_v2); + + p->q->release(); + emit p->q->importedDestroyed(); +} + + + +XdgImportedUnstableV2::XdgImportedUnstableV2(QObject *parent) + : XdgImported(new Private(this), parent) +{ +} + +void XdgImportedUnstableV2::Private::setupV2(zxdg_imported_v2 *arg) +{ + Q_ASSERT(arg); + Q_ASSERT(!imported); + imported.setup(arg); + zxdg_imported_v2_add_listener(imported, &s_listener, this); +} + +XdgImportedUnstableV2::~XdgImportedUnstableV2() +{ +} + + +} +} + diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -45,6 +45,8 @@ textinput_interface_v2.cpp xdgshell_interface.cpp xdgshell_v5_interface.cpp + xdgforeign_v2_interface.cpp + xdgforeign_interface.cpp ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS @@ -141,6 +143,11 @@ BASENAME pointer-constraints-unstable-v1 ) +ecm_add_wayland_server_protocol(SERVER_LIB_SRCS + PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/xdg-foreign-unstable-v2.xml + BASENAME xdg-foreign-unstable-v2 +) + add_library(KF5WaylandServer ${SERVER_LIB_SRCS}) generate_export_header(KF5WaylandServer BASE_NAME @@ -210,6 +217,7 @@ textinput_interface.h touch_interface.h xdgshell_interface.h + xdgforeign_interface.h ) install(FILES diff --git a/src/server/display.h b/src/server/display.h --- a/src/server/display.h +++ b/src/server/display.h @@ -80,6 +80,7 @@ class PointerGesturesInterface; enum class PointerConstraintsInterfaceVersion; class PointerConstraintsInterface; +class XdgForeignInterface; /** * @brief Class holding the Wayland server display loop. @@ -219,6 +220,7 @@ **/ PointerConstraintsInterface *createPointerConstraints(const PointerConstraintsInterfaceVersion &version, QObject *parent = nullptr); + XdgForeignInterface *createXdgForeignUnstableInterface(QObject *parent = nullptr); /** * Gets the ClientConnection for the given @p client. * If there is no ClientConnection yet for the given @p client, it will be created. diff --git a/src/server/display.cpp b/src/server/display.cpp --- a/src/server/display.cpp +++ b/src/server/display.cpp @@ -44,6 +44,7 @@ #include "subcompositor_interface.h" #include "textinput_interface_p.h" #include "xdgshell_v5_interface_p.h" +#include "xdgforeign_interface.h" #include #include @@ -408,6 +409,13 @@ return p; } +XdgForeignInterface *Display::createXdgForeignUnstableInterface(QObject *parent) +{ + XdgForeignInterface *foreign = new XdgForeignInterface(this, parent); + connect(this, &Display::aboutToTerminate, foreign, [this,foreign] { delete foreign; }); + return foreign; +} + void Display::createShm() { Q_ASSERT(d->display); diff --git a/src/server/xdgforeign_interface.h b/src/server/xdgforeign_interface.h new file mode 100644 --- /dev/null +++ b/src/server/xdgforeign_interface.h @@ -0,0 +1,76 @@ +/**************************************************************************** +Copyright 2017 Marco Martin + +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 KWAYLAND_SERVER_XDGFOREIGN_H +#define KWAYLAND_SERVER_XDGFOREIGN_H + +#include "global.h" +#include "resource.h" + +#include + +namespace KWayland +{ +namespace Server +{ + +class Display; +class SurfaceInterface; +class XdgExporterUnstableV2Interface; +class XdgImporterUnstableV2Interface; + +class KWAYLANDSERVER_EXPORT XdgForeignInterface : public QObject +{ + Q_OBJECT +public: + XdgForeignInterface(Display *display, QObject *parent = nullptr); + ~XdgForeignInterface(); + + void create(); + bool isValid(); + + /** + * if a client did set + */ + SurfaceInterface *transientFor(SurfaceInterface *surface); + +Q_SIGNALS: + /** + * A surface got a new imported transient parent + * @param parent is the surface exported by one client and imported into another, which will act as parent. + * @param child is the surface that the importer client did set as child of the surface + * that it imported. + * If one of the two paramenters is nullptr, it means that a previously relation is not + * valid anymore and either one of the surfaces has been unmapped, or the parent surface + * is not exported anymore. + */ + void transientChanged(KWayland::Server::SurfaceInterface *child, KWayland::Server::SurfaceInterface *parent); + +private: + friend class Display; + friend class XdgExporterUnstableV2Interface; + friend class XdgImporterUnstableV2Interface; + class Private; + Private *d; +}; + +} +} + +#endif diff --git a/src/server/xdgforeign_interface.cpp b/src/server/xdgforeign_interface.cpp new file mode 100644 --- /dev/null +++ b/src/server/xdgforeign_interface.cpp @@ -0,0 +1,79 @@ +/**************************************************************************** +Copyright 2017 Marco Martin + +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 "xdgforeign_interface.h" +#include "xdgforeign_v2_interface_p.h" +#include "display.h" +#include "global_p.h" +#include "resource_p.h" +#include "surface_interface_p.h" + +#include "wayland-xdg-foreign-unstable-v2-server-protocol.h" + +#include +#include + +namespace KWayland +{ +namespace Server +{ + +XdgForeignInterface::Private::Private(Display *display, XdgForeignInterface *q) + : q(q) +{ + exporter = new XdgExporterUnstableV2Interface(display, q); + importer = new XdgImporterUnstableV2Interface(display, q); + + connect(importer, &XdgImporterUnstableV2Interface::transientChanged, + q, &XdgForeignInterface::transientChanged); +} + +XdgForeignInterface::XdgForeignInterface(Display *display, QObject *parent) + : QObject(parent), + d(new Private(display, this)) +{ +} + +XdgForeignInterface::~XdgForeignInterface() +{ + delete d->exporter; + delete d->importer; + delete d; +} + +void XdgForeignInterface::create() +{ + d->exporter->create(); + d->importer->create(); +} + +bool XdgForeignInterface::isValid() +{ + return d->exporter->isValid() && d->importer->isValid(); +} + +SurfaceInterface *XdgForeignInterface::transientFor(SurfaceInterface *surface) +{ + return d->importer->transientFor(surface); +} + +} +} + diff --git a/src/server/xdgforeign_v1_interface.cpp b/src/server/xdgforeign_v1_interface.cpp new file mode 100644 --- /dev/null +++ b/src/server/xdgforeign_v1_interface.cpp @@ -0,0 +1,459 @@ +/**************************************************************************** +Copyright 2017 Marco Martin + +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 "xdgforeign_interface.h" +#include "xdgforeign_v1_interface_p.h" +#include "display.h" +#include "global_p.h" +#include "resource_p.h" +#include "surface_interface_p.h" + +#include "wayland-xdg-foreign-unstable-v1-server-protocol.h" + +#include +#include + +namespace KWayland +{ +namespace Server +{ + +class Q_DECL_HIDDEN XdgExporterUnstableV1Interface::Private : public Global::Private +{ +public: + Private(XdgExporterUnstableV1Interface *q, Display *d, XdgForeignInterface *foreignInterface); + + XdgForeignInterface *foreignInterface; + QHash exportedSurfaces; + +private: + void bind(wl_client *client, uint32_t version, uint32_t id) override; + + static void unbind(wl_resource *resource); + static Private *cast(wl_resource *r) { + return reinterpret_cast(wl_resource_get_user_data(r)); + } + + static void destroyCallback(wl_client *client, wl_resource *resource); + static void exportCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface); + + XdgExporterUnstableV1Interface *q; + static const struct zxdg_exporter_v1_interface s_interface; + static const quint32 s_version; +}; + +const quint32 XdgExporterUnstableV1Interface::Private::s_version = 1; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +const struct zxdg_exporter_v1_interface XdgExporterUnstableV1Interface::Private::s_interface = { + destroyCallback, + exportCallback +}; +#endif + +XdgExporterUnstableV1Interface::XdgExporterUnstableV1Interface(Display *display, XdgForeignInterface *parent) + : Global(new Private(this, display, parent), parent) +{ +} + +XdgExporterUnstableV1Interface::~XdgExporterUnstableV1Interface() +{} + +XdgExporterUnstableV1Interface::Private *XdgExporterUnstableV1Interface::d_func() const +{ + return reinterpret_cast(d.data()); +} + +XdgExportedUnstableV1Interface *XdgExporterUnstableV1Interface::exportedSurface(const QString &handle) +{ + Q_D(); + + auto it = d->exportedSurfaces.constFind(handle); + if (it != d->exportedSurfaces.constEnd()) { + return it.value(); + } + return nullptr; +} + +void XdgExporterUnstableV1Interface::Private::destroyCallback(wl_client *client, wl_resource *resource) +{ + Q_UNUSED(client) +} + +void XdgExporterUnstableV1Interface::Private::exportCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface) +{ + auto s = cast(resource); + QPointer e = new XdgExportedUnstableV1Interface(s->q, surface); + + e->create(s->display->getConnection(client), wl_resource_get_version(resource), id); + + if (!e->resource()) { + wl_resource_post_no_memory(resource); + delete e; + return; + } + + const QString handle = QUuid::createUuid().toString(); + + //a surface not exported anymore + connect(e.data(), &XdgExportedUnstableV1Interface::unbound, + s->q, [s, handle]() { + s->exportedSurfaces.remove(handle); + emit s->q->surfaceUnexported(handle); + }); + + //if the surface dies before this, this dies too + connect(SurfaceInterface::get(surface), &Resource::unbound, + s->q, [s, e, handle]() { + if (e) { + e->deleteLater(); + } + s->exportedSurfaces.remove(handle); + emit s->q->surfaceUnexported(handle); + }); + + s->exportedSurfaces[handle] = e; + zxdg_exported_v1_send_handle(e->resource(), handle.toUtf8().constData()); + emit s->q->surfaceExported(handle, e); +} + +XdgExporterUnstableV1Interface::Private::Private(XdgExporterUnstableV1Interface *q, Display *d,XdgForeignInterface *foreignInterface) + : Global::Private(d, &zxdg_exporter_v1_interface, s_version) + , foreignInterface(foreignInterface) + , q(q) +{ +} + +void XdgExporterUnstableV1Interface::Private::bind(wl_client *client, uint32_t version, uint32_t id) +{ + auto c = display->getConnection(client); + wl_resource *resource = c->createResource(&zxdg_exporter_v1_interface, qMin(version, s_version), id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &s_interface, this, unbind); + // TODO: should we track? +} + +void XdgExporterUnstableV1Interface::Private::unbind(wl_resource *resource) +{ + Q_UNUSED(resource) + // TODO: implement? +} + +class Q_DECL_HIDDEN XdgImporterUnstableV1Interface::Private : public Global::Private +{ +public: + Private(XdgImporterUnstableV1Interface *q, Display *d, XdgForeignInterface *foreignInterface); + + XdgForeignInterface *foreignInterface; + + QHash importedSurfaces; + + //child->parent hash + QHash parents; + //parent->child hash + QHash children; + +private: + void bind(wl_client *client, uint32_t version, uint32_t id) override; + + static void unbind(wl_resource *resource); + static Private *cast(wl_resource *r) { + return reinterpret_cast(wl_resource_get_user_data(r)); + } + + static void destroyCallback(wl_client *client, wl_resource *resource); + static void importCallback(wl_client *client, wl_resource *resource, uint32_t id, const char * handle); + + XdgImporterUnstableV1Interface *q; + static const struct zxdg_importer_v1_interface s_interface; + static const quint32 s_version; +}; + +const quint32 XdgImporterUnstableV1Interface::Private::s_version = 1; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +const struct zxdg_importer_v1_interface XdgImporterUnstableV1Interface::Private::s_interface = { + destroyCallback, + importCallback +}; +#endif + +XdgImporterUnstableV1Interface::XdgImporterUnstableV1Interface(Display *display, XdgForeignInterface *parent) + : Global(new Private(this, display, parent), parent) +{ +} + +XdgImporterUnstableV1Interface::~XdgImporterUnstableV1Interface() +{ +} + +XdgImportedUnstableV1Interface *XdgImporterUnstableV1Interface::importedSurface(const QString &handle) +{ + Q_D(); + + auto it = d->importedSurfaces.constFind(handle); + if (it != d->importedSurfaces.constEnd()) { + return it.value(); + } + return nullptr; +} + +SurfaceInterface *XdgImporterUnstableV1Interface::transientFor(SurfaceInterface *surface) +{ + Q_D(); + + auto it = d->parents.constFind(surface); + if (it == d->parents.constEnd()) { + return nullptr; + } + return SurfaceInterface::get((*it)->parentResource()); +} + +XdgImporterUnstableV1Interface::Private *XdgImporterUnstableV1Interface::d_func() const +{ + return reinterpret_cast(d.data()); +} + +void XdgImporterUnstableV1Interface::Private::destroyCallback(wl_client *client, wl_resource *resource) +{ + Q_UNUSED(client) +} + +void XdgImporterUnstableV1Interface::Private::importCallback(wl_client *client, wl_resource *resource, uint32_t id, const char * handle) +{ + auto s = cast(resource); + + Q_ASSERT(s->foreignInterface); + + XdgExportedUnstableV1Interface *exp = s->foreignInterface->d->exporter->exportedSurface(QString::fromUtf8(handle)); + if (!exp) { + zxdg_imported_v1_send_destroyed(resource); + return; + } + + wl_resource *surface = exp->parentResource(); + if (!surface) { + zxdg_imported_v1_send_destroyed(resource); + return; + } + + QPointer imp = new XdgImportedUnstableV1Interface(s->q, surface); + imp->create(s->display->getConnection(client), wl_resource_get_version(resource), id); + + //surface no longer exported + connect(exp, &XdgExportedUnstableV1Interface::unbound, + s->q, [s, imp, handle]() { + //imp valid when the exported is deleted before the imported + if (imp) { + zxdg_imported_v1_send_destroyed(imp->resource()); + imp->deleteLater(); + } + s->importedSurfaces.remove(QString::fromUtf8(handle)); + emit s->q->surfaceUnimported(QString::fromUtf8(handle)); + }); + + connect(imp.data(), &XdgImportedUnstableV1Interface::childChanged, + s->q, [s, imp](SurfaceInterface *child) { + //remove any previous association + auto it = s->children.find(imp); + if (it != s->children.end()) { + s->parents.remove(*it); + s->children.erase(it); + } + + s->parents[child] = imp; + s->children[imp] = child; + SurfaceInterface *parent = SurfaceInterface::get(imp->parentResource()); + emit s->q->transientChanged(child, parent); + + //child surface destroyed + connect(child, &Resource::unbound, + s->q, [s, child]() { + auto it = s->parents.find(child); + if (it != s->parents.end()) { + s->children.remove(*it); + s->parents.erase(it); + emit s->q->transientChanged(nullptr, SurfaceInterface::get((*it)->parentResource())); + } + }); + }); + + //surface no longer imported + connect(imp.data(), &XdgImportedUnstableV1Interface::unbound, + s->q, [s, handle, imp]() { + s->importedSurfaces.remove(QString::fromUtf8(handle)); + emit s->q->surfaceUnimported(QString::fromUtf8(handle)); + + auto it = s->children.find(imp); + if (it != s->children.end()) { + s->parents.remove(*it); + s->children.erase(it); + emit s->q->transientChanged(*it, nullptr); + } + }); + + if (!imp->resource()) { + wl_resource_post_no_memory(resource); + delete imp; + return; + } + + s->importedSurfaces[QString::fromUtf8(handle)] = imp; + emit s->q->surfaceImported(QString::fromUtf8(handle), imp); +} + +XdgImporterUnstableV1Interface::Private::Private(XdgImporterUnstableV1Interface *q, Display *d, XdgForeignInterface *foreignInterface) + : Global::Private(d, &zxdg_importer_v1_interface, s_version) + , foreignInterface(foreignInterface) + , q(q) +{ +} + +void XdgImporterUnstableV1Interface::Private::bind(wl_client *client, uint32_t version, uint32_t id) +{ + auto c = display->getConnection(client); + wl_resource *resource = c->createResource(&zxdg_importer_v1_interface, qMin(version, s_version), id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &s_interface, this, unbind); + // TODO: should we track? +} + +void XdgImporterUnstableV1Interface::Private::unbind(wl_resource *resource) +{ + Q_UNUSED(resource) + // TODO: implement? +} + +class Q_DECL_HIDDEN XdgExportedUnstableV1Interface::Private : public Resource::Private +{ +public: + Private(XdgExportedUnstableV1Interface *q, XdgExporterUnstableV1Interface *c, wl_resource *parentResource); + ~Private(); + +private: + + XdgExportedUnstableV1Interface *q_func() { + return reinterpret_cast(q); + } + + static const struct zxdg_exported_v1_interface s_interface; +}; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +const struct zxdg_exported_v1_interface XdgExportedUnstableV1Interface::Private::s_interface = { + resourceDestroyedCallback +}; +#endif + +XdgExportedUnstableV1Interface::XdgExportedUnstableV1Interface(XdgExporterUnstableV1Interface *parent, wl_resource *parentResource) + : Resource(new Private(this, parent, parentResource)) +{ +} + +XdgExportedUnstableV1Interface::~XdgExportedUnstableV1Interface() +{} + +XdgExportedUnstableV1Interface::Private *XdgExportedUnstableV1Interface::d_func() const +{ + return reinterpret_cast(d.data()); +} + +XdgExportedUnstableV1Interface::Private::Private(XdgExportedUnstableV1Interface *q, XdgExporterUnstableV1Interface *c, wl_resource *parentResource) + : Resource::Private(q, c, parentResource, &zxdg_exported_v1_interface, &s_interface) +{ +} + +XdgExportedUnstableV1Interface::Private::~Private() +{} + +class Q_DECL_HIDDEN XdgImportedUnstableV1Interface::Private : public Resource::Private +{ +public: + Private(XdgImportedUnstableV1Interface *q, XdgImporterUnstableV1Interface *c, wl_resource *parentResource); + ~Private(); + + QPointer parentOf; + +private: + static void setParentOfCallback(wl_client *client, wl_resource *resource, wl_resource * surface); + + XdgImportedUnstableV1Interface *q_func() { + return reinterpret_cast(q); + } + + static const struct zxdg_imported_v1_interface s_interface; +}; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +const struct zxdg_imported_v1_interface XdgImportedUnstableV1Interface::Private::s_interface = { + resourceDestroyedCallback, + setParentOfCallback +}; +#endif + +XdgImportedUnstableV1Interface::XdgImportedUnstableV1Interface(XdgImporterUnstableV1Interface *parent, wl_resource *parentResource) + : Resource(new Private(this, parent, parentResource)) +{ +} + +XdgImportedUnstableV1Interface::~XdgImportedUnstableV1Interface() +{} + +XdgImportedUnstableV1Interface::Private *XdgImportedUnstableV1Interface::d_func() const +{ + return reinterpret_cast(d.data()); +} + +SurfaceInterface *XdgImportedUnstableV1Interface::child() const +{ + Q_D(); + return d->parentOf.data(); +} + +void XdgImportedUnstableV1Interface::Private::setParentOfCallback(wl_client *client, wl_resource *resource, wl_resource * surface) +{ + auto s = cast(resource); + SurfaceInterface *surf = SurfaceInterface::get(surface); + + if (!surf) { + return; + } + + s->parentOf = surf; + emit s->q_func()->childChanged(surf); +} + +XdgImportedUnstableV1Interface::Private::Private(XdgImportedUnstableV1Interface *q, XdgImporterUnstableV1Interface *c, wl_resource *parentResource) + : Resource::Private(q, c, parentResource, &zxdg_imported_v1_interface, &s_interface) +{ +} + +XdgImportedUnstableV1Interface::Private::~Private() +{} + +} +} + diff --git a/src/server/xdgforeign_v1_interface_p.h b/src/server/xdgforeign_v1_interface_p.h new file mode 100644 --- /dev/null +++ b/src/server/xdgforeign_v1_interface_p.h @@ -0,0 +1,124 @@ +/**************************************************************************** +Copyright 2017 Marco Martin + +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 KWAYLAND_SERVER_XDGFOREIGNV1_P_H +#define KWAYLAND_SERVER_XDGFOREIGNV1_P_H + +#include "global.h" +#include "resource.h" + +namespace KWayland +{ +namespace Server +{ + +class Display; +class SurfaceInterface; +class XdgExportedUnstableV1Interface; +class XdgImportedUnstableV1Interface; + +class Q_DECL_HIDDEN XdgForeignInterface::Private +{ +public: + Private(Display *display, XdgForeignInterface *q); + + XdgForeignInterface *q; + XdgExporterUnstableV1Interface *exporter; + XdgImporterUnstableV1Interface *importer; +}; + +class Q_DECL_HIDDEN XdgExporterUnstableV1Interface : public Global +{ + Q_OBJECT +public: + virtual ~XdgExporterUnstableV1Interface(); + + XdgExportedUnstableV1Interface *exportedSurface(const QString &handle); + +Q_SIGNALS: + void surfaceExported(const QString &handle, XdgExportedUnstableV1Interface *exported); + void surfaceUnexported(const QString &handle); + +private: + explicit XdgExporterUnstableV1Interface(Display *display, XdgForeignInterface *parent = nullptr); + friend class Display; + friend class XdgForeignInterface; + class Private; + Private *d_func() const; +}; + +class Q_DECL_HIDDEN XdgImporterUnstableV1Interface : public Global +{ + Q_OBJECT +public: + virtual ~XdgImporterUnstableV1Interface(); + + XdgImportedUnstableV1Interface *importedSurface(const QString &handle); + SurfaceInterface *transientFor(SurfaceInterface *surface); + +Q_SIGNALS: + void surfaceImported(const QString &handle, XdgImportedUnstableV1Interface *imported); + void surfaceUnimported(const QString &handle); + void transientChanged(KWayland::Server::SurfaceInterface *child, KWayland::Server::SurfaceInterface *parent); + +private: + explicit XdgImporterUnstableV1Interface(Display *display, XdgForeignInterface *parent = nullptr); + friend class Display; + friend class XdgForeignInterface; + class Private; + Private *d_func() const; +}; + +class Q_DECL_HIDDEN XdgExportedUnstableV1Interface : public Resource +{ + Q_OBJECT +public: + virtual ~XdgExportedUnstableV1Interface(); + +private: + explicit XdgExportedUnstableV1Interface(XdgExporterUnstableV1Interface *parent, wl_resource *parentResource); + friend class XdgExporterUnstableV1Interface; + + class Private; + Private *d_func() const; +}; + +class Q_DECL_HIDDEN XdgImportedUnstableV1Interface : public Resource +{ + Q_OBJECT +public: + virtual ~XdgImportedUnstableV1Interface(); + + SurfaceInterface *child() const; + +Q_SIGNALS: + void childChanged(KWayland::Server::SurfaceInterface *child); + +private: + explicit XdgImportedUnstableV1Interface(XdgImporterUnstableV1Interface *parent, wl_resource *parentResource); + friend class XdgImporterUnstableV1Interface; + + class Private; + Private *d_func() const; +}; + +} +} + +#endif diff --git a/src/server/xdgforeign_v2_interface.cpp b/src/server/xdgforeign_v2_interface.cpp new file mode 100644 --- /dev/null +++ b/src/server/xdgforeign_v2_interface.cpp @@ -0,0 +1,459 @@ +/**************************************************************************** +Copyright 2017 Marco Martin + +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 "xdgforeign_interface.h" +#include "xdgforeign_v2_interface_p.h" +#include "display.h" +#include "global_p.h" +#include "resource_p.h" +#include "surface_interface_p.h" + +#include "wayland-xdg-foreign-unstable-v2-server-protocol.h" + +#include +#include + +namespace KWayland +{ +namespace Server +{ + +class Q_DECL_HIDDEN XdgExporterUnstableV2Interface::Private : public Global::Private +{ +public: + Private(XdgExporterUnstableV2Interface *q, Display *d, XdgForeignInterface *foreignInterface); + + XdgForeignInterface *foreignInterface; + QHash exportedSurfaces; + +private: + void bind(wl_client *client, uint32_t version, uint32_t id) override; + + static void unbind(wl_resource *resource); + static Private *cast(wl_resource *r) { + return reinterpret_cast(wl_resource_get_user_data(r)); + } + + static void destroyCallback(wl_client *client, wl_resource *resource); + static void exportCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface); + + XdgExporterUnstableV2Interface *q; + static const struct zxdg_exporter_v2_interface s_interface; + static const quint32 s_version; +}; + +const quint32 XdgExporterUnstableV2Interface::Private::s_version = 1; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +const struct zxdg_exporter_v2_interface XdgExporterUnstableV2Interface::Private::s_interface = { + destroyCallback, + exportCallback +}; +#endif + +XdgExporterUnstableV2Interface::XdgExporterUnstableV2Interface(Display *display, XdgForeignInterface *parent) + : Global(new Private(this, display, parent), parent) +{ +} + +XdgExporterUnstableV2Interface::~XdgExporterUnstableV2Interface() +{} + +XdgExporterUnstableV2Interface::Private *XdgExporterUnstableV2Interface::d_func() const +{ + return reinterpret_cast(d.data()); +} + +XdgExportedUnstableV2Interface *XdgExporterUnstableV2Interface::exportedSurface(const QString &handle) +{ + Q_D(); + + auto it = d->exportedSurfaces.constFind(handle); + if (it != d->exportedSurfaces.constEnd()) { + return it.value(); + } + return nullptr; +} + +void XdgExporterUnstableV2Interface::Private::destroyCallback(wl_client *client, wl_resource *resource) +{ + Q_UNUSED(client) +} + +void XdgExporterUnstableV2Interface::Private::exportCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface) +{ + auto s = cast(resource); + QPointer e = new XdgExportedUnstableV2Interface(s->q, surface); + + e->create(s->display->getConnection(client), wl_resource_get_version(resource), id); + + if (!e->resource()) { + wl_resource_post_no_memory(resource); + delete e; + return; + } + + const QString handle = QUuid::createUuid().toString(); + + //a surface not exported anymore + connect(e.data(), &XdgExportedUnstableV2Interface::unbound, + s->q, [s, handle]() { + s->exportedSurfaces.remove(handle); + emit s->q->surfaceUnexported(handle); + }); + + //if the surface dies before this, this dies too + connect(SurfaceInterface::get(surface), &Resource::unbound, + s->q, [s, e, handle]() { + if (e) { + e->deleteLater(); + } + s->exportedSurfaces.remove(handle); + emit s->q->surfaceUnexported(handle); + }); + + s->exportedSurfaces[handle] = e; + zxdg_exported_v2_send_handle(e->resource(), handle.toUtf8().constData()); + emit s->q->surfaceExported(handle, e); +} + +XdgExporterUnstableV2Interface::Private::Private(XdgExporterUnstableV2Interface *q, Display *d,XdgForeignInterface *foreignInterface) + : Global::Private(d, &zxdg_exporter_v2_interface, s_version) + , foreignInterface(foreignInterface) + , q(q) +{ +} + +void XdgExporterUnstableV2Interface::Private::bind(wl_client *client, uint32_t version, uint32_t id) +{ + auto c = display->getConnection(client); + wl_resource *resource = c->createResource(&zxdg_exporter_v2_interface, qMin(version, s_version), id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &s_interface, this, unbind); + // TODO: should we track? +} + +void XdgExporterUnstableV2Interface::Private::unbind(wl_resource *resource) +{ + Q_UNUSED(resource) + // TODO: implement? +} + +class Q_DECL_HIDDEN XdgImporterUnstableV2Interface::Private : public Global::Private +{ +public: + Private(XdgImporterUnstableV2Interface *q, Display *d, XdgForeignInterface *foreignInterface); + + XdgForeignInterface *foreignInterface; + + QHash importedSurfaces; + + //child->parent hash + QHash parents; + //parent->child hash + QHash children; + +private: + void bind(wl_client *client, uint32_t version, uint32_t id) override; + + static void unbind(wl_resource *resource); + static Private *cast(wl_resource *r) { + return reinterpret_cast(wl_resource_get_user_data(r)); + } + + static void destroyCallback(wl_client *client, wl_resource *resource); + static void importCallback(wl_client *client, wl_resource *resource, uint32_t id, const char * handle); + + XdgImporterUnstableV2Interface *q; + static const struct zxdg_importer_v2_interface s_interface; + static const quint32 s_version; +}; + +const quint32 XdgImporterUnstableV2Interface::Private::s_version = 1; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +const struct zxdg_importer_v2_interface XdgImporterUnstableV2Interface::Private::s_interface = { + destroyCallback, + importCallback +}; +#endif + +XdgImporterUnstableV2Interface::XdgImporterUnstableV2Interface(Display *display, XdgForeignInterface *parent) + : Global(new Private(this, display, parent), parent) +{ +} + +XdgImporterUnstableV2Interface::~XdgImporterUnstableV2Interface() +{ +} + +XdgImportedUnstableV2Interface *XdgImporterUnstableV2Interface::importedSurface(const QString &handle) +{ + Q_D(); + + auto it = d->importedSurfaces.constFind(handle); + if (it != d->importedSurfaces.constEnd()) { + return it.value(); + } + return nullptr; +} + +SurfaceInterface *XdgImporterUnstableV2Interface::transientFor(SurfaceInterface *surface) +{ + Q_D(); + + auto it = d->parents.constFind(surface); + if (it == d->parents.constEnd()) { + return nullptr; + } + return SurfaceInterface::get((*it)->parentResource()); +} + +XdgImporterUnstableV2Interface::Private *XdgImporterUnstableV2Interface::d_func() const +{ + return reinterpret_cast(d.data()); +} + +void XdgImporterUnstableV2Interface::Private::destroyCallback(wl_client *client, wl_resource *resource) +{ + Q_UNUSED(client) +} + +void XdgImporterUnstableV2Interface::Private::importCallback(wl_client *client, wl_resource *resource, uint32_t id, const char * handle) +{ + auto s = cast(resource); + + Q_ASSERT(s->foreignInterface); + + XdgExportedUnstableV2Interface *exp = s->foreignInterface->d->exporter->exportedSurface(QString::fromUtf8(handle)); + if (!exp) { + zxdg_imported_v2_send_destroyed(resource); + return; + } + + wl_resource *surface = exp->parentResource(); + if (!surface) { + zxdg_imported_v2_send_destroyed(resource); + return; + } + + QPointer imp = new XdgImportedUnstableV2Interface(s->q, surface); + imp->create(s->display->getConnection(client), wl_resource_get_version(resource), id); + + //surface no longer exported + connect(exp, &XdgExportedUnstableV2Interface::unbound, + s->q, [s, imp, handle]() { + //imp valid when the exported is deleted before the imported + if (imp) { + zxdg_imported_v2_send_destroyed(imp->resource()); + imp->deleteLater(); + } + s->importedSurfaces.remove(QString::fromUtf8(handle)); + emit s->q->surfaceUnimported(QString::fromUtf8(handle)); + }); + + connect(imp.data(), &XdgImportedUnstableV2Interface::childChanged, + s->q, [s, imp](SurfaceInterface *child) { + //remove any previous association + auto it = s->children.find(imp); + if (it != s->children.end()) { + s->parents.remove(*it); + s->children.erase(it); + } + + s->parents[child] = imp; + s->children[imp] = child; + SurfaceInterface *parent = SurfaceInterface::get(imp->parentResource()); + emit s->q->transientChanged(child, parent); + + //child surface destroyed + connect(child, &Resource::unbound, + s->q, [s, child]() { + auto it = s->parents.find(child); + if (it != s->parents.end()) { + s->children.remove(*it); + s->parents.erase(it); + emit s->q->transientChanged(nullptr, SurfaceInterface::get((*it)->parentResource())); + } + }); + }); + + //surface no longer imported + connect(imp.data(), &XdgImportedUnstableV2Interface::unbound, + s->q, [s, handle, imp]() { + s->importedSurfaces.remove(QString::fromUtf8(handle)); + emit s->q->surfaceUnimported(QString::fromUtf8(handle)); + + auto it = s->children.find(imp); + if (it != s->children.end()) { + s->parents.remove(*it); + s->children.erase(it); + emit s->q->transientChanged(*it, nullptr); + } + }); + + if (!imp->resource()) { + wl_resource_post_no_memory(resource); + delete imp; + return; + } + + s->importedSurfaces[QString::fromUtf8(handle)] = imp; + emit s->q->surfaceImported(QString::fromUtf8(handle), imp); +} + +XdgImporterUnstableV2Interface::Private::Private(XdgImporterUnstableV2Interface *q, Display *d, XdgForeignInterface *foreignInterface) + : Global::Private(d, &zxdg_importer_v2_interface, s_version) + , foreignInterface(foreignInterface) + , q(q) +{ +} + +void XdgImporterUnstableV2Interface::Private::bind(wl_client *client, uint32_t version, uint32_t id) +{ + auto c = display->getConnection(client); + wl_resource *resource = c->createResource(&zxdg_importer_v2_interface, qMin(version, s_version), id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &s_interface, this, unbind); + // TODO: should we track? +} + +void XdgImporterUnstableV2Interface::Private::unbind(wl_resource *resource) +{ + Q_UNUSED(resource) + // TODO: implement? +} + +class Q_DECL_HIDDEN XdgExportedUnstableV2Interface::Private : public Resource::Private +{ +public: + Private(XdgExportedUnstableV2Interface *q, XdgExporterUnstableV2Interface *c, wl_resource *parentResource); + ~Private(); + +private: + + XdgExportedUnstableV2Interface *q_func() { + return reinterpret_cast(q); + } + + static const struct zxdg_exported_v2_interface s_interface; +}; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +const struct zxdg_exported_v2_interface XdgExportedUnstableV2Interface::Private::s_interface = { + resourceDestroyedCallback +}; +#endif + +XdgExportedUnstableV2Interface::XdgExportedUnstableV2Interface(XdgExporterUnstableV2Interface *parent, wl_resource *parentResource) + : Resource(new Private(this, parent, parentResource)) +{ +} + +XdgExportedUnstableV2Interface::~XdgExportedUnstableV2Interface() +{} + +XdgExportedUnstableV2Interface::Private *XdgExportedUnstableV2Interface::d_func() const +{ + return reinterpret_cast(d.data()); +} + +XdgExportedUnstableV2Interface::Private::Private(XdgExportedUnstableV2Interface *q, XdgExporterUnstableV2Interface *c, wl_resource *parentResource) + : Resource::Private(q, c, parentResource, &zxdg_exported_v2_interface, &s_interface) +{ +} + +XdgExportedUnstableV2Interface::Private::~Private() +{} + +class Q_DECL_HIDDEN XdgImportedUnstableV2Interface::Private : public Resource::Private +{ +public: + Private(XdgImportedUnstableV2Interface *q, XdgImporterUnstableV2Interface *c, wl_resource *parentResource); + ~Private(); + + QPointer parentOf; + +private: + static void setParentOfCallback(wl_client *client, wl_resource *resource, wl_resource * surface); + + XdgImportedUnstableV2Interface *q_func() { + return reinterpret_cast(q); + } + + static const struct zxdg_imported_v2_interface s_interface; +}; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +const struct zxdg_imported_v2_interface XdgImportedUnstableV2Interface::Private::s_interface = { + resourceDestroyedCallback, + setParentOfCallback +}; +#endif + +XdgImportedUnstableV2Interface::XdgImportedUnstableV2Interface(XdgImporterUnstableV2Interface *parent, wl_resource *parentResource) + : Resource(new Private(this, parent, parentResource)) +{ +} + +XdgImportedUnstableV2Interface::~XdgImportedUnstableV2Interface() +{} + +XdgImportedUnstableV2Interface::Private *XdgImportedUnstableV2Interface::d_func() const +{ + return reinterpret_cast(d.data()); +} + +SurfaceInterface *XdgImportedUnstableV2Interface::child() const +{ + Q_D(); + return d->parentOf.data(); +} + +void XdgImportedUnstableV2Interface::Private::setParentOfCallback(wl_client *client, wl_resource *resource, wl_resource * surface) +{ + auto s = cast(resource); + SurfaceInterface *surf = SurfaceInterface::get(surface); + + if (!surf) { + return; + } + + s->parentOf = surf; + emit s->q_func()->childChanged(surf); +} + +XdgImportedUnstableV2Interface::Private::Private(XdgImportedUnstableV2Interface *q, XdgImporterUnstableV2Interface *c, wl_resource *parentResource) + : Resource::Private(q, c, parentResource, &zxdg_imported_v2_interface, &s_interface) +{ +} + +XdgImportedUnstableV2Interface::Private::~Private() +{} + +} +} + diff --git a/src/server/xdgforeign_v2_interface_p.h b/src/server/xdgforeign_v2_interface_p.h new file mode 100644 --- /dev/null +++ b/src/server/xdgforeign_v2_interface_p.h @@ -0,0 +1,124 @@ +/**************************************************************************** +Copyright 2017 Marco Martin + +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 KWAYLAND_SERVER_XDGFOREIGNV2_P_H +#define KWAYLAND_SERVER_XDGFOREIGNV2_P_H + +#include "global.h" +#include "resource.h" + +namespace KWayland +{ +namespace Server +{ + +class Display; +class SurfaceInterface; +class XdgExportedUnstableV2Interface; +class XdgImportedUnstableV2Interface; + +class Q_DECL_HIDDEN XdgForeignInterface::Private +{ +public: + Private(Display *display, XdgForeignInterface *q); + + XdgForeignInterface *q; + XdgExporterUnstableV2Interface *exporter; + XdgImporterUnstableV2Interface *importer; +}; + +class Q_DECL_HIDDEN XdgExporterUnstableV2Interface : public Global +{ + Q_OBJECT +public: + virtual ~XdgExporterUnstableV2Interface(); + + XdgExportedUnstableV2Interface *exportedSurface(const QString &handle); + +Q_SIGNALS: + void surfaceExported(const QString &handle, XdgExportedUnstableV2Interface *exported); + void surfaceUnexported(const QString &handle); + +private: + explicit XdgExporterUnstableV2Interface(Display *display, XdgForeignInterface *parent = nullptr); + friend class Display; + friend class XdgForeignInterface; + class Private; + Private *d_func() const; +}; + +class Q_DECL_HIDDEN XdgImporterUnstableV2Interface : public Global +{ + Q_OBJECT +public: + virtual ~XdgImporterUnstableV2Interface(); + + XdgImportedUnstableV2Interface *importedSurface(const QString &handle); + SurfaceInterface *transientFor(SurfaceInterface *surface); + +Q_SIGNALS: + void surfaceImported(const QString &handle, XdgImportedUnstableV2Interface *imported); + void surfaceUnimported(const QString &handle); + void transientChanged(KWayland::Server::SurfaceInterface *child, KWayland::Server::SurfaceInterface *parent); + +private: + explicit XdgImporterUnstableV2Interface(Display *display, XdgForeignInterface *parent = nullptr); + friend class Display; + friend class XdgForeignInterface; + class Private; + Private *d_func() const; +}; + +class Q_DECL_HIDDEN XdgExportedUnstableV2Interface : public Resource +{ + Q_OBJECT +public: + virtual ~XdgExportedUnstableV2Interface(); + +private: + explicit XdgExportedUnstableV2Interface(XdgExporterUnstableV2Interface *parent, wl_resource *parentResource); + friend class XdgExporterUnstableV2Interface; + + class Private; + Private *d_func() const; +}; + +class Q_DECL_HIDDEN XdgImportedUnstableV2Interface : public Resource +{ + Q_OBJECT +public: + virtual ~XdgImportedUnstableV2Interface(); + + SurfaceInterface *child() const; + +Q_SIGNALS: + void childChanged(KWayland::Server::SurfaceInterface *child); + +private: + explicit XdgImportedUnstableV2Interface(XdgImporterUnstableV2Interface *parent, wl_resource *parentResource); + friend class XdgImporterUnstableV2Interface; + + class Private; + Private *d_func() const; +}; + +} +} + +#endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -59,3 +59,7 @@ add_executable(plasmasurface-test plasmasurfacetest.cpp) target_link_libraries(plasmasurface-test Qt5::Gui KF5::WaylandClient) ecm_mark_as_test(plasmasurface-test) + +add_executable(xdgforeign-test xdgforeigntest.cpp) +target_link_libraries(xdgforeign-test Qt5::Gui KF5::WaylandClient) +ecm_mark_as_test(xdgforeign-test) diff --git a/tests/xdgforeigntest.cpp b/tests/xdgforeigntest.cpp new file mode 100644 --- /dev/null +++ b/tests/xdgforeigntest.cpp @@ -0,0 +1,208 @@ +/******************************************************************** +Copyright 2016 Martin Gräßlin +Copyright 2017 Marco Martin + +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 "../src/client/compositor.h" +#include "../src/client/connection_thread.h" +#include "../src/client/event_queue.h" +#include "../src/client/registry.h" +#include "../src/client/shell.h" +#include "../src/client/shm_pool.h" +#include "../src/client/server_decoration.h" +#include "../src/client/xdgshell.h" +#include "../src/client/xdgforeign.h" +// Qt +#include +#include +#include +#include +#include +using namespace KWayland::Client; + +class XdgForeignTest : public QObject +{ + Q_OBJECT +public: + explicit XdgForeignTest(QObject *parent = nullptr); + virtual ~XdgForeignTest(); + + void init(); + + +private: + void setupRegistry(Registry *registry); + void render(); + QThread *m_connectionThread; + ConnectionThread *m_connectionThreadObject; + EventQueue *m_eventQueue = nullptr; + Compositor *m_compositor = nullptr; + XdgShell *m_shell = nullptr; + XdgShellSurface *m_shellSurface = nullptr; + ShmPool *m_shm = nullptr; + Surface *m_surface = nullptr; + + XdgShellSurface *m_childShellSurface = nullptr; + Surface *m_childSurface = nullptr; + + KWayland::Client::XdgExporter *m_exporter = nullptr; + KWayland::Client::XdgImporter *m_importer = nullptr; + KWayland::Client::XdgExported *m_exported = nullptr; + KWayland::Client::XdgImported *m_imported = nullptr; + KWayland::Client::ServerSideDecorationManager *m_decoration = nullptr; +}; + +XdgForeignTest::XdgForeignTest(QObject *parent) + : QObject(parent) + , m_connectionThread(new QThread(this)) + , m_connectionThreadObject(new ConnectionThread()) +{ +} + +XdgForeignTest::~XdgForeignTest() +{ + m_connectionThread->quit(); + m_connectionThread->wait(); + m_connectionThreadObject->deleteLater(); +} + +void XdgForeignTest::init() +{ + connect(m_connectionThreadObject, &ConnectionThread::connected, this, + [this] { + m_eventQueue = new EventQueue(this); + m_eventQueue->setup(m_connectionThreadObject); + + Registry *registry = new Registry(this); + setupRegistry(registry); + }, + Qt::QueuedConnection + ); + m_connectionThreadObject->moveToThread(m_connectionThread); + m_connectionThread->start(); + + m_connectionThreadObject->initConnection(); +} + +void XdgForeignTest::setupRegistry(Registry *registry) +{ + connect(registry, &Registry::compositorAnnounced, this, + [this, registry](quint32 name, quint32 version) { + m_compositor = registry->createCompositor(name, version, this); + } + ); + connect(registry, &Registry::xdgShellUnstableV5Announced, this, + [this, registry](quint32 name, quint32 version) { + m_shell = registry->createXdgShell(name, version, this); + } + ); + connect(registry, &Registry::shmAnnounced, this, + [this, registry](quint32 name, quint32 version) { + m_shm = registry->createShmPool(name, version, this); + } + ); + connect(registry, &Registry::exporterUnstableV2Announced, this, + [this, registry](quint32 name, quint32 version) { + m_exporter = registry->createXdgExporterUnstable(name, version, this); + m_exporter->setEventQueue(m_eventQueue); + } + ); + connect(registry, &Registry::importerUnstableV2Announced, this, + [this, registry](quint32 name, quint32 version) { + m_importer = registry->createXdgImporterUnstable(name, version, this); + m_importer->setEventQueue(m_eventQueue); + } + ); + connect(registry, &Registry::serverSideDecorationManagerAnnounced, this, + [this, registry](quint32 name, quint32 version) { + m_decoration = registry->createServerSideDecorationManager(name, version, this); + m_decoration->setEventQueue(m_eventQueue); + } + ); + connect(registry, &Registry::interfacesAnnounced, this, + [this] { + Q_ASSERT(m_compositor); + Q_ASSERT(m_shell); + Q_ASSERT(m_shm); + Q_ASSERT(m_exporter); + Q_ASSERT(m_importer); + m_surface = m_compositor->createSurface(this); + Q_ASSERT(m_surface); + auto parentDeco = m_decoration->create(m_surface, this); + m_shellSurface = m_shell->createSurface(m_surface, this); + Q_ASSERT(m_shellSurface); + connect(m_shellSurface, &XdgShellSurface::sizeChanged, this, &XdgForeignTest::render); + + m_childSurface = m_compositor->createSurface(this); + Q_ASSERT(m_childSurface); + auto childDeco = m_decoration->create(m_childSurface, this); + m_childShellSurface = m_shell->createSurface(m_childSurface, this); + Q_ASSERT(m_childShellSurface); + connect(m_childShellSurface, &XdgShellSurface::sizeChanged, this, &XdgForeignTest::render); + + m_exported = m_exporter->exportSurface(m_surface, this); + Q_ASSERT(m_decoration); + connect(m_exported, &XdgExported::done, this, [this]() { + m_imported = m_importer->import(m_exported->handle(), this); + m_imported->setParentOf(m_childSurface); + }); + render(); + } + ); + registry->setEventQueue(m_eventQueue); + registry->create(m_connectionThreadObject); + registry->setup(); +} + +void XdgForeignTest::render() +{ + QSize size = m_shellSurface->size().isValid() ? m_shellSurface->size() : QSize(500, 500); + auto buffer = m_shm->getBuffer(size, size.width() * 4).toStrongRef(); + buffer->setUsed(true); + QImage image(buffer->address(), size.width(), size.height(), QImage::Format_ARGB32_Premultiplied); + image.fill(QColor(255, 255, 255, 255)); + + m_surface->attachBuffer(*buffer); + m_surface->damage(QRect(QPoint(0, 0), size)); + m_surface->commit(Surface::CommitFlag::None); + buffer->setUsed(false); + + size = m_childShellSurface->size().isValid() ? m_childShellSurface->size() : QSize(200, 200); + buffer = m_shm->getBuffer(size, size.width() * 4).toStrongRef(); + buffer->setUsed(true); + image = QImage(buffer->address(), size.width(), size.height(), QImage::Format_ARGB32_Premultiplied); + image.fill(QColor(255, 0, 0, 255)); + + m_childSurface->attachBuffer(*buffer); + m_childSurface->damage(QRect(QPoint(0, 0), size)); + m_childSurface->commit(Surface::CommitFlag::None); + buffer->setUsed(false); +} + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + + XdgForeignTest client; + + client.init(); + + return app.exec(); +} + +#include "xdgforeigntest.moc"