From a4f7d7c594c70408f299ec8b794211c534709eaa Mon Sep 17 00:00:00 2001 From: Vaxry Date: Mon, 21 Apr 2025 22:22:06 +0100 Subject: [PATCH] protocols: add xdg_toplevel_tag_v1 support Adds a new windowrule to target windows by xdgTag, xdgtag: --- CMakeLists.txt | 3 +- protocols/meson.build | 3 +- src/config/ConfigManager.cpp | 15 +++++++-- src/debug/HyprCtl.cpp | 12 ++++--- src/desktop/Window.cpp | 14 +++++++++ src/desktop/Window.hpp | 2 ++ src/desktop/WindowRule.hpp | 3 +- src/managers/ProtocolManager.cpp | 7 +++-- src/protocols/XDGShell.hpp | 2 ++ src/protocols/XDGTag.cpp | 54 ++++++++++++++++++++++++++++++++ src/protocols/XDGTag.hpp | 36 +++++++++++++++++++++ 11 files changed, 140 insertions(+), 11 deletions(-) create mode 100644 src/protocols/XDGTag.cpp create mode 100644 src/protocols/XDGTag.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 1d330c78..bd5e96b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -132,7 +132,7 @@ pkg_check_modules( xkbcommon uuid wayland-server>=1.22.90 - wayland-protocols>=1.41 + wayland-protocols>=1.43 cairo pango pangocairo @@ -382,6 +382,7 @@ protocolnew("staging/single-pixel-buffer" "single-pixel-buffer-v1" false) protocolnew("staging/security-context" "security-context-v1" false) protocolnew("staging/content-type" "content-type-v1" false) protocolnew("staging/color-management" "color-management-v1" false) +protocolnew("staging/xdg-toplevel-tag" "xdg-toplevel-tag-v1" false) protocolwayland() diff --git a/protocols/meson.build b/protocols/meson.build index 7941a9ff..e4f18234 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -1,6 +1,6 @@ wayland_protos = dependency( 'wayland-protocols', - version: '>=1.41', + version: '>=1.43', fallback: 'wayland-protocols', default_options: ['tests=false'], ) @@ -72,6 +72,7 @@ protocols = [ wayland_protocol_dir / 'staging/security-context/security-context-v1.xml', wayland_protocol_dir / 'staging/content-type/content-type-v1.xml', wayland_protocol_dir / 'staging/color-management/color-management-v1.xml', + wayland_protocol_dir / 'staging/xdg-toplevel-tag/xdg-toplevel-tag-v1.xml', ] wl_protocols = [] diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 476aa148..a6731f07 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -1415,6 +1415,11 @@ std::vector> CConfigManager::getMatchingRules(PHLWINDOW pWindow, } catch (std::exception& e) { Debug::log(ERR, "Rule \"content:{}\" failed with: {}", rule->szContentType, e.what()); } } + if (!rule->szXdgTag.empty()) { + if (pWindow->xdgTag().value_or("") != rule->szXdgTag) + continue; + } + if (!rule->szWorkspace.empty()) { const auto PWORKSPACE = pWindow->m_pWorkspace; @@ -2407,6 +2412,7 @@ std::optional CConfigManager::handleWindowRule(const std::string& c const auto FULLSCREENSTATEPOS = VALUE.find("fullscreenstate:"); const auto ONWORKSPACEPOS = VALUE.find("onworkspace:"); const auto CONTENTTYPEPOS = VALUE.find("content:"); + const auto XDGTAGPOS = VALUE.find("xdgTag:"); // find workspacepos that isn't onworkspacepos size_t WORKSPACEPOS = std::string::npos; @@ -2419,8 +2425,8 @@ std::optional CConfigManager::handleWindowRule(const std::string& c currentPos = VALUE.find("workspace:", currentPos + 1); } - const auto checkPos = std::unordered_set{TAGPOS, TITLEPOS, CLASSPOS, INITIALTITLEPOS, INITIALCLASSPOS, X11POS, FLOATPOS, - FULLSCREENPOS, PINNEDPOS, FULLSCREENSTATEPOS, WORKSPACEPOS, FOCUSPOS, ONWORKSPACEPOS, CONTENTTYPEPOS}; + const auto checkPos = std::unordered_set{TAGPOS, TITLEPOS, CLASSPOS, INITIALTITLEPOS, INITIALCLASSPOS, X11POS, FLOATPOS, FULLSCREENPOS, + PINNEDPOS, FULLSCREENSTATEPOS, WORKSPACEPOS, FOCUSPOS, ONWORKSPACEPOS, CONTENTTYPEPOS, XDGTAGPOS}; if (checkPos.size() == 1 && checkPos.contains(std::string::npos)) { Debug::log(ERR, "Invalid rulev2 syntax: {}", VALUE); return "Invalid rulev2 syntax: " + VALUE; @@ -2459,6 +2465,8 @@ std::optional CConfigManager::handleWindowRule(const std::string& c min = FOCUSPOS; if (CONTENTTYPEPOS > pos && CONTENTTYPEPOS < min) min = CONTENTTYPEPOS; + if (XDGTAGPOS > pos && XDGTAGPOS < min) + min = XDGTAGPOS; result = result.substr(0, min - pos); @@ -2520,6 +2528,9 @@ std::optional CConfigManager::handleWindowRule(const std::string& c if (CONTENTTYPEPOS != std::string::npos) rule->szContentType = extract(CONTENTTYPEPOS + 8); + if (XDGTAGPOS != std::string::npos) + rule->szXdgTag = extract(XDGTAGPOS + 8); + if (RULE == "unset") { std::erase_if(m_windowRules, [&](const auto& other) { if (!other->v2) diff --git a/src/debug/HyprCtl.cpp b/src/debug/HyprCtl.cpp index 508b0e5c..261a36d6 100644 --- a/src/debug/HyprCtl.cpp +++ b/src/debug/HyprCtl.cpp @@ -258,7 +258,9 @@ std::string CHyprCtl::getWindowData(PHLWINDOW w, eHyprCtlOutputFormat format) { "tags": [{}], "swallowing": "0x{:x}", "focusHistoryID": {}, - "inhibitingIdle": {} + "inhibitingIdle": {}, + "xdgTag": "{}", + "xdgDescription": "{}" }},)#", (uintptr_t)w.get(), (w->m_bIsMapped ? "true" : "false"), (w->isHidden() ? "true" : "false"), (int)w->m_vRealPosition->goal().x, (int)w->m_vRealPosition->goal().y, (int)w->m_vRealSize->goal().x, (int)w->m_vRealSize->goal().y, w->m_pWorkspace ? w->workspaceID() : WORKSPACE_INVALID, @@ -266,19 +268,21 @@ std::string CHyprCtl::getWindowData(PHLWINDOW w, eHyprCtlOutputFormat format) { (int64_t)w->monitorID(), escapeJSONStrings(w->m_szClass), escapeJSONStrings(w->m_szTitle), escapeJSONStrings(w->m_szInitialClass), escapeJSONStrings(w->m_szInitialTitle), w->getPID(), ((int)w->m_bIsX11 == 1 ? "true" : "false"), (w->m_bPinned ? "true" : "false"), (uint8_t)w->m_sFullscreenState.internal, (uint8_t)w->m_sFullscreenState.client, getGroupedData(w, format), getTagsData(w, format), (uintptr_t)w->m_pSwallowed.get(), - getFocusHistoryID(w), (g_pInputManager->isWindowInhibiting(w, false) ? "true" : "false")); + getFocusHistoryID(w), (g_pInputManager->isWindowInhibiting(w, false) ? "true" : "false"), escapeJSONStrings(w->xdgTag().value_or("")), + escapeJSONStrings(w->xdgDescription().value_or(""))); } else { return std::format( "Window {:x} -> {}:\n\tmapped: {}\n\thidden: {}\n\tat: {},{}\n\tsize: {},{}\n\tworkspace: {} ({})\n\tfloating: {}\n\tpseudo: {}\n\tmonitor: {}\n\tclass: {}\n\ttitle: " "{}\n\tinitialClass: {}\n\tinitialTitle: {}\n\tpid: " "{}\n\txwayland: {}\n\tpinned: " - "{}\n\tfullscreen: {}\n\tfullscreenClient: {}\n\tgrouped: {}\n\ttags: {}\n\tswallowing: {:x}\n\tfocusHistoryID: {}\n\tinhibitingIdle: {}\n\n", + "{}\n\tfullscreen: {}\n\tfullscreenClient: {}\n\tgrouped: {}\n\ttags: {}\n\tswallowing: {:x}\n\tfocusHistoryID: {}\n\tinhibitingIdle: {}\n\txdgTag: " + "{}\n\txdgDescription: {}\n\n", (uintptr_t)w.get(), w->m_szTitle, (int)w->m_bIsMapped, (int)w->isHidden(), (int)w->m_vRealPosition->goal().x, (int)w->m_vRealPosition->goal().y, (int)w->m_vRealSize->goal().x, (int)w->m_vRealSize->goal().y, w->m_pWorkspace ? w->workspaceID() : WORKSPACE_INVALID, (!w->m_pWorkspace ? "" : w->m_pWorkspace->m_szName), (int)w->m_bIsFloating, (int)w->m_bIsPseudotiled, (int64_t)w->monitorID(), w->m_szClass, w->m_szTitle, w->m_szInitialClass, w->m_szInitialTitle, w->getPID(), (int)w->m_bIsX11, (int)w->m_bPinned, (uint8_t)w->m_sFullscreenState.internal, (uint8_t)w->m_sFullscreenState.client, getGroupedData(w, format), getTagsData(w, format), (uintptr_t)w->m_pSwallowed.get(), getFocusHistoryID(w), - (int)g_pInputManager->isWindowInhibiting(w, false)); + (int)g_pInputManager->isWindowInhibiting(w, false), w->xdgTag().value_or(""), w->xdgDescription().value_or("")); } } diff --git a/src/desktop/Window.cpp b/src/desktop/Window.cpp index ffe49498..e2131133 100644 --- a/src/desktop/Window.cpp +++ b/src/desktop/Window.cpp @@ -1808,3 +1808,17 @@ void CWindow::deactivateGroupMembers() { bool CWindow::isNotResponding() { return g_pANRManager->isNotResponding(m_pSelf.lock()); } + +std::optional CWindow::xdgTag() { + if (!m_pXDGSurface || !m_pXDGSurface->toplevel) + return std::nullopt; + + return m_pXDGSurface->toplevel->m_toplevelTag; +} + +std::optional CWindow::xdgDescription() { + if (!m_pXDGSurface || !m_pXDGSurface->toplevel) + return std::nullopt; + + return m_pXDGSurface->toplevel->m_toplevelDescription; +} diff --git a/src/desktop/Window.hpp b/src/desktop/Window.hpp index 2d99016d..9fbb7522 100644 --- a/src/desktop/Window.hpp +++ b/src/desktop/Window.hpp @@ -407,6 +407,8 @@ class CWindow { void setContentType(NContentType::eContentType contentType); void deactivateGroupMembers(); bool isNotResponding(); + std::optional xdgTag(); + std::optional xdgDescription(); CBox getWindowMainSurfaceBox() const { return {m_vRealPosition->value().x, m_vRealPosition->value().y, m_vRealSize->value().x, m_vRealSize->value().y}; diff --git a/src/desktop/WindowRule.hpp b/src/desktop/WindowRule.hpp index 192d8aa7..465ddbb1 100644 --- a/src/desktop/WindowRule.hpp +++ b/src/desktop/WindowRule.hpp @@ -37,7 +37,7 @@ class CWindowRule { RULE_WORKSPACE, RULE_PROP, RULE_CONTENT, - RULE_PERSISTENTSIZE, + RULE_PERSISTENTSIZE }; eRuleType ruleType = RULE_INVALID; @@ -61,6 +61,7 @@ class CWindowRule { std::string szOnWorkspace = ""; // empty means any std::string szWorkspace = ""; // empty means any std::string szContentType = ""; // empty means any + std::string szXdgTag = ""; // empty means any // precompiled regexes CRuleRegexContainer rTitle; diff --git a/src/managers/ProtocolManager.cpp b/src/managers/ProtocolManager.cpp index 06987dae..c9bc2510 100644 --- a/src/managers/ProtocolManager.cpp +++ b/src/managers/ProtocolManager.cpp @@ -49,7 +49,6 @@ #include "../protocols/SecurityContext.hpp" #include "../protocols/CTMControl.hpp" #include "../protocols/HyprlandSurface.hpp" - #include "../protocols/core/Seat.hpp" #include "../protocols/core/DataDevice.hpp" #include "../protocols/core/Compositor.hpp" @@ -60,6 +59,7 @@ #include "../protocols/XXColorManagement.hpp" #include "../protocols/FrogColorManagement.hpp" #include "../protocols/ContentType.hpp" +#include "../protocols/XDGTag.hpp" #include "../helpers/Monitor.hpp" #include "../render/Renderer.hpp" @@ -182,6 +182,7 @@ CProtocolManager::CProtocolManager() { PROTO::ctm = makeUnique(&hyprland_ctm_control_manager_v1_interface, 2, "CTMControl"); PROTO::hyprlandSurface = makeUnique(&hyprland_surface_manager_v1_interface, 2, "HyprlandSurface"); PROTO::contentType = makeUnique(&wp_content_type_manager_v1_interface, 1, "ContentType"); + PROTO::xdgTag = makeUnique(&xdg_toplevel_tag_manager_v1_interface, 1, "XDGTag"); if (*PENABLECM) PROTO::colorManagement = makeUnique(&wp_color_manager_v1_interface, 1, "ColorManagement", *PDEBUGCM); @@ -271,6 +272,7 @@ CProtocolManager::~CProtocolManager() { PROTO::colorManagement.reset(); PROTO::xxColorManagement.reset(); PROTO::frogColorManagement.reset(); + PROTO::xdgTag.reset(); PROTO::lease.reset(); PROTO::sync.reset(); @@ -321,7 +323,8 @@ bool CProtocolManager::isGlobalPrivileged(const wl_global* global) { PROTO::xdgDialog->getGlobal(), PROTO::singlePixel->getGlobal(), PROTO::primarySelection->getGlobal(), - PROTO::hyprlandSurface->getGlobal(), + PROTO::hyprlandSurface->getGlobal(), + PROTO::xdgTag->getGlobal(), PROTO::sync ? PROTO::sync->getGlobal() : nullptr, PROTO::mesaDRM ? PROTO::mesaDRM->getGlobal() : nullptr, PROTO::linuxDma ? PROTO::linuxDma->getGlobal() : nullptr, diff --git a/src/protocols/XDGShell.hpp b/src/protocols/XDGShell.hpp index b46e0236..12f76774 100644 --- a/src/protocols/XDGShell.hpp +++ b/src/protocols/XDGShell.hpp @@ -141,6 +141,8 @@ class CXDGToplevelResource { WP parent; WP dialog; + std::optional m_toplevelTag, m_toplevelDescription; + bool anyChildModal(); std::vector> children; diff --git a/src/protocols/XDGTag.cpp b/src/protocols/XDGTag.cpp new file mode 100644 index 00000000..9176882e --- /dev/null +++ b/src/protocols/XDGTag.cpp @@ -0,0 +1,54 @@ +#include "XDGTag.hpp" +#include "XDGShell.hpp" + +CXDGToplevelTagManagerResource::CXDGToplevelTagManagerResource(UP&& resource) : m_resource(std::move(resource)) { + if UNLIKELY (!good()) + return; + + m_resource->setDestroy([this](CXdgToplevelTagManagerV1* r) { PROTO::xdgTag->destroyResource(this); }); + m_resource->setOnDestroy([this](CXdgToplevelTagManagerV1* r) { PROTO::xdgTag->destroyResource(this); }); + + resource->setSetToplevelTag([](CXdgToplevelTagManagerV1* r, wl_resource* toplevel, const char* tag) { + auto TOPLEVEL = CXDGToplevelResource::fromResource(toplevel); + + if (!TOPLEVEL) { + r->error(-1, "Invalid toplevel handle"); + return; + } + + TOPLEVEL->m_toplevelTag = tag; + }); + + resource->setSetToplevelDescription([](CXdgToplevelTagManagerV1* r, wl_resource* toplevel, const char* description) { + auto TOPLEVEL = CXDGToplevelResource::fromResource(toplevel); + + if (!TOPLEVEL) { + r->error(-1, "Invalid toplevel handle"); + return; + } + + TOPLEVEL->m_toplevelDescription = description; + }); +} + +bool CXDGToplevelTagManagerResource::good() { + return m_resource->resource(); +} + +CXDGToplevelTagProtocol::CXDGToplevelTagProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { + ; +} + +void CXDGToplevelTagProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { + const auto RESOURCE = + WP{m_vManagers.emplace_back(makeUnique(makeUnique(client, ver, id)))}; + + if UNLIKELY (!RESOURCE->good()) { + wl_client_post_no_memory(client); + return; + } +} + +void CXDGToplevelTagProtocol::destroyResource(CXDGToplevelTagManagerResource* res) { + std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == res; }); +} diff --git a/src/protocols/XDGTag.hpp b/src/protocols/XDGTag.hpp new file mode 100644 index 00000000..cff4da03 --- /dev/null +++ b/src/protocols/XDGTag.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include +#include "WaylandProtocol.hpp" +#include "xdg-toplevel-tag-v1.hpp" + +class CXDGToplevelResource; + +class CXDGToplevelTagManagerResource { + public: + CXDGToplevelTagManagerResource(UP&& resource); + + bool good(); + + private: + UP m_resource; +}; + +class CXDGToplevelTagProtocol : public IWaylandProtocol { + public: + CXDGToplevelTagProtocol(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 destroyResource(CXDGToplevelTagManagerResource* res); + + // + std::vector> m_vManagers; + + friend class CXDGToplevelTagManagerResource; +}; + +namespace PROTO { + inline UP xdgTag; +};