diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,7 +32,7 @@ set(REQUIRED_QT_VERSION 5.7.0) find_package(Qt5 ${REQUIRED_QT_VERSION} CONFIG REQUIRED Concurrent Gui) -find_package(Wayland 1.7 COMPONENTS Client Server) +find_package(Wayland 1.13 COMPONENTS Client Server) set_package_properties(Wayland PROPERTIES TYPE REQUIRED ) diff --git a/autotests/client/CMakeLists.txt b/autotests/client/CMakeLists.txt --- a/autotests/client/CMakeLists.txt +++ b/autotests/client/CMakeLists.txt @@ -379,3 +379,15 @@ target_link_libraries( testPointerConstraints Qt5::Test Qt5::Gui KF5::WaylandServer KF5::WaylandClient Wayland::Client) add_test(NAME kwayland-testPointerConstraints COMMAND testPointerConstraints) ecm_mark_as_test(testPointerConstraints) + + +######################################################## +# Test Filter +######################################################## +set( testFilter_SRCS + test_wayland_filter.cpp + ) +add_executable(testFilter ${testFilter_SRCS}) +target_link_libraries( testFilter Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer Wayland::Server) +add_test(NAME kwayland-testFilter COMMAND testFilter) +ecm_mark_as_test(testFilter) diff --git a/autotests/client/test_wayland_filter.cpp b/autotests/client/test_wayland_filter.cpp new file mode 100644 --- /dev/null +++ b/autotests/client/test_wayland_filter.cpp @@ -0,0 +1,168 @@ +/******************************************************************** +Copyright 2017 David Edmundson + +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/blur.h" +#include "../../src/server/display.h" +#include "../../src/server/compositor_interface.h" +#include "../../src/server/region_interface.h" +#include "../../src/server/blur_interface.h" +#include "../../src/server/filtered_display.h" + +#include + +using namespace KWayland::Client; + +class TestDisplay; + +class TestFilter : public QObject +{ + Q_OBJECT +public: + explicit TestFilter(QObject *parent = nullptr); +private Q_SLOTS: + void init(); + void cleanup(); + void testFilter_data(); + void testFilter(); + +private: + TestDisplay *m_display; + KWayland::Server::CompositorInterface *m_compositorInterface; + KWayland::Server::BlurManagerInterface *m_blurManagerInterface; +}; + +static const QString s_socketName = QStringLiteral("kwayland-test-wayland-blur-0"); + +//The following non-realistic class allows only clients in the m_allowedClients list to access the blur interface +//all other interfaces are allowed +class TestDisplay : public KWayland::Server::FilteredDisplay +{ +public: + TestDisplay(QObject *parent); + bool allowInterface(KWayland::Server::ClientConnection * client, const QByteArray & interfaceName) override; + QList m_allowedClients; +}; + +TestDisplay::TestDisplay(QObject *parent): + KWayland::Server::FilteredDisplay(parent) +{} + +bool TestDisplay::allowInterface(KWayland::Server::ClientConnection* client, const QByteArray& interfaceName) +{ + if (interfaceName == "org_kde_kwin_blur_manager") { + return m_allowedClients.contains(*client); + } + return true; +} + +TestFilter::TestFilter(QObject *parent) + : QObject(parent) + , m_display(nullptr) + , m_compositorInterface(nullptr) +{} + +void TestFilter::init() +{ + using namespace KWayland::Server; + delete m_display; + m_display = new TestDisplay(this); + m_display->setSocketName(s_socketName); + m_display->start(); + QVERIFY(m_display->isRunning()); + + m_compositorInterface = m_display->createCompositor(m_display); + m_compositorInterface->create(); + QVERIFY(m_compositorInterface->isValid()); + + m_blurManagerInterface = m_display->createBlurManager(m_display); + m_blurManagerInterface->create(); + QVERIFY(m_blurManagerInterface->isValid()); +} + +void TestFilter::cleanup() +{ +} + +void TestFilter::testFilter_data() +{ + QTest::addColumn("accessAllowed"); + QTest::newRow("granted") << true; + QTest::newRow("denied") << false; + +} + +void TestFilter::testFilter() +{ + QFETCH(bool, accessAllowed); + + // setup connection + QScopedPointer connection(new KWayland::Client::ConnectionThread()); + QSignalSpy connectedSpy(connection.data(), &ConnectionThread::connected); + QVERIFY(connectedSpy.isValid()); + connection->setSocketName(s_socketName); + + QScopedPointer thread(new QThread(this)); + connection->moveToThread(thread.data()); + thread->start(); + + connection->initConnection(); + QVERIFY(connectedSpy.wait()); + + //use low level API as Server::Display::connections only lists connections which have + //been previous fetched via getConnection() + if (accessAllowed) { + wl_client *clientConnection; + wl_client_for_each(clientConnection, wl_display_get_client_list(*m_display)) { + m_display->m_allowedClients << clientConnection; + } + } + + KWayland::Client::EventQueue queue; + queue.setup(connection.data()); + + Registry registry; + QSignalSpy registryDoneSpy(®istry, &Registry::interfacesAnnounced); + QSignalSpy compositorSpy(®istry, &Registry::compositorAnnounced); + QSignalSpy blurSpy(®istry, &Registry::blurAnnounced); + + registry.setEventQueue(&queue); + registry.create(connection->display()); + QVERIFY(registry.isValid()); + registry.setup(); + + QVERIFY(registryDoneSpy.wait()); + QVERIFY(compositorSpy.count() == 1); + QVERIFY(blurSpy.count() == accessAllowed ? 1 : 0); + + thread->quit(); + thread->wait(); +} + + +QTEST_GUILESS_MAIN(TestFilter) +#include "test_wayland_filter.moc" diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -8,6 +8,7 @@ datasource_interface.cpp display.cpp dpms_interface.cpp + filtered_display.cpp global.cpp idle_interface.cpp idleinhibit_interface.cpp @@ -202,6 +203,7 @@ datasource_interface.h display.h dpms_interface.h + filtered_display.h fakeinput_interface.h global.h idle_interface.h diff --git a/src/server/filtered_display.h b/src/server/filtered_display.h new file mode 100644 --- /dev/null +++ b/src/server/filtered_display.h @@ -0,0 +1,65 @@ +/******************************************************************** +Copyright 2017 David Edmundson + +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_FILTERED_DISPLAY_H +#define KWAYLAND_SERVER_FILTERED_DISPLAY_H + +#include "global.h" +#include "display.h" + +#include + +namespace KWayland +{ +namespace Server +{ + +/** +* Server Implementation that allows one to restrict which globals are available to which clients +* +* Users of this class must implement the virtual @method allowInterface method. +* +* @since 5.FIXME +*/ +class KWAYLANDSERVER_EXPORT FilteredDisplay : public Display +{ + Q_OBJECT +public: + FilteredDisplay(QObject *parent); + ~FilteredDisplay(); + +/** +* Return whether the @arg client can see the interface with the given @arg interfaceName +* +* When false will not see these globals for a given interface in the registry, +* and any manual attempts to bind will fail +* +* @return true if the client should be able to access the global with the following interfaceName +*/ + virtual bool allowInterface(ClientConnection *client, const QByteArray &interfaceName) = 0; +private: + class Private; + QScopedPointer d; +}; + +} +} + +#endif diff --git a/src/server/filtered_display.cpp b/src/server/filtered_display.cpp new file mode 100644 --- /dev/null +++ b/src/server/filtered_display.cpp @@ -0,0 +1,70 @@ +/******************************************************************** +Copyright 2017 David Edmundson + +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 "filtered_display.h" +#include "display.h" + +#include + +#include + +namespace KWayland +{ +namespace Server +{ + +class FilteredDisplay::Private +{ +public: + Private(FilteredDisplay *_q); + FilteredDisplay *q; + static bool globalFilterCallback(const wl_client *client, const wl_global *global, void *data) + { + auto t = static_cast(data); + auto clientConnection = t->q->getConnection(const_cast(client)); + auto interface = wl_global_get_interface(global); + auto name = QByteArray::fromRawData(interface->name, strlen(interface->name)); + return t->q->allowInterface(clientConnection, name); + }; +}; + +FilteredDisplay::Private::Private(FilteredDisplay *_q): + q(_q) +{} + + +FilteredDisplay::FilteredDisplay(QObject *parent): + Display(parent), + d(new Private(this)) +{ + connect(this, &Display::runningChanged, [this](bool running) { + if (!running) { + return; + } + wl_display_set_global_filter(*this, Private::globalFilterCallback, d.data()); + }); +} + +FilteredDisplay::~FilteredDisplay() +{ +} + +} +}