#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 {}; if (window->m_closeableSince > Time::steadyNow()) return actionError("can't close window, it's not closeable yet (noclosefor)", eActionErrorLevel::WARNING, eActionErrorCode::INVALID_STATE); window->sendClose(); return {}; } ActionResult Actions::killWindow(std::optional w) { auto window = xtract(w); if (!window) return {}; kill(window->getPID(), SIGKILL); return {}; } ActionResult Actions::signalWindow(int sig, std::optional w) { auto window = xtract(w); if (!window) return {}; 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 {}; 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 {}; 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 {}; if (!window->m_isFloating || window->isFullscreen()) return actionError("Window does not qualify to be pinned", eActionErrorLevel::WARNING, eActionErrorCode::INVALID_STATE); 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; window->updateFullscreenInputState(); *window->alpha(Desktop::View::WINDOW_ALPHA_FULLSCREEN) = window->isBlockedByFullscreen() ? 0.F : 1.F; const auto PMONITOR = window->m_monitor.lock(); if (!PMONITOR) return actionError("Window has no monitor", eActionErrorLevel::WARNING, eActionErrorCode::INVALID_STATE); 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 {}; 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 {}; 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 {}; if (!ws) return actionError("No workspace to move to", eActionErrorLevel::WARNING, eActionErrorCode::INVALID_ARGUMENT); 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, true) : 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 actionError(std::format("Nothing to focus to in direction {}", Math::toString(dir)), eActionErrorLevel::INFO, eActionErrorCode::NOT_FOUND); const auto PMONITOR = PLASTWINDOW->m_monitor.lock(); if (!PMONITOR) return actionError("Window has no monitor", eActionErrorLevel::WARNING, eActionErrorCode::INVALID_STATE); 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 {}; const auto PWORKSPACE = window->m_workspace; if (!PWORKSPACE) return actionError("Window has no workspace", eActionErrorLevel::WARNING, eActionErrorCode::INVALID_STATE); 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 {}; if (window->isFullscreen()) return actionError("Can't move fullscreen window", eActionErrorLevel::WARNING, eActionErrorCode::INVALID_STATE); 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 {}; if (window->isFullscreen()) return actionError("Can't swap fullscreen window", eActionErrorLevel::WARNING, eActionErrorCode::INVALID_STATE); const auto PWINDOWTOCHANGETO = g_pCompositor->getWindowInDirection(window, dir); if (!PWINDOWTOCHANGETO || PWINDOWTOCHANGETO == window) return actionError("No window to swap with in that direction", eActionErrorLevel::INFO, eActionErrorCode::NOT_FOUND); 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 {}; if (!other) return actionError("No window to swap with", eActionErrorLevel::INFO, eActionErrorCode::NOT_FOUND); if (other == window) return actionError("Can't swap a window with itself", eActionErrorLevel::WARNING, eActionErrorCode::INVALID_STATE); if (window->isFullscreen() || other->isFullscreen()) return actionError("Can't swap fullscreen window", eActionErrorLevel::WARNING, eActionErrorCode::INVALID_STATE); 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 actionError("History too short", eActionErrorLevel::INFO, eActionErrorCode::NOT_FOUND); const auto PWINDOWPREV = HISTORY[HISTORY.size() - 2].lock(); if (!PWINDOWPREV) return actionError("Window not found", eActionErrorLevel::INFO, eActionErrorCode::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 actionError("Window not found", eActionErrorLevel::INFO, eActionErrorCode::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 actionError("No floating window found", eActionErrorLevel::INFO, eActionErrorCode::NOT_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 {}; if (corner < 0 || corner > 3) return actionError("Corner must be 0 - 3", eActionErrorLevel::ERROR, eActionErrorCode::INVALID_ARGUMENT); 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 {}; if (window->isFullscreen()) return actionError("Window is fullscreen", eActionErrorLevel::WARNING, eActionErrorCode::INVALID_STATE); if (!relative && (size.x < 1 || size.y < 1)) return actionError("Invalid size", eActionErrorLevel::ERROR, eActionErrorCode::INVALID_ARGUMENT); 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 {}; if (window->isFullscreen()) return actionError("Window is fullscreen", eActionErrorLevel::WARNING, eActionErrorCode::INVALID_STATE); 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 {}; if (window->m_ruleApplicator->m_tagKeeper.applyTag(tagStr)) { window->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_TAG); window->updateDecorationValues(); } return {}; } ActionResult Actions::clearTags(std::optional w) { auto window = xtract(w); if (!window) return {}; if (window->m_ruleApplicator->m_tagKeeper.clearTags()) { 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 {}; 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 actionError("No window to swap with", eActionErrorLevel::INFO, eActionErrorCode::NOT_FOUND); 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 {}; 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 {}; 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 actionError("Invalid prop name", eActionErrorLevel::ERROR, eActionErrorCode::INVALID_ARGUMENT); } 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 {}; 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 {}; if (!window->m_group) return actionError("Window is not in a group", eActionErrorLevel::WARNING, eActionErrorCode::INVALID_STATE); if (window->m_group->size() == 1) return actionError("Only one window in group", eActionErrorLevel::INFO, eActionErrorCode::INVALID_STATE); window->m_group->moveCurrent(forward); return {}; } ActionResult Actions::setGroupActive(int index, std::optional w) { auto window = xtract(w); if (!window) return {}; if (!window->m_group) return actionError("Window is not in a group", eActionErrorLevel::WARNING, eActionErrorCode::INVALID_STATE); if (window->m_group->size() == 1) return actionError("Only one window in group", eActionErrorLevel::INFO, eActionErrorCode::INVALID_STATE); if (index <= 0) window->m_group->setCurrent(window->m_group->size() - 1); else if (sc(index) > window->m_group->size()) return actionError("Index out of range", eActionErrorLevel::ERROR, eActionErrorCode::INVALID_ARGUMENT); else window->m_group->setCurrent(index - 1); return {}; } ActionResult Actions::changeWorkspace(PHLWORKSPACE ws) { if (!ws) return actionError("Invalid workspace", eActionErrorLevel::WARNING, eActionErrorCode::NO_TARGET); 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 actionError("No focused monitor", eActionErrorLevel::WARNING, eActionErrorCode::INVALID_STATE); 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 actionError("Workspace has no monitor", eActionErrorLevel::WARNING, eActionErrorCode::INVALID_STATE); 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 actionError("Bad workspace", eActionErrorLevel::WARNING, eActionErrorCode::NO_TARGET); return Actions::changeWorkspace(p); } ActionResult Actions::renameWorkspace(PHLWORKSPACE ws, const std::string& s) { if (!ws) return actionError("Bad workspace", eActionErrorLevel::WARNING, eActionErrorCode::NO_TARGET); ws->rename(s); return {}; } ActionResult Actions::moveToMonitor(PHLWORKSPACE ws, PHLMONITOR mon) { if (!ws) return actionError("Bad workspace", eActionErrorLevel::WARNING, eActionErrorCode::NO_TARGET); if (!mon) return actionError("Bad monitor", eActionErrorLevel::WARNING, eActionErrorCode::NO_TARGET); g_pCompositor->moveWorkspaceToMonitor(ws, mon); return {}; } ActionResult Actions::changeWorkspaceOnCurrentMonitor(PHLWORKSPACE ws) { if (!ws) return actionError("Bad workspace", eActionErrorLevel::WARNING, eActionErrorCode::NO_TARGET); const auto PCURRMONITOR = Desktop::focusState()->monitor(); if (!PCURRMONITOR) return actionError("No focused monitor", eActionErrorLevel::WARNING, eActionErrorCode::INVALID_STATE); if (ws->m_monitor != PCURRMONITOR) { const auto POLDMONITOR = ws->m_monitor.lock(); if (!POLDMONITOR) return actionError("Workspace has no monitor", eActionErrorLevel::WARNING, eActionErrorCode::INVALID_STATE); 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 actionError("Bad special workspace", eActionErrorLevel::WARNING, eActionErrorCode::NO_TARGET); const auto PMONITOR = Desktop::focusState()->monitor(); if (!PMONITOR) return actionError("No focused monitor", eActionErrorLevel::WARNING, eActionErrorCode::INVALID_STATE); 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 actionError("Bad monitor", eActionErrorLevel::WARNING, eActionErrorCode::NO_TARGET); tryMoveFocusToMonitor(mon); return {}; } ActionResult Actions::swapActiveWorkspaces(PHLMONITOR mon1, PHLMONITOR mon2) { if (!mon1 || !mon2) return actionError("Bad monitor", eActionErrorLevel::WARNING, eActionErrorCode::NO_TARGET); 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 actionError("No window found", eActionErrorLevel::INFO, eActionErrorCode::NO_TARGET); if (!PWINDOW->m_group) return actionError("Window not in a group", eActionErrorLevel::WARNING, eActionErrorCode::INVALID_STATE); 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 {}; 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 actionError("Groups locked", eActionErrorLevel::INFO, eActionErrorCode::INVALID_STATE); auto window = xtract(w); if (!window) return {}; if (!window->m_group) return actionError("Window not in a group", eActionErrorLevel::WARNING, eActionErrorCode::INVALID_STATE); moveWindowOutOfGroupHelper(window, direction); return {}; } ActionResult Actions::moveGroupWindow(bool forward) { const auto PLASTWINDOW = Desktop::focusState()->window(); if (!PLASTWINDOW) return actionError("No window found", eActionErrorLevel::INFO, eActionErrorCode::NO_TARGET); if (!PLASTWINDOW->m_group) return actionError("Window not in a group", eActionErrorLevel::WARNING, eActionErrorCode::INVALID_STATE); 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 {}; 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 {}; if (!g_pSeatManager->m_keyboard) return actionError("No keyboard connected", eActionErrorLevel::INFO, eActionErrorCode::NO_TARGET); 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 actionError("No keyboard connected", eActionErrorLevel::INFO, eActionErrorCode::NO_TARGET); 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, window->m_workspace && window->m_workspace->m_hasFullscreenWindow); 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 {}; }