mirror of
https://github.com/hyprwm/Hyprland.git
synced 2026-08-24 10:04:19 -05:00
desktop: move window manipulation functions out of compositor (#15256)
This commit is contained in:
@@ -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<PHLWINDOW> 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<Config::INTEGER>("binds:focus_preferred_method");
|
||||
static auto PMONITORFALLBACK = CConfigValue<Config::INTEGER>("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<Math::eDirection, Vector2D> 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 <typename WINDOWPTR>
|
||||
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 <typename WINDOWPTR>
|
||||
static bool isFloatingMatches(WINDOWPTR w, std::optional<bool> floating) {
|
||||
return !floating.has_value() || w->m_isFloating == floating.value();
|
||||
}
|
||||
|
||||
template <typename WINDOWPTR>
|
||||
static bool acceptsInputForCycle(WINDOWPTR w, bool allowFullscreenBlocked) {
|
||||
if (w->acceptsInput())
|
||||
return true;
|
||||
|
||||
return allowFullscreenBlocked && !w->isHidden() && w->isInputBlockedOnly(INPUT_BLOCK_BELOW_FULLSCREEN);
|
||||
}
|
||||
|
||||
template <typename WINDOWPTR>
|
||||
static bool isWindowAvailableForCycle(WINDOWPTR pWindow, WINDOWPTR w, bool focusableOnly, std::optional<bool> 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 <typename Iterator>
|
||||
static PHLWINDOW getWindowPred(Iterator cur, Iterator end, Iterator begin, const std::function<bool(const PHLWINDOW&)> 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 <typename Iterator>
|
||||
static PHLWINDOW getWeakWindowPred(Iterator cur, Iterator end, Iterator begin, const std::function<bool(const PHLWINDOWREF&)> 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<bool> 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<bool> 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
|
||||
|
||||
+17
-27
@@ -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<bool> floating = std::nullopt, bool visible = false, bool prev = false,
|
||||
bool allowFullscreenBlocked = false);
|
||||
PHLWINDOW getWindowCycleHist(PHLWINDOWREF cur, bool focusableOnly = false, std::optional<bool> 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<CBox> 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<CWLSurfaceResource> pSurface, double scale);
|
||||
void setPreferredTransformForSurface(SP<CWLSurfaceResource> pSurface, wl_output_transform transform);
|
||||
void updateSuspendedStates();
|
||||
bool isPointOnAnyMonitor(const Vector2D&);
|
||||
bool isPointOnReservedArea(const Vector2D& point, const PHLMONITOR monitor = nullptr);
|
||||
std::optional<CBox> 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<CWLSurfaceResource> pSurface, double scale);
|
||||
void setPreferredTransformForSurface(SP<CWLSurfaceResource> pSurface, wl_output_transform transform);
|
||||
void updateSuspendedStates();
|
||||
std::optional<unsigned int> getVTNr();
|
||||
bool isVRRActiveOnAnyMonitor() const;
|
||||
|
||||
|
||||
@@ -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::optional<PHLWIND
|
||||
g_layoutManager->changeFloatingMode(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::optional<PHLWIN
|
||||
if (window->isFullscreen())
|
||||
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<PHLWINDOW> 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::optional<PHLWIND
|
||||
return {};
|
||||
|
||||
if (mode == "top")
|
||||
g_pCompositor->changeWindowZOrder(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::optional<PH
|
||||
if (!window)
|
||||
return {};
|
||||
|
||||
auto PWINDOWINDIR = g_pCompositor->getWindowInDirection(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<bool> 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 {};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -45,6 +45,8 @@ namespace Desktop {
|
||||
|
||||
void resetWindowFocus();
|
||||
|
||||
bool isWindowActive(PHLWINDOW w) const;
|
||||
|
||||
SP<CWLSurfaceResource> surface();
|
||||
PHLWINDOW window();
|
||||
PHLMONITOR monitor();
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 <algorithm>
|
||||
#include <cmath>
|
||||
#include <functional>
|
||||
#include <ranges>
|
||||
#include <unordered_map>
|
||||
|
||||
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<Config::INTEGER>("binds:focus_preferred_method");
|
||||
static auto PMONITORFALLBACK = CConfigValue<Config::INTEGER>("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<Math::eDirection, Vector2D> 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 <typename WINDOWPTR>
|
||||
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 <typename WINDOWPTR>
|
||||
static bool isFloatingMatches(WINDOWPTR w, std::optional<bool> floating) {
|
||||
return !floating.has_value() || w->m_isFloating == floating.value();
|
||||
}
|
||||
|
||||
template <typename WINDOWPTR>
|
||||
static bool acceptsInputForCycle(WINDOWPTR w, bool allowFullscreenBlocked) {
|
||||
if (w->acceptsInput())
|
||||
return true;
|
||||
|
||||
return allowFullscreenBlocked && !w->isHidden() && w->isInputBlockedOnly(INPUT_BLOCK_BELOW_FULLSCREEN);
|
||||
}
|
||||
|
||||
template <typename WINDOWPTR>
|
||||
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 <typename Iterator>
|
||||
static PHLWINDOW getWindowPred(Iterator cur, Iterator end, Iterator begin, const std::function<bool(const PHLWINDOW&)> 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 <typename Iterator>
|
||||
static PHLWINDOW getWeakWindowPred(Iterator cur, Iterator end, Iterator begin, const std::function<bool(const PHLWINDOWREF&)> 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);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include "../DesktopTypes.hpp"
|
||||
#include "../../helpers/math/Direction.hpp"
|
||||
#include "../../helpers/math/Math.hpp"
|
||||
|
||||
#include <optional>
|
||||
|
||||
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<bool> 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;
|
||||
};
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
#include "WindowState.hpp"
|
||||
#include "../../event/EventBus.hpp"
|
||||
#include "../../render/Renderer.hpp"
|
||||
#include "../view/Window.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <ranges>
|
||||
|
||||
using namespace Desktop;
|
||||
|
||||
@@ -31,6 +33,65 @@ const std::vector<PHLWINDOW>& 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<PHLWINDOW> 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;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "../../helpers/memory/Memory.hpp"
|
||||
#include "../../helpers/signal/Signal.hpp"
|
||||
#include "../DesktopTypes.hpp"
|
||||
#include "WindowQuery.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
@@ -14,8 +15,9 @@ namespace Desktop {
|
||||
|
||||
const std::vector<PHLWINDOW>& 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<PHLWINDOW> m_windows;
|
||||
|
||||
void moveToTop(PHLWINDOW w);
|
||||
void moveToBottom(PHLWINDOW w);
|
||||
void moveToZ(PHLWINDOW w, bool top);
|
||||
|
||||
struct {
|
||||
CHyprSignalListener viewCreate, viewDestroy;
|
||||
} m_listeners;
|
||||
|
||||
+12
-24
@@ -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<SP<CXWaylandSurface>> 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<std::string> 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());
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<ITarget> 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;
|
||||
|
||||
@@ -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<ITarget> a, SP<ITarget> b) {
|
||||
void CMasterAlgorithm::moveTargetInDirection(SP<ITarget> t, Math::eDirection dir, bool silent) {
|
||||
static auto PMONITORFALLBACK = CConfigValue<Config::INTEGER>("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;
|
||||
|
||||
@@ -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<ITarget> 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() {
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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, SP<I
|
||||
// pointerFocus can target a surface without a Desktop::View (e.g. IME popups), so view() may be null.
|
||||
const auto PVIEW = HLSurf ? HLSurf->view() : nullptr;
|
||||
if (PVIEW && PVIEW->type() == Desktop::View::VIEW_TYPE_WINDOW)
|
||||
g_pCompositor->changeWindowZOrder(dynamicPointerCast<Desktop::View::CWindow>(PVIEW), true);
|
||||
Desktop::windowState()->raise(dynamicPointerCast<Desktop::View::CWindow>(PVIEW));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -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 <ranges>
|
||||
#include <pango/pangocairo.h>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user