core: Implement hyprland-toplevel-mapping-v1 protocol (#322)

* feat: start toplevel-mapping-v1 implementation

* feat: finished toplevel-mapping-v1 implementation

* fix: rename unordered map member variable

* chore: fix formatting

* fix: update share picker for new env

* chore: hyprland protocol version bump

* chore: implement review

* chore: implement feedback

* chore: implemented feedback
This commit is contained in:
WhySoBad 2025-04-28 22:22:05 +02:00 committed by GitHub
parent be6771e754
commit 76bbf1a6b1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 98 additions and 8 deletions

View File

@ -130,7 +130,10 @@ protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-global-shortcuts-v1"
true)
protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-toplevel-export-v1"
true)
protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-toplevel-mapping-v1"
true)
protocolnew("stable/linux-dmabuf" "linux-dmabuf-v1" false)
protocolnew("staging/ext-foreign-toplevel-list" "ext-foreign-toplevel-list-v1" false)
# Installation
install(TARGETS hyprland-share-picker)

View File

@ -61,13 +61,17 @@ std::vector<SWindowEntry> getWindows(const char* env) {
const auto TITLESEPPOS = rolling.find("[HE>]");
const auto TITLESTR = rolling.substr(CLASSSEPPOS + 5, TITLESEPPOS - 5 - CLASSSEPPOS);
// window address
const auto WINDOWSEPPOS = rolling.find("[HA>]");
const auto WINDOWADDR = rolling.substr(TITLESEPPOS + 5, WINDOWSEPPOS - 5 - TITLESEPPOS);
try {
result.push_back({TITLESTR, CLASSSTR, std::stoull(IDSTR)});
} catch (std::exception& e) {
// silent err
}
rolling = rolling.substr(TITLESEPPOS + 5);
rolling = rolling.substr(WINDOWSEPPOS + 5);
}
return result;

View File

@ -4,7 +4,7 @@ wayland_protos = dependency('wayland-protocols',
)
hyprland_protos = dependency('hyprland-protocols',
version: '>=0.2',
version: '>=0.6.4',
fallback: 'hyprland-protocols',
)
@ -21,8 +21,10 @@ client_protocols = [
'wlr-screencopy-unstable-v1.xml',
'wlr-foreign-toplevel-management-unstable-v1.xml',
hl_protocol_dir / 'protocols/hyprland-toplevel-export-v1.xml',
hl_protocol_dir / 'protocols/hyprland-toplevel-mapping-v1.xml',
hl_protocol_dir / 'protocols/hyprland-global-shortcuts-v1.xml',
wl_protocol_dir / 'stable/linux-dmabuf/linux-dmabuf-v1.xml',
wl_protocol_dir / 'staging/ext-foreign-toplevel-list/ext-foreign-toplevel-list-v1.xml',
]
wl_proto_files = []

View File

