algo/dwindle: don't crash on empty swapsplit (#13533)

ref https://github.com/hyprwm/Hyprland/discussions/13530
This commit is contained in:
Vaxry 2026-03-03 11:55:57 +00:00 committed by GitHub
parent a6e3a2478c
commit 3faddf40d0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 36 additions and 26 deletions

View file

@ -84,14 +84,13 @@ static void test13349() {
static void testSplit() { static void testSplit() {
// Test various split methods // Test various split methods
for (auto const& win : {"a", "b"}) { Tests::spawnKitty("a");
if (!Tests::spawnKitty(win)) {
NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win); // these must not crash
++TESTS_FAILED; EXPECT_NOT(getFromSocket("/dispatch layoutmsg swapsplit"), "ok");
ret = 1; EXPECT_NOT(getFromSocket("/dispatch layoutmsg splitratio 1 exact"), "ok");
return;
} Tests::spawnKitty("b");
}
OK(getFromSocket("/dispatch focuswindow class:a")); OK(getFromSocket("/dispatch focuswindow class:a"));
OK(getFromSocket("/dispatch layoutmsg splitratio -0.2")); OK(getFromSocket("/dispatch layoutmsg splitratio -0.2"));

View file

@ -657,11 +657,15 @@ std::expected<void, std::string> CDwindleAlgorithm::layoutMsg(const std::string_
const auto CURRENT_NODE = getNodeFromWindow(Desktop::focusState()->window()); const auto CURRENT_NODE = getNodeFromWindow(Desktop::focusState()->window());
if (ARGS[0] == "togglesplit") { if (ARGS[0] == "togglesplit") {
if (CURRENT_NODE) if (CURRENT_NODE) {
toggleSplit(CURRENT_NODE); if (!toggleSplit(CURRENT_NODE))
return std::unexpected("can't togglesplit in the current workspace");
}
} else if (ARGS[0] == "swapsplit") { } else if (ARGS[0] == "swapsplit") {
if (CURRENT_NODE) if (CURRENT_NODE) {
swapSplit(CURRENT_NODE); if (!swapSplit(CURRENT_NODE))
return std::unexpected("can't swapsplit in the current workspace");
}
} else if (ARGS[0] == "movetoroot") { } else if (ARGS[0] == "movetoroot") {
auto node = CURRENT_NODE; auto node = CURRENT_NODE;
if (!ARGS[1].empty()) { if (!ARGS[1].empty()) {
@ -671,7 +675,8 @@ std::expected<void, std::string> CDwindleAlgorithm::layoutMsg(const std::string_
} }
const auto STABLE = ARGS[2].empty() || ARGS[2] != "unstable"; const auto STABLE = ARGS[2].empty() || ARGS[2] != "unstable";
moveToRoot(node, STABLE); if (!moveToRoot(node, STABLE))
return std::unexpected("can't movetoroot in the current workspace");
} else if (ARGS[0] == "preselect") { } else if (ARGS[0] == "preselect") {
auto direction = ARGS[1]; auto direction = ARGS[1];
@ -730,37 +735,41 @@ std::expected<void, std::string> CDwindleAlgorithm::layoutMsg(const std::string_
return {}; return {};
} }
void CDwindleAlgorithm::toggleSplit(SP<SDwindleNodeData> x) { bool CDwindleAlgorithm::toggleSplit(SP<SDwindleNodeData> x) {
if (!x || !x->pParent) if (!x || !x->pParent)
return; return false;
if (x->pTarget->fullscreenMode() != FSMODE_NONE) if (x->pTarget->fullscreenMode() != FSMODE_NONE)
return; return false;
x->pParent->splitTop = !x->pParent->splitTop; x->pParent->splitTop = !x->pParent->splitTop;
x->pParent->recalcSizePosRecursive(); x->pParent->recalcSizePosRecursive();
return true;
} }
void CDwindleAlgorithm::swapSplit(SP<SDwindleNodeData> x) { bool CDwindleAlgorithm::swapSplit(SP<SDwindleNodeData> x) {
if (x->pTarget->fullscreenMode() != FSMODE_NONE) if (x->pTarget->fullscreenMode() != FSMODE_NONE || !x->pParent)
return; return false;
std::swap(x->pParent->children[0], x->pParent->children[1]); std::swap(x->pParent->children[0], x->pParent->children[1]);
x->pParent->recalcSizePosRecursive(); x->pParent->recalcSizePosRecursive();
return true;
} }
void CDwindleAlgorithm::moveToRoot(SP<SDwindleNodeData> x, bool stable) { bool CDwindleAlgorithm::moveToRoot(SP<SDwindleNodeData> x, bool stable) {
if (!x || !x->pParent) if (!x || !x->pParent)
return; return false;
if (x->pTarget->fullscreenMode() != FSMODE_NONE) if (x->pTarget->fullscreenMode() != FSMODE_NONE)
return; return false;
// already at root // already at root
if (!x->pParent->pParent) if (!x->pParent->pParent)
return; return false;
auto& pNode = x->pParent->children[0] == x ? x->pParent->children[0] : x->pParent->children[1]; auto& pNode = x->pParent->children[0] == x ? x->pParent->children[0] : x->pParent->children[1];
@ -781,4 +790,6 @@ void CDwindleAlgorithm::moveToRoot(SP<SDwindleNodeData> x, bool stable) {
std::swap(pRoot->children[0], pRoot->children[1]); std::swap(pRoot->children[0], pRoot->children[1]);
pRoot->recalcSizePosRecursive(); pRoot->recalcSizePosRecursive();
return true;
} }

View file

@ -48,9 +48,9 @@ namespace Layout::Tiled {
SP<SDwindleNodeData> getClosestNode(const Vector2D&, SP<ITarget> skip = nullptr); SP<SDwindleNodeData> getClosestNode(const Vector2D&, SP<ITarget> skip = nullptr);
SP<SDwindleNodeData> getMasterNode(); SP<SDwindleNodeData> getMasterNode();
void toggleSplit(SP<SDwindleNodeData>); bool toggleSplit(SP<SDwindleNodeData>);
void swapSplit(SP<SDwindleNodeData>); bool swapSplit(SP<SDwindleNodeData>);
void moveToRoot(SP<SDwindleNodeData>, bool stable = true); bool moveToRoot(SP<SDwindleNodeData>, bool stable = true);
Math::eDirection m_overrideDirection = Math::DIRECTION_DEFAULT; Math::eDirection m_overrideDirection = Math::DIRECTION_DEFAULT;
}; };