diff --git a/CMakeLists.txt b/CMakeLists.txt index bd5e96b38..54048a445 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -316,7 +316,7 @@ endfunction() target_link_libraries(Hyprland OpenGL::EGL OpenGL::GL Threads::Threads) -pkg_check_modules(hyprland_protocols_dep hyprland-protocols>=0.6.2) +pkg_check_modules(hyprland_protocols_dep hyprland-protocols>=0.6.4) if(hyprland_protocols_dep_FOUND) pkg_get_variable(HYPRLAND_PROTOCOLS hyprland-protocols pkgdatadir) message(STATUS "hyprland-protocols dependency set to ${HYPRLAND_PROTOCOLS}") @@ -348,6 +348,7 @@ protocolnew("protocols" "wayland-drm" true) protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-ctm-control-v1" true) protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-surface-v1" true) protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-lock-notify-v1" true) +protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-toplevel-mapping-v1" true) protocolnew("staging/tearing-control" "tearing-control-v1" false) protocolnew("staging/fractional-scale" "fractional-scale-v1" false) diff --git a/protocols/meson.build b/protocols/meson.build index e4f18234a..a0cad39be 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -7,7 +7,7 @@ wayland_protos = dependency( hyprland_protos = dependency( 'hyprland-protocols', - version: '>=0.6.2', + version: '>=0.6.4', fallback: 'hyprland-protocols', ) @@ -37,6 +37,7 @@ protocols = [ 'frog-color-management-v1.xml', hyprland_protocol_dir / 'protocols/hyprland-global-shortcuts-v1.xml', hyprland_protocol_dir / 'protocols/hyprland-toplevel-export-v1.xml', + hyprland_protocol_dir / 'protocols/hyprland-toplevel-mapping-v1.xml', hyprland_protocol_dir / 'protocols/hyprland-focus-grab-v1.xml', hyprland_protocol_dir / 'protocols/hyprland-ctm-control-v1.xml', hyprland_protocol_dir / 'protocols/hyprland-surface-v1.xml', diff --git a/src/managers/ProtocolManager.cpp b/src/managers/ProtocolManager.cpp index cbe1d4b10..8ebe80107 100644 --- a/src/managers/ProtocolManager.cpp +++ b/src/managers/ProtocolManager.cpp @@ -42,6 +42,7 @@ #include "../protocols/DRMSyncobj.hpp" #include "../protocols/Screencopy.hpp" #include "../protocols/ToplevelExport.hpp" +#include "../protocols/ToplevelMapping.hpp" #include "../protocols/TextInputV1.hpp" #include "../protocols/GlobalShortcuts.hpp" #include "../protocols/XDGDialog.hpp" @@ -68,6 +69,7 @@ #include #include +#include // ******************************************************************************************** // * IMPORTANT: make sure to .reset() any protocol UP's you create! (put reset in destructor) * @@ -175,6 +177,7 @@ CProtocolManager::CProtocolManager() { PROTO::xwaylandShell = makeUnique(&xwayland_shell_v1_interface, 1, "XWaylandShell"); PROTO::screencopy = makeUnique(&zwlr_screencopy_manager_v1_interface, 3, "Screencopy"); PROTO::toplevelExport = makeUnique(&hyprland_toplevel_export_manager_v1_interface, 2, "ToplevelExport"); + PROTO::toplevelMapping = makeUnique(&hyprland_toplevel_mapping_manager_v1_interface, 1, "ToplevelMapping"); PROTO::globalShortcuts = makeUnique(&hyprland_global_shortcuts_manager_v1_interface, 1, "GlobalShortcuts"); PROTO::xdgDialog = makeUnique(&xdg_wm_dialog_v1_interface, 1, "XDGDialog"); PROTO::singlePixel = makeUnique(&wp_single_pixel_buffer_manager_v1_interface, 1, "SinglePixel"); @@ -262,6 +265,7 @@ CProtocolManager::~CProtocolManager() { PROTO::xwaylandShell.reset(); PROTO::screencopy.reset(); PROTO::toplevelExport.reset(); + PROTO::toplevelMapping.reset(); PROTO::globalShortcuts.reset(); PROTO::xdgDialog.reset(); PROTO::singlePixel.reset(); diff --git a/src/protocols/ForeignToplevel.cpp b/src/protocols/ForeignToplevel.cpp index 164657635..e6c026d65 100644 --- a/src/protocols/ForeignToplevel.cpp +++ b/src/protocols/ForeignToplevel.cpp @@ -6,6 +6,8 @@ CForeignToplevelHandle::CForeignToplevelHandle(SP r if UNLIKELY (!resource_->resource()) return; + resource->setData(this); + resource->setOnDestroy([this](CExtForeignToplevelHandleV1* h) { PROTO::foreignToplevel->destroyHandle(this); }); resource->setDestroy([this](CExtForeignToplevelHandleV1* h) { PROTO::foreignToplevel->destroyHandle(this); }); } @@ -168,3 +170,8 @@ void CForeignToplevelProtocol::destroyHandle(CForeignToplevelHandle* handle) { bool CForeignToplevelProtocol::windowValidForForeign(PHLWINDOW pWindow) { return validMapped(pWindow) && !pWindow->isX11OverrideRedirect(); } + +PHLWINDOW CForeignToplevelProtocol::windowFromHandleResource(wl_resource* res) { + auto data = (CForeignToplevelHandle*)(((CExtForeignToplevelHandleV1*)wl_resource_get_user_data(res))->data()); + return data ? data->window() : nullptr; +} diff --git a/src/protocols/ForeignToplevel.hpp b/src/protocols/ForeignToplevel.hpp index 712b32e0f..2136748bd 100644 --- a/src/protocols/ForeignToplevel.hpp +++ b/src/protocols/ForeignToplevel.hpp @@ -3,6 +3,7 @@ #include #include #include "WaylandProtocol.hpp" +#include "desktop/DesktopTypes.hpp" #include "ext-foreign-toplevel-list-v1.hpp" class CForeignToplevelHandle { @@ -18,6 +19,7 @@ class CForeignToplevelHandle { bool closed = false; friend class CForeignToplevelList; + friend class CForeignToplevelProtocol; }; class CForeignToplevelList { @@ -43,6 +45,7 @@ class CForeignToplevelList { class CForeignToplevelProtocol : public IWaylandProtocol { public: CForeignToplevelProtocol(const wl_interface* iface, const int& ver, const std::string& name); + PHLWINDOW windowFromHandleResource(wl_resource* res); virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); diff --git a/src/protocols/ForeignToplevelWlr.cpp b/src/protocols/ForeignToplevelWlr.cpp index b4210bacf..f1f32d7e8 100644 --- a/src/protocols/ForeignToplevelWlr.cpp +++ b/src/protocols/ForeignToplevelWlr.cpp @@ -10,6 +10,8 @@ CForeignToplevelHandleWlr::CForeignToplevelHandleWlr(SPresource()) return; + resource->setData(this); + resource->setOnDestroy([this](CZwlrForeignToplevelHandleV1* h) { PROTO::foreignToplevelWlr->destroyHandle(this); }); resource->setDestroy([this](CZwlrForeignToplevelHandleV1* h) { PROTO::foreignToplevelWlr->destroyHandle(this); }); @@ -420,14 +422,8 @@ void CForeignToplevelWlrProtocol::destroyHandle(CForeignToplevelHandleWlr* handl } PHLWINDOW CForeignToplevelWlrProtocol::windowFromHandleResource(wl_resource* res) { - for (auto const& h : m_vHandles) { - if (h->res() != res) - continue; - - return h->window(); - } - - return nullptr; + auto data = (CForeignToplevelHandleWlr*)(((CZwlrForeignToplevelHandleV1*)wl_resource_get_user_data(res))->data()); + return data ? data->window() : nullptr; } bool CForeignToplevelWlrProtocol::windowValidForForeign(PHLWINDOW pWindow) { diff --git a/src/protocols/ToplevelMapping.cpp b/src/protocols/ToplevelMapping.cpp new file mode 100644 index 000000000..e65695c8b --- /dev/null +++ b/src/protocols/ToplevelMapping.cpp @@ -0,0 +1,78 @@ +#include "ToplevelMapping.hpp" +#include "hyprland-toplevel-mapping-v1.hpp" +#include "ForeignToplevelWlr.hpp" +#include "ForeignToplevel.hpp" + +CToplevelWindowMappingHandle::CToplevelWindowMappingHandle(SP resource_) : resource(resource_) {} + +CToplevelMappingManager::CToplevelMappingManager(SP resource_) : resource(resource_) { + if UNLIKELY (!resource_->resource()) + return; + + resource->setOnDestroy([this](CHyprlandToplevelMappingManagerV1* h) { PROTO::toplevelMapping->onManagerResourceDestroy(this); }); + resource->setDestroy([this](CHyprlandToplevelMappingManagerV1* h) { PROTO::toplevelMapping->onManagerResourceDestroy(this); }); + + resource->setGetWindowForToplevel([this](CHyprlandToplevelMappingManagerV1* mgr, uint32_t handle, wl_resource* toplevel) { + const auto NEWHANDLE = PROTO::toplevelMapping->m_vHandles.emplace_back( + makeShared(makeShared(resource->client(), resource->version(), handle))); + + if UNLIKELY (!NEWHANDLE->resource->resource()) { + LOGM(ERR, "Couldn't alloc mapping handle! (no memory)"); + resource->noMemory(); + return; + } + + NEWHANDLE->resource->setOnDestroy([](CHyprlandToplevelWindowMappingHandleV1* h) { PROTO::toplevelMapping->destroyHandle(h); }); + NEWHANDLE->resource->setDestroy([](CHyprlandToplevelWindowMappingHandleV1* h) { PROTO::toplevelMapping->destroyHandle(h); }); + + const auto WINDOW = PROTO::foreignToplevel->windowFromHandleResource(toplevel); + if (!WINDOW) + NEWHANDLE->resource->sendFailed(); + else + NEWHANDLE->resource->sendWindowAddress((uint64_t)WINDOW.get() >> 32 & 0xFFFFFFFF, (uint64_t)WINDOW.get() & 0xFFFFFFFF); + }); + resource->setGetWindowForToplevelWlr([this](CHyprlandToplevelMappingManagerV1* mgr, uint32_t handle, wl_resource* toplevel) { + const auto NEWHANDLE = PROTO::toplevelMapping->m_vHandles.emplace_back( + makeShared(makeShared(resource->client(), resource->version(), handle))); + + if UNLIKELY (!NEWHANDLE->resource->resource()) { + LOGM(ERR, "Couldn't alloc mapping handle! (no memory)"); + resource->noMemory(); + return; + } + + NEWHANDLE->resource->setOnDestroy([](CHyprlandToplevelWindowMappingHandleV1* h) { PROTO::toplevelMapping->destroyHandle(h); }); + NEWHANDLE->resource->setDestroy([](CHyprlandToplevelWindowMappingHandleV1* h) { PROTO::toplevelMapping->destroyHandle(h); }); + + const auto WINDOW = PROTO::foreignToplevelWlr->windowFromHandleResource(toplevel); + if (!WINDOW) + NEWHANDLE->resource->sendFailed(); + else + NEWHANDLE->resource->sendWindowAddress((uint64_t)WINDOW.get() >> 32 & 0xFFFFFFFF, (uint64_t)WINDOW.get() & 0xFFFFFFFF); + }); +} + +bool CToplevelMappingManager::good() const { + return resource->resource(); +} + +CToplevelMappingProtocol::CToplevelMappingProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {} + +void CToplevelMappingProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { + const auto RESOURCE = m_vManagers.emplace_back(makeUnique(makeShared(client, ver, id))).get(); + + if UNLIKELY (!RESOURCE->good()) { + LOGM(ERR, "Couldn't create a toplevel mapping manager"); + wl_client_post_no_memory(client); + m_vManagers.pop_back(); + return; + } +} + +void CToplevelMappingProtocol::onManagerResourceDestroy(CToplevelMappingManager* mgr) { + std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == mgr; }); +} + +void CToplevelMappingProtocol::destroyHandle(CHyprlandToplevelWindowMappingHandleV1* handle) { + std::erase_if(m_vHandles, [&](const auto& other) { return other->resource.get() == handle; }); +} \ No newline at end of file diff --git a/src/protocols/ToplevelMapping.hpp b/src/protocols/ToplevelMapping.hpp new file mode 100644 index 000000000..ffe94b149 --- /dev/null +++ b/src/protocols/ToplevelMapping.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include +#include "WaylandProtocol.hpp" +#include "hyprland-toplevel-mapping-v1.hpp" + +class CToplevelWindowMappingHandle { + public: + CToplevelWindowMappingHandle(SP resource_); + + private: + SP resource; + + friend class CToplevelMappingManager; + friend class CToplevelMappingProtocol; +}; + +class CToplevelMappingManager { + public: + CToplevelMappingManager(SP resource_); + + bool good() const; + + private: + SP resource; +}; + +class CToplevelMappingProtocol : IWaylandProtocol { + public: + CToplevelMappingProtocol(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(CToplevelMappingManager* mgr); + void destroyHandle(CHyprlandToplevelWindowMappingHandleV1* handle); + + std::vector> m_vManagers; + std::vector> m_vHandles; + + friend class CToplevelMappingManager; +}; + +namespace PROTO { + inline UP toplevelMapping; +}; \ No newline at end of file diff --git a/subprojects/hyprland-protocols b/subprojects/hyprland-protocols index 755aef8da..3a5c2bda1 160000 --- a/subprojects/hyprland-protocols +++ b/subprojects/hyprland-protocols @@ -1 +1 @@ -Subproject commit 755aef8dab49d0fc4663c715fa4ad221b2aedaed +Subproject commit 3a5c2bda1c1a4e55cc1330c782547695a93f05b2