summaryrefslogtreecommitdiffstats
path: root/xbmc/windowing/wayland/Registry.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--xbmc/windowing/wayland/Registry.cpp164
1 files changed, 164 insertions, 0 deletions
diff --git a/xbmc/windowing/wayland/Registry.cpp b/xbmc/windowing/wayland/Registry.cpp
new file mode 100644
index 0000000..68cd745
--- /dev/null
+++ b/xbmc/windowing/wayland/Registry.cpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2017-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "Registry.h"
+
+#include "WinEventsWayland.h"
+#include "utils/log.h"
+
+#include <wayland-client-protocol.h>
+
+using namespace KODI::WINDOWING::WAYLAND;
+
+namespace
+{
+
+void TryBind(wayland::registry_t& registry, wayland::proxy_t& target, std::uint32_t name, std::string const& interface, std::uint32_t minVersion, std::uint32_t maxVersion, std::uint32_t offeredVersion)
+{
+ if (offeredVersion < minVersion)
+ {
+ CLog::Log(LOGWARNING,
+ "Not binding Wayland protocol {} because server has only version {} (we need at "
+ "least version {})",
+ interface, offeredVersion, minVersion);
+ }
+ else
+ {
+ // Binding below the offered version is OK
+ auto bindVersion = std::min(maxVersion, offeredVersion);
+ CLog::Log(LOGDEBUG, "Binding Wayland protocol {} version {} (server has version {})", interface,
+ bindVersion, offeredVersion);
+ registry.bind(name, target, bindVersion);
+ }
+}
+
+}
+
+CRegistry::CRegistry(CConnection& connection)
+: m_connection{connection}
+{
+}
+
+void CRegistry::RequestSingletonInternal(wayland::proxy_t& target, std::string const& interfaceName, std::uint32_t minVersion, std::uint32_t maxVersion, bool required)
+{
+ if (m_registry)
+ {
+ throw std::logic_error("Cannot request more binds from registry after binding has started");
+ }
+ m_singletonBinds.emplace(std::piecewise_construct, std::forward_as_tuple(interfaceName), std::forward_as_tuple(target, minVersion, maxVersion, required));
+}
+
+void CRegistry::RequestInternal(std::function<wayland::proxy_t()> constructor, const std::string& interfaceName, std::uint32_t minVersion, std::uint32_t maxVersion, AddHandler addHandler, RemoveHandler removeHandler)
+{
+ if (m_registry)
+ {
+ throw std::logic_error("Cannot request more binds from registry after binding has started");
+ }
+ m_binds.emplace(std::piecewise_construct, std::forward_as_tuple(interfaceName), std::forward_as_tuple(constructor, minVersion, maxVersion, addHandler, removeHandler));
+}
+
+void CRegistry::Bind()
+{
+ if (m_registry)
+ {
+ throw std::logic_error("Cannot start binding on registry twice");
+ }
+
+ // We want to block in this function until we have received the global interfaces
+ // from the compositor - no matter whether the global event pump is running
+ // or not.
+ // If it is running, we have to take special precautions not to drop events between
+ // the creation of the registry and attaching event handlers, so we create
+ // an extra queue and use that to dispatch the singleton globals. Then
+ // we switch back to the global queue for further dispatch of interfaces
+ // added/removed dynamically.
+
+ auto registryRoundtripQueue = m_connection.GetDisplay().create_queue();
+
+ auto displayProxy = m_connection.GetDisplay().proxy_create_wrapper();
+ displayProxy.set_queue(registryRoundtripQueue);
+
+ m_registry = displayProxy.get_registry();
+
+ m_registry.on_global() = [this](std::uint32_t name, const std::string& interface,
+ std::uint32_t version) {
+ {
+ auto it = m_singletonBinds.find(interface);
+ if (it != m_singletonBinds.end())
+ {
+ auto& bind = it->second;
+ auto registryProxy = m_registry.proxy_create_wrapper();
+ // Events on the bound global should always go to the main queue
+ registryProxy.set_queue(wayland::event_queue_t());
+ TryBind(registryProxy, bind.target, name, interface, bind.minVersion, bind.maxVersion, version);
+ return;
+ }
+ }
+
+ {
+ auto it = m_binds.find(interface);
+ if (it != m_binds.end())
+ {
+ auto& bind = it->second;
+ wayland::proxy_t target{bind.constructor()};
+ auto registryProxy = m_registry.proxy_create_wrapper();
+ // Events on the bound global should always go to the main queue
+ registryProxy.set_queue(wayland::event_queue_t());
+ TryBind(registryProxy, target, name, interface, bind.minVersion, bind.maxVersion, version);
+ if (target)
+ {
+ m_boundNames.emplace(name, bind);
+ bind.addHandler(name, std::move(target));
+ }
+ return;
+ }
+ }
+ };
+
+ m_registry.on_global_remove() = [this] (std::uint32_t name)
+ {
+ auto it = m_boundNames.find(name);
+ if (it != m_boundNames.end())
+ {
+ it->second.get().removeHandler(name);
+ m_boundNames.erase(it);
+ }
+ };
+
+ CLog::Log(LOGDEBUG, "Wayland connection: Waiting for global interfaces");
+ m_connection.GetDisplay().roundtrip_queue(registryRoundtripQueue);
+ CLog::Log(LOGDEBUG, "Wayland connection: Roundtrip complete");
+
+ CheckRequired();
+
+ // Now switch it to the global queue for further runtime binds
+ m_registry.set_queue(wayland::event_queue_t());
+ // Roundtrip extra queue one last time in case something got queued up there.
+ // Do it on the event thread so it does not race/run in parallel with the
+ // dispatch of newly arrived registry messages in the default queue.
+ CWinEventsWayland::RoundtripQueue(registryRoundtripQueue);
+}
+
+void CRegistry::UnbindSingletons()
+{
+ for (auto& bind : m_singletonBinds)
+ {
+ bind.second.target.proxy_release();
+ }
+}
+
+void CRegistry::CheckRequired()
+{
+ for (auto const& bind : m_singletonBinds)
+ {
+ if (bind.second.required && !bind.second.target)
+ {
+ throw std::runtime_error(std::string("Missing required ") + bind.first + " protocol");
+ }
+ }
+} \ No newline at end of file