diff --git a/hyprctl/hyprctl.usage b/hyprctl/hyprctl.usage index 8e75917ea07..334eefe7556 100644 --- a/hyprctl/hyprctl.usage +++ b/hyprctl/hyprctl.usage @@ -117,6 +117,7 @@ hyprctl []... | (movewindowpixel) "Move a selected window" | (cyclenext) "Focus the next window on a workspace" | (swapnext) "Swap the focused window with the next window" + | (tagwindow) "Apply a tag to the window" | (focuswindow) "Focus the first window matching" | (focusmonitor) "Focus a monitor" | (splitratio) "Change the split ratio" diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index f65b5abbbeb..b1c5951d9ed 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include extern "C" char** environ; @@ -994,7 +995,7 @@ SMonitorRule CConfigManager::getMonitorRuleFor(const CMonitor& PMONITOR) { Debug::log(WARN, "No rule found for {}, trying to use the first.", PMONITOR.szName); for (auto& r : m_dMonitorRules) { - if (r.name == "") { + if (r.name.empty()) { return r; } } @@ -1080,12 +1081,17 @@ std::vector CConfigManager::getMatchingRules(PHLWINDOW pWindow, boo bool hasFloating = pWindow->m_bIsFloating; bool hasFullscreen = pWindow->m_bIsFullscreen; + // local tags for dynamic tag rule match + auto tags = pWindow->m_tags; + for (auto& rule : m_dWindowRules) { // check if we have a matching rule if (!rule.v2) { try { + if (rule.szValue.starts_with("tag:") && !tags.isTagged(rule.szValue.substr(4))) + continue; + if (rule.szValue.starts_with("title:")) { - // we have a title rule. std::regex RULECHECK(rule.szValue.substr(6)); if (!std::regex_search(title, RULECHECK)) @@ -1102,28 +1108,31 @@ std::vector CConfigManager::getMatchingRules(PHLWINDOW pWindow, boo } } else { try { - if (rule.szClass != "") { + if (!rule.szTag.empty() && !tags.isTagged(rule.szTag)) + continue; + + if (!rule.szClass.empty()) { std::regex RULECHECK(rule.szClass); if (!std::regex_search(appidclass, RULECHECK)) continue; } - if (rule.szTitle != "") { + if (!rule.szTitle.empty()) { std::regex RULECHECK(rule.szTitle); if (!std::regex_search(title, RULECHECK)) continue; } - if (rule.szInitialTitle != "") { + if (!rule.szInitialTitle.empty()) { std::regex RULECHECK(rule.szInitialTitle); if (!std::regex_search(pWindow->m_szInitialTitle, RULECHECK)) continue; } - if (rule.szInitialClass != "") { + if (!rule.szInitialClass.empty()) { std::regex RULECHECK(rule.szInitialClass); if (!std::regex_search(pWindow->m_szInitialClass, RULECHECK)) @@ -1192,6 +1201,13 @@ std::vector CConfigManager::getMatchingRules(PHLWINDOW pWindow, boo returns.push_back(rule); + // apply tag with local tags + if (rule.szRule.starts_with("tag")) { + CVarList vars{rule.szRule, 0, 's', true}; + if (vars.size() == 2 && vars[0] == "tag") + tags.applyTag(vars[1], true); + } + if (dynamic) continue; @@ -1696,7 +1712,7 @@ std::optional CConfigManager::handleMonitor(const std::string& comm newrule.resolution = Vector2D(-1, -2); } else if (parseModeLine(ARGS[1], newrule.drmMode)) { newrule.resolution = Vector2D(newrule.drmMode.hdisplay, newrule.drmMode.vdisplay); - newrule.refreshRate = newrule.drmMode.vrefresh / 1000; + newrule.refreshRate = float(newrule.drmMode.vrefresh) / 1000; } else { if (!ARGS[1].contains("x")) { @@ -2023,7 +2039,7 @@ std::optional CConfigManager::handleBind(const std::string& command if ((KEY != "") || multiKey) { SParsedKey parsedKey = parseKey(KEY); - if (parsedKey.catchAll && m_szCurrentSubmap == "") { + if (parsedKey.catchAll && m_szCurrentSubmap.empty()) { Debug::log(ERR, "Catchall not allowed outside of submap!"); return "Invalid catchall, catchall keybinds are only allowed in submaps."; } @@ -2048,18 +2064,24 @@ std::optional CConfigManager::handleUnbind(const std::string& comma } bool windowRuleValid(const std::string& RULE) { - return RULE == "float" || RULE == "tile" || RULE.starts_with("opacity") || RULE.starts_with("move") || RULE.starts_with("size") || RULE.starts_with("minsize") || - RULE.starts_with("maxsize") || RULE.starts_with("pseudo") || RULE.starts_with("monitor") || RULE.starts_with("idleinhibit") || RULE == "nofocus" || RULE == "noblur" || - RULE == "noshadow" || RULE == "nodim" || RULE == "noborder" || RULE == "opaque" || RULE == "forceinput" || RULE == "fullscreen" || RULE == "fakefullscreen" || - RULE == "nomaxsize" || RULE == "pin" || RULE == "noanim" || RULE == "dimaround" || RULE == "windowdance" || RULE == "maximize" || RULE == "keepaspectratio" || - RULE == "focusonactivate" || RULE.starts_with("animation") || RULE.starts_with("rounding") || RULE.starts_with("workspace") || RULE.starts_with("bordercolor") || - RULE == "forcergbx" || RULE == "noinitialfocus" || RULE == "stayfocused" || RULE.starts_with("bordersize") || RULE.starts_with("xray") || RULE.starts_with("center") || - RULE.starts_with("group") || RULE == "immediate" || RULE == "nearestneighbor" || RULE.starts_with("suppressevent") || RULE.starts_with("plugin:"); + static const auto rules = std::unordered_set{ + "dimaround", "fakefullscreen", "float", "focusonactivate", "forceinput", "forcergbx", "fullscreen", "immediate", + "keepaspectratio", "maximize", "nearestneighbor", "noanim", "noblur", "noborder", "nodim", "nofocus", + "noinitialfocus", "nomaxsize", "noshadow", "opaque", "pin", "stayfocused", "tile", "windowdance", + }; + static const auto rulesPrefix = std::vector{ + "animation", "bordercolor", "bordersize", "center", "group", "idleinhibit", "maxsize", "minsize", "monitor", "move", + "opacity", "plugin:", "pseudo", "rounding", "size", "suppressevent", "tag", "workspace", "xray", + }; + + return rules.contains(RULE) || std::any_of(rulesPrefix.begin(), rulesPrefix.end(), [&RULE](auto prefix) { return RULE.starts_with(prefix); }); } bool layerRuleValid(const std::string& RULE) { - return RULE == "noanim" || RULE == "blur" || RULE == "blurpopups" || RULE.starts_with("ignorealpha") || RULE.starts_with("ignorezero") || RULE == "dimaround" || - RULE.starts_with("xray") || RULE.starts_with("animation"); + static const auto rules = std::unordered_set{"noanim", "blur", "blurpopups", "dimaround"}; + static const auto rulesPrefix = std::vector{"ignorealpha", "ignorezero", "xray", "animation"}; + + return rules.contains(RULE) || std::any_of(rulesPrefix.begin(), rulesPrefix.end(), [&RULE](auto prefix) { return RULE.starts_with(prefix); }); } std::optional CConfigManager::handleWindowRule(const std::string& command, const std::string& value) { @@ -2067,7 +2089,7 @@ std::optional CConfigManager::handleWindowRule(const std::string& c const auto VALUE = removeBeginEndSpacesTabs(value.substr(value.find_first_of(',') + 1)); // check rule and value - if (RULE == "" || VALUE == "") + if (RULE.empty() || VALUE.empty()) return "empty rule?"; if (RULE == "unset") { @@ -2094,7 +2116,7 @@ std::optional CConfigManager::handleLayerRule(const std::string& co const auto VALUE = removeBeginEndSpacesTabs(value.substr(value.find_first_of(',') + 1)); // check rule and value - if (RULE == "" || VALUE == "") + if (RULE.empty() || VALUE.empty()) return "empty rule?"; if (RULE == "unset") { @@ -2132,6 +2154,7 @@ std::optional CConfigManager::handleWindowRuleV2(const std::string& rule.szRule = RULE; rule.szValue = VALUE; + const auto TAGPOS = VALUE.find("tag:"); const auto TITLEPOS = VALUE.find("title:"); const auto CLASSPOS = VALUE.find("class:"); const auto INITIALTITLEPOS = VALUE.find("initialTitle:"); @@ -2154,9 +2177,10 @@ std::optional CConfigManager::handleWindowRuleV2(const std::string& currentPos = VALUE.find("workspace:", currentPos + 1); } - if (TITLEPOS == std::string::npos && CLASSPOS == std::string::npos && INITIALTITLEPOS == std::string::npos && INITIALCLASSPOS == std::string::npos && - X11POS == std::string::npos && FLOATPOS == std::string::npos && FULLSCREENPOS == std::string::npos && PINNEDPOS == std::string::npos && WORKSPACEPOS == std::string::npos && - FOCUSPOS == std::string::npos && ONWORKSPACEPOS == std::string::npos) { + const auto checkPos = std::unordered_set{ + TAGPOS, TITLEPOS, CLASSPOS, INITIALTITLEPOS, INITIALCLASSPOS, X11POS, FLOATPOS, FULLSCREENPOS, PINNEDPOS, WORKSPACEPOS, FOCUSPOS, ONWORKSPACEPOS, + }; + if (checkPos.size() == 1 && checkPos.contains(std::string::npos)) { Debug::log(ERR, "Invalid rulev2 syntax: {}", VALUE); return "Invalid rulev2 syntax: " + VALUE; } @@ -2166,6 +2190,8 @@ std::optional CConfigManager::handleWindowRuleV2(const std::string& result = VALUE.substr(pos); size_t min = 999999; + if (TAGPOS > pos && TAGPOS < min) + min = TAGPOS; if (TITLEPOS > pos && TITLEPOS < min) min = TITLEPOS; if (CLASSPOS > pos && CLASSPOS < min) @@ -2199,6 +2225,9 @@ std::optional CConfigManager::handleWindowRuleV2(const std::string& return result; }; + if (TAGPOS != std::string::npos) + rule.szTag = extract(TAGPOS + 4); + if (CLASSPOS != std::string::npos) rule.szClass = extract(CLASSPOS + 6); @@ -2237,6 +2266,9 @@ std::optional CConfigManager::handleWindowRuleV2(const std::string& if (!other.v2) { return other.szClass == rule.szClass && !rule.szClass.empty(); } else { + if (!rule.szTag.empty() && rule.szTag != other.szTag) + return false; + if (!rule.szClass.empty() && rule.szClass != other.szClass) return false; diff --git a/src/debug/HyprCtl.cpp b/src/debug/HyprCtl.cpp index d19752794de..bbe5019aee3 100644 --- a/src/debug/HyprCtl.cpp +++ b/src/debug/HyprCtl.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include "../config/ConfigDataValues.hpp" #include "../config/ConfigValue.hpp" @@ -131,10 +132,9 @@ std::string monitorsRequest(eHyprCtlOutputFormat format, std::string request) { continue; result += std::format( - "Monitor {} (ID {}):\n\t{}x{}@{:.5f} at {}x{}\n\tdescription: {}\n\tmake: {}\n\tmodel: {}\n\tserial: {}\n\tactive workspace: {} ({})\n\tspecial " - "workspace: {} ({})\n\treserved: {} " - "{} {} {}\n\tscale: {:.2f}\n\ttransform: " - "{}\n\tfocused: {}\n\tdpmsStatus: {}\n\tvrr: {}\n\tactivelyTearing: {}\n\tdisabled: {}\n\tcurrentFormat: {}\n\tavailableModes: {}\n\n", + "Monitor {} (ID {}):\n\t{}x{}@{:.5f} at {}x{}\n\tdescription: {}\n\tmake: {}\n\tmodel: {}\n\tserial: {}\n\tactive workspace: {} ({})\n\t" + "special workspace: {} ({})\n\treserved: {} {} {} {}\n\tscale: {:.2f}\n\ttransform: {}\n\tfocused: {}\n\t" + "dpmsStatus: {}\n\tvrr: {}\n\tactivelyTearing: {}\n\tdisabled: {}\n\tcurrentFormat: {}\n\tavailableModes: {}\n\n", m->szName, m->ID, (int)m->vecPixelSize.x, (int)m->vecPixelSize.y, m->refreshRate, (int)m->vecPosition.x, (int)m->vecPosition.y, m->szShortDescription, (m->output->make ? m->output->make : ""), (m->output->model ? m->output->model : ""), (m->output->serial ? m->output->serial : ""), m->activeWorkspaceID(), (!m->activeWorkspace ? "" : m->activeWorkspace->m_szName), m->activeSpecialWorkspaceID(), (m->activeSpecialWorkspace ? m->activeSpecialWorkspace->m_szName : ""), @@ -147,6 +147,16 @@ std::string monitorsRequest(eHyprCtlOutputFormat format, std::string request) { return result; } +static std::string getTagsData(PHLWINDOW w, eHyprCtlOutputFormat format) { + const auto tags = w->m_tags.getTags(); + + if (format == eHyprCtlOutputFormat::FORMAT_JSON) + return std::accumulate(tags.begin(), tags.end(), std::string(), + [](const std::string& a, const std::string& b) { return a.empty() ? std::format("\"{}\"", b) : std::format("{}, \"{}\"", a, b); }); + else + return std::accumulate(tags.begin(), tags.end(), std::string(), [](const std::string& a, const std::string& b) { return a.empty() ? b : a + ", " + b; }); +} + static std::string getGroupedData(PHLWINDOW w, eHyprCtlOutputFormat format) { const bool isJson = format == eHyprCtlOutputFormat::FORMAT_JSON; if (w->m_sGroupData.pNextWindow.expired()) @@ -205,6 +215,7 @@ static std::string getWindowData(PHLWINDOW w, eHyprCtlOutputFormat format) { "fullscreenMode": {}, "fakeFullscreen": {}, "grouped": [{}], + "tags": [{}], "swallowing": "0x{:x}", "focusHistoryID": {} }},)#", @@ -214,18 +225,18 @@ static std::string getWindowData(PHLWINDOW w, eHyprCtlOutputFormat format) { 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"), (w->m_bIsFullscreen ? "true" : "false"), (w->m_bIsFullscreen ? (w->m_pWorkspace ? (int)w->m_pWorkspace->m_efFullscreenMode : 0) : 0), w->m_bFakeFullscreenState ? "true" : "false", getGroupedData(w, format), - (uintptr_t)w->m_pSwallowed.lock().get(), getFocusHistoryID(w)); + getTagsData(w, format), (uintptr_t)w->m_pSwallowed.lock().get(), getFocusHistoryID(w)); } else { return std::format("Window {:x} -> {}:\n\tmapped: {}\n\thidden: {}\n\tat: {},{}\n\tsize: {},{}\n\tworkspace: {} ({})\n\tfloating: {}\n\tmonitor: {}\n\tclass: {}\n\ttitle: " "{}\n\tinitialClass: {}\n\tinitialTitle: {}\n\tpid: " "{}\n\txwayland: {}\n\tpinned: " - "{}\n\tfullscreen: {}\n\tfullscreenmode: {}\n\tfakefullscreen: {}\n\tgrouped: {}\n\tswallowing: {:x}\n\tfocusHistoryID: {}\n\n", + "{}\n\tfullscreen: {}\n\tfullscreenmode: {}\n\tfakefullscreen: {}\n\tgrouped: {}\n\ttags: {}\n\tswallowing: {:x}\n\tfocusHistoryID: {}\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, (int64_t)w->m_iMonitorID, w->m_szClass, w->m_szTitle, w->m_szInitialClass, w->m_szInitialTitle, w->getPID(), (int)w->m_bIsX11, (int)w->m_bPinned, (int)w->m_bIsFullscreen, (w->m_bIsFullscreen ? (w->m_pWorkspace ? w->m_pWorkspace->m_efFullscreenMode : 0) : 0), (int)w->m_bFakeFullscreenState, getGroupedData(w, format), - (uintptr_t)w->m_pSwallowed.lock().get(), getFocusHistoryID(w)); + getTagsData(w, format), (uintptr_t)w->m_pSwallowed.lock().get(), getFocusHistoryID(w)); } } diff --git a/src/desktop/Window.cpp b/src/desktop/Window.cpp index 093cab5e6d9..60592452cac 100644 --- a/src/desktop/Window.cpp +++ b/src/desktop/Window.cpp @@ -1,10 +1,12 @@ +#include +#include +#include #include "Window.hpp" #include "../Compositor.hpp" #include "../render/decorations/CHyprDropShadowDecoration.hpp" #include "../render/decorations/CHyprGroupBarDecoration.hpp" #include "../render/decorations/CHyprBorderDecoration.hpp" #include "../config/ConfigValue.hpp" -#include #include "../managers/TokenManager.hpp" #include "../protocols/XDGShell.hpp" #include "../xwayland/XWayland.hpp" @@ -619,6 +621,13 @@ void CWindow::applyDynamicRule(const SWindowRule& r) { m_sAdditionalConfigData.forceTearing = true; } else if (r.szRule == "nearestneighbor") { m_sAdditionalConfigData.nearestNeighbor = true; + } else if (r.szRule.starts_with("tag")) { + CVarList vars{r.szRule, 0, 's', true}; + + if (vars.size() == 2 && vars[0] == "tag") + m_tags.applyTag(vars[1], true); + else + Debug::log(ERR, "Tag rule invalid: {}", r.szRule); } else if (r.szRule.starts_with("rounding")) { try { m_sAdditionalConfigData.rounding = std::stoi(r.szRule.substr(r.szRule.find_first_of(' ') + 1)); @@ -800,6 +809,8 @@ void CWindow::updateDynamicRules() { m_sAdditionalConfigData.nearestNeighbor = false; m_eIdleInhibitMode = IDLEINHIBIT_NONE; + m_tags.removeDynamicTags(); + m_vMatchedRules = g_pConfigManager->getMatchingRules(m_pSelf.lock()); for (auto& r : m_vMatchedRules) { applyDynamicRule(r); @@ -1370,6 +1381,7 @@ void CWindow::onUpdateState() { void CWindow::onUpdateMeta() { const auto NEWTITLE = fetchTitle(); + bool doUpdate = false; if (m_szTitle != NEWTITLE) { m_szTitle = NEWTITLE; @@ -1382,11 +1394,8 @@ void CWindow::onUpdateMeta() { EMIT_HOOK_EVENT("activeWindow", m_pSelf.lock()); } - updateDynamicRules(); - g_pCompositor->updateWindowAnimatedDecorationValues(m_pSelf.lock()); - updateToplevel(); - Debug::log(LOG, "Window {:x} set title to {}", (uintptr_t)this, m_szTitle); + doUpdate = true; } const auto NEWCLASS = fetchClass(); @@ -1399,11 +1408,14 @@ void CWindow::onUpdateMeta() { EMIT_HOOK_EVENT("activeWindow", m_pSelf.lock()); } + Debug::log(LOG, "Window {:x} set class to {}", (uintptr_t)this, m_szClass); + doUpdate = true; + } + + if (doUpdate) { updateDynamicRules(); g_pCompositor->updateWindowAnimatedDecorationValues(m_pSelf.lock()); updateToplevel(); - - Debug::log(LOG, "Window {:x} set class to {}", (uintptr_t)this, m_szClass); } } diff --git a/src/desktop/Window.hpp b/src/desktop/Window.hpp index 473ce361c95..d4e26d5d22b 100644 --- a/src/desktop/Window.hpp +++ b/src/desktop/Window.hpp @@ -1,18 +1,21 @@ #pragma once -#include "../defines.hpp" -#include "Subsurface.hpp" -#include "../helpers/AnimatedVariable.hpp" -#include "../render/decorations/IHyprWindowDecoration.hpp" #include +#include + #include "../config/ConfigDataValues.hpp" +#include "../defines.hpp" +#include "../helpers/AnimatedVariable.hpp" #include "../helpers/Vector2D.hpp" -#include "WLSurface.hpp" -#include "Popup.hpp" +#include "../helpers/signal/Signal.hpp" +#include "../helpers/TagKeeper.hpp" #include "../macros.hpp" #include "../managers/XWaylandManager.hpp" +#include "../render/decorations/IHyprWindowDecoration.hpp" #include "DesktopTypes.hpp" -#include "../helpers/signal/Signal.hpp" +#include "Popup.hpp" +#include "Subsurface.hpp" +#include "WLSurface.hpp" class CXDGSurfaceResource; class CXWaylandSurface; @@ -183,6 +186,7 @@ struct SWindowRule { std::string szClass; std::string szInitialTitle; std::string szInitialClass; + std::string szTag; int bX11 = -1; // -1 means "ANY" int bFloating = -1; int bFullscreen = -1; @@ -365,6 +369,9 @@ class CWindow { // stores the currently matched window rules std::vector m_vMatchedRules; + // window tags + CTagKeeper m_tags; + // For the list lookup bool operator==(const CWindow& rhs) { return m_pXDGSurface == rhs.m_pXDGSurface && m_pXWaylandSurface == rhs.m_pXWaylandSurface && m_vPosition == rhs.m_vPosition && m_vSize == rhs.m_vSize && diff --git a/src/helpers/TagKeeper.cpp b/src/helpers/TagKeeper.cpp new file mode 100644 index 00000000000..f960accba26 --- /dev/null +++ b/src/helpers/TagKeeper.cpp @@ -0,0 +1,40 @@ +#include "TagKeeper.hpp" + +bool CTagKeeper::isTagged(const std::string& tag, bool strict) { + return m_tags.contains(tag) || (!strict && m_tags.contains(tag + "*")); +} + +bool CTagKeeper::applyTag(const std::string& tag, bool dynamic) { + + std::string tagReal = tag; + + if (dynamic && !tag.ends_with("*")) + tagReal += "*"; + + bool changed = true; + bool setTag = true; + + if (tagReal.starts_with("-")) { // unset + tagReal = tagReal.substr(1); + changed = isTagged(tagReal, true); + setTag = false; + } else if (tagReal.starts_with("+")) { // set + tagReal = tagReal.substr(1); + changed = !isTagged(tagReal, true); + } else // toggle if without prefix + setTag = !isTagged(tagReal, true); + + if (!changed) + return false; + + if (setTag) + m_tags.emplace(tagReal); + else + m_tags.erase(tagReal); + + return true; +} + +bool CTagKeeper::removeDynamicTags() { + return std::erase_if(m_tags, [](const auto& tag) { return tag.ends_with("*"); }); +} diff --git a/src/helpers/TagKeeper.hpp b/src/helpers/TagKeeper.hpp new file mode 100644 index 00000000000..deeabf48586 --- /dev/null +++ b/src/helpers/TagKeeper.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +class CTagKeeper { + public: + bool isTagged(const std::string& tag, bool strict = false); + bool applyTag(const std::string& tag, bool dynamic = false); + bool removeDynamicTags(); + + inline const auto& getTags() { + return m_tags; + }; + + private: + std::set m_tags; +}; diff --git a/src/managers/KeybindManager.cpp b/src/managers/KeybindManager.cpp index 41b82d1092c..4f89315d668 100644 --- a/src/managers/KeybindManager.cpp +++ b/src/managers/KeybindManager.cpp @@ -1,17 +1,16 @@ -#include "KeybindManager.hpp" -#include "../render/decorations/CHyprGroupBarDecoration.hpp" -#include "debug/Log.hpp" -#include "helpers/VarList.hpp" #include "../config/ConfigValue.hpp" -#include "TokenManager.hpp" -#include "../protocols/ShortcutsInhibit.hpp" #include "../devices/IKeyboard.hpp" #include "../managers/SeatManager.hpp" +#include "../protocols/ShortcutsInhibit.hpp" +#include "../render/decorations/CHyprGroupBarDecoration.hpp" +#include "KeybindManager.hpp" +#include "TokenManager.hpp" +#include "debug/Log.hpp" +#include "helpers/VarList.hpp" #include -#include #include -#include +#include #include #include @@ -87,6 +86,7 @@ CKeybindManager::CKeybindManager() { m_mDispatchers["cyclenext"] = circleNext; m_mDispatchers["focuswindowbyclass"] = focusWindow; m_mDispatchers["focuswindow"] = focusWindow; + m_mDispatchers["tagwindow"] = tagWindow; m_mDispatchers["submap"] = setSubmap; m_mDispatchers["pass"] = pass; m_mDispatchers["sendshortcut"] = sendshortcut; @@ -1928,6 +1928,23 @@ void CKeybindManager::focusWindow(std::string regexp) { g_pCompositor->warpCursorTo(PWINDOW->middle()); } +void CKeybindManager::tagWindow(std::string args) { + PHLWINDOW PWINDOW = nullptr; + CVarList vars{args, 0, 's', true}; + + if (vars.size() == 1) + PWINDOW = g_pCompositor->m_pLastWindow.lock(); + else if (vars.size() == 2) + PWINDOW = g_pCompositor->getWindowByRegex(vars[1]); + else + return; + + if (PWINDOW && PWINDOW->m_tags.applyTag(vars[0])) { + PWINDOW->updateDynamicRules(); + g_pCompositor->updateWindowAnimatedDecorationValues(PWINDOW->m_pSelf.lock()); + } +} + void CKeybindManager::setSubmap(std::string submap) { if (submap == "reset" || submap == "") { m_szCurrentSelectedSubmap = ""; diff --git a/src/managers/KeybindManager.hpp b/src/managers/KeybindManager.hpp index a5499987283..305e563fceb 100644 --- a/src/managers/KeybindManager.hpp +++ b/src/managers/KeybindManager.hpp @@ -181,6 +181,7 @@ class CKeybindManager { static void resizeWindow(std::string); static void circleNext(std::string); static void focusWindow(std::string); + static void tagWindow(std::string); static void setSubmap(std::string); static void pass(std::string); static void sendshortcut(std::string);