diff --git a/src/Compositor.cpp b/src/Compositor.cpp index 3e642ad90..f97447d3c 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -807,343 +807,6 @@ void CCompositor::startCompositor() { g_pEventLoopManager->enterLoop(); } -bool CCompositor::isWindowActive(PHLWINDOW pWindow) { - if (!Desktop::focusState()->window() && !Desktop::focusState()->surface()) - return false; - - if (!pWindow->m_isMapped) - return false; - - const auto PSURFACE = pWindow->wlSurface()->resource(); - - return PSURFACE == Desktop::focusState()->surface() || pWindow == Desktop::focusState()->window(); -} - -void CCompositor::changeWindowZOrder(PHLWINDOW pWindow, bool top) { - if (!validMapped(pWindow)) - return; - - if (top) - pWindow->m_createdOverFullscreen = true; - else - pWindow->m_createdOverFullscreen = false; - - pWindow->updateFullscreenInputState(); - *pWindow->alpha(WINDOW_ALPHA_FULLSCREEN) = pWindow->isBlockedByFullscreen() ? 0.F : 1.F; - - const auto& WINDOWS = Desktop::windowState()->windows(); - - if (pWindow == (top ? WINDOWS.back() : WINDOWS.front())) - return; - - auto moveToZ = [&](PHLWINDOW pw, bool top) -> void { - if (top) - Desktop::windowState()->moveToTop(pw); - else - Desktop::windowState()->moveToBottom(pw); - - if (pw->m_isMapped) - g_pHyprRenderer->damageMonitor(pw->m_monitor.lock()); - }; - - if (!pWindow->m_isX11) - moveToZ(pWindow, top); - else { - // move X11 window stack - - std::vector toMove; - - auto x11Stack = [&](PHLWINDOW pw, bool top, auto&& x11Stack) -> void { - if (top) - toMove.emplace_back(pw); - else - toMove.insert(toMove.begin(), pw); - - for (auto const& w : WINDOWS) { - if (w->m_isMapped && !w->isHidden() && w->m_isX11 && w->x11TransientFor() == pw && w != pw && std::ranges::find(toMove, w) == toMove.end()) { - x11Stack(w, top, x11Stack); - } - } - }; - - x11Stack(pWindow, top, x11Stack); - for (const auto& it : toMove) { - moveToZ(it, top); - } - } -} - -PHLWINDOW CCompositor::getWindowInDirection(PHLWINDOW pWindow, Math::eDirection dir) { - if (dir == Math::DIRECTION_DEFAULT) - return nullptr; - - const auto PMONITOR = pWindow->m_monitor.lock(); - - if (!PMONITOR) - return nullptr; // ?? - - const auto WINDOWIDEALBB = pWindow->isFullscreen() ? CBox{PMONITOR->m_position, PMONITOR->m_size} : pWindow->getWindowIdealBoundingBoxIgnoreReserved(); - const auto PWORKSPACE = pWindow->m_workspace; - - if (!PWORKSPACE) - return nullptr; // ?? - - return getWindowInDirection(WINDOWIDEALBB, PWORKSPACE, dir, pWindow->m_isFloating, pWindow, pWindow->m_isFloating); -} - -PHLWINDOW CCompositor::getWindowInDirection(const CBox& box, PHLWORKSPACE pWorkspace, Math::eDirection dir, bool floatingPreference, PHLWINDOW ignoreWindow, bool useVectorAngles) { - if (dir == Math::DIRECTION_DEFAULT) - return nullptr; - - // 0 -> history, 1 -> shared length - static auto PMETHOD = CConfigValue("binds:focus_preferred_method"); - static auto PMONITORFALLBACK = CConfigValue("binds:window_direction_monitor_fallback"); - - const auto POSA = box.pos(); - const auto SIZEA = box.size(); - - auto leaderValue = -1; - PHLWINDOW leaderWindow = nullptr; - - if (!useVectorAngles) { - // helper to check if two rectangles are adjacent along an axis, considering slight overlaps. - // returns true if: STICKS (delta <= 2) OR rectangles overlap but no more than 50% of the smaller dimension. - static auto isAdjacent = [](const double aMin, const double aMax, const double bMin, const double bMax) -> bool { - constexpr double STICK_THRESHOLD = 2.0; - constexpr double MAX_OVERLAP_RATIO = 0.5; - - const double aEdge = aMin; - const double bEdge = bMax; - const double delta = aEdge - bEdge; - - // old STICKS check for 2px - if (std::abs(delta) < STICK_THRESHOLD) - return true; - - if (delta >= 0) - return false; - - const double overlap = -delta; - const double sizeA = aMax - aMin; - const double sizeB = bMax - bMin; - - // reject if one rectangle fully contains the other - if ((bMin <= aMin && bMax >= aMax) || (aMin <= bMin && aMax >= bMax)) - return false; - - // accept if overlap is at most 50% of the smaller dimension - return overlap <= std::min(sizeA, sizeB) * MAX_OVERLAP_RATIO; - }; - - auto find = [&]() { - for (auto const& w : Desktop::windowState()->windows()) { - if (w == ignoreWindow || !w->m_workspace || !w->m_isMapped || (!w->isFullscreen() && w->m_isFloating) || !w->m_workspace->isVisible()) - continue; - - if (w->isHidden()) - continue; - - // check if the input is blocked by anything except BELOW_FULLSCREEN - if (w->isInputBlocked(INPUT_BLOCK_ALL & (~INPUT_BLOCK_BELOW_FULLSCREEN))) - continue; - - if (pWorkspace->m_monitor == w->m_monitor && pWorkspace != w->m_workspace) - continue; - - if (pWorkspace->m_hasFullscreenWindow && !w->isAllowedOverFullscreen()) - continue; - - if (!*PMONITORFALLBACK && pWorkspace->m_monitor != w->m_monitor) - continue; - - if (w->m_isFloating != floatingPreference) - continue; - - // prioritize windows on the same workspace. - // this is especially important for scrolling layouts - we want to first move to a window - // on the same workspace before moving onto another. - const auto LEADER_IS_ON_SAME_WORKSPACE = leaderWindow && leaderWindow->m_workspace == pWorkspace; - - if (LEADER_IS_ON_SAME_WORKSPACE && w->m_workspace != pWorkspace) - continue; - - const auto BWINDOWIDEALBB = w->getWindowIdealBoundingBoxIgnoreReserved(); - - const auto POSB = Vector2D(BWINDOWIDEALBB.x, BWINDOWIDEALBB.y); - const auto SIZEB = Vector2D(BWINDOWIDEALBB.width, BWINDOWIDEALBB.height); - - double intersectLength = -1; - - switch (dir) { - case Math::DIRECTION_LEFT: - if (isAdjacent(POSA.x, POSA.x + SIZEA.x, POSB.x, POSB.x + SIZEB.x)) - intersectLength = std::max(0.0, std::min(POSA.y + SIZEA.y, POSB.y + SIZEB.y) - std::max(POSA.y, POSB.y)); - break; - case Math::DIRECTION_RIGHT: - if (isAdjacent(POSB.x, POSB.x + SIZEB.x, POSA.x, POSA.x + SIZEA.x)) - intersectLength = std::max(0.0, std::min(POSA.y + SIZEA.y, POSB.y + SIZEB.y) - std::max(POSA.y, POSB.y)); - break; - case Math::DIRECTION_UP: - if (isAdjacent(POSA.y, POSA.y + SIZEA.y, POSB.y, POSB.y + SIZEB.y)) - intersectLength = std::max(0.0, std::min(POSA.x + SIZEA.x, POSB.x + SIZEB.x) - std::max(POSA.x, POSB.x)); - break; - case Math::DIRECTION_DOWN: - if (isAdjacent(POSB.y, POSB.y + SIZEB.y, POSA.y, POSA.y + SIZEA.y)) - intersectLength = std::max(0.0, std::min(POSA.x + SIZEA.x, POSB.x + SIZEB.x) - std::max(POSA.x, POSB.x)); - break; - default: break; - } - - // if we have a leader on another workspace, and this window is on the same workspace, - // override minimum requirements and always select this as the new leader - const bool OVERRIDE_MIN_REQ = leaderWindow && !LEADER_IS_ON_SAME_WORKSPACE && w->m_workspace == pWorkspace; - - // ...as long as there is any intersect. - if (intersectLength <= 1) - continue; - - if (*PMETHOD == 0 /* history */) { - // get idx - int windowIDX = -1; - const auto& HISTORY = Desktop::History::windowTracker()->fullHistory(); - for (int64_t i = HISTORY.size() - 1; i >= 0; --i) { - if (HISTORY[i] == w) { - windowIDX = i; - break; - } - } - - if (windowIDX > leaderValue || OVERRIDE_MIN_REQ) { - leaderValue = windowIDX; - leaderWindow = w; - } - } else /* length */ { - if (intersectLength > leaderValue || OVERRIDE_MIN_REQ) { - leaderValue = intersectLength; - leaderWindow = w; - } - } - } - }; - - // Find the window, then if we don't find one with preferred - // float status, try the opposite. - find(); - - if (!leaderWindow) { - floatingPreference = !floatingPreference; - find(); - } - - } else { - static const std::unordered_map VECTORS = { - {Math::DIRECTION_RIGHT, {1, 0}}, {Math::DIRECTION_UP, {0, -1}}, {Math::DIRECTION_DOWN, {0, 1}}, {Math::DIRECTION_LEFT, {-1, 0}}}; - - // - auto vectorAngles = [](const Vector2D& a, const Vector2D& b) -> double { - double dot = (a.x * b.x) + (a.y * b.y); - double ang = std::acos(dot / (a.size() * b.size())); - return ang; - }; - - float bestAngleAbs = 2.0 * M_PI; - constexpr float THRESHOLD = 0.3 * M_PI; - - for (auto const& w : Desktop::windowState()->windows()) { - if (w == ignoreWindow || !w->m_isMapped || !w->m_workspace || !w->acceptsInput() || (!w->isFullscreen() && !w->m_isFloating) || !w->m_workspace->isVisible()) - continue; - - if (pWorkspace->m_monitor == w->m_monitor && pWorkspace != w->m_workspace) - continue; - - if (pWorkspace->m_hasFullscreenWindow && !w->isAllowedOverFullscreen()) - continue; - - if (!*PMONITORFALLBACK && pWorkspace->m_monitor != w->m_monitor) - continue; - - const auto DIST = w->middle().distance(box.middle()); - const auto ANGLE = vectorAngles(Vector2D{w->middle() - box.middle()}, VECTORS.at(dir)); - - if (ANGLE > M_PI_2) - continue; // if the angle is over 90 degrees, ignore. Wrong direction entirely. - - if ((bestAngleAbs < THRESHOLD && DIST < leaderValue && ANGLE < THRESHOLD) || (ANGLE < bestAngleAbs && bestAngleAbs > THRESHOLD) || leaderValue == -1) { - leaderValue = DIST; - bestAngleAbs = ANGLE; - leaderWindow = w; - } - } - - if (!leaderWindow && pWorkspace->m_hasFullscreenWindow) - leaderWindow = pWorkspace->getFullscreenWindow(); - } - - if (leaderValue != -1) - return leaderWindow; - - return nullptr; -} - -template -static bool isWorkspaceMatches(WINDOWPTR pWindow, const WINDOWPTR w, bool anyWorkspace) { - return anyWorkspace ? w->m_workspace && w->m_workspace->isVisible() : w->m_workspace == pWindow->m_workspace; -} - -template -static bool isFloatingMatches(WINDOWPTR w, std::optional floating) { - return !floating.has_value() || w->m_isFloating == floating.value(); -} - -template -static bool acceptsInputForCycle(WINDOWPTR w, bool allowFullscreenBlocked) { - if (w->acceptsInput()) - return true; - - return allowFullscreenBlocked && !w->isHidden() && w->isInputBlockedOnly(INPUT_BLOCK_BELOW_FULLSCREEN); -} - -template -static bool isWindowAvailableForCycle(WINDOWPTR pWindow, WINDOWPTR w, bool focusableOnly, std::optional floating, bool anyWorkspace = false, - bool allowFullscreenBlocked = false) { - return isFloatingMatches(w, floating) && - (w != pWindow && isWorkspaceMatches(pWindow, w, anyWorkspace) && w->m_isMapped && acceptsInputForCycle(w, allowFullscreenBlocked) && - (!focusableOnly || !w->m_ruleApplicator->noFocus().valueOrDefault())); -} - -template -static PHLWINDOW getWindowPred(Iterator cur, Iterator end, Iterator begin, const std::function PRED) { - const auto IN_ONE_SIDE = std::find_if(cur, end, PRED); - if (IN_ONE_SIDE != end) - return *IN_ONE_SIDE; - const auto IN_OTHER_SIDE = std::find_if(begin, cur, PRED); - return *IN_OTHER_SIDE; -} - -template -static PHLWINDOW getWeakWindowPred(Iterator cur, Iterator end, Iterator begin, const std::function PRED) { - const auto IN_ONE_SIDE = std::find_if(cur, end, PRED); - if (IN_ONE_SIDE != end) - return IN_ONE_SIDE->lock(); - const auto IN_OTHER_SIDE = std::find_if(begin, cur, PRED); - return IN_OTHER_SIDE->lock(); -} - -PHLWINDOW CCompositor::getWindowCycleHist(PHLWINDOWREF cur, bool focusableOnly, std::optional floating, bool visible, bool next, bool allowFullscreenBlocked) { - const auto FINDER = [&](const PHLWINDOWREF& w) { return isWindowAvailableForCycle(cur, w, focusableOnly, floating, visible, allowFullscreenBlocked); }; - // also m_vWindowFocusHistory has reverse order, so when it is next - we need to reverse again - const auto& HISTORY = Desktop::History::windowTracker()->fullHistory(); - return next ? getWeakWindowPred(std::ranges::find(HISTORY, cur), HISTORY.end(), HISTORY.begin(), FINDER) : - getWeakWindowPred(std::ranges::find(HISTORY | std::views::reverse, cur), HISTORY.rend(), HISTORY.rbegin(), FINDER); -} - -PHLWINDOW CCompositor::getWindowCycle(PHLWINDOW cur, bool focusableOnly, std::optional floating, bool visible, bool prev, bool allowFullscreenBlocked) { - const auto FINDER = [&](const PHLWINDOW& w) { return isWindowAvailableForCycle(cur, w, focusableOnly, floating, visible, allowFullscreenBlocked); }; - const auto& WINDOWS = Desktop::windowState()->windows(); - return prev ? getWindowPred(std::ranges::find(WINDOWS | std::views::reverse, cur), WINDOWS.rend(), WINDOWS.rbegin(), FINDER) : - getWindowPred(std::ranges::find(WINDOWS, cur), WINDOWS.end(), WINDOWS.begin(), FINDER); -} - bool CCompositor::isPointOnAnyMonitor(const Vector2D& point) { return std::ranges::any_of(State::monitorState()->monitors(), [&](const PHLMONITOR& m) { return VECINRECT(point, m->m_position.x, m->m_position.y, m->m_size.x + m->m_position.x, m->m_size.y + m->m_position.y); @@ -1565,21 +1228,6 @@ void CCompositor::setWindowFullscreenState(const PHLWINDOW PWINDOW, Desktop::Vie Config::monitorRuleMgr()->ensureVRR(PMONITOR); } -PHLWINDOW CCompositor::getX11Parent(PHLWINDOW pWindow) { - if (!pWindow->m_isX11) - return nullptr; - - for (auto const& w : Desktop::windowState()->windows()) { - if (!w->m_isX11) - continue; - - if (w->m_xwaylandSurface == pWindow->m_xwaylandSurface->m_parent) - return w; - } - - return nullptr; -} - void CCompositor::warpCursorTo(const Vector2D& pos, bool force) { // warpCursorTo should only be used for warps that diff --git a/src/Compositor.hpp b/src/Compositor.hpp index c72e5ad56..9e0304923 100644 --- a/src/Compositor.hpp +++ b/src/Compositor.hpp @@ -73,33 +73,23 @@ class CCompositor { // ------------------------------------------------- // - bool isWindowActive(PHLWINDOW); - void changeWindowZOrder(PHLWINDOW, bool); - PHLWINDOW getWindowInDirection(PHLWINDOW, Math::eDirection); - PHLWINDOW getWindowInDirection(const CBox& box, PHLWORKSPACE pWorkspace, Math::eDirection dir, bool floatingPreference, PHLWINDOW ignoreWindow = nullptr, - bool useVectorAngles = false); - PHLWINDOW getWindowCycle(PHLWINDOW cur, bool focusableOnly = false, std::optional floating = std::nullopt, bool visible = false, bool prev = false, - bool allowFullscreenBlocked = false); - PHLWINDOW getWindowCycleHist(PHLWINDOWREF cur, bool focusableOnly = false, std::optional floating = std::nullopt, bool visible = false, bool next = false, - bool allowFullscreenBlocked = false); - bool isPointOnAnyMonitor(const Vector2D&); - bool isPointOnReservedArea(const Vector2D& point, const PHLMONITOR monitor = nullptr); - std::optional calculateX11WorkArea(); - void updateAllWindowsAnimatedDecorationValues(); - void moveWorkspaceToMonitor(PHLWORKSPACE, PHLMONITOR, bool noWarpCursor = false); - void swapActiveWorkspaces(PHLMONITOR, PHLMONITOR); - void setWindowFullscreenInternal(const PHLWINDOW PWINDOW, const eFullscreenMode MODE); - void setWindowFullscreenClient(const PHLWINDOW PWINDOW, const eFullscreenMode MODE); - void setWindowFullscreenState(const PHLWINDOW PWINDOW, const Desktop::View::SFullscreenState state); - void changeWindowFullscreenModeClient(const PHLWINDOW PWINDOW, const eFullscreenMode MODE, const bool ON); - PHLWINDOW getX11Parent(PHLWINDOW); - void warpCursorTo(const Vector2D&, bool force = false); - Vector2D parseWindowVectorArgsRelative(const std::string&, const Vector2D&); - void performUserChecks(); - void moveWindowToWorkspaceSafe(PHLWINDOW pWindow, PHLWORKSPACE pWorkspace); - void setPreferredScaleForSurface(SP pSurface, double scale); - void setPreferredTransformForSurface(SP pSurface, wl_output_transform transform); - void updateSuspendedStates(); + bool isPointOnAnyMonitor(const Vector2D&); + bool isPointOnReservedArea(const Vector2D& point, const PHLMONITOR monitor = nullptr); + std::optional calculateX11WorkArea(); + void updateAllWindowsAnimatedDecorationValues(); + void moveWorkspaceToMonitor(PHLWORKSPACE, PHLMONITOR, bool noWarpCursor = false); + void swapActiveWorkspaces(PHLMONITOR, PHLMONITOR); + void setWindowFullscreenInternal(const PHLWINDOW PWINDOW, const eFullscreenMode MODE); + void setWindowFullscreenClient(const PHLWINDOW PWINDOW, const eFullscreenMode MODE); + void setWindowFullscreenState(const PHLWINDOW PWINDOW, const Desktop::View::SFullscreenState state); + void changeWindowFullscreenModeClient(const PHLWINDOW PWINDOW, const eFullscreenMode MODE, const bool ON); + void warpCursorTo(const Vector2D&, bool force = false); + Vector2D parseWindowVectorArgsRelative(const std::string&, const Vector2D&); + void performUserChecks(); + void moveWindowToWorkspaceSafe(PHLWINDOW pWindow, PHLWORKSPACE pWorkspace); + void setPreferredScaleForSurface(SP pSurface, double scale); + void setPreferredTransformForSurface(SP pSurface, wl_output_transform transform); + void updateSuspendedStates(); std::optional getVTNr(); bool isVRRActiveOnAnyMonitor() const; diff --git a/src/config/shared/actions/ConfigActions.cpp b/src/config/shared/actions/ConfigActions.cpp index f210231bf..be4b9f1f7 100644 --- a/src/config/shared/actions/ConfigActions.cpp +++ b/src/config/shared/actions/ConfigActions.cpp @@ -1,6 +1,7 @@ #include "ConfigActions.hpp" #include "../parserUtils/ParserUtils.hpp" #include "../../../desktop/state/FocusState.hpp" +#include "../../../desktop/state/WindowState.hpp" #include "../../../desktop/view/Window.hpp" #include "../../../desktop/view/Group.hpp" #include "../../../desktop/history/WindowHistoryTracker.hpp" @@ -186,7 +187,7 @@ ActionResult Actions::floatWindow(eTogglableAction action, std::optionalchangeFloatingMode(window->layoutTarget()); if (window->m_isFloating) - g_pCompositor->changeWindowZOrder(window, true); + Desktop::windowState()->raise(window); if (window->m_workspace) { window->m_workspace->updateWindows(); @@ -362,8 +363,9 @@ ActionResult Actions::moveFocus(Math::eDirection dir) { } 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); + Desktop::windowState()->query().cycle(PLASTWINDOW, + {.focusableOnly = true, .previous = dir != Math::DIRECTION_DOWN && dir != Math::DIRECTION_RIGHT, .allowFullscreenBlocked = true}) : + Desktop::windowState()->query().inDirection(PLASTWINDOW, dir); if (*PGROUPCYCLE && PLASTWINDOW->m_group) { auto isTheOnlyGroupOnWs = !PWINDOWTOCHANGETO && State::monitorState()->monitors().size() == 1; @@ -421,8 +423,13 @@ ActionResult Actions::moveFocus(Math::eDirection dir) { default: break; } - const auto PWINDOWCANDIDATE = g_pCompositor->getWindowInDirection(box, PMONITOR->m_activeSpecialWorkspace ? PMONITOR->m_activeSpecialWorkspace : PMONITOR->m_activeWorkspace, - dir, PLASTWINDOW->m_isFloating, PLASTWINDOW, PLASTWINDOW->m_isFloating); + const auto PWINDOWCANDIDATE = + Desktop::windowState()->query().inDirection({.origin = box, + .workspace = PMONITOR->m_activeSpecialWorkspace ? PMONITOR->m_activeSpecialWorkspace : PMONITOR->m_activeWorkspace, + .direction = dir, + .floatingPreference = PLASTWINDOW->m_isFloating, + .ignoreWindow = PLASTWINDOW, + .useVectorAngles = PLASTWINDOW->m_isFloating}); if (PWINDOWCANDIDATE) switchToWindow(PWINDOWCANDIDATE); @@ -473,7 +480,7 @@ ActionResult Actions::swapInDirection(Math::eDirection dir, std::optionalisFullscreen()) return actionError("Can't swap fullscreen window", eActionErrorLevel::WARNING, eActionErrorCode::INVALID_STATE); - const auto PWINDOWTOCHANGETO = g_pCompositor->getWindowInDirection(window, dir); + const auto PWINDOWTOCHANGETO = Desktop::windowState()->query().inDirection(window, dir); if (!PWINDOWTOCHANGETO || PWINDOWTOCHANGETO == window) return actionError("No window to swap with in that direction", eActionErrorLevel::INFO, eActionErrorCode::NOT_FOUND); @@ -639,10 +646,10 @@ ActionResult Actions::swapNext(const bool next, std::optional w) { 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); + auto toSwap = Desktop::windowState()->query().cycle(PLASTCYCLED ? PLASTCYCLED : window, {.focusableOnly = true, .previous = !next}); if (toSwap == window) - toSwap = g_pCompositor->getWindowCycle(window, true, std::nullopt, false, !next); + toSwap = Desktop::windowState()->query().cycle(window, {.focusableOnly = true, .previous = !next}); if (!toSwap) return actionError("No window to swap with", eActionErrorLevel::INFO, eActionErrorCode::NOT_FOUND); @@ -660,9 +667,9 @@ ActionResult Actions::alterZOrder(const std::string& mode, std::optionalchangeWindowZOrder(window, true); + Desktop::windowState()->raise(window); else if (mode == "bottom") - g_pCompositor->changeWindowZOrder(window, false); + Desktop::windowState()->lower(window); else return std::unexpected(std::format("Bad z-order position: {}", mode)); @@ -1309,7 +1316,7 @@ ActionResult Actions::moveIntoGroup(Math::eDirection direction, std::optionalgetWindowInDirection(window, direction); + auto PWINDOWINDIR = Desktop::windowState()->query().inDirection(window, direction); if (!PWINDOWINDIR || !PWINDOWINDIR->m_group) return {}; @@ -1371,7 +1378,7 @@ ActionResult Actions::moveWindowOrGroup(Math::eDirection direction, std::optiona return {}; } - const auto PWINDOWINDIR = g_pCompositor->getWindowInDirection(window, direction); + const auto PWINDOWINDIR = Desktop::windowState()->query().inDirection(window, direction); const bool ISWINDOWGROUP = !!window->m_group; const bool ISWINDOWGROUPLOCKED = ISWINDOWGROUP && window->m_group->locked(); @@ -1675,7 +1682,9 @@ ActionResult Actions::cycleNext(const bool next, std::optional onlyTiled, if (onlyTiled.value_or(false) != onlyFloating.value_or(false)) tileOrFloatOnly = onlyFloating.value_or(false); - const auto& cycled = g_pCompositor->getWindowCycle(window, true, tileOrFloatOnly, false, !next, window->m_workspace && window->m_workspace->m_hasFullscreenWindow); + const auto& cycled = Desktop::windowState()->query().cycle( + window, + {.focusableOnly = true, .floating = tileOrFloatOnly, .previous = !next, .allowFullscreenBlocked = window->m_workspace && window->m_workspace->m_hasFullscreenWindow}); switchToWindow(cycled); @@ -1698,7 +1707,7 @@ ActionResult Actions::moveIntoOrCreateGroup(Math::eDirection dir, std::optional< if (!PWINDOW) return {}; - auto PWINDOWINDIR = g_pCompositor->getWindowInDirection(PWINDOW, dir); + auto PWINDOWINDIR = Desktop::windowState()->query().inDirection(PWINDOW, dir); if (!PWINDOWINDIR) return {}; diff --git a/src/desktop/state/FocusState.cpp b/src/desktop/state/FocusState.cpp index 702b02c43..6bdf8c53c 100644 --- a/src/desktop/state/FocusState.cpp +++ b/src/desktop/state/FocusState.cpp @@ -307,6 +307,21 @@ void CFocusState::resetWindowFocus() { m_focusSurface.reset(); } +bool CFocusState::isWindowActive(PHLWINDOW pWindow) const { + const auto FOCUSWINDOW = m_focusWindow.lock(); + const auto FOCUSSURFACE = m_focusSurface.lock(); + + if (!FOCUSWINDOW && !FOCUSSURFACE) + return false; + + if (!pWindow || !pWindow->m_isMapped) + return false; + + const auto PSURFACE = pWindow->wlSurface()->resource(); + + return PSURFACE == FOCUSSURFACE || pWindow == FOCUSWINDOW; +} + bool Desktop::isHardInputFocusReason(eFocusReason r) { return r == FOCUS_REASON_NEW_WINDOW || r == FOCUS_REASON_KEYBIND || r == FOCUS_REASON_GHOSTS || r == FOCUS_REASON_CLICK || r == FOCUS_REASON_DESKTOP_STATE_CHANGE || r == FOCUS_REASON_UNMAP_WINDOW_TILING || r == FOCUS_REASON_SWITCH_TO_WINDOW_HARD; diff --git a/src/desktop/state/FocusState.hpp b/src/desktop/state/FocusState.hpp index 334c426d6..927ebdb14 100644 --- a/src/desktop/state/FocusState.hpp +++ b/src/desktop/state/FocusState.hpp @@ -45,6 +45,8 @@ namespace Desktop { void resetWindowFocus(); + bool isWindowActive(PHLWINDOW w) const; + SP surface(); PHLWINDOW window(); PHLMONITOR monitor(); diff --git a/src/desktop/state/ViewHitTester.cpp b/src/desktop/state/ViewHitTester.cpp index 19fdce94e..ed98b2d2d 100644 --- a/src/desktop/state/ViewHitTester.cpp +++ b/src/desktop/state/ViewHitTester.cpp @@ -186,7 +186,7 @@ PHLWINDOW CViewHitTester::windowAt(const Vector2D& pos, uint16_t properties, PHL if ((properties & INPUT_EXTENTS) && BORDER_GRAB_AREA > 0 && !w->isX11OverrideRedirect()) { const auto WORKAREA = PWORKSPACE->m_space->workArea(); auto isWindowCloseToWorkAreaEdge = [&](const Math::eDirection dir) -> bool { - constexpr double STICK_THRESHOLD = 2.0; // This constant is taken from isAdjacent in CCompositor::getWindowInDirection + constexpr double STICK_THRESHOLD = 2.0; // This constant is taken from isAdjacent in CWindowQuery::inDirection double aEdge = -1; double bEdge = -1; diff --git a/src/desktop/state/WindowQuery.cpp b/src/desktop/state/WindowQuery.cpp new file mode 100644 index 000000000..649d64079 --- /dev/null +++ b/src/desktop/state/WindowQuery.cpp @@ -0,0 +1,300 @@ +#include "WindowQuery.hpp" +#include "WindowState.hpp" +#include "../Workspace.hpp" +#include "../history/WindowHistoryTracker.hpp" +#include "../view/Window.hpp" +#include "../../config/ConfigValue.hpp" +#include "../../output/Monitor.hpp" + +#include +#include +#include +#include +#include + +using namespace Desktop; +using namespace Desktop::View; + +CWindowQuery::CWindowQuery(const CWindowState& state) : m_state(state) { + ; +} + +PHLWINDOW CWindowQuery::inDirection(PHLWINDOW window, Math::eDirection direction) const { + if (direction == Math::DIRECTION_DEFAULT) + return nullptr; + + if (!window) + return nullptr; + + const auto PMONITOR = window->m_monitor.lock(); + + if (!PMONITOR) + return nullptr; // ?? + + const auto WINDOWIDEALBB = window->isFullscreen() ? CBox{PMONITOR->m_position, PMONITOR->m_size} : window->getWindowIdealBoundingBoxIgnoreReserved(); + const auto PWORKSPACE = window->m_workspace; + + if (!PWORKSPACE) + return nullptr; // ?? + + return inDirection({.origin = WINDOWIDEALBB, + .workspace = PWORKSPACE, + .direction = direction, + .floatingPreference = window->m_isFloating, + .ignoreWindow = window, + .useVectorAngles = window->m_isFloating}); +} + +PHLWINDOW CWindowQuery::inDirection(const SWindowDirectionQuery& query) const { + if (query.direction == Math::DIRECTION_DEFAULT) + return nullptr; + + if (!query.workspace) + return nullptr; + + // 0 -> history, 1 -> shared length + static auto PMETHOD = CConfigValue("binds:focus_preferred_method"); + static auto PMONITORFALLBACK = CConfigValue("binds:window_direction_monitor_fallback"); + + const auto POSA = query.origin.pos(); + const auto SIZEA = query.origin.size(); + + auto leaderValue = -1; + auto floatingPreference = query.floatingPreference; + PHLWINDOW leaderWindow = nullptr; + + if (!query.useVectorAngles) { + // helper to check if two rectangles are adjacent along an axis, considering slight overlaps. + // returns true if: STICKS (delta <= 2) OR rectangles overlap but no more than 50% of the smaller dimension. + static auto isAdjacent = [](const double aMin, const double aMax, const double bMin, const double bMax) -> bool { + constexpr double STICK_THRESHOLD = 2.0; + constexpr double MAX_OVERLAP_RATIO = 0.5; + + const double aEdge = aMin; + const double bEdge = bMax; + const double delta = aEdge - bEdge; + + // old STICKS check for 2px + if (std::abs(delta) < STICK_THRESHOLD) + return true; + + if (delta >= 0) + return false; + + const double overlap = -delta; + const double sizeA = aMax - aMin; + const double sizeB = bMax - bMin; + + // reject if one rectangle fully contains the other + if ((bMin <= aMin && bMax >= aMax) || (aMin <= bMin && aMax >= bMax)) + return false; + + // accept if overlap is at most 50% of the smaller dimension + return overlap <= std::min(sizeA, sizeB) * MAX_OVERLAP_RATIO; + }; + + auto find = [&]() { + for (auto const& w : m_state.windows()) { + if (w == query.ignoreWindow || !w->m_workspace || !w->m_isMapped || (!w->isFullscreen() && w->m_isFloating) || !w->m_workspace->isVisible()) + continue; + + if (w->isHidden()) + continue; + + // check if the input is blocked by anything except BELOW_FULLSCREEN + if (w->isInputBlocked(INPUT_BLOCK_ALL & (~INPUT_BLOCK_BELOW_FULLSCREEN))) + continue; + + if (query.workspace->m_monitor == w->m_monitor && query.workspace != w->m_workspace) + continue; + + if (query.workspace->m_hasFullscreenWindow && !w->isAllowedOverFullscreen()) + continue; + + if (!*PMONITORFALLBACK && query.workspace->m_monitor != w->m_monitor) + continue; + + if (w->m_isFloating != floatingPreference) + continue; + + // prioritize windows on the same workspace. + // this is especially important for scrolling layouts - we want to first move to a window + // on the same workspace before moving onto another. + const auto LEADER_IS_ON_SAME_WORKSPACE = leaderWindow && leaderWindow->m_workspace == query.workspace; + + if (LEADER_IS_ON_SAME_WORKSPACE && w->m_workspace != query.workspace) + continue; + + const auto BWINDOWIDEALBB = w->getWindowIdealBoundingBoxIgnoreReserved(); + + const auto POSB = Vector2D(BWINDOWIDEALBB.x, BWINDOWIDEALBB.y); + const auto SIZEB = Vector2D(BWINDOWIDEALBB.width, BWINDOWIDEALBB.height); + + double intersectLength = -1; + + switch (query.direction) { + case Math::DIRECTION_LEFT: + if (isAdjacent(POSA.x, POSA.x + SIZEA.x, POSB.x, POSB.x + SIZEB.x)) + intersectLength = std::max(0.0, std::min(POSA.y + SIZEA.y, POSB.y + SIZEB.y) - std::max(POSA.y, POSB.y)); + break; + case Math::DIRECTION_RIGHT: + if (isAdjacent(POSB.x, POSB.x + SIZEB.x, POSA.x, POSA.x + SIZEA.x)) + intersectLength = std::max(0.0, std::min(POSA.y + SIZEA.y, POSB.y + SIZEB.y) - std::max(POSA.y, POSB.y)); + break; + case Math::DIRECTION_UP: + if (isAdjacent(POSA.y, POSA.y + SIZEA.y, POSB.y, POSB.y + SIZEB.y)) + intersectLength = std::max(0.0, std::min(POSA.x + SIZEA.x, POSB.x + SIZEB.x) - std::max(POSA.x, POSB.x)); + break; + case Math::DIRECTION_DOWN: + if (isAdjacent(POSB.y, POSB.y + SIZEB.y, POSA.y, POSA.y + SIZEA.y)) + intersectLength = std::max(0.0, std::min(POSA.x + SIZEA.x, POSB.x + SIZEB.x) - std::max(POSA.x, POSB.x)); + break; + default: break; + } + + // if we have a leader on another workspace, and this window is on the same workspace, + // override minimum requirements and always select this as the new leader + const bool OVERRIDE_MIN_REQ = leaderWindow && !LEADER_IS_ON_SAME_WORKSPACE && w->m_workspace == query.workspace; + + // ...as long as there is any intersect. + if (intersectLength <= 1) + continue; + + if (*PMETHOD == 0 /* history */) { + // get idx + int windowIDX = -1; + const auto& HISTORY = Desktop::History::windowTracker()->fullHistory(); + for (int64_t i = HISTORY.size() - 1; i >= 0; --i) { + if (HISTORY[i] == w) { + windowIDX = i; + break; + } + } + + if (windowIDX > leaderValue || OVERRIDE_MIN_REQ) { + leaderValue = windowIDX; + leaderWindow = w; + } + } else /* length */ { + if (intersectLength > leaderValue || OVERRIDE_MIN_REQ) { + leaderValue = intersectLength; + leaderWindow = w; + } + } + } + }; + + // Find the window, then if we don't find one with preferred + // float status, try the opposite. + find(); + + if (!leaderWindow) { + floatingPreference = !floatingPreference; + find(); + } + + } else { + static const std::unordered_map VECTORS = { + {Math::DIRECTION_RIGHT, {1, 0}}, {Math::DIRECTION_UP, {0, -1}}, {Math::DIRECTION_DOWN, {0, 1}}, {Math::DIRECTION_LEFT, {-1, 0}}}; + + auto vectorAngles = [](const Vector2D& a, const Vector2D& b) -> double { + double dot = (a.x * b.x) + (a.y * b.y); + double ang = std::acos(dot / (a.size() * b.size())); + return ang; + }; + + float bestAngleAbs = 2.0 * M_PI; + constexpr float THRESHOLD = 0.3 * M_PI; + + for (auto const& w : m_state.windows()) { + if (w == query.ignoreWindow || !w->m_isMapped || !w->m_workspace || !w->acceptsInput() || (!w->isFullscreen() && !w->m_isFloating) || !w->m_workspace->isVisible()) + continue; + + if (query.workspace->m_monitor == w->m_monitor && query.workspace != w->m_workspace) + continue; + + if (query.workspace->m_hasFullscreenWindow && !w->isAllowedOverFullscreen()) + continue; + + if (!*PMONITORFALLBACK && query.workspace->m_monitor != w->m_monitor) + continue; + + const auto DIST = w->middle().distance(query.origin.middle()); + const auto ANGLE = vectorAngles(Vector2D{w->middle() - query.origin.middle()}, VECTORS.at(query.direction)); + + if (ANGLE > M_PI_2) + continue; // if the angle is over 90 degrees, ignore. Wrong direction entirely. + + if ((bestAngleAbs < THRESHOLD && DIST < leaderValue && ANGLE < THRESHOLD) || (ANGLE < bestAngleAbs && bestAngleAbs > THRESHOLD) || leaderValue == -1) { + leaderValue = DIST; + bestAngleAbs = ANGLE; + leaderWindow = w; + } + } + + if (!leaderWindow && query.workspace->m_hasFullscreenWindow) + leaderWindow = query.workspace->getFullscreenWindow(); + } + + if (leaderValue != -1) + return leaderWindow; + + return nullptr; +} + +template +static bool isWorkspaceMatches(WINDOWPTR pWindow, const WINDOWPTR w, bool anyWorkspace) { + return anyWorkspace ? w->m_workspace && w->m_workspace->isVisible() : w->m_workspace == pWindow->m_workspace; +} + +template +static bool isFloatingMatches(WINDOWPTR w, std::optional floating) { + return !floating.has_value() || w->m_isFloating == floating.value(); +} + +template +static bool acceptsInputForCycle(WINDOWPTR w, bool allowFullscreenBlocked) { + if (w->acceptsInput()) + return true; + + return allowFullscreenBlocked && !w->isHidden() && w->isInputBlockedOnly(INPUT_BLOCK_BELOW_FULLSCREEN); +} + +template +static bool isWindowAvailableForCycle(WINDOWPTR pWindow, WINDOWPTR w, const SWindowCycleOptions& options) { + return isFloatingMatches(w, options.floating) && + (w != pWindow && isWorkspaceMatches(pWindow, w, options.visible) && w->m_isMapped && acceptsInputForCycle(w, options.allowFullscreenBlocked) && + (!options.focusableOnly || !w->m_ruleApplicator->noFocus().valueOrDefault())); +} + +template +static PHLWINDOW getWindowPred(Iterator cur, Iterator end, Iterator begin, const std::function PRED) { + const auto IN_ONE_SIDE = std::find_if(cur, end, PRED); + if (IN_ONE_SIDE != end) + return *IN_ONE_SIDE; + const auto IN_OTHER_SIDE = std::find_if(begin, cur, PRED); + return *IN_OTHER_SIDE; +} + +template +static PHLWINDOW getWeakWindowPred(Iterator cur, Iterator end, Iterator begin, const std::function PRED) { + const auto IN_ONE_SIDE = std::find_if(cur, end, PRED); + if (IN_ONE_SIDE != end) + return IN_ONE_SIDE->lock(); + const auto IN_OTHER_SIDE = std::find_if(begin, cur, PRED); + return IN_OTHER_SIDE->lock(); +} + +PHLWINDOW CWindowQuery::cycleHistory(PHLWINDOWREF current, const SWindowCycleOptions& options) const { + const auto FINDER = [&](const PHLWINDOWREF& w) { return isWindowAvailableForCycle(current, w, options); }; + // also m_vWindowFocusHistory has reverse order, so when it is next - we need to reverse again + const auto& HISTORY = Desktop::History::windowTracker()->fullHistory(); + return !options.previous ? getWeakWindowPred(std::ranges::find(HISTORY, current), HISTORY.end(), HISTORY.begin(), FINDER) : + getWeakWindowPred(std::ranges::find(HISTORY | std::views::reverse, current), HISTORY.rend(), HISTORY.rbegin(), FINDER); +} + +PHLWINDOW CWindowQuery::cycle(PHLWINDOW current, const SWindowCycleOptions& options) const { + const auto FINDER = [&](const PHLWINDOW& w) { return isWindowAvailableForCycle(current, w, options); }; + return options.previous ? getWindowPred(std::ranges::find(m_state.windows() | std::views::reverse, current), m_state.windows().rend(), m_state.windows().rbegin(), FINDER) : + getWindowPred(std::ranges::find(m_state.windows(), current), m_state.windows().end(), m_state.windows().begin(), FINDER); +} diff --git a/src/desktop/state/WindowQuery.hpp b/src/desktop/state/WindowQuery.hpp new file mode 100644 index 000000000..2deb7eab4 --- /dev/null +++ b/src/desktop/state/WindowQuery.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include "../DesktopTypes.hpp" +#include "../../helpers/math/Direction.hpp" +#include "../../helpers/math/Math.hpp" + +#include + +namespace Desktop { + class CWindowState; + + struct SWindowDirectionQuery { + CBox origin = {}; + PHLWORKSPACE workspace = nullptr; + Math::eDirection direction = Math::DIRECTION_DEFAULT; + bool floatingPreference = false; + PHLWINDOW ignoreWindow = nullptr; + bool useVectorAngles = false; + }; + + struct SWindowCycleOptions { + bool focusableOnly = false; + std::optional floating = std::nullopt; + bool visible = false; + bool previous = false; + bool allowFullscreenBlocked = false; + }; + + class CWindowQuery { + public: + CWindowQuery(const CWindowState& state); + ~CWindowQuery() = default; + + PHLWINDOW inDirection(PHLWINDOW window, Math::eDirection direction) const; + PHLWINDOW inDirection(const SWindowDirectionQuery& query) const; + + PHLWINDOW cycle(PHLWINDOW current, const SWindowCycleOptions& options = {}) const; + PHLWINDOW cycleHistory(PHLWINDOWREF current, const SWindowCycleOptions& options = {}) const; + + private: + const CWindowState& m_state; + }; +} diff --git a/src/desktop/state/WindowState.cpp b/src/desktop/state/WindowState.cpp index ac3900eb8..1b76daeca 100644 --- a/src/desktop/state/WindowState.cpp +++ b/src/desktop/state/WindowState.cpp @@ -1,8 +1,10 @@ #include "WindowState.hpp" #include "../../event/EventBus.hpp" +#include "../../render/Renderer.hpp" #include "../view/Window.hpp" #include +#include using namespace Desktop; @@ -31,6 +33,65 @@ const std::vector& CWindowState::windows() const { return m_windows; } +CWindowQuery CWindowState::query() const { + return CWindowQuery{*this}; +} + +void CWindowState::raise(PHLWINDOW w) { + moveToZ(w, true); +} + +void CWindowState::lower(PHLWINDOW w) { + moveToZ(w, false); +} + +void CWindowState::moveToZ(PHLWINDOW w, bool top) { + if (!View::validMapped(w) || m_windows.empty()) + return; + + w->m_createdOverFullscreen = top; + w->updateFullscreenInputState(); + *w->alpha(View::WINDOW_ALPHA_FULLSCREEN) = w->isBlockedByFullscreen() ? 0.F : 1.F; + + if (w == (top ? m_windows.back() : m_windows.front())) + return; + + auto moveSingleToZ = [&](PHLWINDOW pw) -> void { + if (top) + moveToTop(pw); + else + moveToBottom(pw); + + if (pw->m_isMapped) + g_pHyprRenderer->damageMonitor(pw->m_monitor.lock()); + }; + + if (!w->m_isX11) { + moveSingleToZ(w); + return; + } + + // move X11 transient stack + std::vector toMove; + + auto collectX11Stack = [&](PHLWINDOW pw, auto&& collectX11Stack) -> void { + if (top) + toMove.emplace_back(pw); + else + toMove.insert(toMove.begin(), pw); + + for (auto const& other : m_windows) { + if (other->m_isMapped && !other->isHidden() && other->m_isX11 && other->x11Parent() == pw && other != pw && std::ranges::find(toMove, other) == toMove.end()) + collectX11Stack(other, collectX11Stack); + } + }; + + collectX11Stack(w, collectX11Stack); + for (auto const& it : toMove) { + moveSingleToZ(it); + } +} + void CWindowState::moveToTop(PHLWINDOW w) { if (!w || m_windows.empty() || m_windows.back() == w) return; diff --git a/src/desktop/state/WindowState.hpp b/src/desktop/state/WindowState.hpp index 3f244535e..e26522d6d 100644 --- a/src/desktop/state/WindowState.hpp +++ b/src/desktop/state/WindowState.hpp @@ -3,6 +3,7 @@ #include "../../helpers/memory/Memory.hpp" #include "../../helpers/signal/Signal.hpp" #include "../DesktopTypes.hpp" +#include "WindowQuery.hpp" #include @@ -14,8 +15,9 @@ namespace Desktop { const std::vector& windows() const; - void moveToTop(PHLWINDOW w); - void moveToBottom(PHLWINDOW w); + CWindowQuery query() const; + void raise(PHLWINDOW w); + void lower(PHLWINDOW w); void clear(); // kept for compat with old code, should be removed ASAP @@ -25,6 +27,10 @@ namespace Desktop { private: std::vector m_windows; + void moveToTop(PHLWINDOW w); + void moveToBottom(PHLWINDOW w); + void moveToZ(PHLWINDOW w, bool top); + struct { CHyprSignalListener viewCreate, viewDestroy; } m_listeners; diff --git a/src/desktop/view/Window.cpp b/src/desktop/view/Window.cpp index 4610420ec..3693c160c 100644 --- a/src/desktop/view/Window.cpp +++ b/src/desktop/view/Window.cpp @@ -21,6 +21,7 @@ #include "LayerSurface.hpp" #include "../state/FocusState.hpp" #include "../state/FloatState.hpp" +#include "../state/WindowState.hpp" #include "../history/WindowHistoryTracker.hpp" #include "../../Compositor.hpp" #include "../../render/decorations/CHyprDropShadowDecoration.hpp" @@ -580,29 +581,16 @@ void CWindow::moveToWorkspace(PHLWORKSPACE pWorkspace) { } } -PHLWINDOW CWindow::x11TransientFor() { - if (!m_xwaylandSurface || !m_xwaylandSurface->m_parent) +PHLWINDOW CWindow::x11Parent() const { + if (!m_isX11 || !m_xwaylandSurface || !m_xwaylandSurface->m_parent) return nullptr; - auto s = m_xwaylandSurface->m_parent; - std::vector> visited; - while (s) { - // break loops. Some X apps make them, and it seems like it's valid behavior?!?!?! - // TODO: we should reject loops being created in the first place. - if (std::ranges::find(visited.begin(), visited.end(), s) != visited.end()) - break; - - visited.emplace_back(s.lock()); - s = s->m_parent; - } - - if (s == m_xwaylandSurface) - return nullptr; // dead-ass circle - for (auto const& w : Desktop::windowState()->windows()) { - if (w->m_xwaylandSurface != s) + if (!w->m_isX11) continue; - return w; + + if (w->m_xwaylandSurface == m_xwaylandSurface->m_parent) + return w; } return nullptr; @@ -1388,7 +1376,7 @@ void CWindow::activate(bool force) { } if (m_isFloating) - g_pCompositor->changeWindowZOrder(m_self.lock(), true); + Desktop::windowState()->raise(m_self.lock()); Desktop::focusState()->fullWindowFocus(m_self.lock(), FOCUS_REASON_DESKTOP_STATE_CHANGE); warpCursor(); @@ -1589,7 +1577,7 @@ void CWindow::onX11ConfigureRequest(CBox box) { m_workspace = monitorByRequestedPosition->m_activeWorkspace; } - g_pCompositor->changeWindowZOrder(m_self.lock(), true); + Desktop::windowState()->raise(m_self.lock()); m_createdOverFullscreen = true; @@ -1794,7 +1782,7 @@ std::optional CWindow::xdgDescription() { PHLWINDOW CWindow::parent() { if (m_isX11) { - auto t = x11TransientFor(); + auto t = x11Parent(); // don't return a parent that's not mapped if (!validMapped(t)) @@ -2385,7 +2373,7 @@ void CWindow::mapWindow() { // because the windows are animated on RealSize m_target->setPseudoSize(m_realSize->goal()); - g_pCompositor->changeWindowZOrder(m_self.lock(), true); + Desktop::windowState()->raise(m_self.lock()); } else { bool setPseudo = false; @@ -2849,7 +2837,7 @@ void CWindow::unmanagedSetGeometry() { m_workspace = State::monitorState()->query().vec(m_realPosition->value() + m_realSize->value() / 2.f).run()->m_activeWorkspace; - g_pCompositor->changeWindowZOrder(m_self.lock(), true); + Desktop::windowState()->raise(m_self.lock()); updateWindowDecos(); g_pHyprRenderer->damageWindow(m_self.lock()); diff --git a/src/desktop/view/Window.hpp b/src/desktop/view/Window.hpp index d39fe622a..b05f34eb2 100644 --- a/src/desktop/view/Window.hpp +++ b/src/desktop/view/Window.hpp @@ -314,7 +314,7 @@ namespace Desktop::View { void updateToplevel(); void updateSurfaceScaleTransformDetails(bool force = false); void moveToWorkspace(PHLWORKSPACE); - PHLWINDOW x11TransientFor(); + PHLWINDOW x11Parent() const; void onUnmap(); void onMap(); void setHidden(bool hidden); diff --git a/src/layout/algorithm/floating/default/DefaultFloatingAlgorithm.cpp b/src/layout/algorithm/floating/default/DefaultFloatingAlgorithm.cpp index d541e546a..fab937a8f 100644 --- a/src/layout/algorithm/floating/default/DefaultFloatingAlgorithm.cpp +++ b/src/layout/algorithm/floating/default/DefaultFloatingAlgorithm.cpp @@ -6,6 +6,7 @@ #include "../../../space/Space.hpp" #include "../../../../Compositor.hpp" +#include "../../../../desktop/state/WindowState.hpp" #include "../../../../output/Monitor.hpp" #include "../../../../state/MonitorState.hpp" @@ -111,7 +112,7 @@ void CDefaultFloatingAlgorithm::newTarget(SP target) { } if (!PWINDOW->isX11OverrideRedirect()) - g_pCompositor->changeWindowZOrder(PWINDOW, true); + Desktop::windowState()->raise(PWINDOW); else { PWINDOW->m_pendingReportedSize = PWINDOW->m_realSize->goal(); PWINDOW->m_reportedSize = PWINDOW->m_pendingReportedSize; diff --git a/src/layout/algorithm/tiled/master/MasterAlgorithm.cpp b/src/layout/algorithm/tiled/master/MasterAlgorithm.cpp index 49506ea70..72f68deb1 100644 --- a/src/layout/algorithm/tiled/master/MasterAlgorithm.cpp +++ b/src/layout/algorithm/tiled/master/MasterAlgorithm.cpp @@ -8,6 +8,7 @@ #include "../../../../config/shared/actions/ConfigActions.hpp" #include "../../../../config/shared/workspace/WorkspaceRuleManager.hpp" #include "../../../../desktop/state/FocusState.hpp" +#include "../../../../desktop/state/WindowState.hpp" #include "../../../../output/Monitor.hpp" #include "../../../../Compositor.hpp" #include "../../../../render/Renderer.hpp" @@ -464,7 +465,7 @@ void CMasterAlgorithm::swapTargets(SP a, SP b) { void CMasterAlgorithm::moveTargetInDirection(SP t, Math::eDirection dir, bool silent) { static auto PMONITORFALLBACK = CConfigValue("binds:window_direction_monitor_fallback"); - const auto PWINDOW2 = g_pCompositor->getWindowInDirection(t->window(), dir); + const auto PWINDOW2 = Desktop::windowState()->query().inDirection(t->window(), dir); if (!t->window()) return; diff --git a/src/layout/supplementary/DragController.cpp b/src/layout/supplementary/DragController.cpp index 41b8571a8..469e9c8ce 100644 --- a/src/layout/supplementary/DragController.cpp +++ b/src/layout/supplementary/DragController.cpp @@ -5,6 +5,7 @@ #include "../../Compositor.hpp" #include "../../managers/cursor/CursorShapeOverrideController.hpp" #include "../../desktop/state/FocusState.hpp" +#include "../../desktop/state/WindowState.hpp" #include "../../desktop/view/Group.hpp" #include "../../render/Renderer.hpp" #include "../../state/MonitorState.hpp" @@ -160,7 +161,7 @@ void CDragStateController::dragBegin(SP target, eMouseBindMode mode) { if (DRAGGINGTARGET->window()) { Desktop::focusState()->rawWindowFocus(DRAGGINGTARGET->window(), Desktop::FOCUS_REASON_DESKTOP_STATE_CHANGE); - g_pCompositor->changeWindowZOrder(DRAGGINGTARGET->window(), true); + Desktop::windowState()->raise(DRAGGINGTARGET->window()); } } void CDragStateController::dragEnd() { diff --git a/src/managers/input/IdleInhibitor.cpp b/src/managers/input/IdleInhibitor.cpp index e214229b7..08c3372bd 100644 --- a/src/managers/input/IdleInhibitor.cpp +++ b/src/managers/input/IdleInhibitor.cpp @@ -1,5 +1,6 @@ #include "InputManager.hpp" #include "../../Compositor.hpp" +#include "../../desktop/state/FocusState.hpp" #include "../../protocols/IdleInhibit.hpp" #include "../../protocols/IdleNotify.hpp" #include "../../protocols/core/Compositor.hpp" @@ -64,7 +65,7 @@ bool CInputManager::isWindowInhibiting(const PHLWINDOW& w, bool onlyHl) { if (w->m_ruleApplicator->idleInhibitMode().valueOrDefault() == Desktop::Rule::IDLEINHIBIT_ALWAYS) return true; - if (w->m_ruleApplicator->idleInhibitMode().valueOrDefault() == Desktop::Rule::IDLEINHIBIT_FOCUS && g_pCompositor->isWindowActive(w)) + if (w->m_ruleApplicator->idleInhibitMode().valueOrDefault() == Desktop::Rule::IDLEINHIBIT_FOCUS && Desktop::focusState()->isWindowActive(w)) return true; if (w->m_ruleApplicator->idleInhibitMode().valueOrDefault() == Desktop::Rule::IDLEINHIBIT_FULLSCREEN && w->isFullscreen() && w->m_workspace && w->m_workspace->isVisible()) diff --git a/src/managers/input/InputManager.cpp b/src/managers/input/InputManager.cpp index 243351796..5f8121f80 100644 --- a/src/managers/input/InputManager.cpp +++ b/src/managers/input/InputManager.cpp @@ -10,6 +10,7 @@ #include "../../config/legacy/ConfigManager.hpp" #include "../../desktop/view/WLSurface.hpp" #include "../../desktop/state/FocusState.hpp" +#include "../../desktop/state/WindowState.hpp" #include "../../protocols/CursorShape.hpp" #include "../../protocols/IdleInhibit.hpp" #include "../../protocols/RelativePointer.hpp" @@ -893,7 +894,7 @@ void CInputManager::processMouseDownNormal(const IPointer::SButtonEvent& e, SPview() : nullptr; if (PVIEW && PVIEW->type() == Desktop::View::VIEW_TYPE_WINDOW) - g_pCompositor->changeWindowZOrder(dynamicPointerCast(PVIEW), true); + Desktop::windowState()->raise(dynamicPointerCast(PVIEW)); break; } diff --git a/src/render/decorations/CHyprGroupBarDecoration.cpp b/src/render/decorations/CHyprGroupBarDecoration.cpp index 02d67b4fb..9af92d30f 100644 --- a/src/render/decorations/CHyprGroupBarDecoration.cpp +++ b/src/render/decorations/CHyprGroupBarDecoration.cpp @@ -2,6 +2,7 @@ #include "../../Compositor.hpp" #include "../../config/ConfigValue.hpp" #include "../../desktop/state/FocusState.hpp" +#include "../../desktop/state/WindowState.hpp" #include "../../desktop/view/Group.hpp" #include #include @@ -406,7 +407,7 @@ bool CHyprGroupBarDecoration::onBeginWindowDragOnDeco(const Vector2D& pos) { // start a move drag on it g_layoutManager->dragController()->dragBegin(pWindow->layoutTarget(), MBIND_MOVE); - if (!g_pCompositor->isWindowActive(pWindow)) + if (!Desktop::focusState()->isWindowActive(pWindow)) Desktop::focusState()->rawWindowFocus(pWindow, Desktop::FOCUS_REASON_CLICK); return true; @@ -465,7 +466,7 @@ bool CHyprGroupBarDecoration::onMouseButtonOnDeco(const Vector2D& pos, const IPo const auto TABPAD = !*PSTACKED && (BARRELATIVEX - (m_barWidth + *PINNERGAP) * WINDOWINDEX > m_barWidth); const auto STACKPAD = *PSTACKED && (BARRELATIVEY - (m_barHeight + *POUTERGAP) * WINDOWINDEX < *POUTERGAP); if (TABPAD || STACKPAD) { - if (!g_pCompositor->isWindowActive(m_window.lock())) + if (!Desktop::focusState()->isWindowActive(m_window.lock())) Desktop::focusState()->rawWindowFocus(m_window.lock(), Desktop::FOCUS_REASON_CLICK); return true; } @@ -475,11 +476,11 @@ bool CHyprGroupBarDecoration::onMouseButtonOnDeco(const Vector2D& pos, const IPo if (pWindow != m_window) pWindow->m_group->setCurrent(pWindow); - if (!g_pCompositor->isWindowActive(pWindow) && *PFOLLOWMOUSE != 3) + if (!Desktop::focusState()->isWindowActive(pWindow) && *PFOLLOWMOUSE != 3) Desktop::focusState()->rawWindowFocus(pWindow, Desktop::FOCUS_REASON_CLICK); if (pWindow->m_isFloating) - g_pCompositor->changeWindowZOrder(pWindow, true); + Desktop::windowState()->raise(pWindow); return true; }