mirror of
https://github.com/hyprwm/Hyprland.git
synced 2026-08-24 10:04:19 -05:00
protocols: reimplement unstable/xdg-foreign-v2 (#13716)
This commit is contained in:
@@ -534,6 +534,7 @@ protocolnew("unstable/keyboard-shortcuts-inhibit"
|
||||
protocolnew("unstable/text-input" "text-input-unstable-v3" false)
|
||||
protocolnew("unstable/pointer-constraints" "pointer-constraints-unstable-v1"
|
||||
false)
|
||||
protocolnew("unstable/xdg-foreign" "xdg-foreign-unstable-v2" false)
|
||||
protocolnew("staging/xdg-activation" "xdg-activation-v1" false)
|
||||
protocolnew("staging/ext-idle-notify" "ext-idle-notify-v1" false)
|
||||
protocolnew("staging/ext-session-lock" "ext-session-lock-v1" false)
|
||||
|
||||
@@ -67,6 +67,7 @@
|
||||
#include "../protocols/PointerWarp.hpp"
|
||||
#include "../protocols/Fifo.hpp"
|
||||
#include "../protocols/CommitTiming.hpp"
|
||||
#include "../protocols/XDGForeignV2.hpp"
|
||||
|
||||
#include "../helpers/Monitor.hpp"
|
||||
#include "../event/EventBus.hpp"
|
||||
@@ -194,6 +195,8 @@ CProtocolManager::CProtocolManager() {
|
||||
PROTO::extDataDevice = makeUnique<CExtDataDeviceProtocol>(&ext_data_control_manager_v1_interface, 1, "ExtDataDevice");
|
||||
PROTO::pointerWarp = makeUnique<CPointerWarpProtocol>(&wp_pointer_warp_v1_interface, 1, "PointerWarp");
|
||||
PROTO::fifo = makeUnique<CFifoProtocol>(&wp_fifo_manager_v1_interface, 1, "Fifo");
|
||||
PROTO::xdgForeignExporter = makeUnique<CXDGForeignExporterProtocolV2>(&zxdg_exporter_v2_interface, 1, "XDGForeignExporter");
|
||||
PROTO::xdgForeignImporter = makeUnique<CXDGForeignImporterProtocolV2>(&zxdg_importer_v2_interface, 1, "XDGForeignImporter");
|
||||
|
||||
if (*PENABLECT)
|
||||
PROTO::commitTiming = makeUnique<CCommitTimingProtocol>(&wp_commit_timing_manager_v1_interface, 1, "CommitTiming");
|
||||
@@ -301,6 +304,8 @@ CProtocolManager::~CProtocolManager() {
|
||||
PROTO::extDataDevice.reset();
|
||||
PROTO::pointerWarp.reset();
|
||||
PROTO::fifo.reset();
|
||||
PROTO::xdgForeignExporter.reset();
|
||||
PROTO::xdgForeignImporter.reset();
|
||||
PROTO::commitTiming.reset();
|
||||
PROTO::imageCaptureSource.reset();
|
||||
|
||||
|
||||
@@ -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(); });
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
+41
-44
@@ -220,54 +220,51 @@ 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;
|
||||
|
||||
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);
|
||||
|
||||
if (any)
|
||||
break;
|
||||
}
|
||||
return any;
|
||||
};
|
||||
|
||||
if (exploreChildren(m_self, newp)) {
|
||||
r->error(XDG_TOPLEVEL_ERROR_INVALID_PARENT, "Parent cannot be a descendant");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_parent = newp;
|
||||
|
||||
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)newp.get(), (oldParent ? std::format(" (was {:x})", (uintptr_t)oldParent.get()) : ""));
|
||||
setNewParent(newp);
|
||||
});
|
||||
}
|
||||
|
||||
void CXDGToplevelResource::setNewParent(SP<CXDGToplevelResource> newParent) {
|
||||
auto oldParent = m_parent;
|
||||
|
||||
if (m_parent)
|
||||
std::erase(m_parent->m_children, m_self);
|
||||
|
||||
if (newParent) {
|
||||
if (m_self == newParent) {
|
||||
m_resource->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);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
m_events.destroy.emit();
|
||||
if (m_parent)
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user