summaryrefslogtreecommitdiff
path: root/src/protocols
diff options
context:
space:
mode:
authorVaxry <vaxry@vaxry.net>2024-04-24 15:45:59 +0100
committerVaxry <vaxry@vaxry.net>2024-04-24 15:48:06 +0100
commit0d1bb65c75d7ddc323afd31eb844b2332588bdf5 (patch)
tree9cd507c107117db5271d8bc6b2bc5273bf097592 /src/protocols
parent34413d1f367b56e17614ee0455b6bf0b61a5aed1 (diff)
ext-foreign-toplevel: add implementation
Diffstat (limited to 'src/protocols')
-rw-r--r--src/protocols/ForeignToplevel.cpp148
-rw-r--r--src/protocols/ForeignToplevel.hpp64
2 files changed, 212 insertions, 0 deletions
diff --git a/src/protocols/ForeignToplevel.cpp b/src/protocols/ForeignToplevel.cpp
new file mode 100644
index 00000000..c502029c
--- /dev/null
+++ b/src/protocols/ForeignToplevel.cpp
@@ -0,0 +1,148 @@
+#include "ForeignToplevel.hpp"
+#include "../Compositor.hpp"
+
+#define LOGM PROTO::foreignToplevel->protoLog
+
+CForeignToplevelHandle::CForeignToplevelHandle(SP<CExtForeignToplevelHandleV1> resource_, CWindow* pWindow_) : resource(resource_), pWindow(pWindow_) {
+ if (!resource_->resource())
+ return;
+
+ resource->setOnDestroy([this](CExtForeignToplevelHandleV1* h) { PROTO::foreignToplevel->destroyHandle(this); });
+ resource->setDestroy([this](CExtForeignToplevelHandleV1* h) { PROTO::foreignToplevel->destroyHandle(this); });
+}
+
+bool CForeignToplevelHandle::good() {
+ return resource->resource();
+}
+
+CWindow* CForeignToplevelHandle::window() {
+ return pWindow;
+}
+
+CForeignToplevelList::CForeignToplevelList(SP<CExtForeignToplevelListV1> resource_) : resource(resource_) {
+ if (!resource_->resource())
+ return;
+
+ resource->setOnDestroy([this](CExtForeignToplevelListV1* h) { PROTO::foreignToplevel->onManagerResourceDestroy(this); });
+ resource->setDestroy([this](CExtForeignToplevelListV1* h) { PROTO::foreignToplevel->onManagerResourceDestroy(this); });
+
+ resource->setStop([this](CExtForeignToplevelListV1* h) {
+ resource->sendFinished();
+ finished = true;
+ LOGM(LOG, "CForeignToplevelList: finished");
+ });
+
+ for (auto& w : g_pCompositor->m_vWindows) {
+ if (!w->m_bIsMapped || w->m_bFadingOut)
+ continue;
+
+ onMap(w.get());
+ }
+}
+
+void CForeignToplevelList::onMap(CWindow* pWindow) {
+ if (finished)
+ return;
+
+ const auto NEWHANDLE = PROTO::foreignToplevel->m_vHandles.emplace_back(std::make_shared<CForeignToplevelHandle>(
+ std::make_shared<CExtForeignToplevelHandleV1>(wl_resource_get_client(resource->resource()), wl_resource_get_version(resource->resource()), 0), pWindow));
+
+ if (!NEWHANDLE->good()) {
+ LOGM(ERR, "Couldn't create a foreign handle");
+ wl_resource_post_no_memory(resource->resource());
+ PROTO::foreignToplevel->m_vHandles.pop_back();
+ return;
+ }
+
+ const auto IDENTIFIER = std::format("{:08x}->{:016x}", static_cast<uint32_t>((uintptr_t)this & 0xFFFFFFFF), (uintptr_t)pWindow);
+
+ LOGM(LOG, "Newly mapped window gets an identifier of {}", IDENTIFIER);
+ resource->sendToplevel(NEWHANDLE->resource.get());
+ NEWHANDLE->resource->sendIdentifier(IDENTIFIER.c_str());
+ NEWHANDLE->resource->sendAppId(pWindow->m_szInitialClass.c_str());
+ NEWHANDLE->resource->sendTitle(pWindow->m_szInitialTitle.c_str());
+ NEWHANDLE->resource->sendDone();
+}
+
+SP<CForeignToplevelHandle> CForeignToplevelList::handleForWindow(CWindow* pWindow) {
+ std::erase_if(handles, [](const auto& wp) { return !wp.lock(); });
+ const auto IT = std::find_if(handles.begin(), handles.end(), [pWindow](const auto& h) { return h.lock()->window() == pWindow; });
+ return IT == handles.end() ? SP<CForeignToplevelHandle>{} : IT->lock();
+}
+
+void CForeignToplevelList::onTitle(CWindow* pWindow) {
+ if (finished)
+ return;
+
+ const auto H = handleForWindow(pWindow);
+ if (!H || H->closed)
+ return;
+
+ H->resource->sendTitle(pWindow->m_szTitle.c_str());
+}
+
+void CForeignToplevelList::onClass(CWindow* pWindow) {
+ if (finished)
+ return;
+
+ const auto H = handleForWindow(pWindow);
+ if (!H || H->closed)
+ return;
+
+ H->resource->sendAppId(g_pXWaylandManager->getAppIDClass(pWindow).c_str());
+}
+
+void CForeignToplevelList::onUnmap(CWindow* pWindow) {
+ if (finished)
+ return;
+
+ const auto H = handleForWindow(pWindow);
+ if (!H)
+ return;
+
+ H->resource->sendClosed();
+ H->closed = true;
+}
+
+bool CForeignToplevelList::good() {
+ return resource->resource();
+}
+
+CForeignToplevelProtocol::CForeignToplevelProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
+ static auto P = g_pHookSystem->hookDynamic("openWindow", [this](void* self, SCallbackInfo& info, std::any data) {
+ for (auto& m : m_vManagers) {
+ m->onMap(std::any_cast<CWindow*>(data));
+ }
+ });
+
+ static auto P1 = g_pHookSystem->hookDynamic("closeWindow", [this](void* self, SCallbackInfo& info, std::any data) {
+ for (auto& m : m_vManagers) {
+ m->onUnmap(std::any_cast<CWindow*>(data));
+ }
+ });
+
+ static auto P2 = g_pHookSystem->hookDynamic("windowTitle", [this](void* self, SCallbackInfo& info, std::any data) {
+ for (auto& m : m_vManagers) {
+ m->onTitle(std::any_cast<CWindow*>(data));
+ }
+ });
+}
+
+void CForeignToplevelProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
+ const auto RESOURCE = m_vManagers.emplace_back(std::make_unique<CForeignToplevelList>(std::make_shared<CExtForeignToplevelListV1>(client, ver, id))).get();
+
+ if (!RESOURCE->good()) {
+ LOGM(ERR, "Couldn't create a foreign list");
+ wl_client_post_no_memory(client);
+ m_vManagers.pop_back();
+ return;
+ }
+}
+
+void CForeignToplevelProtocol::onManagerResourceDestroy(CForeignToplevelList* mgr) {
+ std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == mgr; });
+}
+
+void CForeignToplevelProtocol::destroyHandle(CForeignToplevelHandle* handle) {
+ std::erase_if(m_vHandles, [&](const auto& other) { return other.get() == handle; });
+}
diff --git a/src/protocols/ForeignToplevel.hpp b/src/protocols/ForeignToplevel.hpp
new file mode 100644
index 00000000..939e48cd
--- /dev/null
+++ b/src/protocols/ForeignToplevel.hpp
@@ -0,0 +1,64 @@
+#pragma once
+
+#include <memory>
+#include <vector>
+#include <unordered_map>
+#include "WaylandProtocol.hpp"
+#include "ext-foreign-toplevel-list-v1.hpp"
+
+class CForeignToplevelHandle {
+ public:
+ CForeignToplevelHandle(SP<CExtForeignToplevelHandleV1> resource_, CWindow* pWindow);
+
+ bool good();
+ CWindow* window();
+
+ private:
+ SP<CExtForeignToplevelHandleV1> resource;
+ CWindow* pWindow = nullptr;
+ bool closed = false;
+
+ friend class CForeignToplevelList;
+};
+
+class CForeignToplevelList {
+ public:
+ CForeignToplevelList(SP<CExtForeignToplevelListV1> resource_);
+
+ void onMap(CWindow* pWindow);
+ void onTitle(CWindow* pWindow);
+ void onClass(CWindow* pWindow);
+ void onUnmap(CWindow* pWindow);
+
+ bool good();
+
+ private:
+ SP<CExtForeignToplevelListV1> resource;
+ bool finished = false;
+
+ SP<CForeignToplevelHandle> handleForWindow(CWindow* pWindow);
+
+ std::vector<WP<CForeignToplevelHandle>> handles;
+};
+
+class CForeignToplevelProtocol : public IWaylandProtocol {
+ public:
+ CForeignToplevelProtocol(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);
+
+ private:
+ void onManagerResourceDestroy(CForeignToplevelList* mgr);
+ void destroyHandle(CForeignToplevelHandle* handle);
+
+ //
+ std::vector<UP<CForeignToplevelList>> m_vManagers;
+ std::vector<SP<CForeignToplevelHandle>> m_vHandles;
+
+ friend class CForeignToplevelList;
+ friend class CForeignToplevelHandle;
+};
+
+namespace PROTO {
+ inline UP<CForeignToplevelProtocol> foreignToplevel;
+};