From 9feb359676ac58d652ca1a717279c1d2196181e4 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Fri, 13 Oct 2017 11:29:17 +0200 Subject: [PATCH] Wayland foreign protocol Summary: Implement the "foreign" wayland protocol. A client can export a surface with an unique string as handle, then another client can refer to that surface and set an own surface as child of that surface. Potential use cases are out-of-process dialogs, such as file dialogs, meant to be used by sandboxed processes that may not have the access it needs to implement such dialogs. The handle needs to be shared between the processes with other means, such as dbus or command line paramenters. The public api of the server side only tracks parent/child relationships as this is the only data kwin would need it for, the rest of the api is not exported so should be safer from eventual protocol changes Test Plan: the autotest works, but has a lot of random crashes when deleting surfaces, unfortunately backtraces don't tell much and the crashes never occur when running into valgrind behavior may still be wrong, depending on how the protocol is supposed to work if more clients try to set the same exported surface as parent Reviewers: #plasma, #kwin, davidedmundson, graesslin Reviewed By: #plasma, #kwin, graesslin Subscribers: davidedmundson, graesslin, plasma-devel, #frameworks Tags: #frameworks, #plasma_on_wayland Differential Revision: https://phabricator.kde.org/D7369 --- src/wayland/CMakeLists.txt | 8 + src/wayland/autotests/client/CMakeLists.txt | 11 + .../autotests/client/test_xdg_foreign.cpp | 401 +++++++++++++++ src/wayland/display.cpp | 8 + src/wayland/display.h | 8 + src/wayland/server/xdgforeign_interface.cpp | 79 +++ src/wayland/server/xdgforeign_interface.h | 97 ++++ src/wayland/tests/CMakeLists.txt | 4 + src/wayland/tests/xdgforeigntest.cpp | 208 ++++++++ src/wayland/xdgforeign_v2_interface.cpp | 459 ++++++++++++++++++ src/wayland/xdgforeign_v2_interface_p.h | 124 +++++ 11 files changed, 1407 insertions(+) create mode 100644 src/wayland/autotests/client/test_xdg_foreign.cpp create mode 100644 src/wayland/server/xdgforeign_interface.cpp create mode 100644 src/wayland/server/xdgforeign_interface.h create mode 100644 src/wayland/tests/xdgforeigntest.cpp create mode 100644 src/wayland/xdgforeign_v2_interface.cpp create mode 100644 src/wayland/xdgforeign_v2_interface_p.h diff --git a/src/wayland/CMakeLists.txt b/src/wayland/CMakeLists.txt index c424fd1a09..6fd35937d5 100644 --- a/src/wayland/CMakeLists.txt +++ b/src/wayland/CMakeLists.txt @@ -45,6 +45,8 @@ set(SERVER_LIB_SRCS textinput_interface_v2.cpp xdgshell_interface.cpp xdgshell_v5_interface.cpp + xdgforeign_v2_interface.cpp + xdgforeign_interface.cpp xdgshell_v6_interface.cpp ) @@ -147,6 +149,11 @@ ecm_add_wayland_server_protocol(SERVER_LIB_SRCS 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 @@ -216,6 +223,7 @@ set(SERVER_LIB_HEADERS textinput_interface.h touch_interface.h xdgshell_interface.h + xdgforeign_interface.h ) install(FILES diff --git a/src/wayland/autotests/client/CMakeLists.txt b/src/wayland/autotests/client/CMakeLists.txt index 65aaeef1d4..9431bd9347 100644 --- a/src/wayland/autotests/client/CMakeLists.txt +++ b/src/wayland/autotests/client/CMakeLists.txt @@ -349,6 +349,17 @@ target_link_libraries( testXdgShellV5 Qt5::Test Qt5::Gui KF5::WaylandServer KF5: add_test(NAME kwayland-testXdgShellV5 COMMAND testXdgShellV5) 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 XdgShellV6 ######################################################## diff --git a/src/wayland/autotests/client/test_xdg_foreign.cpp b/src/wayland/autotests/client/test_xdg_foreign.cpp new file mode 100644 index 0000000000..8f234b96ad --- /dev/null +++ b/src/wayland/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->createXdgForeignInterface(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.createXdgExporter(exporterSpy.first().first().value(), exporterSpy.first().last().value(), this); + m_importer = registry.createXdgImporter(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->exportTopLevel(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->importTopLevel(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->exportTopLevel(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->importTopLevel(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->importTopLevel(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/wayland/display.cpp b/src/wayland/display.cpp index 83fa256ec6..f3c504f845 100644 --- a/src/wayland/display.cpp +++ b/src/wayland/display.cpp @@ -44,6 +44,7 @@ License along with this library. If not, see . #include "subcompositor_interface.h" #include "textinput_interface_p.h" #include "xdgshell_v5_interface_p.h" +#include "xdgforeign_interface.h" #include "xdgshell_v6_interface_p.h" #include @@ -412,6 +413,13 @@ PointerConstraintsInterface *Display::createPointerConstraints(const PointerCons return p; } +XdgForeignInterface *Display::createXdgForeignInterface(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/wayland/display.h b/src/wayland/display.h index 1ae6685ef4..5a9f65e689 100644 --- a/src/wayland/display.h +++ b/src/wayland/display.h @@ -80,6 +80,7 @@ enum class PointerGesturesInterfaceVersion; class PointerGesturesInterface; enum class PointerConstraintsInterfaceVersion; class PointerConstraintsInterface; +class XdgForeignInterface; /** * @brief Class holding the Wayland server display loop. @@ -219,6 +220,13 @@ public: **/ PointerConstraintsInterface *createPointerConstraints(const PointerConstraintsInterfaceVersion &version, QObject *parent = nullptr); + /** + * Creates the XdgForeignInterface in interface @p version + * + * @returns The created manager object + * @since 5.40 + **/ + XdgForeignInterface *createXdgForeignInterface(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/wayland/server/xdgforeign_interface.cpp b/src/wayland/server/xdgforeign_interface.cpp new file mode 100644 index 0000000000..579a8508fb --- /dev/null +++ b/src/wayland/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/wayland/server/xdgforeign_interface.h b/src/wayland/server/xdgforeign_interface.h new file mode 100644 index 0000000000..ffa5fcdc7f --- /dev/null +++ b/src/wayland/server/xdgforeign_interface.h @@ -0,0 +1,97 @@ +/**************************************************************************** +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; + +/** + * This class encapsulates the server side logic of the XdgForeign protocol. + * a process can export a surface to be identifiable by a server-wide unique + * string handle, and another process can in turn import that surface, and set it + * as transient parent for one of its own surfaces. + * This parent relationship is traced by the transientChanged signal and the + * transientFor method. + * + * @since 5.40 + */ +class KWAYLANDSERVER_EXPORT XdgForeignInterface : public QObject +{ + Q_OBJECT +public: + XdgForeignInterface(Display *display, QObject *parent = nullptr); + ~XdgForeignInterface(); + + /** + * Creates the native zxdg_exporter_v2 and zxdg_importer_v2 interfaces + * and announces them to the client. + */ + void create(); + + /** + * @returns true if theimporter and exporter are valid and functional + */ + bool isValid(); + + /** + * If a client did import a surface and set one of its own as child of the + * imported one, this returns the mapping. + * @param surface the child surface we want to search an imported transientParent for. + * @returns the transient parent of the surface, if found, nullptr otherwise. + */ + 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/wayland/tests/CMakeLists.txt b/src/wayland/tests/CMakeLists.txt index 8110a53227..91f6b7e27a 100644 --- a/src/wayland/tests/CMakeLists.txt +++ b/src/wayland/tests/CMakeLists.txt @@ -60,7 +60,11 @@ 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) add_executable(xdg-test xdgtest.cpp) target_link_libraries(xdg-test Qt5::Gui KF5::WaylandClient) ecm_mark_as_test(xdg-test) + diff --git a/src/wayland/tests/xdgforeigntest.cpp b/src/wayland/tests/xdgforeigntest.cpp new file mode 100644 index 0000000000..6101408c86 --- /dev/null +++ b/src/wayland/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->createXdgExporter(name, version, this); + m_exporter->setEventQueue(m_eventQueue); + } + ); + connect(registry, &Registry::importerUnstableV2Announced, this, + [this, registry](quint32 name, quint32 version) { + m_importer = registry->createXdgImporter(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->exportTopLevel(m_surface, this); + Q_ASSERT(m_decoration); + connect(m_exported, &XdgExported::done, this, [this]() { + m_imported = m_importer->importTopLevel(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" diff --git a/src/wayland/xdgforeign_v2_interface.cpp b/src/wayland/xdgforeign_v2_interface.cpp new file mode 100644 index 0000000000..ca79976946 --- /dev/null +++ b/src/wayland/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/wayland/xdgforeign_v2_interface_p.h b/src/wayland/xdgforeign_v2_interface_p.h new file mode 100644 index 0000000000..d0424300fe --- /dev/null +++ b/src/wayland/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