summaryrefslogtreecommitdiff
path: root/src/protocols
diff options
context:
space:
mode:
Diffstat (limited to 'src/protocols')
-rw-r--r--src/protocols/XDGForeignV2.cpp175
-rw-r--r--src/protocols/XDGForeignV2.hpp92
-rw-r--r--src/protocols/XDGShell.cpp69
-rw-r--r--src/protocols/XDGShell.hpp2
4 files changed, 301 insertions, 37 deletions
diff --git a/src/protocols/XDGForeignV2.cpp b/src/protocols/XDGForeignV2.cpp
new file mode 100644
index 00000000..f67cb86c
--- /dev/null
+++ b/src/protocols/XDGForeignV2.cpp
@@ -0,0 +1,175 @@
+#include "protocols/XDGForeignV2.hpp"
+#include "managers/TokenManager.hpp"
+#include "protocols/XDGShell.hpp"
+#include "xdg-foreign-unstable-v2.hpp"
+#include <hyprutils/memory/SharedPtr.hpp>
+#include <hyprutils/memory/UniquePtr.hpp>
+#include <string>
+#include <string_view>
+#include <unordered_map>
+#include <wayland-server.h>
+#include "protocols/core/Compositor.hpp"
+
+CXDGExportedResourceV2::CXDGExportedResourceV2(SP<CZxdgExportedV2> resource, SP<CXDGToplevelResource> toplevel, const std::string& handle) :
+ m_resource(resource), m_toplevel(toplevel), m_handle(handle) {
+
+ if UNLIKELY (!good())
+ return;
+
+ m_resource->setData(this);
+ m_resource->sendHandle(handle.c_str());
+ m_listeners.topLevelDestroyed = toplevel->m_events.destroy.listen([this] {
+ m_topLevelDestroyed = true;
+ m_listeners.topLevelDestroyed.reset();
+ m_events.destroy.emit();
+ });
+ m_resource->setOnDestroy([this](CZxdgExportedV2*) { PROTO::xdgForeignExporter->destroyExported(this); });
+ m_resource->setDestroy([this](CZxdgExportedV2*) { PROTO::xdgForeignExporter->destroyExported(this); });
+}
+
+CXDGExportedResourceV2::~CXDGExportedResourceV2() {
+ if (!m_topLevelDestroyed)
+ m_events.destroy.emit();
+}
+
+bool CXDGExportedResourceV2::good() const {
+ return m_resource->resource();
+}
+
+WP<CXDGToplevelResource> CXDGExportedResourceV2::xdgSurf() const {
+ return m_toplevel;
+}
+
+std::string_view CXDGExportedResourceV2::handle() const {
+ return m_handle;
+}
+
+CXDGForeignExporterProtocolV2::CXDGForeignExporterProtocolV2(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {}
+
+void CXDGForeignExporterProtocolV2::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
+ const auto RESOURCE = m_exporters.emplace_back(makeUnique<CZxdgExporterV2>(client, ver, id)).get();
+
+ if UNLIKELY (!RESOURCE->resource()) {
+ wl_client_post_no_memory(client);
+ m_exporters.pop_back();
+ return;
+ }
+
+ RESOURCE->setExportToplevel([this](CZxdgExporterV2* exporter, uint32_t id, wl_resource* surface) {
+ auto wlSurf = CWLSurfaceResource::fromResource(surface);
+
+ if (wlSurf->m_role != SURFACE_ROLE_XDG_SHELL) {
+ exporter->error(zxdgExporterV2Error::ZXDG_EXPORTER_V2_ERROR_INVALID_SURFACE, "surface must be an xdg_toplevel");
+ return;
+ }
+
+ auto xdgSurfResource = sc<CXDGSurfaceRole*>(wlSurf->m_role.get())->m_xdgSurface.lock();
+ if (xdgSurfResource->m_toplevel.expired())
+ return;
+
+ auto xdgSurf = xdgSurfResource->m_toplevel.lock();
+ const std::string HANDLE = g_pTokenManager->getRandomUUID();
+ const auto [ELM, EMPLACED] =
+ m_exported.emplace(HANDLE, makeShared<CXDGExportedResourceV2>(makeShared<CZxdgExportedV2>(exporter->client(), exporter->version(), id), xdgSurf, HANDLE));
+
+ // This should only happen if we have our generated handles collide.
+ if UNLIKELY (!EMPLACED) {
+ wl_client_post_no_memory(exporter->client());
+ return;
+ }
+
+ if UNLIKELY (!ELM->second->good()) {
+ wl_client_post_no_memory(exporter->client());
+ destroyExported(ELM->second.get());
+ }
+ });
+
+ RESOURCE->setDestroy([this](CZxdgExporterV2* e) { onExporterDestroyed(e); });
+ RESOURCE->setOnDestroy([this](CZxdgExporterV2* e) { onExporterDestroyed(e); });
+}
+
+SP<CXDGExportedResourceV2> CXDGForeignExporterProtocolV2::getExported(const std::string& handle) const {
+ return m_exported.contains(handle) ? m_exported.at(handle) : nullptr;
+}
+
+void CXDGForeignExporterProtocolV2::onExporterDestroyed(CZxdgExporterV2* exporter) {
+ std::erase_if(m_exporters, [exporter](const auto& other) { return exporter == other.get(); });
+}
+
+void CXDGForeignExporterProtocolV2::destroyExported(CXDGExportedResourceV2* r) {
+ PROTO::xdgForeignExporter->m_exported.erase(r->m_handle);
+}
+
+CXDGImportedResourceV2::CXDGImportedResourceV2(SP<CZxdgImportedV2> imported, SP<CXDGExportedResourceV2> exported, const std::string& handle) :
+ m_resource(imported), m_exported(exported), m_handle(handle) {
+ if UNLIKELY (!m_resource->resource() || m_exported.expired())
+ return;
+
+ m_resource->setData(this);
+
+ m_resource->setSetParentOf([this](CZxdgImportedV2* r, wl_resource* surf) {
+ const auto CHILDSURF = CWLSurfaceResource::fromResource(surf);
+
+ if (CHILDSURF->m_role != SURFACE_ROLE_XDG_SHELL) {
+ m_resource->error(zxdgImportedV2Error::ZXDG_IMPORTED_V2_ERROR_INVALID_SURFACE, "surface must be an xdg_toplevel");
+ return;
+ }
+
+ const auto CHILDXDGSURF = sc<CXDGSurfaceRole*>(CHILDSURF->m_role.get())->m_xdgSurface.lock();
+ if (CHILDXDGSURF->m_toplevel.expired())
+ return;
+
+ if LIKELY (auto exportedTopLevel = m_exported->xdgSurf(); !exportedTopLevel.expired())
+ CHILDXDGSURF->m_toplevel->setNewParent(exportedTopLevel.lock());
+ });
+
+ m_listeners.exportedDestroyed = m_exported->m_events.destroy.listen([this]() { PROTO::xdgForeignImporter->destroyImported(this); });
+ m_resource->setDestroy([this](CZxdgImportedV2*) { PROTO::xdgForeignImporter->destroyImported(this); });
+ m_resource->setOnDestroy([this](CZxdgImportedV2*) { PROTO::xdgForeignImporter->destroyImported(this); });
+}
+
+CXDGImportedResourceV2::~CXDGImportedResourceV2() {
+ m_resource->sendDestroyed();
+}
+
+CXDGForeignImporterProtocolV2::CXDGForeignImporterProtocolV2(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
+ ;
+}
+
+void CXDGForeignImporterProtocolV2::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
+ const auto RESOURCE = m_importers.emplace_back(makeUnique<CZxdgImporterV2>(client, ver, id)).get();
+
+ if UNLIKELY (!RESOURCE->resource()) {
+ wl_client_post_no_memory(client);
+ m_importers.pop_back();
+ return;
+ }
+
+ RESOURCE->setImportToplevel([this](CZxdgImporterV2* importer, uint32_t id, const char* _handle) {
+ const std::string HANDLE = _handle;
+ auto exported = PROTO::xdgForeignExporter->getExported(HANDLE);
+ auto imported =
+ m_imports.emplace_back(makeUnique<CXDGImportedResourceV2>(makeShared<CZxdgImportedV2>(importer->client(), importer->version(), id), exported, HANDLE)).get();
+
+ if UNLIKELY (!imported->m_resource->resource()) {
+ wl_client_post_no_memory(importer->client());
+ m_imports.pop_back();
+ return;
+ }
+
+ // Couldn't find the handle.
+ if UNLIKELY (imported->m_exported.expired())
+ destroyImported(imported);
+ });
+
+ RESOURCE->setDestroy([this](CZxdgImporterV2* r) { onImporterDestroyed(r); });
+ RESOURCE->setOnDestroy([this](CZxdgImporterV2* r) { onImporterDestroyed(r); });
+}
+
+void CXDGForeignImporterProtocolV2::onImporterDestroyed(CZxdgImporterV2* importer) {
+ std::erase_if(m_importers, [importer](const auto& other) { return importer == other.get(); });
+}
+
+void CXDGForeignImporterProtocolV2::destroyImported(CXDGImportedResourceV2* imported) {
+ std::erase_if(m_imports, [imported](const auto& other) { return imported == other.get(); });
+} \ No newline at end of file
diff --git a/src/protocols/XDGForeignV2.hpp b/src/protocols/XDGForeignV2.hpp
new file mode 100644
index 00000000..78c8dbf7
--- /dev/null
+++ b/src/protocols/XDGForeignV2.hpp
@@ -0,0 +1,92 @@
+#include <hyprutils/signal/Listener.hpp>
+#include <string>
+#include <string_view>
+#include <unordered_map>
+#include <vector>
+#include "WaylandProtocol.hpp"
+#include "xdg-foreign-unstable-v2.hpp"
+#include "../helpers/signal/Signal.hpp"
+
+class CXDGImportedResourceV2;
+class CWLSurface;
+class CXDGToplevelResource;
+
+class CXDGExportedResourceV2 {
+ public:
+ CXDGExportedResourceV2(SP<CZxdgExportedV2> resource_, SP<CXDGToplevelResource> surface_, const std::string& handle);
+ ~CXDGExportedResourceV2();
+
+ struct {
+ CSignalT<> destroy;
+ } m_events;
+
+ bool good() const;
+ std::string_view handle() const;
+ WP<CXDGToplevelResource> xdgSurf() const;
+
+ private:
+ bool m_topLevelDestroyed = false;
+ struct {
+ CHyprSignalListener topLevelDestroyed;
+ } m_listeners;
+ SP<CZxdgExportedV2> m_resource;
+ WP<CXDGToplevelResource> m_toplevel;
+ std::string m_handle;
+
+ friend class CXDGForeignExporterProtocolV2;
+};
+
+// zxdg_imported_v2
+class CXDGImportedResourceV2 {
+ public:
+ CXDGImportedResourceV2(SP<CZxdgImportedV2> resource, SP<CXDGExportedResourceV2> exported, const std::string& handle);
+ ~CXDGImportedResourceV2();
+
+ private:
+ SP<CZxdgImportedV2> m_resource;
+ WP<CXDGExportedResourceV2> m_exported;
+ std::string m_handle;
+
+ struct {
+ CHyprSignalListener exportedDestroyed;
+ } m_listeners;
+
+ friend class CXDGForeignImporterProtocolV2;
+};
+
+class CXDGForeignExporterProtocolV2 : public IWaylandProtocol {
+ public:
+ CXDGForeignExporterProtocolV2(const wl_interface* iface, const int& ver, const std::string& name);
+
+ virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) override;
+
+ SP<CXDGExportedResourceV2> getExported(const std::string& handle) const;
+
+ private:
+ void destroyExported(CXDGExportedResourceV2*);
+ void onExporterDestroyed(CZxdgExporterV2*);
+ std::vector<UP<CZxdgExporterV2>> m_exporters;
+ std::unordered_map<std::string, SP<CXDGExportedResourceV2>> m_exported;
+
+ friend class CXDGExportedResourceV2;
+};
+
+class CXDGForeignImporterProtocolV2 : public IWaylandProtocol {
+ public:
+ CXDGForeignImporterProtocolV2(const wl_interface* iface, const int& ver, const std::string& name);
+
+ virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) override;
+
+ private:
+ void destroyImported(CXDGImportedResourceV2*);
+ void onImporterDestroyed(CZxdgImporterV2*);
+ std::vector<UP<CZxdgImporterV2>> m_importers;
+ std::vector<UP<CXDGImportedResourceV2>> m_imports;
+
+ friend class CXDGImportedResourceV2;
+};
+
+namespace PROTO {
+ inline UP<CXDGForeignExporterProtocolV2> xdgForeignExporter;
+ inline UP<CXDGForeignImporterProtocolV2> xdgForeignImporter;
+}; \ No newline at end of file
diff --git a/src/protocols/XDGShell.cpp b/src/protocols/XDGShell.cpp
index 969556a3..774346d4 100644
--- a/src/protocols/XDGShell.cpp
+++ b/src/protocols/XDGShell.cpp
@@ -220,52 +220,49 @@ CXDGToplevelResource::CXDGToplevelResource(SP<CXdgToplevel> resource_, SP<CXDGSu
});
m_resource->setSetParent([this](CXdgToplevel* r, wl_resource* parentR) {
- auto oldParent = m_parent;
-
- if (m_parent)
- std::erase(m_parent->m_children, m_self);
-
auto newp = parentR ? CXDGToplevelResource::fromResource(parentR) : nullptr;
+ setNewParent(newp);
+ });
+}
- if (newp) {
- // check for protocol constraints
- if (newp == m_self) {
- r->error(XDG_TOPLEVEL_ERROR_INVALID_PARENT, "Parent cannot be self");
- return;
- }
-
- static std::function<bool(WP<CXDGToplevelResource>, WP<CXDGToplevelResource>)> exploreChildren = [](WP<CXDGToplevelResource> tl,
- WP<CXDGToplevelResource> target) -> bool {
- bool any = false;
- for (const auto& c : tl->m_children) {
- if (c == target)
- return true;
-
- any = any || exploreChildren(c, target);
+void CXDGToplevelResource::setNewParent(SP<CXDGToplevelResource> newParent) {
+ auto oldParent = m_parent;
- if (any)
- break;
- }
- return any;
- };
+ if (m_parent)
+ std::erase(m_parent->m_children, m_self);
- if (exploreChildren(m_self, newp)) {
- r->error(XDG_TOPLEVEL_ERROR_INVALID_PARENT, "Parent cannot be a descendant");
- return;
- }
+ if (newParent) {
+ if (m_self == newParent) {
+ m_resource->error(XDG_TOPLEVEL_ERROR_INVALID_PARENT, "Parent cannot be self");
+ return;
}
- m_parent = newp;
+ static std::function<bool(WP<CXDGToplevelResource>, WP<CXDGToplevelResource>)> exploreChildren = [](WP<CXDGToplevelResource> tl, WP<CXDGToplevelResource> target) -> bool {
+ bool any = false;
+ for (const auto& c : tl->m_children) {
+ if (c == target)
+ return true;
- if (m_parent) {
- m_parent->m_children.emplace_back(m_self);
+ any = any || exploreChildren(c, target);
- if (m_parent->m_window && m_parent->m_window->m_pinned)
- m_self->m_window->m_pinned = true;
+ if (any)
+ break;
+ }
+ return any;
+ };
+ if (exploreChildren(m_self, newParent)) {
+ m_resource->error(XDG_TOPLEVEL_ERROR_INVALID_PARENT, "Parent cannot be a descendant of itself");
+ return;
}
+ }
- LOGM(Log::DEBUG, "Toplevel {:x} sets parent to {:x}{}", (uintptr_t)this, (uintptr_t)newp.get(), (oldParent ? std::format(" (was {:x})", (uintptr_t)oldParent.get()) : ""));
- });
+ m_parent = newParent;
+ if (m_parent) {
+ m_parent->m_children.emplace_back(m_self);
+ if (m_parent->m_window && m_parent->m_window->m_pinned)
+ m_self->m_window->m_pinned = true;
+ }
+ LOGM(Log::DEBUG, "Toplevel {:x} sets parent to {:x}{}", (uintptr_t)this, (uintptr_t)newParent.get(), (oldParent ? std::format(" (was {:x})", (uintptr_t)oldParent.get()) : ""));
}
CXDGToplevelResource::~CXDGToplevelResource() {
diff --git a/src/protocols/XDGShell.hpp b/src/protocols/XDGShell.hpp
index fbb6ba94..3d94d282 100644
--- a/src/protocols/XDGShell.hpp
+++ b/src/protocols/XDGShell.hpp
@@ -97,7 +97,6 @@ class CXDGToplevelResource {
PHLWINDOWREF m_window;
bool good();
-
Vector2D layoutMinSize();
Vector2D layoutMaxSize();
@@ -107,6 +106,7 @@ class CXDGToplevelResource {
uint32_t setFullscreen(bool fullscreen);
uint32_t setActive(bool active);
uint32_t setSuspeneded(bool sus);
+ void setNewParent(SP<CXDGToplevelResource> newParent);
void close();