From bd5fe4f78581d098f3ac6d9521331b5e9a6321e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Mon, 18 Aug 2014 14:05:35 +0200 Subject: [PATCH] [kwin_wayland] Add a Wayland::Registry class The Wayland::Registry class wraps wl_registry handling. It keeps track of the interfaces in the registry and emits signals whenever a known interface gets announced or removed. So far it only tracks the interfaces which are used and needed by KWin. --- CMakeLists.txt | 4 +- autotests/wayland_client/CMakeLists.txt | 13 + .../wayland_client/test_wayland_registry.cpp | 180 +++++++++++++ wayland_backend.cpp | 85 +++---- wayland_backend.h | 9 +- wayland_client/registry.cpp | 237 ++++++++++++++++++ wayland_client/registry.h | 107 ++++++++ 7 files changed, 573 insertions(+), 62 deletions(-) create mode 100644 autotests/wayland_client/test_wayland_registry.cpp create mode 100644 wayland_client/registry.cpp create mode 100644 wayland_client/registry.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f74de850be..d352c8681e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -400,7 +400,9 @@ if(Wayland_Client_FOUND AND XKB_FOUND) set(kwin_KDEINIT_SRCS ${kwin_KDEINIT_SRCS} wayland_backend.cpp - wayland_client/connection_thread.cpp) + wayland_client/connection_thread.cpp + wayland_client/registry.cpp + ) if(KWIN_HAVE_EGL AND Wayland_Egl_FOUND) set(kwin_KDEINIT_SRCS ${kwin_KDEINIT_SRCS} egl_wayland_backend.cpp) endif() diff --git a/autotests/wayland_client/CMakeLists.txt b/autotests/wayland_client/CMakeLists.txt index 68bf544bd2..020ff068e3 100644 --- a/autotests/wayland_client/CMakeLists.txt +++ b/autotests/wayland_client/CMakeLists.txt @@ -6,3 +6,16 @@ add_executable(testWaylandConnectionThread ${testWaylandConnectionThread_SRCS}) target_link_libraries( testWaylandConnectionThread Qt5::Test Wayland::Client) add_test(kwin-testWaylandConnectionThread testWaylandConnectionThread) ecm_mark_as_test(testWaylandConnectionThread) + +######################################################## +# Test WaylandRegistry +######################################################## +set( testWaylandRegistry_SRCS + test_wayland_registry.cpp + ${KWIN_SOURCE_DIR}/wayland_client/connection_thread.cpp + ${KWIN_SOURCE_DIR}/wayland_client/registry.cpp + ) +add_executable(testWaylandRegistry ${testWaylandRegistry_SRCS}) +target_link_libraries( testWaylandRegistry Qt5::Test Wayland::Client) +add_test(kwin-testWaylandRegistry testWaylandRegistry) +ecm_mark_as_test(testWaylandRegistry) diff --git a/autotests/wayland_client/test_wayland_registry.cpp b/autotests/wayland_client/test_wayland_registry.cpp new file mode 100644 index 0000000000..9f9a9a726b --- /dev/null +++ b/autotests/wayland_client/test_wayland_registry.cpp @@ -0,0 +1,180 @@ +/******************************************************************** +KWin - the KDE window manager +This file is part of the KDE project. + +Copyright (C) 2014 Martin Gräßlin + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ +// Qt +#include +// KWin +#include "../../wayland_client/connection_thread.h" +#include "../../wayland_client/registry.h" +// Wayland +#include + +class TestWaylandRegistry : public QObject +{ + Q_OBJECT +public: + explicit TestWaylandRegistry(QObject *parent = nullptr); +private Q_SLOTS: + void init(); + void cleanup(); + + void testCreate(); + void testBindCompositor(); + void testBindShell(); + void testBindOutput(); + void testBindShm(); + void testBindSeat(); + + // TODO: add tests for removal - requires more control over the compositor + +private: + QProcess *m_westonProcess; +}; + +static const QString s_socketName = QStringLiteral("kwin-test-wayland-registry-0"); + +TestWaylandRegistry::TestWaylandRegistry(QObject *parent) + : QObject(parent) + , m_westonProcess(nullptr) +{ +} + +void TestWaylandRegistry::init() +{ + QVERIFY(!m_westonProcess); + // starts weston + m_westonProcess = new QProcess(this); + m_westonProcess->setProgram(QStringLiteral("weston")); + + m_westonProcess->setArguments(QStringList({QStringLiteral("--socket=%1").arg(s_socketName), QStringLiteral("--no-config")})); + m_westonProcess->start(); + QVERIFY(m_westonProcess->waitForStarted()); + + // wait for the socket to appear + QDir runtimeDir(qgetenv("XDG_RUNTIME_DIR")); + if (runtimeDir.exists(s_socketName)) { + return; + } + QFileSystemWatcher *socketWatcher = new QFileSystemWatcher(QStringList({runtimeDir.absolutePath()}), this); + QSignalSpy socketSpy(socketWatcher, SIGNAL(directoryChanged(QString))); + + // limit to maximum of 10 waits + for (int i = 0; i < 10; ++i) { + QVERIFY(socketSpy.wait()); + if (runtimeDir.exists(s_socketName)) { + delete socketWatcher; + return; + } + } +} + +void TestWaylandRegistry::cleanup() +{ + // terminates weston + m_westonProcess->terminate(); + QVERIFY(m_westonProcess->waitForFinished()); + delete m_westonProcess; + m_westonProcess = nullptr; +} + +void TestWaylandRegistry::testCreate() +{ + if (m_westonProcess->state() != QProcess::Running) { + QSKIP("This test requires a running wayland server"); + } + KWin::Wayland::ConnectionThread connection; + QSignalSpy connectedSpy(&connection, SIGNAL(connected())); + connection.setSocketName(s_socketName); + connection.initConnection(); + QVERIFY(connectedSpy.wait()); + + KWin::Wayland::Registry registry; + QVERIFY(!registry.isValid()); + registry.create(connection.display()); + QVERIFY(registry.isValid()); + registry.release(); + QVERIFY(!registry.isValid()); +} + +#define TEST_BIND(interface, signalName, bindMethod, destroyFunction) \ + if (m_westonProcess->state() != QProcess::Running) { \ + QSKIP("This test requires a running wayland server"); \ + } \ + KWin::Wayland::ConnectionThread connection; \ + QSignalSpy connectedSpy(&connection, SIGNAL(connected())); \ + connection.setSocketName(s_socketName); \ + connection.initConnection(); \ + QVERIFY(connectedSpy.wait()); \ + \ + KWin::Wayland::Registry registry; \ + /* before registry is created, we cannot bind the interface*/ \ + QVERIFY(!registry.bindMethod(0, 0)); \ + \ + QVERIFY(!registry.isValid()); \ + registry.create(connection.display()); \ + QVERIFY(registry.isValid()); \ + /* created but not yet connected still results in no bind */ \ + QVERIFY(!registry.bindMethod(0, 0)); \ + \ + /* now lets register */ \ + QSignalSpy announced(®istry, signalName); \ + QVERIFY(announced.isValid()); \ + registry.setup(); \ + wl_display_flush(connection.display()); \ + QVERIFY(announced.wait()); \ + const quint32 name = announced.first().first().value(); \ + const quint32 version = announced.first().last().value(); \ + \ + /* registry should now about the interface now */ \ + QVERIFY(registry.hasInterface(interface)); \ + QVERIFY(!registry.bindMethod(name+1, version)); \ + QVERIFY(!registry.bindMethod(name, version+1)); \ + auto *c = registry.bindMethod(name, version); \ + QVERIFY(c); \ + destroyFunction(c); \ + +void TestWaylandRegistry::testBindCompositor() +{ + TEST_BIND(KWin::Wayland::Registry::Interface::Compositor, SIGNAL(compositorAnnounced(quint32,quint32)), bindCompositor, wl_compositor_destroy) +} + +void TestWaylandRegistry::testBindShell() +{ + TEST_BIND(KWin::Wayland::Registry::Interface::Shell, SIGNAL(shellAnnounced(quint32,quint32)), bindShell, free) +} + +void TestWaylandRegistry::testBindOutput() +{ + TEST_BIND(KWin::Wayland::Registry::Interface::Output, SIGNAL(outputAnnounced(quint32,quint32)), bindOutput, wl_output_destroy) +} + +void TestWaylandRegistry::testBindSeat() +{ + TEST_BIND(KWin::Wayland::Registry::Interface::Seat, SIGNAL(seatAnnounced(quint32,quint32)), bindSeat, wl_seat_destroy) +} + +void TestWaylandRegistry::testBindShm() +{ + TEST_BIND(KWin::Wayland::Registry::Interface::Shm, SIGNAL(shmAnnounced(quint32,quint32)), bindShm, wl_shm_destroy) +} + +#undef TEST_BIND + +QTEST_MAIN(TestWaylandRegistry) +#include "test_wayland_registry.moc" diff --git a/wayland_backend.cpp b/wayland_backend.cpp index c7dd754019..859d8c4eea 100644 --- a/wayland_backend.cpp +++ b/wayland_backend.cpp @@ -23,6 +23,7 @@ along with this program. If not, see . #include "cursor.h" #include "input.h" #include "wayland_client/connection_thread.h" +#include "wayland_client/registry.h" // Qt #include #include @@ -43,40 +44,6 @@ namespace KWin namespace Wayland { -/** - * Callback for announcing global objects in the registry - **/ -static void registryHandleGlobal(void *data, struct wl_registry *registry, - uint32_t name, const char *interface, uint32_t version) -{ - Q_UNUSED(version) - WaylandBackend *d = reinterpret_cast(data); - - if (strcmp(interface, "wl_compositor") == 0) { - d->setCompositor(reinterpret_cast(wl_registry_bind(registry, name, &wl_compositor_interface, 1))); - } else if (strcmp(interface, "wl_shell") == 0) { - d->setShell(reinterpret_cast(wl_registry_bind(registry, name, &wl_shell_interface, 1))); - } else if (strcmp(interface, "wl_seat") == 0) { - d->createSeat(name); - } else if (strcmp(interface, "wl_shm") == 0) { - d->createShm(name); - } else if (strcmp(interface, "wl_output") == 0) { - d->addOutput(reinterpret_cast(wl_registry_bind(registry, name, &wl_output_interface, 1))); - } - qDebug() << "Wayland Interface: " << interface; -} - -/** - * Callback for removal of global objects in the registry - **/ -static void registryHandleGlobalRemove(void *data, struct wl_registry *registry, uint32_t name) -{ - Q_UNUSED(data) - Q_UNUSED(registry) - Q_UNUSED(name) - // TODO: implement me -} - /** * Call back for ping from Wayland Shell. **/ @@ -259,11 +226,6 @@ static void outputHandleScale(void *data, wl_output *output, int32_t scale) } // handlers -static const struct wl_registry_listener s_registryListener = { - registryHandleGlobal, - registryHandleGlobalRemove -}; - static const struct wl_shell_surface_listener s_shellSurfaceListener = { handlePing, handleConfigure, @@ -733,7 +695,7 @@ WaylandBackend::WaylandBackend(QObject *parent) : QObject(parent) , m_display(nullptr) , m_eventQueue(nullptr) - , m_registry(nullptr) + , m_registry(new Registry(this)) , m_compositor(NULL) , m_shell(NULL) , m_surface(NULL) @@ -744,6 +706,23 @@ WaylandBackend::WaylandBackend(QObject *parent) , m_connectionThreadObject(nullptr) , m_connectionThread(nullptr) { + connect(m_registry, &Registry::compositorAnnounced, this, + [this](quint32 name) { + setCompositor(m_registry->bindCompositor(name, 1)); + } + ); + connect(m_registry, &Registry::shellAnnounced, this, + [this](quint32 name) { + setShell(m_registry->bindShell(name, 1)); + } + ); + connect(m_registry, &Registry::outputAnnounced, this, + [this](quint32 name) { + addOutput(m_registry->bindOutput(name, 1)); + } + ); + connect(m_registry, &Registry::seatAnnounced, this, &WaylandBackend::createSeat); + connect(m_registry, &Registry::shmAnnounced, this, &WaylandBackend::createShm); initConnection(); } @@ -762,9 +741,7 @@ WaylandBackend::~WaylandBackend() if (m_compositor) { wl_compositor_destroy(m_compositor); } - if (m_registry) { - wl_registry_destroy(m_registry); - } + m_registry->release(); m_seat.reset(); m_shm.reset(); @@ -791,10 +768,9 @@ void WaylandBackend::initConnection() m_display = m_connectionThreadObject->display(); m_eventQueue = wl_display_create_queue(m_display); // setup registry - m_registry = wl_display_get_registry(m_display); - wl_proxy_set_queue((wl_proxy*)m_registry, m_eventQueue); - // setup the registry - wl_registry_add_listener(m_registry, &s_registryListener, this); + m_registry->create(m_display); + wl_proxy_set_queue((wl_proxy*)m_registry->registry(), m_eventQueue); + m_registry->setup(); wl_display_flush(m_display); }, Qt::QueuedConnection); @@ -829,10 +805,7 @@ void WaylandBackend::initConnection() free(m_compositor); m_compositor = nullptr; } - if (m_registry) { - free(m_registry); - m_registry = nullptr; - } + m_registry->destroy(); if (m_display) { m_display = nullptr; } @@ -849,8 +822,7 @@ void WaylandBackend::initConnection() void WaylandBackend::createSeat(uint32_t name) { - wl_seat *seat = reinterpret_cast(wl_registry_bind(m_registry, name, &wl_seat_interface, 1)); - m_seat.reset(new WaylandSeat(seat, this)); + m_seat.reset(new WaylandSeat(m_registry->bindSeat(name, 1), this)); } void WaylandBackend::installCursorImage(Qt::CursorShape shape) @@ -877,7 +849,7 @@ void WaylandBackend::createSurface() void WaylandBackend::createShm(uint32_t name) { - m_shm.reset(new ShmPool(reinterpret_cast(wl_registry_bind(m_registry, name, &wl_shm_interface, 1)))); + m_shm.reset(new ShmPool(m_registry->bindShm(name, 1))); if (!m_shm->isValid()) { m_shm.reset(); } @@ -919,6 +891,11 @@ void WaylandBackend::dispatchEvents() wl_display_flush(m_display); } +wl_registry *WaylandBackend::registry() +{ + return m_registry->registry(); +} + } } // KWin diff --git a/wayland_backend.h b/wayland_backend.h index 6af4abb0ac..9fbaff944e 100644 --- a/wayland_backend.h +++ b/wayland_backend.h @@ -47,6 +47,7 @@ class ShmPool; class WaylandBackend; class WaylandSeat; class ConnectionThread; +class Registry; class CursorData { @@ -241,7 +242,7 @@ private: void destroyOutputs(); wl_display *m_display; wl_event_queue *m_eventQueue; - wl_registry *m_registry; + Registry *m_registry; wl_compositor *m_compositor; wl_shell *m_shell; wl_surface *m_surface; @@ -352,12 +353,6 @@ wl_display *WaylandBackend::display() return m_display; } -inline -wl_registry *WaylandBackend::registry() -{ - return m_registry; -} - inline void WaylandBackend::setCompositor(wl_compositor *c) { diff --git a/wayland_client/registry.cpp b/wayland_client/registry.cpp new file mode 100644 index 0000000000..d03e747f28 --- /dev/null +++ b/wayland_client/registry.cpp @@ -0,0 +1,237 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2014 Martin Gräßlin + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ +#include "registry.h" + +#include + +namespace KWin +{ +namespace Wayland +{ + +Registry::Registry(QObject *parent) + : QObject(parent) + , m_registry(nullptr) +{ +} + +Registry::~Registry() +{ + release(); +} + +void Registry::release() +{ + if (m_registry) { + wl_registry_destroy(m_registry); + m_registry = nullptr; + } +} + +void Registry::destroy() +{ + if (m_registry) { + free(m_registry); + m_registry = nullptr; + } +} + +void Registry::create(wl_display *display) +{ + Q_ASSERT(display); + Q_ASSERT(!isValid()); + m_registry = wl_display_get_registry(display); +} + +void Registry::setup() +{ + Q_ASSERT(isValid()); + wl_registry_add_listener(m_registry, &s_registryListener, this); +} + +const struct wl_registry_listener Registry::s_registryListener = { + Registry::globalAnnounce, + Registry::globalRemove +}; + +void Registry::globalAnnounce(void *data, wl_registry *registry, uint32_t name, const char *interface, uint32_t version) +{ + Registry *r = reinterpret_cast(data); + Q_ASSERT(registry == r->m_registry); + r->handleAnnounce(name, interface, version); +} + +void Registry::globalRemove(void *data, wl_registry *registry, uint32_t name) +{ + Registry *r = reinterpret_cast(data); + Q_ASSERT(registry == r->m_registry); + r->handleRemove(name); +} + +static Registry::Interface nameToInterface(const char *interface) +{ + if (strcmp(interface, "wl_compositor") == 0) { + return Registry::Interface::Compositor; + } else if (strcmp(interface, "wl_shell") == 0) { + return Registry::Interface::Shell; + } else if (strcmp(interface, "wl_seat") == 0) { + return Registry::Interface::Seat; + } else if (strcmp(interface, "wl_shm") == 0) { + return Registry::Interface::Shm; + } else if (strcmp(interface, "wl_output") == 0) { + return Registry::Interface::Output; + } + return Registry::Interface::Unknown; +} + +void Registry::handleAnnounce(uint32_t name, const char *interface, uint32_t version) +{ + Interface i = nameToInterface(interface); + if (i == Interface::Unknown) { + qDebug() << "Unknown interface announced: " << interface << "/" << name << "/" << version; + return; + } + qDebug() << "Wayland Interface: " << interface << "/" << name << "/" << version; + m_interfaces.append({i, name, version}); + switch (i) { + case Interface::Compositor: + emit compositorAnnounced(name, version); + break; + case Interface::Shell: + emit shellAnnounced(name, version); + break; + case Interface::Output: + emit outputAnnounced(name, version); + break; + case Interface::Seat: + emit seatAnnounced(name, version); + break; + case Interface::Shm: + emit shmAnnounced(name, version); + break; + case Interface::Unknown: + default: + // nothing + break; + } +} + +void Registry::handleRemove(uint32_t name) +{ + auto it = std::find_if(m_interfaces.begin(), m_interfaces.end(), + [name](const InterfaceData &data) { + return data.name == name; + } + ); + if (it != m_interfaces.end()) { + InterfaceData data = *(it); + m_interfaces.erase(it); + switch (data.interface) { + case Interface::Compositor: + emit compositorRemoved(data.name); + break; + case Interface::Output: + emit outputRemoved(data.name); + break; + case Interface::Seat: + emit seatRemoved(data.name); + break; + case Interface::Shell: + emit shellRemoved(data.name); + break; + case Interface::Shm: + emit shmRemoved(data.name); + break; + case Interface::Unknown: + default: + // nothing + break; + } + } +} + +bool Registry::hasInterface(Registry::Interface interface) const +{ + auto it = std::find_if(m_interfaces.begin(), m_interfaces.end(), + [interface](const InterfaceData &data) { + return data.interface == interface; + } + ); + return it != m_interfaces.end(); +} + +wl_compositor *Registry::bindCompositor(uint32_t name, uint32_t version) const +{ + return reinterpret_cast(bind(Interface::Compositor, name, version)); +} + +wl_output *Registry::bindOutput(uint32_t name, uint32_t version) const +{ + return reinterpret_cast(bind(Interface::Output, name, version)); +} + +wl_seat *Registry::bindSeat(uint32_t name, uint32_t version) const +{ + return reinterpret_cast(bind(Interface::Seat, name, version)); +} + +wl_shell *Registry::bindShell(uint32_t name, uint32_t version) const +{ + return reinterpret_cast(bind(Interface::Shell, name, version)); +} + +wl_shm *Registry::bindShm(uint32_t name, uint32_t version) const +{ + return reinterpret_cast(bind(Interface::Shm, name, version)); +} + +static const wl_interface *wlInterface(Registry::Interface interface) +{ + switch (interface) { + case Registry::Interface::Compositor: + return &wl_compositor_interface; + case Registry::Interface::Output: + return &wl_output_interface; + case Registry::Interface::Seat: + return &wl_seat_interface; + case Registry::Interface::Shell: + return &wl_shell_interface; + case Registry::Interface::Shm: + return &wl_shm_interface; + case Registry::Interface::Unknown: + default: + return nullptr; + } +} + +void *Registry::bind(Registry::Interface interface, uint32_t name, uint32_t version) const +{ + auto it = std::find_if(m_interfaces.begin(), m_interfaces.end(), [=](const InterfaceData &data) { + return data.interface == interface && data.name == name && data.version >= version; + }); + if (it == m_interfaces.end()) { + qDebug() << "Don't have interface " << int(interface) << "with name " << name << "and minimum version" << version; + return nullptr; + } + return wl_registry_bind(m_registry, name, wlInterface(interface), version); +} + +} +} diff --git a/wayland_client/registry.h b/wayland_client/registry.h new file mode 100644 index 0000000000..a9f16d83bd --- /dev/null +++ b/wayland_client/registry.h @@ -0,0 +1,107 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2014 Martin Gräßlin + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ +#ifndef KWIN_WAYLAND_REGISTRY_H +#define KWIN_WAYLAND_REGISTRY_H + +#include +#include + +#include + +namespace KWin +{ +namespace Wayland +{ + +class Registry : public QObject +{ + Q_OBJECT +public: + enum class Interface { + Compositor, // wl_compositor + Shell, // wl_shell + Seat, // wl_seat + Shm, // wl_shm + Output, // wl_output + Unknown + }; + explicit Registry(QObject *parent = nullptr); + virtual ~Registry(); + + void release(); + void destroy(); + void create(wl_display *display); + void setup(); + + bool isValid() const { + return m_registry != nullptr; + } + bool hasInterface(Interface interface) const; + + wl_compositor *bindCompositor(uint32_t name, uint32_t version) const; + wl_shell *bindShell(uint32_t name, uint32_t version) const; + wl_seat *bindSeat(uint32_t name, uint32_t version) const; + wl_shm *bindShm(uint32_t name, uint32_t version) const; + wl_output *bindOutput(uint32_t name, uint32_t version) const; + + operator wl_registry*() { + return m_registry; + } + operator wl_registry*() const { + return m_registry; + } + wl_registry *registry() { + return m_registry; + } + + static void globalAnnounce(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version); + static void globalRemove(void *data, struct wl_registry *registry, uint32_t name); + +Q_SIGNALS: + void compositorAnnounced(quint32 name, quint32 version); + void shellAnnounced(quint32 name, quint32 version); + void seatAnnounced(quint32 name, quint32 version); + void shmAnnounced(quint32 name, quint32 version); + void outputAnnounced(quint32 name, quint32 version); + void compositorRemoved(quint32 name); + void shellRemoved(quint32 name); + void seatRemoved(quint32 name); + void shmRemoved(quint32 name); + void outputRemoved(quint32 name); + +private: + static const struct wl_registry_listener s_registryListener; + void handleAnnounce(uint32_t name, const char *interface, uint32_t version); + void handleRemove(uint32_t name); + void *bind(Interface interface, uint32_t name, uint32_t version) const; + + wl_registry *m_registry; + struct InterfaceData { + Interface interface; + uint32_t name; + uint32_t version; + }; + QList m_interfaces; +}; + +} +} + +#endif