diff --git a/src/Compositor.cpp b/src/Compositor.cpp index 42ba9b5f..3ff67f20 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -1290,10 +1290,10 @@ void CCompositor::sanityCheckWorkspaces() { } } -int CCompositor::getWindowsOnWorkspace(const int& id) { +int CCompositor::getWindowsOnWorkspace(const int& id, std::optional onlyTiled) { int no = 0; for (auto& w : m_vWindows) { - if (w->m_iWorkspaceID == id && w->m_bIsMapped) + if (w->m_iWorkspaceID == id && w->m_bIsMapped && !(onlyTiled.has_value() && !w->m_bIsFloating != onlyTiled.value())) no++; } diff --git a/src/Compositor.hpp b/src/Compositor.hpp index 84667656..56b83774 100644 --- a/src/Compositor.hpp +++ b/src/Compositor.hpp @@ -149,7 +149,7 @@ class CCompositor { CWorkspace* getWorkspaceByString(const std::string&); void sanityCheckWorkspaces(); void updateWorkspaceWindowDecos(const int&); - int getWindowsOnWorkspace(const int&); + int getWindowsOnWorkspace(const int& id, std::optional onlyTiled = {}); CWindow* getUrgentWindow(); bool hasUrgentWindowOnWorkspace(const int&); CWindow* getFirstWindowOnWorkspace(const int&); diff --git a/src/Window.hpp b/src/Window.hpp index 8350e197..d98bc921 100644 --- a/src/Window.hpp +++ b/src/Window.hpp @@ -175,13 +175,13 @@ struct SWindowRule { std::string szClass; std::string szInitialTitle; std::string szInitialClass; - int bX11 = -1; // -1 means "ANY" - int bFloating = -1; - int bFullscreen = -1; - int bPinned = -1; - int bFocus = -1; - int iOnWorkspace = -1; - std::string szWorkspace = ""; // empty means any + int bX11 = -1; // -1 means "ANY" + int bFloating = -1; + int bFullscreen = -1; + int bPinned = -1; + int bFocus = -1; + std::string szOnWorkspace = ""; // empty means any + std::string szWorkspace = ""; // empty means any }; class CWindow { diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index ecb0937f..ee7b90bf 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -937,13 +937,7 @@ SMonitorRule CConfigManager::getMonitorRuleFor(const CMonitor& PMONITOR) { } SWorkspaceRule CConfigManager::getWorkspaceRuleFor(CWorkspace* pWorkspace) { - const auto WORKSPACEIDSTR = std::to_string(pWorkspace->m_iID); - const auto IT = std::find_if(m_dWorkspaceRules.begin(), m_dWorkspaceRules.end(), [&](const auto& other) { - return other.workspaceName == pWorkspace->m_szName /* name matches */ - || (pWorkspace->m_bIsSpecialWorkspace && other.workspaceName.starts_with("special:") && - other.workspaceName.substr(8) == pWorkspace->m_szName) /* special and special:name */ - || (pWorkspace->m_iID > 0 && WORKSPACEIDSTR == other.workspaceName); /* id matches and workspace is numerical */ - }); + const auto IT = std::find_if(m_dWorkspaceRules.begin(), m_dWorkspaceRules.end(), [&](const auto& other) { return pWorkspace->matchesStaticSelector(other.workspaceString); }); if (IT == m_dWorkspaceRules.end()) return SWorkspaceRule{}; return *IT; @@ -1039,8 +1033,9 @@ std::vector CConfigManager::getMatchingRules(CWindow* pWindow, bool continue; } - if (rule.iOnWorkspace != -1) { - if (rule.iOnWorkspace != g_pCompositor->getWindowsOnWorkspace(pWindow->m_iWorkspaceID)) + if (!rule.szOnWorkspace.empty()) { + const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(pWindow->m_iWorkspaceID); + if (!PWORKSPACE || !PWORKSPACE->matchesStaticSelector(rule.szOnWorkspace)) continue; } @@ -1327,9 +1322,8 @@ std::string CConfigManager::getBoundMonitorStringForWS(const std::string& wsname for (auto& wr : m_dWorkspaceRules) { const auto WSNAME = wr.workspaceName.starts_with("name:") ? wr.workspaceName.substr(5) : wr.workspaceName; - if (WSNAME == wsname) { + if (WSNAME == wsname) return wr.monitor; - } } return ""; @@ -2065,7 +2059,7 @@ std::optional CConfigManager::handleWindowRuleV2(const std::string& rule.bFocus = extract(FOCUSPOS + 6) == "1" ? 1 : 0; if (ONWORKSPACEPOS != std::string::npos) - rule.iOnWorkspace = configStringToInt(extract(ONWORKSPACEPOS + 12)); + rule.szOnWorkspace = extract(ONWORKSPACEPOS + 12); if (RULE == "unset") { std::erase_if(m_dWindowRules, [&](const SWindowRule& other) { @@ -2102,7 +2096,7 @@ std::optional CConfigManager::handleWindowRuleV2(const std::string& if (rule.bFocus != -1 && rule.bFocus != other.bFocus) return false; - if (rule.iOnWorkspace != -1 && rule.iOnWorkspace != other.iOnWorkspace) + if (!rule.szOnWorkspace.empty() && rule.szOnWorkspace != other.szOnWorkspace) return false; return true; @@ -2165,21 +2159,21 @@ std::optional CConfigManager::handleWorkspaceRules(const std::strin auto rules = value.substr(FIRST_DELIM + 1); SWorkspaceRule wsRule; wsRule.workspaceString = first_ident; - if (id == WORKSPACE_INVALID) { - // it could be the monitor. If so, second value MUST be - // the workspace. - const auto WORKSPACE_DELIM = value.find_first_of(',', FIRST_DELIM + 1); - auto wsIdent = removeBeginEndSpacesTabs(value.substr(FIRST_DELIM + 1, (WORKSPACE_DELIM - FIRST_DELIM - 1))); - id = getWorkspaceIDFromString(wsIdent, name); - if (id == WORKSPACE_INVALID) { - Debug::log(ERR, "Invalid workspace identifier found: {}", wsIdent); - return "Invalid workspace identifier found: " + wsIdent; - } - wsRule.monitor = first_ident; - wsRule.workspaceString = wsIdent; - wsRule.isDefault = true; // backwards compat - rules = value.substr(WORKSPACE_DELIM + 1); - } + // if (id == WORKSPACE_INVALID) { + // // it could be the monitor. If so, second value MUST be + // // the workspace. + // const auto WORKSPACE_DELIM = value.find_first_of(',', FIRST_DELIM + 1); + // auto wsIdent = removeBeginEndSpacesTabs(value.substr(FIRST_DELIM + 1, (WORKSPACE_DELIM - FIRST_DELIM - 1))); + // id = getWorkspaceIDFromString(wsIdent, name); + // if (id == WORKSPACE_INVALID) { + // Debug::log(ERR, "Invalid workspace identifier found: {}", wsIdent); + // return "Invalid workspace identifier found: " + wsIdent; + // } + // wsRule.monitor = first_ident; + // wsRule.workspaceString = wsIdent; + // wsRule.isDefault = true; // backwards compat + // rules = value.substr(WORKSPACE_DELIM + 1); + // } const static std::string ruleOnCreatedEmtpy = "on-created-empty:"; const static int ruleOnCreatedEmtpyLen = ruleOnCreatedEmtpy.length(); diff --git a/src/desktop/Workspace.cpp b/src/desktop/Workspace.cpp index 23f76309..42d59dbb 100644 --- a/src/desktop/Workspace.cpp +++ b/src/desktop/Workspace.cpp @@ -182,3 +182,205 @@ std::string CWorkspace::getConfigName() { return "name:" + m_szName; } + +bool CWorkspace::matchesStaticSelector(const std::string& selector_) { + auto selector = removeBeginEndSpacesTabs(selector_); + + if (selector.empty()) + return true; + + if (isNumber(selector)) { + + std::string wsname = ""; + int wsid = getWorkspaceIDFromString(selector, wsname); + + if (wsid == WORKSPACE_INVALID) + return false; + + return wsid == m_iID; + + } else if (selector.starts_with("name:")) { + return m_szName == selector.substr(5); + } else { + // parse selector + + for (size_t i = 0; i < selector.length(); ++i) { + const char& cur = selector[i]; + if (std::isspace(cur)) + continue; + + // Allowed selectors: + // r - range: r[1-5] + // s - special: s[true] + // n - named: n[true] or n[s:string] or n[e:string] + // m - monitor: m[monitor_selector] + // w - windowCount: w[0-4] or w[1], optional flag t or f for tiled or floating, e.g. w[t0-1] + + const auto NEXTSPACE = selector.find_first_of(' ', i); + std::string prop = selector.substr(i, NEXTSPACE == std::string::npos ? std::string::npos : NEXTSPACE - i); + i = NEXTSPACE; + + if (cur == 'r') { + int from = 0, to = 0; + if (!prop.starts_with("r[") || !prop.ends_with("]")) { + Debug::log(LOG, "Invalid selector {}", selector); + return false; + } + + prop = prop.substr(2, prop.length() - 3); + + if (!prop.contains("-")) { + Debug::log(LOG, "Invalid selector {}", selector); + return false; + } + + const auto DASHPOS = prop.find("-"); + const auto LHS = prop.substr(0, DASHPOS), RHS = prop.substr(DASHPOS + 1); + + if (!isNumber(LHS) || !isNumber(RHS)) { + Debug::log(LOG, "Invalid selector {}", selector); + return false; + } + + try { + from = std::stoll(LHS); + to = std::stoll(RHS); + } catch (std::exception& e) { + Debug::log(LOG, "Invalid selector {}", selector); + return false; + } + + if (to < from || to < 1 || from < 1) { + Debug::log(LOG, "Invalid selector {}", selector); + return false; + } + + if (std::clamp(m_iID, from, to) != m_iID) + return false; + continue; + } + + if (cur == 's') { + if (!prop.starts_with("s[") || !prop.ends_with("]")) { + Debug::log(LOG, "Invalid selector {}", selector); + return false; + } + + prop = prop.substr(2, prop.length() - 3); + + const auto SHOULDBESPECIAL = configStringToInt(prop); + + if ((bool)SHOULDBESPECIAL != m_bIsSpecialWorkspace) + return false; + continue; + } + + if (cur == 'm') { + if (!prop.starts_with("m[") || !prop.ends_with("]")) { + Debug::log(LOG, "Invalid selector {}", selector); + return false; + } + + prop = prop.substr(2, prop.length() - 3); + + const auto PMONITOR = g_pCompositor->getMonitorFromString(prop); + + if (!(PMONITOR ? PMONITOR->ID == m_iMonitorID : false)) + return false; + continue; + } + + if (cur == 'n') { + if (!prop.starts_with("n[") || !prop.ends_with("]")) { + Debug::log(LOG, "Invalid selector {}", selector); + return false; + } + + prop = prop.substr(2, prop.length() - 3); + + if (prop.starts_with("s:")) + return m_szName.starts_with(prop.substr(2)); + if (prop.starts_with("e:")) + return m_szName.ends_with(prop.substr(2)); + + const auto WANTSNAMED = configStringToInt(prop); + + if (WANTSNAMED != (m_iID <= -1337)) + return false; + continue; + } + + if (cur == 'w') { + int from = 0, to = 0; + if (!prop.starts_with("w[") || !prop.ends_with("]")) { + Debug::log(LOG, "Invalid selector {}", selector); + return false; + } + + prop = prop.substr(2, prop.length() - 3); + + int wantsOnlyTiled = -1; + + if (prop.starts_with("t")) { + wantsOnlyTiled = 1; + prop = prop.substr(1); + } else if (prop.starts_with("f")) { + wantsOnlyTiled = 0; + prop = prop.substr(1); + } + + if (!prop.contains("-")) { + // try single + + if (!isNumber(prop)) { + Debug::log(LOG, "Invalid selector {}", selector); + return false; + } + + try { + from = std::stoll(prop); + } catch (std::exception& e) { + Debug::log(LOG, "Invalid selector {}", selector); + return false; + } + + return g_pCompositor->getWindowsOnWorkspace(m_iID, wantsOnlyTiled == -1 ? std::nullopt : std::optional((bool)wantsOnlyTiled)) == from; + } + + const auto DASHPOS = prop.find("-"); + const auto LHS = prop.substr(0, DASHPOS), RHS = prop.substr(DASHPOS + 1); + + if (!isNumber(LHS) || !isNumber(RHS)) { + Debug::log(LOG, "Invalid selector {}", selector); + return false; + } + + try { + from = std::stoll(LHS); + to = std::stoll(RHS); + } catch (std::exception& e) { + Debug::log(LOG, "Invalid selector {}", selector); + return false; + } + + if (to < from || to < 1 || from < 1) { + Debug::log(LOG, "Invalid selector {}", selector); + return false; + } + + const auto WINDOWSONWORKSPACE = g_pCompositor->getWindowsOnWorkspace(m_iID, wantsOnlyTiled == -1 ? std::nullopt : std::optional((bool)wantsOnlyTiled)); + if (std::clamp(WINDOWSONWORKSPACE, from, to) != WINDOWSONWORKSPACE) + return false; + continue; + } + + Debug::log(LOG, "Invalid selector {}", selector); + return false; + } + + return true; + } + + UNREACHABLE(); + return false; +} \ No newline at end of file diff --git a/src/desktop/Workspace.hpp b/src/desktop/Workspace.hpp index 15b7c0e2..f1bdb333 100644 --- a/src/desktop/Workspace.hpp +++ b/src/desktop/Workspace.hpp @@ -64,4 +64,6 @@ class CWorkspace { void rememberPrevWorkspace(const CWorkspace* prevWorkspace); std::string getConfigName(); + + bool matchesStaticSelector(const std::string& selector); }; diff --git a/src/managers/KeybindManager.cpp b/src/managers/KeybindManager.cpp index 4b406cb6..8a1d022b 100644 --- a/src/managers/KeybindManager.cpp +++ b/src/managers/KeybindManager.cpp @@ -871,10 +871,12 @@ void toggleActiveFloatingCore(std::string args, std::optional floatState) curr->updateSpecialRenderData(); curr = curr->m_sGroupData.pNextWindow; } + + g_pCompositor->updateWorkspaceWindows(PWINDOW->m_iWorkspaceID); } else { PWINDOW->m_bIsFloating = !PWINDOW->m_bIsFloating; - PWINDOW->updateDynamicRules(); + g_pCompositor->updateWorkspaceWindows(PWINDOW->m_iWorkspaceID); g_pLayoutManager->getCurrentLayout()->changeWindowFloatingMode(PWINDOW); }