protocols: add xdg_toplevel_tag_v1 support

Adds a new windowrule to target windows by xdgTag, xdgtag:
This commit is contained in:
Vaxry 2025-04-21 22:22:06 +01:00
parent 55e953b383
commit a4f7d7c594
No known key found for this signature in database
GPG Key ID: 665806380871D640
11 changed files with 140 additions and 11 deletions

View File

@ -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()

View File

@ -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 = []

View File

@ -1415,6 +1415,11 @@ std::vector<SP<CWindowRule>> 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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)

View File

@ -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(""));
}
}

View File

@ -1808,3 +1808,17 @@ void CWindow::deactivateGroupMembers() {
bool CWindow::isNotResponding() {
return g_pANRManager->isNotResponding(m_pSelf.lock());
}
std::optional<std::string> CWindow::xdgTag() {
if (!m_pXDGSurface || !m_pXDGSurface->toplevel)
return std::nullopt;
return m_pXDGSurface->toplevel->m_toplevelTag;
}
std::optional<std::string> CWindow::xdgDescription() {
if (!m_pXDGSurface || !m_pXDGSurface->toplevel)
return std::nullopt;
return m_pXDGSurface->toplevel->m_toplevelDescription;
}

View File

@ -407,6 +407,8 @@ class CWindow {
void setContentType(NContentType::eContentType contentType);
void deactivateGroupMembers();
bool isNotResponding();
std::optional<std::string> xdgTag();
std::optional<std::string> xdgDescription();
CBox getWindowMainSurfaceBox() const {
return {m_vRealPosition->value().x, m_vRealPosition->value().y, m_vRealSize->value().x, m_vRealSize->value().y};

View File

@ -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;

View File

@ -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<CHyprlandCTMControlProtocol>(&hyprland_ctm_control_manager_v1_interface, 2, "CTMControl");
PROTO::hyprlandSurface = makeUnique<CHyprlandSurfaceProtocol>(&hyprland_surface_manager_v1_interface, 2, "HyprlandSurface");
PROTO::contentType = makeUnique<CContentTypeProtocol>(&wp_content_type_manager_v1_interface, 1, "ContentType");
PROTO::xdgTag = makeUnique<CXDGToplevelTagProtocol>(&xdg_toplevel_tag_manager_v1_interface, 1, "XDGTag");
if (*PENABLECM)
PROTO::colorManagement = makeUnique<CColorManagementProtocol>(&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,

View File

@ -141,6 +141,8 @@ class CXDGToplevelResource {
WP<CXDGToplevelResource> parent;
WP<CXDGDialogV1Resource> dialog;
std::optional<std::string> m_toplevelTag, m_toplevelDescription;
bool anyChildModal();
std::vector<WP<CXDGToplevelResource>> children;

54
src/protocols/XDGTag.cpp Normal file
View File

@ -0,0 +1,54 @@
#include "XDGTag.hpp"
#include "XDGShell.hpp"
CXDGToplevelTagManagerResource::CXDGToplevelTagManagerResource(UP<CXdgToplevelTagManagerV1>&& 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<CXDGToplevelTagManagerResource>{m_vManagers.emplace_back(makeUnique<CXDGToplevelTagManagerResource>(makeUnique<CXdgToplevelTagManagerV1>(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; });
}

36
src/protocols/XDGTag.hpp Normal file
View File

@ -0,0 +1,36 @@
#pragma once
#include <vector>
#include "WaylandProtocol.hpp"
#include "xdg-toplevel-tag-v1.hpp"
class CXDGToplevelResource;
class CXDGToplevelTagManagerResource {
public:
CXDGToplevelTagManagerResource(UP<CXdgToplevelTagManagerV1>&& resource);
bool good();
private:
UP<CXdgToplevelTagManagerV1> 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<UP<CXDGToplevelTagManagerResource>> m_vManagers;
friend class CXDGToplevelTagManagerResource;
};
namespace PROTO {
inline UP<CXDGToplevelTagProtocol> xdgTag;
};