From 5ba33f846129f3e6c0505b19f94634b24505e828 Mon Sep 17 00:00:00 2001 From: Vaxry <43317083+vaxerski@users.noreply.github.com> Date: Sun, 26 Apr 2026 15:16:36 +0100 Subject: config/lua: init lua config manager, use lua if available (#13817) Adds a new lua config manager, which will automatically be used if there is a lua config present. --- src/config/shared/actions/ConfigActions.cpp | 1697 +++++++++++++++++++++++++++ 1 file changed, 1697 insertions(+) create mode 100644 src/config/shared/actions/ConfigActions.cpp (limited to 'src/config/shared/actions/ConfigActions.cpp') diff --git a/src/config/shared/actions/ConfigActions.cpp b/src/config/shared/actions/ConfigActions.cpp new file mode 100644 index 00000000..f9e9eae7 --- /dev/null +++ b/src/config/shared/actions/ConfigActions.cpp @@ -0,0 +1,1697 @@ +#include "ConfigActions.hpp" +#include "../../../desktop/state/FocusState.hpp" +#include "../../../desktop/view/Window.hpp" +#include "../../../desktop/view/Group.hpp" +#include "../../../desktop/history/WindowHistoryTracker.hpp" +#include "../../../desktop/history/WorkspaceHistoryTracker.hpp" +#include "../../../Compositor.hpp" +#include "../../../managers/SeatManager.hpp" +#include "../../../managers/PointerManager.hpp" +#include "../../../managers/EventManager.hpp" +#include "../../../managers/KeybindManager.hpp" +#include "../../../managers/input/InputManager.hpp" +#include "../../../layout/LayoutManager.hpp" +#include "../../../layout/space/Space.hpp" +#include "../../../render/Renderer.hpp" +#include "../../../config/ConfigValue.hpp" +#include "../../../config/shared/monitor/MonitorRuleManager.hpp" +#include "../../../protocols/IdleNotify.hpp" +#include "../../../protocols/GlobalShortcuts.hpp" +#include "../../../event/EventBus.hpp" +#include "../../../managers/XWaylandManager.hpp" +#include "../../../layout/algorithm/Algorithm.hpp" +#include "../../../layout/algorithm/tiled/master/MasterAlgorithm.hpp" +#include "../../../layout/algorithm/tiled/monocle/MonocleAlgorithm.hpp" + +#include +#include + +using namespace Config; +using namespace Config::Actions; + +UP& Actions::state() { + static UP p = makeUnique(); + return p; +} + +static PHLWINDOW xtract(std::optional window) { + return window.value_or(Desktop::focusState()->window()); +} + +static void updateRelativeCursorCoords() { + static auto PNOWARPS = CConfigValue("cursor:no_warps"); + + if (*PNOWARPS) + return; + + if (Desktop::focusState()->window()) + Desktop::focusState()->window()->m_relativeCursorCoordsOnLastWarp = g_pInputManager->getMouseCoordsInternal() - Desktop::focusState()->window()->m_position; +} + +static void switchToWindow(PHLWINDOW PWINDOWTOCHANGETO, bool forceFSCycle = false) { + static auto PFOLLOWMOUSE = CConfigValue("input:follow_mouse"); + static auto PNOWARPS = CConfigValue("cursor:no_warps"); + + const auto PLASTWINDOW = Desktop::focusState()->window(); + + if (PWINDOWTOCHANGETO == PLASTWINDOW || !PWINDOWTOCHANGETO) + return; + + g_pInputManager->unconstrainMouse(); + + if (PLASTWINDOW && PLASTWINDOW->m_workspace == PWINDOWTOCHANGETO->m_workspace && PLASTWINDOW->isFullscreen()) + Desktop::focusState()->fullWindowFocus(PWINDOWTOCHANGETO, Desktop::FOCUS_REASON_KEYBIND, nullptr, forceFSCycle); + else { + updateRelativeCursorCoords(); + Desktop::focusState()->fullWindowFocus(PWINDOWTOCHANGETO, Desktop::FOCUS_REASON_KEYBIND, nullptr, forceFSCycle); + PWINDOWTOCHANGETO->warpCursor(); + + if (*PNOWARPS == 0 || *PFOLLOWMOUSE < 2) { + g_pInputManager->m_forcedFocus = PWINDOWTOCHANGETO; + g_pInputManager->simulateMouseMovement(); + g_pInputManager->m_forcedFocus.reset(); + } + + if (PLASTWINDOW && PLASTWINDOW->m_monitor != PWINDOWTOCHANGETO->m_monitor) { + const auto PNEWMON = PWINDOWTOCHANGETO->m_monitor.lock(); + Desktop::focusState()->rawMonitorFocus(PNEWMON); + } + } +} + +static bool tryMoveFocusToMonitor(PHLMONITOR monitor) { + if (!monitor) + return false; + + const auto LASTMONITOR = Desktop::focusState()->monitor(); + if (!LASTMONITOR || LASTMONITOR == monitor) + return false; + + static auto PFOLLOWMOUSE = CConfigValue("input:follow_mouse"); + static auto PNOWARPS = CConfigValue("cursor:no_warps"); + + g_pInputManager->unconstrainMouse(); + + const auto PNEWMAINWORKSPACE = monitor->m_activeWorkspace; + const auto PNEWWORKSPACE = monitor->m_activeSpecialWorkspace ? monitor->m_activeSpecialWorkspace : PNEWMAINWORKSPACE; + const auto PNEWWINDOW = PNEWWORKSPACE->getLastFocusedWindow(); + + if (PNEWWINDOW) { + updateRelativeCursorCoords(); + Desktop::focusState()->fullWindowFocus(PNEWWINDOW, Desktop::FOCUS_REASON_KEYBIND); + PNEWWINDOW->warpCursor(); + + if (*PNOWARPS == 0 || *PFOLLOWMOUSE < 2) { + g_pInputManager->m_forcedFocus = PNEWWINDOW; + g_pInputManager->simulateMouseMovement(); + g_pInputManager->m_forcedFocus.reset(); + } + } else { + Desktop::focusState()->rawWindowFocus(nullptr, Desktop::FOCUS_REASON_KEYBIND); + g_pCompositor->warpCursorTo(monitor->middle()); + } + Desktop::focusState()->rawMonitorFocus(monitor); + + return true; +} + +static std::string dirToString(Math::eDirection dir) { + switch (dir) { + case Math::DIRECTION_LEFT: return "l"; + case Math::DIRECTION_RIGHT: return "r"; + case Math::DIRECTION_UP: return "u"; + case Math::DIRECTION_DOWN: return "d"; + default: return ""; + } +} + +ActionResult Actions::closeWindow(std::optional w) { + auto window = xtract(w); + if (!window) + return std::unexpected("No target found."); + + if (window->m_closeableSince > Time::steadyNow()) + return std::unexpected("can't close window, it's not closeable yet (noclosefor)"); + + window->sendClose(); + + return {}; +} + +ActionResult Actions::killWindow(std::optional w) { + auto window = xtract(w); + if (!window) + return std::unexpected("No target found."); + + kill(window->getPID(), SIGKILL); + + return {}; +} + +ActionResult Actions::signalWindow(int sig, std::optional w) { + auto window = xtract(w); + if (!window) + return std::unexpected("No target found."); + + if (sig < 1 || sig > 31) + return std::unexpected(std::format("Invalid signal number {}", sig)); + + kill(window->getPID(), sig); + + return {}; +} + +ActionResult Actions::floatWindow(eTogglableAction action, std::optional w) { + auto window = xtract(w); + if (!window) + return std::unexpected("No target found."); + + bool wantFloat = false; + switch (action) { + case TOGGLE_ACTION_TOGGLE: wantFloat = !window->m_isFloating; break; + case TOGGLE_ACTION_ENABLE: wantFloat = true; break; + case TOGGLE_ACTION_DISABLE: wantFloat = false; break; + } + + if (wantFloat == window->m_isFloating) + return {}; + + if (g_layoutManager->dragController()->target()) + CKeybindManager::changeMouseBindMode(MBIND_INVALID); + + g_layoutManager->changeFloatingMode(window->layoutTarget()); + + if (window->m_isFloating) + g_pCompositor->changeWindowZOrder(window, true); + + if (window->m_workspace) { + window->m_workspace->updateWindows(); + window->m_workspace->updateWindowData(); + } + + g_pCompositor->updateAllWindowsAnimatedDecorationValues(); + + return {}; +} + +ActionResult Actions::pseudoWindow(eTogglableAction action, std::optional w) { + auto window = xtract(w); + if (!window) + return std::unexpected("No target found."); + + bool wantPseudo = false; + switch (action) { + case TOGGLE_ACTION_TOGGLE: wantPseudo = !window->layoutTarget()->isPseudo(); break; + case TOGGLE_ACTION_ENABLE: wantPseudo = true; break; + case TOGGLE_ACTION_DISABLE: wantPseudo = false; break; + } + + window->layoutTarget()->setPseudo(wantPseudo); + + return {}; +} + +ActionResult Actions::pinWindow(eTogglableAction action, std::optional w) { + auto window = xtract(w); + if (!window) + return std::unexpected("No target found."); + + if (!window->m_isFloating || window->isFullscreen()) + return std::unexpected("Window does not qualify to be pinned"); + + bool wantPin = false; + switch (action) { + case TOGGLE_ACTION_TOGGLE: wantPin = !window->m_pinned; break; + case TOGGLE_ACTION_ENABLE: wantPin = true; break; + case TOGGLE_ACTION_DISABLE: wantPin = false; break; + } + + if (wantPin == window->m_pinned) + return {}; + + window->m_pinned = wantPin; + + const auto PMONITOR = window->m_monitor.lock(); + if (!PMONITOR) + return std::unexpected("Window has no monitor"); + + window->layoutTarget()->assignToSpace(PMONITOR->m_activeWorkspace->m_space); + window->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_PINNED); + + const auto PWORKSPACE = window->m_workspace; + PWORKSPACE->m_lastFocusedWindow = + g_pCompositor->vectorToWindowUnified(g_pInputManager->getMouseCoordsInternal(), Desktop::View::RESERVED_EXTENTS | Desktop::View::INPUT_EXTENTS); + + g_pEventManager->postEvent(SHyprIPCEvent{.event = "pin", .data = std::format("{:x},{}", rc(window.get()), sc(window->m_pinned))}); + Event::bus()->m_events.window.pin.emit(window); + + g_pHyprRenderer->damageWindow(window, true); + + return {}; +} + +ActionResult Actions::fullscreenWindow(eFullscreenMode mode, std::optional w) { + auto window = xtract(w); + if (!window) + return std::unexpected("No target found."); + + if (window->isEffectiveInternalFSMode(mode)) + g_pCompositor->setWindowFullscreenInternal(window, FSMODE_NONE); + else + g_pCompositor->setWindowFullscreenInternal(window, mode); + + return {}; +} + +ActionResult Actions::fullscreenWindow(eFullscreenMode internalMode, eFullscreenMode clientMode, std::optional w) { + auto window = xtract(w); + if (!window) + return std::unexpected("No target found."); + + window->m_ruleApplicator->syncFullscreenOverride(Desktop::Types::COverridableVar(false, Desktop::Types::PRIORITY_SET_PROP)); + + const Desktop::View::SFullscreenState STATE = {.internal = internalMode, .client = clientMode}; + + if (window->m_fullscreenState.internal == STATE.internal && window->m_fullscreenState.client == STATE.client) + g_pCompositor->setWindowFullscreenState(window, Desktop::View::SFullscreenState{.internal = FSMODE_NONE, .client = FSMODE_NONE}); + else + g_pCompositor->setWindowFullscreenState(window, STATE); + + window->m_ruleApplicator->syncFullscreenOverride( + Desktop::Types::COverridableVar(window->m_fullscreenState.internal == window->m_fullscreenState.client, Desktop::Types::PRIORITY_SET_PROP)); + + return {}; +} + +ActionResult Actions::moveToWorkspace(PHLWORKSPACE ws, bool silent, std::optional w) { + auto window = xtract(w); + if (!window) + return std::unexpected("No target found."); + + if (!ws) + return std::unexpected("Invalid workspace"); + + if (ws->m_id == window->workspaceID()) + return {}; + + const auto POLDWS = window->m_workspace; + + updateRelativeCursorCoords(); + g_pHyprRenderer->damageWindow(window); + + if (silent) { + const auto OLDMIDDLE = window->middle(); + g_pCompositor->moveWindowToWorkspaceSafe(window, ws); + + if (window == Desktop::focusState()->window()) { + if (const auto PATCOORDS = + g_pCompositor->vectorToWindowUnified(OLDMIDDLE, Desktop::View::RESERVED_EXTENTS | Desktop::View::INPUT_EXTENTS | Desktop::View::ALLOW_FLOATING, window); + PATCOORDS) + Desktop::focusState()->fullWindowFocus(PATCOORDS, Desktop::FOCUS_REASON_KEYBIND); + else + g_pInputManager->refocus(); + } + } else { + PHLMONITOR pMonitor = nullptr; + + const auto FULLSCREENMODE = window->m_fullscreenState.internal; + g_pCompositor->moveWindowToWorkspaceSafe(window, ws); + pMonitor = ws->m_monitor.lock(); + Desktop::focusState()->rawMonitorFocus(pMonitor); + g_pCompositor->setWindowFullscreenInternal(window, FULLSCREENMODE); + + POLDWS->m_lastFocusedWindow = POLDWS->getFirstWindow(); + + if (ws->m_isSpecialWorkspace) + pMonitor->setSpecialWorkspace(ws); + else if (POLDWS->m_isSpecialWorkspace) + POLDWS->m_monitor.lock()->setSpecialWorkspace(nullptr); + + pMonitor->changeWorkspace(ws); + + Desktop::focusState()->fullWindowFocus(window, Desktop::FOCUS_REASON_KEYBIND); + window->warpCursor(); + } + + return {}; +} + +ActionResult Actions::moveFocus(Math::eDirection dir) { + static auto PFULLCYCLE = CConfigValue("binds:movefocus_cycles_fullscreen"); + static auto PGROUPCYCLE = CConfigValue("binds:movefocus_cycles_groupfirst"); + static auto PMONITORFALLBACK = CConfigValue("binds:window_direction_monitor_fallback"); + + const auto PLASTWINDOW = Desktop::focusState()->window(); + if (!PLASTWINDOW || !PLASTWINDOW->aliveAndVisible()) { + if (*PMONITORFALLBACK) + tryMoveFocusToMonitor(g_pCompositor->getMonitorInDirection(dir)); + return {}; + } + + const auto PWINDOWTOCHANGETO = *PFULLCYCLE && PLASTWINDOW->isFullscreen() ? + g_pCompositor->getWindowCycle(PLASTWINDOW, true, {}, false, dir != Math::DIRECTION_DOWN && dir != Math::DIRECTION_RIGHT) : + g_pCompositor->getWindowInDirection(PLASTWINDOW, dir); + + if (*PGROUPCYCLE && PLASTWINDOW->m_group) { + auto isTheOnlyGroupOnWs = !PWINDOWTOCHANGETO && g_pCompositor->m_monitors.size() == 1; + if (dir == Math::DIRECTION_LEFT && (PLASTWINDOW != PLASTWINDOW->m_group->head() || isTheOnlyGroupOnWs)) { + PLASTWINDOW->m_group->moveCurrent(false); + return {}; + } else if (dir == Math::DIRECTION_RIGHT && (PLASTWINDOW != PLASTWINDOW->m_group->tail() || isTheOnlyGroupOnWs)) { + PLASTWINDOW->m_group->moveCurrent(true); + return {}; + } + } + + if (PWINDOWTOCHANGETO) { + switchToWindow(PWINDOWTOCHANGETO, *PFULLCYCLE && PLASTWINDOW->isFullscreen()); + return {}; + } + + if (*PMONITORFALLBACK && tryMoveFocusToMonitor(g_pCompositor->getMonitorInDirection(dir))) + return {}; + + static auto PNOFALLBACK = CConfigValue("general:no_focus_fallback"); + if (*PNOFALLBACK) + return std::unexpected(std::format("Nothing to focus to in direction {}", Math::toString(dir))); + + const auto PMONITOR = PLASTWINDOW->m_monitor.lock(); + if (!PMONITOR) + return std::unexpected("Window has no monitor"); + + if (dir == Math::DIRECTION_LEFT || dir == Math::DIRECTION_RIGHT) { + if (STICKS(PLASTWINDOW->m_position.x, PMONITOR->m_position.x) && STICKS(PLASTWINDOW->m_size.x, PMONITOR->m_size.x)) + return {}; + } else if (STICKS(PLASTWINDOW->m_position.y, PMONITOR->m_position.y) && STICKS(PLASTWINDOW->m_size.y, PMONITOR->m_size.y)) + return {}; + + CBox box = PMONITOR->logicalBox(); + switch (dir) { + case Math::DIRECTION_LEFT: + box.x += box.w; + box.w = 1; + break; + case Math::DIRECTION_RIGHT: + box.x -= 1; + box.w = 1; + break; + case Math::DIRECTION_UP: + box.y += box.h; + box.h = 1; + break; + case Math::DIRECTION_DOWN: + box.y -= 1; + box.h = 1; + break; + default: break; + } + + const auto PWINDOWCANDIDATE = g_pCompositor->getWindowInDirection(box, PMONITOR->m_activeSpecialWorkspace ? PMONITOR->m_activeSpecialWorkspace : PMONITOR->m_activeWorkspace, + dir, PLASTWINDOW, PLASTWINDOW->m_isFloating); + if (PWINDOWCANDIDATE) + switchToWindow(PWINDOWCANDIDATE); + + return {}; +} + +ActionResult Actions::focus(PHLWINDOW window) { + if (!window) + return std::unexpected("No target found."); + + const auto PWORKSPACE = window->m_workspace; + if (!PWORKSPACE) + return std::unexpected("Window has no workspace"); + + updateRelativeCursorCoords(); + + if (Desktop::focusState()->monitor() && Desktop::focusState()->monitor()->m_activeWorkspace != window->m_workspace && + Desktop::focusState()->monitor()->m_activeSpecialWorkspace != window->m_workspace) // NOLINTNEXTLINE + Actions::changeWorkspace(PWORKSPACE); + + Desktop::focusState()->fullWindowFocus(window, Desktop::FOCUS_REASON_KEYBIND, nullptr, false); + window->warpCursor(); + + return {}; +} + +ActionResult Actions::moveInDirection(Math::eDirection dir, std::optional w) { + auto window = xtract(w); + if (!window) + return std::unexpected("No target found."); + + if (window->isFullscreen()) + return std::unexpected("Can't move fullscreen window"); + + updateRelativeCursorCoords(); + + g_layoutManager->moveInDirection(window->layoutTarget(), dirToString(dir)); + window->warpCursor(); + + return {}; +} + +ActionResult Actions::swapInDirection(Math::eDirection dir, std::optional w) { + auto window = xtract(w); + if (!window) + return std::unexpected("No target found."); + + if (window->isFullscreen()) + return std::unexpected("Can't swap fullscreen window"); + + const auto PWINDOWTOCHANGETO = g_pCompositor->getWindowInDirection(window, dir); + + if (!PWINDOWTOCHANGETO || PWINDOWTOCHANGETO == window) + return std::unexpected("No window to swap with in that direction"); + + updateRelativeCursorCoords(); + g_layoutManager->switchTargets(window->layoutTarget(), PWINDOWTOCHANGETO->layoutTarget(), true); + window->warpCursor(); + + return {}; +} + +ActionResult Actions::swapWith(PHLWINDOW other, std::optional w) { + auto window = xtract(w); + if (!window) + return std::unexpected("No target found."); + + if (!other) + return std::unexpected("No window to swap with"); + + if (other == window) + return std::unexpected("Can't swap a window with itself"); + + if (window->isFullscreen() || other->isFullscreen()) + return std::unexpected("Can't swap fullscreen window"); + + updateRelativeCursorCoords(); + g_layoutManager->switchTargets(window->layoutTarget(), other->layoutTarget(), true); + window->warpCursor(); + + return {}; +} + +ActionResult Actions::focusCurrentOrLast() { + const auto& HISTORY = Desktop::History::windowTracker()->fullHistory(); + + if (HISTORY.size() <= 1) + return std::unexpected("History too short"); + + const auto PWINDOWPREV = HISTORY[HISTORY.size() - 2].lock(); + + if (!PWINDOWPREV) + return std::unexpected("Window not found"); + + switchToWindow(PWINDOWPREV); + + return {}; +} + +ActionResult Actions::focusUrgentOrLast() { + const auto& HISTORY = Desktop::History::windowTracker()->fullHistory(); + const auto PWINDOWURGENT = g_pCompositor->getUrgentWindow(); + const auto PWINDOWPREV = Desktop::focusState()->window() ? (HISTORY.size() < 2 ? nullptr : HISTORY[1].lock()) : (HISTORY.empty() ? nullptr : HISTORY[0].lock()); + + if (!PWINDOWURGENT && !PWINDOWPREV) + return std::unexpected("Window not found"); + + switchToWindow(PWINDOWURGENT ? PWINDOWURGENT : PWINDOWPREV); + + return {}; +} + +ActionResult Actions::center(std::optional w) { + auto window = xtract(w); + if (!window || !window->m_isFloating || window->isFullscreen()) + return std::unexpected("No floating window found"); + + const auto PMONITOR = window->m_monitor.lock(); + + window->layoutTarget()->setPositionGlobal(CBox{PMONITOR->logicalBoxMinusReserved().middle() - window->m_realSize->goal() / 2.F, window->layoutTarget()->position().size()}); + + return {}; +} + +ActionResult Actions::moveCursorToCorner(int corner, std::optional w) { + auto window = xtract(w); + if (!window) + return std::unexpected("No target found."); + + if (corner < 0 || corner > 3) + return std::unexpected("Corner must be 0-3"); + + switch (corner) { + case 0: g_pCompositor->warpCursorTo({window->m_realPosition->value().x, window->m_realPosition->value().y + window->m_realSize->value().y}, true); break; + case 1: + g_pCompositor->warpCursorTo({window->m_realPosition->value().x + window->m_realSize->value().x, window->m_realPosition->value().y + window->m_realSize->value().y}, + true); + break; + case 2: g_pCompositor->warpCursorTo({window->m_realPosition->value().x + window->m_realSize->value().x, window->m_realPosition->value().y}, true); break; + case 3: g_pCompositor->warpCursorTo({window->m_realPosition->value().x, window->m_realPosition->value().y}, true); break; + default: break; + } + + return {}; +} + +ActionResult Actions::resize(const Vector2D& size, bool relative, std::optional w) { + auto window = xtract(w); + if (!window) + return std::unexpected("No target found."); + + if (window->isFullscreen()) + return std::unexpected("Window is fullscreen"); + + if (!relative && (size.x < 1 || size.y < 1)) + return std::unexpected("Invalid size"); + + const auto delta = relative ? size : size - window->m_realSize->goal(); + + g_layoutManager->resizeTarget(delta, window->layoutTarget()); + + if (window->m_realSize->goal().x > 1 && window->m_realSize->goal().y > 1) + window->setHidden(false); + + return {}; +} + +ActionResult Actions::move(const Vector2D& pos, bool relative, std::optional w) { + auto window = xtract(w); + if (!window) + return std::unexpected("No target found."); + + if (window->isFullscreen()) + return std::unexpected("Window is fullscreen"); + + const auto delta = relative ? pos : pos - window->m_realPosition->goal(); + + g_layoutManager->moveTarget(delta, window->layoutTarget()); + + return {}; +} + +ActionResult Actions::tag(const std::string& tagStr, std::optional w) { + auto window = xtract(w); + if (!window) + return std::unexpected("No target found."); + + if (window->m_ruleApplicator->m_tagKeeper.applyTag(tagStr)) { + window->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_TAG); + window->updateDecorationValues(); + } + + return {}; +} + +ActionResult Actions::swapNext(const bool next, std::optional w) { + auto window = xtract(w); + if (!window) + return std::unexpected("No target found."); + + const auto PLASTCYCLED = + validMapped(window->m_lastCycledWindow) && window->m_lastCycledWindow->m_workspace == window->m_workspace ? window->m_lastCycledWindow.lock() : nullptr; + + auto toSwap = g_pCompositor->getWindowCycle(PLASTCYCLED ? PLASTCYCLED : window, true, std::nullopt, false, !next); + + if (toSwap == window) + toSwap = g_pCompositor->getWindowCycle(window, true, std::nullopt, false, !next); + + if (!toSwap) + return std::unexpected("No window to swap with"); + + g_layoutManager->switchTargets(window->layoutTarget(), toSwap->layoutTarget(), false); + window->m_lastCycledWindow = toSwap; + Desktop::focusState()->fullWindowFocus(window, Desktop::FOCUS_REASON_KEYBIND); + + return {}; +} + +ActionResult Actions::alterZOrder(const std::string& mode, std::optional w) { + auto window = xtract(w); + if (!window) + return std::unexpected("No target found."); + + if (mode == "top") + g_pCompositor->changeWindowZOrder(window, true); + else if (mode == "bottom") + g_pCompositor->changeWindowZOrder(window, false); + else + return std::unexpected(std::format("Bad z-order position: {}", mode)); + + g_pInputManager->simulateMouseMovement(); + + return {}; +} + +template +static void parsePropTrivial(Desktop::Types::COverridableVar& prop, const std::string& s) { + static_assert(std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v, + "Invalid type passed to parsePropTrivial"); + + if (s == "unset") { + prop.unset(Desktop::Types::PRIORITY_SET_PROP); + return; + } + + try { + if constexpr (std::is_same_v) { + if (s == "toggle") + prop.increment(true, Desktop::Types::PRIORITY_SET_PROP); + else + prop = Desktop::Types::COverridableVar(truthy(s), Desktop::Types::PRIORITY_SET_PROP); + } else if constexpr (std::is_same_v || std::is_same_v) { + if (s.starts_with("relative")) { + const auto VAL = std::stoi(s.substr(s.find(' ') + 1)); + prop.increment(VAL, Desktop::Types::PRIORITY_SET_PROP); + } else + prop = Desktop::Types::COverridableVar(std::stoull(s), Desktop::Types::PRIORITY_SET_PROP); + } else if constexpr (std::is_same_v) { + if (s.starts_with("relative")) { + const auto VAL = std::stof(s.substr(s.find(' ') + 1)); + prop.increment(VAL, Desktop::Types::PRIORITY_SET_PROP); + } else + prop = Desktop::Types::COverridableVar(std::stof(s), Desktop::Types::PRIORITY_SET_PROP); + } else if constexpr (std::is_same_v) + prop = Desktop::Types::COverridableVar(s, Desktop::Types::PRIORITY_SET_PROP); + } catch (...) { Log::logger->log(Log::ERR, "Hyprctl: parsePropTrivial: failed to parse setprop for {}", s); } +} + +ActionResult Actions::setProp(const std::string& PROP, const std::string& VAL, std::optional w) { + auto PWINDOW = xtract(w); + if (!PWINDOW) + return std::unexpected("No target found."); + + try { + if (PROP == "max_size") { + const auto SIZE = PWINDOW->calculateExpression(VAL); + if (!SIZE) { + Log::logger->log(Log::ERR, "failed to parse {} as an expression", VAL); + throw "failed to parse expression"; + } + PWINDOW->m_ruleApplicator->maxSizeOverride(Desktop::Types::COverridableVar(*SIZE, Desktop::Types::PRIORITY_SET_PROP)); + PWINDOW->clampWindowSize(std::nullopt, PWINDOW->m_ruleApplicator->maxSize().value()); + PWINDOW->setHidden(false); + } else if (PROP == "min_size") { + const auto SIZE = PWINDOW->calculateExpression(VAL); + if (!SIZE) { + Log::logger->log(Log::ERR, "failed to parse {} as an expression", VAL); + throw "failed to parse expression"; + } + PWINDOW->m_ruleApplicator->minSizeOverride(Desktop::Types::COverridableVar(*SIZE, Desktop::Types::PRIORITY_SET_PROP)); + PWINDOW->clampWindowSize(PWINDOW->m_ruleApplicator->minSize().value(), std::nullopt); + PWINDOW->setHidden(false); + } else if (PROP == "active_border_color" || PROP == "inactive_border_color") { + Config::CGradientValueData colorData = {}; + + Hyprutils::String::CVarList2 vars(VAL, 0, 's', true); + + if (vars.size() > 1) { + for (int i = 1; i < sc(vars.size()); ++i) { + const auto TOKEN = vars[i]; + if (TOKEN.ends_with("deg")) + colorData.m_angle = std::stoi(std::string(TOKEN.substr(0, TOKEN.size() - 3))) * (PI / 180.0); + else + configStringToInt(std::string(TOKEN)).and_then([&colorData](const auto& e) { + colorData.m_colors.push_back(e); + return std::invoke_result_t(1); + }); + } + } else if (VAL != "-1") + configStringToInt(VAL).and_then([&colorData](const auto& e) { + colorData.m_colors.push_back(e); + return std::invoke_result_t(1); + }); + + colorData.updateColorsOk(); + + if (PROP == "active_border_color") + PWINDOW->m_ruleApplicator->activeBorderColorOverride(Desktop::Types::COverridableVar(colorData, Desktop::Types::PRIORITY_SET_PROP)); + else + PWINDOW->m_ruleApplicator->inactiveBorderColorOverride(Desktop::Types::COverridableVar(colorData, Desktop::Types::PRIORITY_SET_PROP)); + } else if (PROP == "opacity") { + PWINDOW->m_ruleApplicator->alphaOverride(Desktop::Types::COverridableVar( + Desktop::Types::SAlphaValue{std::stof(VAL), PWINDOW->m_ruleApplicator->alpha().valueOrDefault().overridden}, Desktop::Types::PRIORITY_SET_PROP)); + } else if (PROP == "opacity_inactive") { + PWINDOW->m_ruleApplicator->alphaInactiveOverride(Desktop::Types::COverridableVar( + Desktop::Types::SAlphaValue{std::stof(VAL), PWINDOW->m_ruleApplicator->alphaInactive().valueOrDefault().overridden}, Desktop::Types::PRIORITY_SET_PROP)); + } else if (PROP == "opacity_fullscreen") { + PWINDOW->m_ruleApplicator->alphaFullscreenOverride(Desktop::Types::COverridableVar( + Desktop::Types::SAlphaValue{std::stof(VAL), PWINDOW->m_ruleApplicator->alphaFullscreen().valueOrDefault().overridden}, Desktop::Types::PRIORITY_SET_PROP)); + } else if (PROP == "opacity_override") { + PWINDOW->m_ruleApplicator->alphaOverride(Desktop::Types::COverridableVar( + Desktop::Types::SAlphaValue{PWINDOW->m_ruleApplicator->alpha().valueOrDefault().alpha, sc(configStringToInt(VAL).value_or(0))}, + Desktop::Types::PRIORITY_SET_PROP)); + } else if (PROP == "opacity_inactive_override") { + PWINDOW->m_ruleApplicator->alphaInactiveOverride(Desktop::Types::COverridableVar( + Desktop::Types::SAlphaValue{PWINDOW->m_ruleApplicator->alphaInactive().valueOrDefault().alpha, sc(configStringToInt(VAL).value_or(0))}, + Desktop::Types::PRIORITY_SET_PROP)); + } else if (PROP == "opacity_fullscreen_override") { + PWINDOW->m_ruleApplicator->alphaFullscreenOverride(Desktop::Types::COverridableVar( + Desktop::Types::SAlphaValue{PWINDOW->m_ruleApplicator->alphaFullscreen().valueOrDefault().alpha, sc(configStringToInt(VAL).value_or(0))}, + Desktop::Types::PRIORITY_SET_PROP)); + } else if (PROP == "allows_input") + parsePropTrivial(PWINDOW->m_ruleApplicator->allowsInput(), VAL); + else if (PROP == "decorate") + parsePropTrivial(PWINDOW->m_ruleApplicator->decorate(), VAL); + else if (PROP == "focus_on_activate") + parsePropTrivial(PWINDOW->m_ruleApplicator->focusOnActivate(), VAL); + else if (PROP == "keep_aspect_ratio") + parsePropTrivial(PWINDOW->m_ruleApplicator->keepAspectRatio(), VAL); + else if (PROP == "nearest_neighbor") + parsePropTrivial(PWINDOW->m_ruleApplicator->nearestNeighbor(), VAL); + else if (PROP == "no_anim") + parsePropTrivial(PWINDOW->m_ruleApplicator->noAnim(), VAL); + else if (PROP == "no_blur") + parsePropTrivial(PWINDOW->m_ruleApplicator->noBlur(), VAL); + else if (PROP == "no_dim") + parsePropTrivial(PWINDOW->m_ruleApplicator->noDim(), VAL); + else if (PROP == "no_focus") + parsePropTrivial(PWINDOW->m_ruleApplicator->noFocus(), VAL); + else if (PROP == "no_max_size") + parsePropTrivial(PWINDOW->m_ruleApplicator->noMaxSize(), VAL); + else if (PROP == "no_shadow") + parsePropTrivial(PWINDOW->m_ruleApplicator->noShadow(), VAL); + else if (PROP == "no_shortcuts_inhibit") + parsePropTrivial(PWINDOW->m_ruleApplicator->noShortcutsInhibit(), VAL); + else if (PROP == "dim_around") + parsePropTrivial(PWINDOW->m_ruleApplicator->dimAround(), VAL); + else if (PROP == "opaque") + parsePropTrivial(PWINDOW->m_ruleApplicator->opaque(), VAL); + else if (PROP == "force_rgbx") + parsePropTrivial(PWINDOW->m_ruleApplicator->RGBX(), VAL); + else if (PROP == "sync_fullscreen") + parsePropTrivial(PWINDOW->m_ruleApplicator->syncFullscreen(), VAL); + else if (PROP == "immediate") + parsePropTrivial(PWINDOW->m_ruleApplicator->tearing(), VAL); + else if (PROP == "xray") + parsePropTrivial(PWINDOW->m_ruleApplicator->xray(), VAL); + else if (PROP == "render_unfocused") + parsePropTrivial(PWINDOW->m_ruleApplicator->renderUnfocused(), VAL); + else if (PROP == "no_follow_mouse") + parsePropTrivial(PWINDOW->m_ruleApplicator->noFollowMouse(), VAL); + else if (PROP == "no_screen_share") + parsePropTrivial(PWINDOW->m_ruleApplicator->noScreenShare(), VAL); + else if (PROP == "no_vrr") + parsePropTrivial(PWINDOW->m_ruleApplicator->noVRR(), VAL); + else if (PROP == "persistent_size") + parsePropTrivial(PWINDOW->m_ruleApplicator->persistentSize(), VAL); + else if (PROP == "stay_focused") + parsePropTrivial(PWINDOW->m_ruleApplicator->stayFocused(), VAL); + else if (PROP == "idle_inhibit") + parsePropTrivial(PWINDOW->m_ruleApplicator->idleInhibitMode(), VAL); + else if (PROP == "border_size") + parsePropTrivial(PWINDOW->m_ruleApplicator->borderSize(), VAL); + else if (PROP == "rounding") + parsePropTrivial(PWINDOW->m_ruleApplicator->rounding(), VAL); + else if (PROP == "rounding_power") + parsePropTrivial(PWINDOW->m_ruleApplicator->roundingPower(), VAL); + else if (PROP == "scroll_mouse") + parsePropTrivial(PWINDOW->m_ruleApplicator->scrollMouse(), VAL); + else if (PROP == "scroll_touchpad") + parsePropTrivial(PWINDOW->m_ruleApplicator->scrollTouchpad(), VAL); + else if (PROP == "animation") + parsePropTrivial(PWINDOW->m_ruleApplicator->animationStyle(), VAL); + else + return std::unexpected("prop not found"); + + } catch (std::exception& e) { return std::unexpected(std::format("Error parsing prop value: {}", std::string(e.what()))); } + + g_pCompositor->updateAllWindowsAnimatedDecorationValues(); + + if (PROP == "no_vrr") + Config::monitorRuleMgr()->ensureVRR(); + + for (auto const& m : g_pCompositor->m_monitors) { + if (m->m_activeWorkspace) + m->m_activeWorkspace->m_space->recalculate(); + } + + return {}; +} + +ActionResult Actions::toggleGroup(std::optional w) { + auto window = xtract(w); + if (!window) + return std::unexpected("No target found."); + + if (window->isFullscreen()) + g_pCompositor->setWindowFullscreenInternal(window, FSMODE_NONE); + + if (!window->m_group) + window->m_group = Desktop::View::CGroup::create({window}); + else + window->m_group->destroy(); + + return {}; +} + +ActionResult Actions::changeGroupActive(bool forward, std::optional w) { + auto window = xtract(w); + if (!window) + return std::unexpected("No target found."); + + if (!window->m_group) + return std::unexpected("Window is not in a group"); + + if (window->m_group->size() == 1) + return std::unexpected("Only one window in group"); + + window->m_group->moveCurrent(forward); + + return {}; +} + +ActionResult Actions::setGroupActive(int index, std::optional w) { + auto window = xtract(w); + if (!window) + return std::unexpected("No target found."); + + if (!window->m_group) + return std::unexpected("Window is not in a group"); + + if (window->m_group->size() == 1) + return std::unexpected("Only one window in group"); + + if (index <= 0) + window->m_group->setCurrent(window->m_group->size() - 1); + else if (sc(index) > window->m_group->size()) + return std::unexpected("Index out of range"); + else + window->m_group->setCurrent(index - 1); + + return {}; +} + +ActionResult Actions::changeWorkspace(PHLWORKSPACE ws) { + if (!ws) + return std::unexpected("Invalid workspace"); + + static auto PHIDESPECIALONWORKSPACECHANGE = CConfigValue("binds:hide_special_on_workspace_change"); + static auto PWORKSPACECENTERON = CConfigValue("binds:workspace_center_on"); + + const auto PMONITOR = Desktop::focusState()->monitor(); + if (!PMONITOR) + return std::unexpected("No monitor"); + + if (ws->m_isSpecialWorkspace) { + PMONITOR->setSpecialWorkspace(ws); + g_pInputManager->simulateMouseMovement(); + return {}; + } + + g_pInputManager->unconstrainMouse(); + g_pInputManager->m_emptyFocusCursorSet = false; + g_pInputManager->releaseAllMouseButtons(); + + const auto PMONITORWORKSPACEOWNER = PMONITOR == ws->m_monitor ? PMONITOR : ws->m_monitor.lock(); + if (!PMONITORWORKSPACEOWNER) + return std::unexpected("Workspace has no monitor"); + + updateRelativeCursorCoords(); + + Desktop::focusState()->rawMonitorFocus(PMONITORWORKSPACEOWNER); + + if (*PHIDESPECIALONWORKSPACECHANGE) + PMONITORWORKSPACEOWNER->setSpecialWorkspace(nullptr); + PMONITORWORKSPACEOWNER->changeWorkspace(ws, false, true); + + if (PMONITOR != PMONITORWORKSPACEOWNER) { + Vector2D middle = PMONITORWORKSPACEOWNER->middle(); + if (const auto PLAST = ws->getLastFocusedWindow(); PLAST) { + Desktop::focusState()->fullWindowFocus(PLAST, Desktop::FOCUS_REASON_KEYBIND); + if (*PWORKSPACECENTERON == 1) + middle = PLAST->middle(); + } + g_pCompositor->warpCursorTo(middle); + } + + if (!g_pInputManager->m_lastFocusOnLS) { + if (Desktop::focusState()->surface()) + g_pInputManager->sendMotionEventsToFocused(); + else + g_pInputManager->simulateMouseMovement(); + } + + const static auto PWARPONWORKSPACECHANGE = CConfigValue("cursor:warp_on_change_workspace"); + + if (*PWARPONWORKSPACECHANGE > 0) { + auto PLAST = ws->getLastFocusedWindow(); + auto HLSurface = Desktop::View::CWLSurface::fromResource(g_pSeatManager->m_state.pointerFocus.lock()); + + if (PLAST && (!HLSurface || HLSurface->view()->type() == Desktop::View::VIEW_TYPE_WINDOW)) + PLAST->warpCursor(*PWARPONWORKSPACECHANGE == 2); + } + + return {}; +} + +static PHLWORKSPACE resolveWorkspaceForChange(const std::string& args) { + static auto PBACKANDFORTH = CConfigValue("binds:workspace_back_and_forth"); + + const auto PMONITOR = Desktop::focusState()->monitor(); + if (!PMONITOR) + return nullptr; + + const auto PCURRENTWORKSPACE = PMONITOR->m_activeWorkspace; + const bool EXPLICITPREVIOUS = args.contains("previous"); + + // handle "previous" workspace + if (args.starts_with("previous")) { + const bool PER_MON = args.contains("_per_monitor"); + const SWorkspaceIDName PPREVWS = PER_MON ? Desktop::History::workspaceTracker()->previousWorkspaceIDName(PCURRENTWORKSPACE, PMONITOR) : + Desktop::History::workspaceTracker()->previousWorkspaceIDName(PCURRENTWORKSPACE); + if (PPREVWS.id == -1 || PPREVWS.id == PCURRENTWORKSPACE->m_id) + return nullptr; + + auto ws = g_pCompositor->getWorkspaceByID(PPREVWS.id); + if (!ws) + ws = g_pCompositor->createNewWorkspace(PPREVWS.id, PMONITOR->m_id, PPREVWS.name.empty() ? std::to_string(PPREVWS.id) : PPREVWS.name); + return ws; + } + + const auto& [workspaceToChangeTo, workspaceName, isAutoID] = getWorkspaceIDNameFromString(args); + if (workspaceToChangeTo == WORKSPACE_INVALID || workspaceToChangeTo == WORKSPACE_NOT_CHANGED) + return nullptr; + + // back_and_forth: if switching to current workspace, go to previous + if (workspaceToChangeTo == PCURRENTWORKSPACE->m_id && (*PBACKANDFORTH || EXPLICITPREVIOUS)) { + const SWorkspaceIDName PPREVWS = args.contains("_per_monitor") ? Desktop::History::workspaceTracker()->previousWorkspaceIDName(PCURRENTWORKSPACE, PMONITOR) : + Desktop::History::workspaceTracker()->previousWorkspaceIDName(PCURRENTWORKSPACE); + if (PPREVWS.id == -1) + return nullptr; + + auto ws = g_pCompositor->getWorkspaceByID(PPREVWS.id); + if (!ws) + ws = g_pCompositor->createNewWorkspace(PPREVWS.id, PMONITOR->m_id, PPREVWS.name.empty() ? std::to_string(PPREVWS.id) : PPREVWS.name); + return ws; + } + + auto ws = g_pCompositor->getWorkspaceByID(workspaceToChangeTo); + if (!ws) + ws = g_pCompositor->createNewWorkspace(workspaceToChangeTo, PMONITOR->m_id, workspaceName); + return ws; +} + +ActionResult Actions::changeWorkspace(const std::string& ws) { + auto p = resolveWorkspaceForChange(ws); + if (!p) + return std::unexpected("invalid workspace"); + return Actions::changeWorkspace(p); +} + +ActionResult Actions::renameWorkspace(PHLWORKSPACE ws, const std::string& s) { + if (!ws) + return std::unexpected("Invalid workspace"); + + ws->rename(s); + + return {}; +} + +ActionResult Actions::moveToMonitor(PHLWORKSPACE ws, PHLMONITOR mon) { + if (!ws) + return std::unexpected("Invalid workspace"); + if (!mon) + return std::unexpected("Invalid monitor"); + + g_pCompositor->moveWorkspaceToMonitor(ws, mon); + + return {}; +} + +ActionResult Actions::changeWorkspaceOnCurrentMonitor(PHLWORKSPACE ws) { + if (!ws) + return std::unexpected("Invalid workspace"); + + const auto PCURRMONITOR = Desktop::focusState()->monitor(); + if (!PCURRMONITOR) + return std::unexpected("No current monitor"); + + if (ws->m_monitor != PCURRMONITOR) { + const auto POLDMONITOR = ws->m_monitor.lock(); + if (!POLDMONITOR) + return std::unexpected("Workspace has no monitor"); + + if (POLDMONITOR->activeWorkspaceID() == ws->m_id) { + g_pCompositor->swapActiveWorkspaces(POLDMONITOR, PCURRMONITOR); + return {}; + } else { + g_pCompositor->moveWorkspaceToMonitor(ws, PCURRMONITOR, true); + } + } + + return changeWorkspace(ws); +} + +ActionResult Actions::toggleSpecial(PHLWORKSPACE special) { + if (!special || !special->m_isSpecialWorkspace) + return std::unexpected("Invalid special workspace"); + + const auto PMONITOR = Desktop::focusState()->monitor(); + if (!PMONITOR) + return std::unexpected("No monitor"); + + bool requestedWorkspaceIsAlreadyOpen = false; + auto specialOpenOnMonitor = PMONITOR->activeSpecialWorkspaceID(); + + for (auto const& m : g_pCompositor->m_monitors) { + if (m->activeSpecialWorkspaceID() == special->m_id) { + requestedWorkspaceIsAlreadyOpen = true; + break; + } + } + + updateRelativeCursorCoords(); + + PHLWORKSPACEREF focusedWorkspace; + + if (requestedWorkspaceIsAlreadyOpen && specialOpenOnMonitor == special->m_id) { + PMONITOR->setSpecialWorkspace(nullptr); + focusedWorkspace = PMONITOR->m_activeWorkspace; + } else { + PMONITOR->setSpecialWorkspace(special); + focusedWorkspace = special; + } + + const static auto PWARPONTOGGLESPECIAL = CConfigValue("cursor:warp_on_toggle_special"); + + if (*PWARPONTOGGLESPECIAL > 0) { + auto PLAST = focusedWorkspace->getLastFocusedWindow(); + auto HLSurface = Desktop::View::CWLSurface::fromResource(g_pSeatManager->m_state.pointerFocus.lock()); + + if (PLAST && (!HLSurface || HLSurface->view()->type() == Desktop::View::VIEW_TYPE_WINDOW)) + PLAST->warpCursor(*PWARPONTOGGLESPECIAL == 2); + } + + return {}; +} + +ActionResult Actions::focusMonitor(PHLMONITOR mon) { + if (!mon) + return std::unexpected("Invalid monitor"); + + tryMoveFocusToMonitor(mon); + + return {}; +} + +ActionResult Actions::swapActiveWorkspaces(PHLMONITOR mon1, PHLMONITOR mon2) { + if (!mon1 || !mon2) + return std::unexpected("Invalid monitor"); + + if (mon1 == mon2) + return {}; + + g_pCompositor->swapActiveWorkspaces(mon1, mon2); + + return {}; +} + +ActionResult Actions::layoutMessage(const std::string& msg) { + auto ret = g_layoutManager->layoutMsg(msg); + if (!ret) + return std::unexpected(ret.error()); + return {}; +} + +ActionResult Actions::moveCursor(const Vector2D& pos) { + g_pCompositor->warpCursorTo(pos, true); + g_pInputManager->simulateMouseMovement(); + return {}; +} + +ActionResult Actions::exit() { + Event::bus()->m_events.exit.emit(); + + if (g_pCompositor->m_finalRequests) + return {}; + + g_pCompositor->stopCompositor(); + return {}; +} + +ActionResult Actions::forceRendererReload() { + bool overAgain = false; + + for (auto const& m : g_pCompositor->m_monitors) { + if (!m->m_output) + continue; + + auto rule = Config::monitorRuleMgr()->get(m); + if (!m->applyMonitorRule(std::move(rule), true)) { + overAgain = true; + break; + } + } + + if (overAgain) // NOLINTNEXTLINE + forceRendererReload(); + + return {}; +} + +ActionResult Actions::toggleSwallow() { + PHLWINDOWREF pWindow = Desktop::focusState()->window(); + + if (!valid(pWindow) || !valid(pWindow->m_swallowed)) + return {}; + + if (pWindow->m_swallowed->m_currentlySwallowed) { + pWindow->m_swallowed->m_currentlySwallowed = false; + pWindow->m_swallowed->setHidden(false); + g_layoutManager->newTarget(pWindow->m_swallowed->layoutTarget(), pWindow->m_workspace->m_space); + } else { + pWindow->m_swallowed->m_currentlySwallowed = true; + pWindow->m_swallowed->setHidden(true); + g_layoutManager->removeTarget(pWindow->m_swallowed->layoutTarget()); + } + + return {}; +} + +ActionResult Actions::dpms(eTogglableAction action, std::optional mon) { + for (auto const& m : g_pCompositor->m_realMonitors) { + if (!m->m_enabled) + continue; + + if (mon.has_value() && m != mon.value()) + continue; + + bool enable; + switch (action) { + case TOGGLE_ACTION_TOGGLE: enable = !m->m_dpmsStatus; break; + case TOGGLE_ACTION_ENABLE: enable = true; break; + case TOGGLE_ACTION_DISABLE: enable = false; break; + } + + m->setDPMS(enable); + g_pCompositor->m_dpmsStateOn = enable; + } + + g_pPointerManager->recheckEnteredOutputs(); + + return {}; +} + +ActionResult Actions::forceIdle(float seconds) { + PROTO::idle->setTimers(seconds * 1000.0); + return {}; +} + +ActionResult Actions::event(const std::string& data) { + g_pEventManager->postEvent(SHyprIPCEvent{.event = "custom", .data = data}); + return {}; +} + +ActionResult Actions::lockGroups(eTogglableAction action) { + switch (action) { + case TOGGLE_ACTION_TOGGLE: g_pKeybindManager->m_groupsLocked = !g_pKeybindManager->m_groupsLocked; break; + case TOGGLE_ACTION_ENABLE: g_pKeybindManager->m_groupsLocked = true; break; + case TOGGLE_ACTION_DISABLE: g_pKeybindManager->m_groupsLocked = false; break; + } + + g_pEventManager->postEvent(SHyprIPCEvent{.event = "lockgroups", .data = g_pKeybindManager->m_groupsLocked ? "1" : "0"}); + g_pCompositor->updateAllWindowsAnimatedDecorationValues(); + + return {}; +} + +ActionResult Actions::lockActiveGroup(eTogglableAction action) { + const auto PWINDOW = Desktop::focusState()->window(); + if (!PWINDOW) + return std::unexpected("No window found"); + + if (!PWINDOW->m_group) + return std::unexpected("Window not in a group"); + + switch (action) { + case TOGGLE_ACTION_TOGGLE: PWINDOW->m_group->setLocked(!PWINDOW->m_group->locked()); break; + case TOGGLE_ACTION_ENABLE: PWINDOW->m_group->setLocked(true); break; + case TOGGLE_ACTION_DISABLE: PWINDOW->m_group->setLocked(false); break; + } + + PWINDOW->updateDecorationValues(); + + return {}; +} + +static void moveWindowIntoGroupHelper(PHLWINDOW pWindow, PHLWINDOW pWindowInDirection) { + if (!pWindowInDirection->m_group || pWindowInDirection->m_group->denied()) + return; + + updateRelativeCursorCoords(); + + if (pWindow->m_monitor != pWindowInDirection->m_monitor) { + pWindow->moveToWorkspace(pWindowInDirection->m_workspace); + pWindow->m_monitor = pWindowInDirection->m_monitor; + } + + if (pWindow->m_group) + pWindow->m_group->remove(pWindow); + + pWindowInDirection->m_group->add(pWindow); + pWindowInDirection->m_group->setCurrent(pWindow); + pWindow->updateWindowDecos(); + Desktop::focusState()->fullWindowFocus(pWindow, Desktop::FOCUS_REASON_KEYBIND); + pWindow->warpCursor(); + + g_pEventManager->postEvent(SHyprIPCEvent{.event = "moveintogroup", .data = std::format("{:x}", rc(pWindow.get()))}); +} + +static void moveWindowOutOfGroupHelper(PHLWINDOW pWindow, Math::eDirection direction = Math::DIRECTION_DEFAULT) { + static auto BFOCUSREMOVEDWINDOW = CConfigValue("group:focus_removed_window"); + + if (!pWindow->m_group) + return; + + WP group = pWindow->m_group; + + pWindow->m_group->remove(pWindow, direction); + + if (*BFOCUSREMOVEDWINDOW || !group) { + Desktop::focusState()->fullWindowFocus(pWindow, Desktop::FOCUS_REASON_KEYBIND); + pWindow->warpCursor(); + } else { + Desktop::focusState()->fullWindowFocus(group->current(), Desktop::FOCUS_REASON_KEYBIND); + group->current()->warpCursor(); + } + + g_pEventManager->postEvent(SHyprIPCEvent{.event = "moveoutofgroup", .data = std::format("{:x}", rc(pWindow.get()))}); +} + +ActionResult Actions::moveIntoGroup(Math::eDirection direction, std::optional w) { + static auto PIGNOREGROUPLOCK = CConfigValue("binds:ignore_group_lock"); + + if (!*PIGNOREGROUPLOCK && g_pKeybindManager->m_groupsLocked) + return {}; + + auto window = xtract(w); + if (!window) + return std::unexpected("No target found."); + + auto PWINDOWINDIR = g_pCompositor->getWindowInDirection(window, direction); + + if (!PWINDOWINDIR || !PWINDOWINDIR->m_group) + return {}; + + if (!*PIGNOREGROUPLOCK && (PWINDOWINDIR->m_group->locked() || (window->m_group && window->m_group->locked()))) + return {}; + + moveWindowIntoGroupHelper(window, PWINDOWINDIR); + + return {}; +} + +ActionResult Actions::moveOutOfGroup(Math::eDirection direction, std::optional w) { + static auto PIGNOREGROUPLOCK = CConfigValue("binds:ignore_group_lock"); + + if (!*PIGNOREGROUPLOCK && g_pKeybindManager->m_groupsLocked) + return std::unexpected("Groups locked"); + + auto window = xtract(w); + if (!window) + return std::unexpected("No target found."); + + if (!window->m_group) + return std::unexpected("Window not in a group"); + + moveWindowOutOfGroupHelper(window, direction); + + return {}; +} + +ActionResult Actions::moveGroupWindow(bool forward) { + const auto PLASTWINDOW = Desktop::focusState()->window(); + if (!PLASTWINDOW) + return std::unexpected("No window found"); + + if (!PLASTWINDOW->m_group) + return std::unexpected("Window not in a group"); + + if (forward) + PLASTWINDOW->m_group->swapWithNext(); + else + PLASTWINDOW->m_group->swapWithLast(); + + return {}; +} + +ActionResult Actions::moveWindowOrGroup(Math::eDirection direction, std::optional w) { + static auto PIGNOREGROUPLOCK = CConfigValue("binds:ignore_group_lock"); + + auto window = xtract(w); + if (!window) + return std::unexpected("No target found."); + + if (window->isFullscreen()) + return {}; + + if (!*PIGNOREGROUPLOCK && g_pKeybindManager->m_groupsLocked) { + g_layoutManager->moveInDirection(window->layoutTarget(), dirToString(direction)); + return {}; + } + + const auto PWINDOWINDIR = g_pCompositor->getWindowInDirection(window, direction); + + const bool ISWINDOWGROUP = window->m_group; + const bool ISWINDOWGROUPLOCKED = ISWINDOWGROUP && window->m_group->locked(); + const bool ISWINDOWGROUPSINGLE = ISWINDOWGROUP && window->m_group->size() == 1; + const bool ISWINDOWGROUPDENIED = ISWINDOWGROUP && window->m_group->denied(); + + updateRelativeCursorCoords(); + + if (PWINDOWINDIR && PWINDOWINDIR->m_group) { + if (!*PIGNOREGROUPLOCK && (PWINDOWINDIR->m_group->locked() || ISWINDOWGROUPLOCKED || ISWINDOWGROUPDENIED)) { + g_layoutManager->moveInDirection(window->layoutTarget(), dirToString(direction)); + window->warpCursor(); + } else + moveWindowIntoGroupHelper(window, PWINDOWINDIR); + } else if (PWINDOWINDIR) { + if ((!*PIGNOREGROUPLOCK && ISWINDOWGROUPLOCKED) || !ISWINDOWGROUP || (ISWINDOWGROUPSINGLE && window->m_groupRules & Desktop::View::GROUP_SET_ALWAYS)) { + g_layoutManager->moveInDirection(window->layoutTarget(), dirToString(direction)); + window->warpCursor(); + } else + moveWindowOutOfGroupHelper(window, direction); + } else if ((*PIGNOREGROUPLOCK || !ISWINDOWGROUPLOCKED) && ISWINDOWGROUP) { + moveWindowOutOfGroupHelper(window, direction); + } else if (!PWINDOWINDIR && !ISWINDOWGROUP) { + g_layoutManager->moveInDirection(window->layoutTarget(), dirToString(direction)); + window->warpCursor(); + } + + window->updateDecorationValues(); + + return {}; +} + +ActionResult Actions::denyWindowFromGroup(eTogglableAction action) { + const auto PWINDOW = Desktop::focusState()->window(); + if (!PWINDOW || !PWINDOW->m_group) + return {}; + + switch (action) { + case TOGGLE_ACTION_TOGGLE: PWINDOW->m_group->setDenied(!PWINDOW->m_group->denied()); break; + case TOGGLE_ACTION_ENABLE: PWINDOW->m_group->setDenied(true); break; + case TOGGLE_ACTION_DISABLE: PWINDOW->m_group->setDenied(false); break; + } + + PWINDOW->updateDecorationValues(); + + return {}; +} + +ActionResult Actions::pass(std::optional w) { + auto window = xtract(w); + if (!window) + return std::unexpected("No target found."); + + if (!g_pSeatManager->m_keyboard) + return std::unexpected("No keyboard"); + + const auto& S = *Config::Actions::state(); + const auto XWTOXW = window->m_isX11 && Desktop::focusState()->window() && Desktop::focusState()->window()->m_isX11; + const auto LASTMOUSESURF = g_pSeatManager->m_state.pointerFocus.lock(); + const auto LASTKBSURF = g_pSeatManager->m_state.keyboardFocus.lock(); + + if (!XWTOXW) { + if (S.m_lastCode != 0) + g_pSeatManager->setKeyboardFocus(window->wlSurface()->resource()); + else + g_pSeatManager->setPointerFocus(window->wlSurface()->resource(), {1, 1}); + } + + g_pSeatManager->sendKeyboardMods(g_pInputManager->getModsFromAllKBs(), 0, 0, 0); + + if (S.m_passPressed == 1) { + if (S.m_lastCode != 0) + g_pSeatManager->sendKeyboardKey(S.m_timeLastMs, S.m_lastCode - 8, WL_KEYBOARD_KEY_STATE_PRESSED); + else + g_pSeatManager->sendPointerButton(S.m_timeLastMs, S.m_lastMouseCode, WL_POINTER_BUTTON_STATE_PRESSED); + } else if (S.m_passPressed == 0) { + if (S.m_lastCode != 0) + g_pSeatManager->sendKeyboardKey(S.m_timeLastMs, S.m_lastCode - 8, WL_KEYBOARD_KEY_STATE_RELEASED); + else + g_pSeatManager->sendPointerButton(S.m_timeLastMs, S.m_lastMouseCode, WL_POINTER_BUTTON_STATE_RELEASED); + } else { + if (S.m_lastCode != 0) { + g_pSeatManager->sendKeyboardKey(S.m_timeLastMs, S.m_lastCode - 8, WL_KEYBOARD_KEY_STATE_PRESSED); + g_pSeatManager->sendKeyboardKey(S.m_timeLastMs, S.m_lastCode - 8, WL_KEYBOARD_KEY_STATE_RELEASED); + } else { + g_pSeatManager->sendPointerButton(S.m_timeLastMs, S.m_lastMouseCode, WL_POINTER_BUTTON_STATE_PRESSED); + g_pSeatManager->sendPointerButton(S.m_timeLastMs, S.m_lastMouseCode, WL_POINTER_BUTTON_STATE_RELEASED); + } + } + + if (XWTOXW) + return {}; + + if (window->m_isX11) { + if (S.m_lastCode != 0) { + g_pSeatManager->m_state.keyboardFocus.reset(); + g_pSeatManager->m_state.keyboardFocusResource.reset(); + } else { + g_pSeatManager->m_state.pointerFocus.reset(); + g_pSeatManager->m_state.pointerFocusResource.reset(); + } + } + + const auto SL = window->m_realPosition->goal() - g_pInputManager->getMouseCoordsInternal(); + + if (S.m_lastCode != 0) + g_pSeatManager->setKeyboardFocus(LASTKBSURF); + else + g_pSeatManager->setPointerFocus(LASTMOUSESURF, SL); + + return {}; +} + +ActionResult Actions::pass(uint32_t modMask, uint32_t key, std::optional w) { + auto window = xtract(w); + + const auto& S = *Config::Actions::state(); + const bool isMouse = key >= 272 && key < 0x160; // mouse button range + const auto LASTSURFACE = Desktop::focusState()->surface(); + + if (window) { + if (!g_pSeatManager->m_keyboard) + return std::unexpected("No keyboard"); + + if (!isMouse) + g_pSeatManager->setKeyboardFocus(window->wlSurface()->resource()); + else + g_pSeatManager->setPointerFocus(window->wlSurface()->resource(), {1, 1}); + + // if wl -> xwl, activate destination + if (window->m_isX11 && Desktop::focusState()->window() && !Desktop::focusState()->window()->m_isX11) + g_pXWaylandManager->activateSurface(window->wlSurface()->resource(), true); + // if xwl -> xwl, send to current + if (window->m_isX11 && Desktop::focusState()->window() && Desktop::focusState()->window()->m_isX11) + window = nullptr; + } + + g_pSeatManager->sendKeyboardMods(modMask, 0, 0, 0); + + if (S.m_passPressed == 1) { + if (!isMouse) + g_pSeatManager->sendKeyboardKey(S.m_timeLastMs, key - 8, WL_KEYBOARD_KEY_STATE_PRESSED); + else + g_pSeatManager->sendPointerButton(S.m_timeLastMs, key, WL_POINTER_BUTTON_STATE_PRESSED); + } else if (S.m_passPressed == 0) { + if (!isMouse) + g_pSeatManager->sendKeyboardKey(S.m_timeLastMs, key - 8, WL_KEYBOARD_KEY_STATE_RELEASED); + else + g_pSeatManager->sendPointerButton(S.m_timeLastMs, key, WL_POINTER_BUTTON_STATE_RELEASED); + } else { + if (!isMouse) { + g_pSeatManager->sendKeyboardKey(S.m_timeLastMs, key - 8, WL_KEYBOARD_KEY_STATE_PRESSED); + g_pSeatManager->sendKeyboardKey(S.m_timeLastMs, key - 8, WL_KEYBOARD_KEY_STATE_RELEASED); + } else { + g_pSeatManager->sendPointerButton(S.m_timeLastMs, key, WL_POINTER_BUTTON_STATE_PRESSED); + g_pSeatManager->sendPointerButton(S.m_timeLastMs, key, WL_POINTER_BUTTON_STATE_RELEASED); + } + } + + g_pSeatManager->sendKeyboardMods(0, 0, 0, 0); + + if (!window) + return {}; + + if (window->m_isX11) { + if (!isMouse) { + g_pSeatManager->m_state.keyboardFocus.reset(); + g_pSeatManager->m_state.keyboardFocusResource.reset(); + } else { + g_pSeatManager->m_state.pointerFocus.reset(); + g_pSeatManager->m_state.pointerFocusResource.reset(); + } + } + + const auto SL = window->m_realPosition->goal() - g_pInputManager->getMouseCoordsInternal(); + + if (!isMouse) + g_pSeatManager->setKeyboardFocus(LASTSURFACE); + else + g_pSeatManager->setPointerFocus(LASTSURFACE, SL); + + return {}; +} + +ActionResult Actions::sendKeyState(uint32_t modMask, uint32_t key, uint32_t keyState, std::optional w) { + // keyState: 0 = up, 1 = down, 2 = repeat (down+down) + const int oldPassPressed = Config::Actions::state()->m_passPressed; + + if (keyState == 1 || keyState == 2) + Config::Actions::state()->m_passPressed = 1; + else + Config::Actions::state()->m_passPressed = 0; + + auto result = Actions::pass(modMask, key, w); + + if (keyState == 2 && result.has_value()) + result = Actions::pass(modMask, key, w); + + Config::Actions::state()->m_passPressed = oldPassPressed; + + return result; +} + +ActionResult Actions::global(const std::string& action) { + const auto COLONPOS = action.find_first_of(':'); + if (COLONPOS == std::string::npos) + return {}; + + const auto APPID = action.substr(0, COLONPOS); + const auto NAME = action.substr(COLONPOS + 1); + + if (NAME.empty()) + return {}; + + if (!PROTO::globalShortcuts->isTaken(APPID, NAME)) + return {}; + + PROTO::globalShortcuts->sendGlobalShortcutEvent(APPID, NAME, Config::Actions::state()->m_passPressed); + + return {}; +} + +ActionResult Actions::mouse(const std::string& action) { + const bool PRESSED = Config::Actions::state()->m_passPressed == 1; + + if (!PRESSED) + return SActionResult{.passEvent = CKeybindManager::changeMouseBindMode(MBIND_INVALID).passEvent}; + + if (action == "movewindow") + return SActionResult{.passEvent = CKeybindManager::changeMouseBindMode(MBIND_MOVE).passEvent}; + + // resizewindow with optional ratio mode + try { + const auto SPACEPOS = action.find(' '); + if (SPACEPOS != std::string::npos) { + switch (std::stoi(action.substr(SPACEPOS + 1))) { + case 1: return SActionResult{.passEvent = CKeybindManager::changeMouseBindMode(MBIND_RESIZE_FORCE_RATIO).passEvent}; + case 2: return SActionResult{.passEvent = CKeybindManager::changeMouseBindMode(MBIND_RESIZE_BLOCK_RATIO).passEvent}; + default: break; + } + } + } catch (...) { /* stoi failed, fall through to default resize */ + } + + return SActionResult{.passEvent = CKeybindManager::changeMouseBindMode(MBIND_RESIZE).passEvent}; +} + +ActionResult Actions::setSubmap(const std::string& submap) { + if (submap == "reset" || submap.empty()) { + Config::Actions::state()->m_currentSubmap = ""; + g_pEventManager->postEvent(SHyprIPCEvent{.event = "submap", .data = ""}); + Event::bus()->m_events.keybinds.submap.emit(std::string("")); + return {}; + } + + for (const auto& k : g_pKeybindManager->m_keybinds) { + if (k->submap.name == submap) { + Config::Actions::state()->m_currentSubmap = submap; + g_pEventManager->postEvent(SHyprIPCEvent{.event = "submap", .data = submap}); + Event::bus()->m_events.keybinds.submap.emit(submap); + return {}; + } + } + + return std::unexpected(std::format("Cannot set submap {}, submap doesn't exist (wasn't registered!)", submap)); +} + +ActionResult Actions::cycleNext(const bool next, std::optional onlyTiled, std::optional onlyFloating, std::optional w) { + auto window = xtract(w); + + if (!window) { + const auto PWS = Desktop::focusState()->monitor()->m_activeWorkspace; + if (PWS && PWS->getWindows() > 0) { + const auto PFIRST = PWS->getFirstWindow(); + switchToWindow(PFIRST); + } + return {}; + } + + // If requesting tiled-only and we're on a tiled window, try layout message for supported layouts + if (onlyTiled.value_or(false) && !window->m_isFloating) { + if (const auto SPACE = window->layoutTarget()->space(); SPACE) { + constexpr const std::array LAYOUTS_WITH_CYCLE_NEXT = { + &typeid(Layout::Tiled::CMonocleAlgorithm), + &typeid(Layout::Tiled::CMasterAlgorithm), + }; + + if (std::ranges::contains(LAYOUTS_WITH_CYCLE_NEXT, &typeid(*SPACE->algorithm()->tiledAlgo().get()))) { + // NOLINTNEXTLINE + Actions::layoutMessage(!next ? "cyclenext, b" : "cyclenext"); + return {}; + } + } + } + + std::optional floatStatus = {}; + if (onlyFloating.value_or(false)) + floatStatus = true; + + const auto& cycled = g_pCompositor->getWindowCycle(window, true, floatStatus, false, !next); + + switchToWindow(cycled); + + return {}; +} + +ActionResult Actions::moveIntoOrCreateGroup(Math::eDirection dir, std::optional w) { + static auto PIGNOREGROUPLOCK = CConfigValue("binds:ignore_group_lock"); + + if (!*PIGNOREGROUPLOCK && g_pKeybindManager->m_groupsLocked) + return {}; + + if (dir == Math::DIRECTION_DEFAULT) { + Log::logger->log(Log::ERR, "Cannot move into or create group in direction {}, unsupported direction. Supported: l,r,u/t,d/b", dirToString(dir)); + return std::unexpected(std::format("Cannot move into or create group in direction {}, unsupported direction. Supported: l,r,u/t,d/b", dirToString(dir))); + } + + const auto PWINDOW = xtract(w); + + if (!PWINDOW) + return {}; + + auto PWINDOWINDIR = g_pCompositor->getWindowInDirection(PWINDOW, dir); + + if (!PWINDOWINDIR) + return {}; + + if (!PWINDOWINDIR->m_group) { + if (PWINDOWINDIR->isFullscreen()) + return {}; + + PWINDOWINDIR->m_group = Desktop::View::CGroup::create({PWINDOWINDIR}); + } + + const auto GROUP = PWINDOWINDIR->m_group; + + if (!*PIGNOREGROUPLOCK && (GROUP->locked() || (PWINDOW->m_group && PWINDOW->m_group->locked()))) + return {}; + + moveWindowIntoGroupHelper(PWINDOW, PWINDOWINDIR); + + return {}; +} -- cgit v1.2.3