From 8855934511cd4f14db117cca62b9ded3e51c4daf Mon Sep 17 00:00:00 2001 From: Natrix Date: Fri, 10 Jan 2025 18:04:46 +0100 Subject: [PATCH 1/3] feat:Added dwindle layoutmsg handler for specifying the opening node of a new tile --- src/layout/DwindleLayout.cpp | 67 +++++++++++++++++++++++++++++++++++- src/layout/DwindleLayout.hpp | 1 + 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/src/layout/DwindleLayout.cpp b/src/layout/DwindleLayout.cpp index 2ee41eba9f6..511baacd51e 100644 --- a/src/layout/DwindleLayout.cpp +++ b/src/layout/DwindleLayout.cpp @@ -244,13 +244,18 @@ void CHyprDwindleLayout::onWindowCreatedTiling(PHLWINDOW pWindow, eDirection dir const auto MOUSECOORDS = m_vOverrideFocalPoint.value_or(g_pInputManager->getMouseCoordsInternal()); const auto MONFROMCURSOR = g_pCompositor->getMonitorFromVector(MOUSECOORDS); - if (PMONITOR->ID == MONFROMCURSOR->ID && + if (m_pOpenNextOn && m_pOpenNextOn->valid && m_pOpenNextOn->workspaceID == pWindow->workspaceID()) { + OPENINGON = m_pOpenNextOn; + m_pOpenNextOn = nullptr; + + } else if (PMONITOR->ID == MONFROMCURSOR->ID && (PNODE->workspaceID == PMONITOR->activeWorkspaceID() || (g_pCompositor->isWorkspaceSpecial(PNODE->workspaceID) && PMONITOR->activeSpecialWorkspace)) && !*PUSEACTIVE) { OPENINGON = getNodeFromWindow(g_pCompositor->vectorToWindowUnified(MOUSECOORDS, RESERVED_EXTENTS | INPUT_EXTENTS)); if (!OPENINGON && g_pCompositor->isPointOnReservedArea(MOUSECOORDS, PMONITOR)) OPENINGON = getClosestNodeOnWorkspace(PNODE->workspaceID, MOUSECOORDS); + } else if (*PUSEACTIVE) { if (g_pCompositor->m_pLastWindow.lock() && !g_pCompositor->m_pLastWindow->m_bIsFloating && g_pCompositor->m_pLastWindow.lock() != pWindow && g_pCompositor->m_pLastWindow->m_pWorkspace == pWindow->m_pWorkspace && g_pCompositor->m_pLastWindow->m_bIsMapped) { @@ -455,6 +460,7 @@ void CHyprDwindleLayout::onWindowRemovedTiling(PHLWINDOW pWindow) { if (!PPARENT) { Debug::log(LOG, "Removing last node (dwindle)"); m_lDwindleNodesData.remove(*PNODE); + m_pOpenNextOn = nullptr; return; } @@ -479,6 +485,9 @@ void CHyprDwindleLayout::onWindowRemovedTiling(PHLWINDOW pWindow) { else PSIBLING->recalcSizePosRecursive(); + if (PPARENT == m_pOpenNextOn || PNODE == m_pOpenNextOn) + m_pOpenNextOn = nullptr; + m_lDwindleNodesData.remove(*PPARENT); m_lDwindleNodesData.remove(*PNODE); } @@ -968,6 +977,61 @@ std::any CHyprDwindleLayout::layoutMessage(SLayoutMessageHeader header, std::str break; } } + } else if (ARGS[0] == "opennexton") { + const auto RELATION = ARGS[1]; + const auto WINDOW = ARGS[2].empty() ? header.pWindow : g_pCompositor->getWindowByRegex(ARGS[2]); + auto pNode = getNodeFromWindow(WINDOW); + for (const auto c : RELATION) { + if (!pNode || !pNode->valid) break; + + switch (c) { + case '^': { + // Step to the parent of the current node + pNode = pNode->pParent; + break; + } + + case 'c': { + // Clear anything previously set + m_pOpenNextOn = nullptr; + return ""; + } + + case '.': { + // Steps nowhere (if you simply want to use the second argument) + break; + } + + case '0': { + // Step to the first child + pNode = pNode->children[0]; + break; + } + + case '1': { + // Step to the second child + pNode = pNode->children[1]; + break; + } + + case '/': { + // Step to the root of the current node's workspace + pNode = getMasterNodeOnWorkspace(pNode->workspaceID); + break; + } + + default: { + Debug::log(ERR, "Unknown relation operator"); + return ""; + } + } + } + + if (pNode && pNode->valid) + m_pOpenNextOn = pNode; + else + Debug::log(ERR, "Invalid dwindle node"); + } return ""; @@ -1065,6 +1129,7 @@ void CHyprDwindleLayout::onEnable() { void CHyprDwindleLayout::onDisable() { m_lDwindleNodesData.clear(); + m_pOpenNextOn = nullptr; } Vector2D CHyprDwindleLayout::predictSizeForNewWindowTiled() { diff --git a/src/layout/DwindleLayout.hpp b/src/layout/DwindleLayout.hpp index be47e724dad..aa9ffa781b8 100644 --- a/src/layout/DwindleLayout.hpp +++ b/src/layout/DwindleLayout.hpp @@ -89,6 +89,7 @@ class CHyprDwindleLayout : public IHyprLayout { void moveToRoot(PHLWINDOW, bool stable = true); eDirection overrideDirection = DIRECTION_DEFAULT; + SDwindleNodeData* m_pOpenNextOn = nullptr; friend struct SDwindleNodeData; }; From 6d0164bc65ec64893e626a0ec5122f18be6fe287 Mon Sep 17 00:00:00 2001 From: Natrix Date: Mon, 13 Jan 2025 19:02:45 +0100 Subject: [PATCH 2/3] feat:Added a hyprctl socket command which forwards to a layout manager and allows for returning a response --- src/debug/HyprCtl.cpp | 12 ++++ src/layout/DwindleLayout.cpp | 107 +++++++++++++++++++++++++++++++++++ src/layout/DwindleLayout.hpp | 1 + src/layout/IHyprLayout.cpp | 7 +++ src/layout/IHyprLayout.hpp | 5 ++ 5 files changed, 132 insertions(+) diff --git a/src/debug/HyprCtl.cpp b/src/debug/HyprCtl.cpp index 7edace21406..e1d33bce0ce 100644 --- a/src/debug/HyprCtl.cpp +++ b/src/debug/HyprCtl.cpp @@ -534,6 +534,17 @@ static std::string layoutsRequest(eHyprCtlOutputFormat format, std::string reque return result; } +static std::string layoutDataRequest(eHyprCtlOutputFormat format, std::string request) { + const auto CURRENT_LAYOUT = g_pLayoutManager->getCurrentLayout(); + + if (!CURRENT_LAYOUT) { + Debug::log(ERR, "No current layout?!!"); + return ""; + } + + return CURRENT_LAYOUT->layoutDataRequest(format, request); +} + static std::string configErrorsRequest(eHyprCtlOutputFormat format, std::string request) { std::string result = ""; std::string currErrors = g_pConfigManager->getErrors(); @@ -1648,6 +1659,7 @@ CHyprCtl::CHyprCtl() { registerCommand(SHyprCtlCommand{"descriptions", true, getDescriptions}); registerCommand(SHyprCtlCommand{"submap", true, submapRequest}); + registerCommand(SHyprCtlCommand{"layoutdata", false, layoutDataRequest}); registerCommand(SHyprCtlCommand{"monitors", false, monitorsRequest}); registerCommand(SHyprCtlCommand{"reload", false, reloadRequest}); registerCommand(SHyprCtlCommand{"plugin", false, dispatchPlugin}); diff --git a/src/layout/DwindleLayout.cpp b/src/layout/DwindleLayout.cpp index 511baacd51e..cbf161a78ff 100644 --- a/src/layout/DwindleLayout.cpp +++ b/src/layout/DwindleLayout.cpp @@ -2,6 +2,7 @@ #include "../Compositor.hpp" #include "../config/ConfigValue.hpp" #include "../render/decorations/CHyprGroupBarDecoration.hpp" +#include void SDwindleNodeData::recalcSizePosRecursive(bool force, bool horizontalOverride, bool verticalOverride) { if (children[0]) { @@ -1037,6 +1038,112 @@ std::any CHyprDwindleLayout::layoutMessage(SLayoutMessageHeader header, std::str return ""; } +static bool printNodeTree(const SDwindleNodeData * const node, eHyprCtlOutputFormat format, std::string& indent, std::string& out) { + if (!node || !node->valid) { + Debug::log(ERR, "Invalid dwindle Node"); + return false; + } + + const auto INDENTLVL = indent.length(); + indent += format == eHyprCtlOutputFormat::FORMAT_JSON ? " " : "\t"; + + if (node->children[0] && node->children[0]->valid) { + if (format == eHyprCtlOutputFormat::FORMAT_JSON) + out += std::format("\n{}\"{}\": {{", indent, (node->splitTop ? "top" : "left")); + else + out += std::format("\n{}{}:", indent, (node->splitTop ? "top" : "left")); + + if (!printNodeTree(node->children[0], format, indent, out)) { + indent.erase(INDENTLVL); + return false; + } + + if (format == eHyprCtlOutputFormat::FORMAT_JSON) + out += std::format("\n{}}},", indent); + + if (node->children[1] && node->children[1]->valid) { + if (format == eHyprCtlOutputFormat::FORMAT_JSON) + out += std::format("\n{}\"{}\": {{", indent, (node->splitTop ? "bottom" : "right")); + else + out += std::format("\n{}{}:", indent, (node->splitTop ? "bottom" : "right")); + + if (!printNodeTree(node->children[1], format, indent, out)) { + indent.erase(INDENTLVL); + return false; + } + + if (format == eHyprCtlOutputFormat::FORMAT_JSON) + out += std::format("\n{}}},", indent); + } + + if (format == eHyprCtlOutputFormat::FORMAT_JSON) + out += std::format("\n{}\"splitRatio\": {}", indent, node->splitRatio); + else + out += std::format("\n{}splitRatio: {}", indent, node->splitRatio); + + } else if (node->pWindow) { + if (format == eHyprCtlOutputFormat::FORMAT_JSON) + out += std::format( + "\n{}\"address\": \"0x{:x}\"," + "\n{}\"class\": \"{}\"," + "\n{}\"title\": \"{}\"," + "\n{}\"initialClass\": \"{}\"," + "\n{}\"initialTitle\": \"{}\"," + "\n{}\"pid\": \"{}\"", + indent, (uintptr_t)node->pWindow.get(), + indent, escapeJSONStrings(node->pWindow->m_szClass), + indent, escapeJSONStrings(node->pWindow->m_szTitle), + indent, escapeJSONStrings(node->pWindow->m_szInitialClass), + indent, escapeJSONStrings(node->pWindow->m_szInitialTitle), + indent, node->pWindow->getPID()); + else + out += std::format( + "\n{}address: 0x{:x}" + "\n{}class: {}" + "\n{}title: {}" + "\n{}initialClass: {}" + "\n{}initialTitle: {}" + "\n{}pid: {}", + indent, (uintptr_t)node->pWindow.get(), + indent, escapeJSONStrings(node->pWindow->m_szClass), + indent, escapeJSONStrings(node->pWindow->m_szTitle), + indent, escapeJSONStrings(node->pWindow->m_szInitialClass), + indent, escapeJSONStrings(node->pWindow->m_szInitialTitle), + indent, node->pWindow->getPID()); + } + + indent.erase(INDENTLVL); + return true; +} + +std::string CHyprDwindleLayout::layoutDataRequest(eHyprCtlOutputFormat format, std::string request) { + const auto ARGS = CVarList(request, 0, ' '); + std::string result = ""; + + if (ARGS[1] == "workspaceinfo") { + const auto& [WORKSPACEID, workspaceName] = getWorkspaceIDNameFromString(ARGS[2]); + if (WORKSPACEID == WORKSPACE_INVALID) { + Debug::log(ERR, "Invalid workspace in layoutdata workspaceinfo"); + } + + auto PHEAD_NODE = getMasterNodeOnWorkspace(WORKSPACEID); + std::string indent = ""; + + if (format == eHyprCtlOutputFormat::FORMAT_JSON) + result += "{"; + else + result += "root:"; + printNodeTree(PHEAD_NODE, format, indent, result); + if (format == eHyprCtlOutputFormat::FORMAT_JSON) result += "\n}"; + + } else { + Debug::log(LOG, "Unknown layoutdata request"); + } + + return result; +} + + void CHyprDwindleLayout::toggleSplit(PHLWINDOW pWindow) { const auto PNODE = getNodeFromWindow(pWindow); diff --git a/src/layout/DwindleLayout.hpp b/src/layout/DwindleLayout.hpp index aa9ffa781b8..18d39e967db 100644 --- a/src/layout/DwindleLayout.hpp +++ b/src/layout/DwindleLayout.hpp @@ -53,6 +53,7 @@ class CHyprDwindleLayout : public IHyprLayout { virtual void resizeActiveWindow(const Vector2D&, eRectCorner corner = CORNER_NONE, PHLWINDOW pWindow = nullptr); virtual void fullscreenRequestForWindow(PHLWINDOW pWindow, const eFullscreenMode CURRENT_EFFECTIVE_MODE, const eFullscreenMode EFFECTIVE_MODE); virtual std::any layoutMessage(SLayoutMessageHeader, std::string); + virtual std::string layoutDataRequest(eHyprCtlOutputFormat format, std::string request); virtual SWindowRenderLayoutHints requestRenderHints(PHLWINDOW); virtual void switchWindows(PHLWINDOW, PHLWINDOW); virtual void moveWindowTo(PHLWINDOW, const std::string& dir, bool silent); diff --git a/src/layout/IHyprLayout.cpp b/src/layout/IHyprLayout.cpp index eeebd81520f..7dedca326f2 100644 --- a/src/layout/IHyprLayout.cpp +++ b/src/layout/IHyprLayout.cpp @@ -945,4 +945,11 @@ Vector2D IHyprLayout::predictSizeForNewWindow(PHLWINDOW pWindow) { return sizePredicted; } +std::string IHyprLayout::layoutDataRequest(eHyprCtlOutputFormat format, std::string request) { + if (format == eHyprCtlOutputFormat::FORMAT_JSON) + return "\"Not implemented for the current layout\""; + else + return "Not implemented for the current layout"; +} + IHyprLayout::~IHyprLayout() = default; diff --git a/src/layout/IHyprLayout.hpp b/src/layout/IHyprLayout.hpp index ab188b9b99c..5b8ca96f7e2 100644 --- a/src/layout/IHyprLayout.hpp +++ b/src/layout/IHyprLayout.hpp @@ -204,6 +204,11 @@ class IHyprLayout { virtual Vector2D predictSizeForNewWindow(PHLWINDOW pWindow); virtual Vector2D predictSizeForNewWindowFloating(PHLWINDOW pWindow); + /* + Like 'dispatch layoutmsg' but with possible return values for use with hyprctl. + */ + virtual std::string layoutDataRequest(eHyprCtlOutputFormat format, std::string request); + private: int m_iMouseMoveEventCount; Vector2D m_vBeginDragXY; From 802dc56979ab87708b4808c2739b3b3f32dba13b Mon Sep 17 00:00:00 2001 From: Natrix Date: Mon, 13 Jan 2025 19:09:58 +0100 Subject: [PATCH 3/3] fix:Format the dwindle layout changes --- src/layout/DwindleLayout.cpp | 70 ++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 39 deletions(-) diff --git a/src/layout/DwindleLayout.cpp b/src/layout/DwindleLayout.cpp index cbf161a78ff..4034bd39abb 100644 --- a/src/layout/DwindleLayout.cpp +++ b/src/layout/DwindleLayout.cpp @@ -246,17 +246,17 @@ void CHyprDwindleLayout::onWindowCreatedTiling(PHLWINDOW pWindow, eDirection dir const auto MONFROMCURSOR = g_pCompositor->getMonitorFromVector(MOUSECOORDS); if (m_pOpenNextOn && m_pOpenNextOn->valid && m_pOpenNextOn->workspaceID == pWindow->workspaceID()) { - OPENINGON = m_pOpenNextOn; + OPENINGON = m_pOpenNextOn; m_pOpenNextOn = nullptr; } else if (PMONITOR->ID == MONFROMCURSOR->ID && - (PNODE->workspaceID == PMONITOR->activeWorkspaceID() || (g_pCompositor->isWorkspaceSpecial(PNODE->workspaceID) && PMONITOR->activeSpecialWorkspace)) && !*PUSEACTIVE) { + (PNODE->workspaceID == PMONITOR->activeWorkspaceID() || (g_pCompositor->isWorkspaceSpecial(PNODE->workspaceID) && PMONITOR->activeSpecialWorkspace)) && + !*PUSEACTIVE) { OPENINGON = getNodeFromWindow(g_pCompositor->vectorToWindowUnified(MOUSECOORDS, RESERVED_EXTENTS | INPUT_EXTENTS)); if (!OPENINGON && g_pCompositor->isPointOnReservedArea(MOUSECOORDS, PMONITOR)) OPENINGON = getClosestNodeOnWorkspace(PNODE->workspaceID, MOUSECOORDS); - } else if (*PUSEACTIVE) { if (g_pCompositor->m_pLastWindow.lock() && !g_pCompositor->m_pLastWindow->m_bIsFloating && g_pCompositor->m_pLastWindow.lock() != pWindow && g_pCompositor->m_pLastWindow->m_pWorkspace == pWindow->m_pWorkspace && g_pCompositor->m_pLastWindow->m_bIsMapped) { @@ -980,10 +980,11 @@ std::any CHyprDwindleLayout::layoutMessage(SLayoutMessageHeader header, std::str } } else if (ARGS[0] == "opennexton") { const auto RELATION = ARGS[1]; - const auto WINDOW = ARGS[2].empty() ? header.pWindow : g_pCompositor->getWindowByRegex(ARGS[2]); - auto pNode = getNodeFromWindow(WINDOW); + const auto WINDOW = ARGS[2].empty() ? header.pWindow : g_pCompositor->getWindowByRegex(ARGS[2]); + auto pNode = getNodeFromWindow(WINDOW); for (const auto c : RELATION) { - if (!pNode || !pNode->valid) break; + if (!pNode || !pNode->valid) + break; switch (c) { case '^': { @@ -1032,13 +1033,12 @@ std::any CHyprDwindleLayout::layoutMessage(SLayoutMessageHeader header, std::str m_pOpenNextOn = pNode; else Debug::log(ERR, "Invalid dwindle node"); - } return ""; } -static bool printNodeTree(const SDwindleNodeData * const node, eHyprCtlOutputFormat format, std::string& indent, std::string& out) { +static bool printNodeTree(const SDwindleNodeData* const node, eHyprCtlOutputFormat format, std::string& indent, std::string& out) { if (!node || !node->valid) { Debug::log(ERR, "Invalid dwindle Node"); return false; @@ -1083,33 +1083,25 @@ static bool printNodeTree(const SDwindleNodeData * const node, eHyprCtlOutputFor } else if (node->pWindow) { if (format == eHyprCtlOutputFormat::FORMAT_JSON) - out += std::format( - "\n{}\"address\": \"0x{:x}\"," - "\n{}\"class\": \"{}\"," - "\n{}\"title\": \"{}\"," - "\n{}\"initialClass\": \"{}\"," - "\n{}\"initialTitle\": \"{}\"," - "\n{}\"pid\": \"{}\"", - indent, (uintptr_t)node->pWindow.get(), - indent, escapeJSONStrings(node->pWindow->m_szClass), - indent, escapeJSONStrings(node->pWindow->m_szTitle), - indent, escapeJSONStrings(node->pWindow->m_szInitialClass), - indent, escapeJSONStrings(node->pWindow->m_szInitialTitle), - indent, node->pWindow->getPID()); + out += std::format("\n{}\"address\": \"0x{:x}\"," + "\n{}\"class\": \"{}\"," + "\n{}\"title\": \"{}\"," + "\n{}\"initialClass\": \"{}\"," + "\n{}\"initialTitle\": \"{}\"," + "\n{}\"pid\": \"{}\"", + indent, (uintptr_t)node->pWindow.get(), indent, escapeJSONStrings(node->pWindow->m_szClass), indent, escapeJSONStrings(node->pWindow->m_szTitle), + indent, escapeJSONStrings(node->pWindow->m_szInitialClass), indent, escapeJSONStrings(node->pWindow->m_szInitialTitle), indent, + node->pWindow->getPID()); else - out += std::format( - "\n{}address: 0x{:x}" - "\n{}class: {}" - "\n{}title: {}" - "\n{}initialClass: {}" - "\n{}initialTitle: {}" - "\n{}pid: {}", - indent, (uintptr_t)node->pWindow.get(), - indent, escapeJSONStrings(node->pWindow->m_szClass), - indent, escapeJSONStrings(node->pWindow->m_szTitle), - indent, escapeJSONStrings(node->pWindow->m_szInitialClass), - indent, escapeJSONStrings(node->pWindow->m_szInitialTitle), - indent, node->pWindow->getPID()); + out += std::format("\n{}address: 0x{:x}" + "\n{}class: {}" + "\n{}title: {}" + "\n{}initialClass: {}" + "\n{}initialTitle: {}" + "\n{}pid: {}", + indent, (uintptr_t)node->pWindow.get(), indent, escapeJSONStrings(node->pWindow->m_szClass), indent, escapeJSONStrings(node->pWindow->m_szTitle), + indent, escapeJSONStrings(node->pWindow->m_szInitialClass), indent, escapeJSONStrings(node->pWindow->m_szInitialTitle), indent, + node->pWindow->getPID()); } indent.erase(INDENTLVL); @@ -1117,7 +1109,7 @@ static bool printNodeTree(const SDwindleNodeData * const node, eHyprCtlOutputFor } std::string CHyprDwindleLayout::layoutDataRequest(eHyprCtlOutputFormat format, std::string request) { - const auto ARGS = CVarList(request, 0, ' '); + const auto ARGS = CVarList(request, 0, ' '); std::string result = ""; if (ARGS[1] == "workspaceinfo") { @@ -1126,15 +1118,16 @@ std::string CHyprDwindleLayout::layoutDataRequest(eHyprCtlOutputFormat format, s Debug::log(ERR, "Invalid workspace in layoutdata workspaceinfo"); } - auto PHEAD_NODE = getMasterNodeOnWorkspace(WORKSPACEID); - std::string indent = ""; + auto PHEAD_NODE = getMasterNodeOnWorkspace(WORKSPACEID); + std::string indent = ""; if (format == eHyprCtlOutputFormat::FORMAT_JSON) result += "{"; else result += "root:"; printNodeTree(PHEAD_NODE, format, indent, result); - if (format == eHyprCtlOutputFormat::FORMAT_JSON) result += "\n}"; + if (format == eHyprCtlOutputFormat::FORMAT_JSON) + result += "\n}"; } else { Debug::log(LOG, "Unknown layoutdata request"); @@ -1143,7 +1136,6 @@ std::string CHyprDwindleLayout::layoutDataRequest(eHyprCtlOutputFormat format, s return result; } - void CHyprDwindleLayout::toggleSplit(PHLWINDOW pWindow) { const auto PNODE = getNodeFromWindow(pWindow);