@ -192,6 +192,11 @@ void CPortalManager::onGlobal(uint32_t name, const char* interface, uint32_t ver
if (!std::any_cast<Hyprlang::INT>(m_sConfig.config->getConfigValue("general:toplevel_dynamic_bind")))
m_sHelpers.toplevel->activate();
}
else if (INTERFACE == hyprland_toplevel_mapping_manager_v1_interface.name) {
m_sHelpers.toplevelMapping = std::make_unique<CToplevelMappingManager>(makeShared<CCHyprlandToplevelMappingManagerV1>(
(wl_proxy*)wl_registry_bind((wl_registry*)m_sWaylandConnection.registry->resource(), name, &hyprland_toplevel_mapping_manager_v1_interface, version)));
}
}
void CPortalManager::onGlobalRemoved(uint32_t name) {

View File

@ -10,6 +10,7 @@
#include "../portals/GlobalShortcuts.hpp"
#include "../helpers/Timer.hpp"
#include "../shared/ToplevelManager.hpp"
#include "../shared/ToplevelMappingManager.hpp"
#include <gbm.h>
#include <xf86drm.h>
@ -64,6 +65,7 @@ class CPortalManager {
struct {
std::unique_ptr<CToplevelManager> toplevel;
std::unique_ptr<CToplevelMappingManager> toplevelMapping;
} m_sHelpers;
struct {

View File

@ -31,8 +31,9 @@ std::string buildWindowList() {
return result;
for (auto& e : g_pPortalManager->m_sHelpers.toplevel->m_vToplevels) {
result += std::format("{}[HC>]{}[HT>]{}[HE>]", (uint32_t)(((uint64_t)e->handle->resource()) & 0xFFFFFFFF), sanitizeNameForWindowList(e->windowClass),
sanitizeNameForWindowList(e->windowTitle));
result += std::format("{}[HC>]{}[HT>]{}[HE>]{}[HA>]", (uint32_t)(((uint64_t)e->handle->resource()) & 0xFFFFFFFF), sanitizeNameForWindowList(e->windowClass),
sanitizeNameForWindowList(e->windowTitle),
g_pPortalManager->m_sHelpers.toplevelMapping ? g_pPortalManager->m_sHelpers.toplevelMapping->getWindowForToplevel(e->handle) : 0);
}
return result;

View File

@ -19,6 +19,8 @@ SToplevelHandle::SToplevelHandle(SP<CCZwlrForeignToplevelHandleV1> handle_) : ha
Debug::log(TRACE, "[toplevel] toplevel at {} closed", (void*)this);
std::erase_if(g_pPortalManager->m_sHelpers.toplevel->m_vToplevels, [&](const auto& e) { return e.get() == this; });
if (g_pPortalManager->m_sHelpers.toplevelMapping)
g_pPortalManager->m_sHelpers.toplevelMapping->m_muAddresses.erase(this->handle);
});
}
@ -41,9 +43,15 @@ void CToplevelManager::activate() {
m_pManager->setToplevel([this](CCZwlrForeignToplevelManagerV1* r, wl_proxy* newHandle) {
Debug::log(TRACE, "[toplevel] New toplevel at {}", (void*)newHandle);
m_vToplevels.emplace_back(makeShared<SToplevelHandle>(makeShared<CCZwlrForeignToplevelHandleV1>(newHandle)));
const auto HANDLE = m_vToplevels.emplace_back(makeShared<SToplevelHandle>(makeShared<CCZwlrForeignToplevelHandleV1>(newHandle)));
if (g_pPortalManager->m_sHelpers.toplevelMapping)
g_pPortalManager->m_sHelpers.toplevelMapping->fetchWindowForToplevel(HANDLE->handle);
});
m_pManager->setFinished([this](CCZwlrForeignToplevelManagerV1* r) {
m_vToplevels.clear();
if (g_pPortalManager->m_sHelpers.toplevelMapping)
g_pPortalManager->m_sHelpers.toplevelMapping->m_muAddresses.clear();
});
m_pManager->setFinished([this](CCZwlrForeignToplevelManagerV1* r) { m_vToplevels.clear(); });
wl_display_roundtrip(g_pPortalManager->m_sWaylandConnection.display);
@ -60,6 +68,8 @@ void CToplevelManager::deactivate() {
m_pManager.reset();
m_vToplevels.clear();
if (g_pPortalManager->m_sHelpers.toplevelMapping)
g_pPortalManager->m_sHelpers.toplevelMapping->m_muAddresses.clear();
Debug::log(LOG, "[toplevel] unbound manager");
}

View File

@ -0,0 +1,39 @@
#include "ToplevelMappingManager.hpp"
#include "../helpers/Log.hpp"
CToplevelMappingManager::CToplevelMappingManager(SP<CCHyprlandToplevelMappingManagerV1> mgr) : m_pManager(mgr) {
Debug::log(LOG, "[toplevel mapping] registered manager");
}
void CToplevelMappingManager::fetchWindowForToplevel(SP<CCZwlrForeignToplevelHandleV1> handle) {
if (!handle)
return;
Debug::log(TRACE, "[toplevel mapping] fetching window for toplevel at {}", (void*)handle.get());
auto const HANDLE = makeShared<CCHyprlandToplevelWindowMappingHandleV1>(m_pManager->sendGetWindowForToplevelWlr(handle->resource()));
m_vHandles.push_back(HANDLE);
HANDLE->setWindowAddress([this, handle](CCHyprlandToplevelWindowMappingHandleV1* h, uint32_t address_hi, uint32_t address) {
const auto ADDRESS = (uint64_t)address_hi << 32 | address;
m_muAddresses.insert_or_assign(handle, ADDRESS);
Debug::log(TRACE, "[toplevel mapping] mapped toplevel at {} to window {}", (void*)handle.get(), ADDRESS);
std::erase_if(m_vHandles, [&](const auto& other) { return other.get() == h; });
});
HANDLE->setFailed([this, handle](CCHyprlandToplevelWindowMappingHandleV1* h) {
Debug::log(TRACE, "[toplevel mapping] failed to map toplevel at {} to window", (void*)handle.get());
std::erase_if(m_vHandles, [&](const auto& other) { return other.get() == h; });
});
}
uint64_t CToplevelMappingManager::getWindowForToplevel(CSharedPointer<CCZwlrForeignToplevelHandleV1> handle) {
auto iter = m_muAddresses.find(handle);
if (iter != m_muAddresses.end())
return iter->second;
if (handle)
Debug::log(TRACE, "[toplevel mapping] did not find window address for toplevel at {}", (void*)handle.get());
return 0;
}

View File

@ -0,0 +1,24 @@
#pragma once
#include "wayland.hpp"
#include "hyprland-toplevel-mapping-v1.hpp"
#include "wlr-foreign-toplevel-management-unstable-v1.hpp"
#include <memory>
#include "../includes.hpp"
class CToplevelMappingManager {
public:
CToplevelMappingManager(SP<CCHyprlandToplevelMappingManagerV1> mgr);
uint64_t getWindowForToplevel(SP<CCZwlrForeignToplevelHandleV1> handle);
private:
SP<CCHyprlandToplevelMappingManagerV1> m_pManager = nullptr;
std::unordered_map<SP<CCZwlrForeignToplevelHandleV1>, uint64_t> m_muAddresses;
std::vector<SP<CCHyprlandToplevelWindowMappingHandleV1>> m_vHandles;
void fetchWindowForToplevel(SP<CCZwlrForeignToplevelHandleV1> handle);
friend struct SToplevelHandle;
friend class CToplevelManager;
};

@ -1 +1 @@
Subproject commit 4d29e48433270a2af06b8bc711ca1fe5109746cd
Subproject commit 3a5c2bda1c1a4e55cc1330c782547695a93f05b2