diff --git a/CMakeLists.txt b/CMakeLists.txt index 9330f7e7..8f036dbf 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -262,6 +262,8 @@ protocol("subprojects/hyprland-protocols/protocols/hyprland-toplevel-export-v1.x protocol("stable/xdg-shell/xdg-shell.xml" "xdg-shell" false) protocol("unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml" "linux-dmabuf-unstable-v1" false) protocol("unstable/text-input/text-input-unstable-v1.xml" "text-input-unstable-v1" false) + +protocolNew("protocols/wlr-gamma-control-unstable-v1.xml" "wlr-gamma-control-unstable-v1" true) protocolNew("staging/tearing-control/tearing-control-v1.xml" "tearing-control-v1" false) protocolNew("staging/fractional-scale/fractional-scale-v1.xml" "fractional-scale-v1" false) protocolNew("unstable/xdg-output/xdg-output-unstable-v1.xml" "xdg-output-unstable-v1" false) @@ -270,7 +272,7 @@ protocolNew("unstable/idle-inhibit/idle-inhibit-unstable-v1.xml" "idle-inhibit-u protocolNew("unstable/relative-pointer/relative-pointer-unstable-v1.xml" "relative-pointer-unstable-v1" false) protocolNew("unstable/xdg-decoration/xdg-decoration-unstable-v1.xml" "xdg-decoration-unstable-v1" false) protocolNew("staging/alpha-modifier/alpha-modifier-v1.xml" "alpha-modifier-v1" false) -protocolNew("protocols/wlr-gamma-control-unstable-v1.xml" "wlr-gamma-control-unstable-v1" true) +protocolNew("staging/ext-foreign-toplevel-list/ext-foreign-toplevel-list-v1.xml" "ext-foreign-toplevel-list-v1" false) # tools add_subdirectory(hyprctl) diff --git a/protocols/meson.build b/protocols/meson.build index eaeaada0..c431a31c 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -48,6 +48,7 @@ new_protocols = [ [wl_protocol_dir, 'unstable/relative-pointer/relative-pointer-unstable-v1.xml'], [wl_protocol_dir, 'unstable/xdg-decoration/xdg-decoration-unstable-v1.xml'], [wl_protocol_dir, 'staging/alpha-modifier/alpha-modifier-v1.xml'], + [wl_protocol_dir, 'staging/ext-foreign-toplevel-list/ext-foreign-toplevel-list-v1.xml'], ] wl_protos_src = [] diff --git a/src/managers/ProtocolManager.cpp b/src/managers/ProtocolManager.cpp index 96a0707b..53535346 100644 --- a/src/managers/ProtocolManager.cpp +++ b/src/managers/ProtocolManager.cpp @@ -9,6 +9,7 @@ #include "../protocols/XDGDecoration.hpp" #include "../protocols/AlphaModifier.hpp" #include "../protocols/GammaControl.hpp" +#include "../protocols/ForeignToplevel.hpp" #include "tearing-control-v1.hpp" #include "fractional-scale-v1.hpp" @@ -19,6 +20,7 @@ #include "xdg-decoration-unstable-v1.hpp" #include "alpha-modifier-v1.hpp" #include "wlr-gamma-control-unstable-v1.hpp" +#include "ext-foreign-toplevel-list-v1.hpp" CProtocolManager::CProtocolManager() { @@ -31,6 +33,7 @@ CProtocolManager::CProtocolManager() { PROTO::xdgDecoration = std::make_unique(&zxdg_decoration_manager_v1_interface, 1, "XDGDecoration"); PROTO::alphaModifier = std::make_unique(&wp_alpha_modifier_v1_interface, 1, "AlphaModifier"); PROTO::gamma = std::make_unique(&zwlr_gamma_control_manager_v1_interface, 1, "GammaControl"); + PROTO::foreignToplevel = std::make_unique(&ext_foreign_toplevel_list_v1_interface, 1, "ForeignToplevel"); // Old protocol implementations. // TODO: rewrite them to use hyprwayland-scanner. 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 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 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( + std::make_shared(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((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 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{} : 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(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(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(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(std::make_shared(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 +#include +#include +#include "WaylandProtocol.hpp" +#include "ext-foreign-toplevel-list-v1.hpp" + +class CForeignToplevelHandle { + public: + CForeignToplevelHandle(SP resource_, CWindow* pWindow); + + bool good(); + CWindow* window(); + + private: + SP resource; + CWindow* pWindow = nullptr; + bool closed = false; + + friend class CForeignToplevelList; +}; + +class CForeignToplevelList { + public: + CForeignToplevelList(SP resource_); + + void onMap(CWindow* pWindow); + void onTitle(CWindow* pWindow); + void onClass(CWindow* pWindow); + void onUnmap(CWindow* pWindow); + + bool good(); + + private: + SP resource; + bool finished = false; + + SP handleForWindow(CWindow* pWindow); + + std::vector> 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> m_vManagers; + std::vector> m_vHandles; + + friend class CForeignToplevelList; + friend class CForeignToplevelHandle; +}; + +namespace PROTO { + inline UP foreignToplevel; +};