summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Compositor.cpp142
-rw-r--r--src/Compositor.hpp3
-rw-r--r--src/config/shared/actions/ConfigActions.cpp2
-rw-r--r--src/desktop/Workspace.cpp19
-rw-r--r--src/desktop/Workspace.hpp1
-rw-r--r--src/desktop/state/FocusState.cpp3
-rw-r--r--src/desktop/view/Window.cpp17
-rw-r--r--src/desktop/view/Window.hpp17
-rw-r--r--src/helpers/Monitor.cpp34
-rw-r--r--src/layout/LayoutManager.cpp8
-rw-r--r--src/layout/LayoutManager.hpp45
-rw-r--r--src/layout/algorithm/Algorithm.cpp19
-rw-r--r--src/layout/algorithm/Algorithm.hpp4
-rw-r--r--src/layout/algorithm/ModeAlgorithm.cpp13
-rw-r--r--src/layout/algorithm/ModeAlgorithm.hpp13
-rw-r--r--src/layout/algorithm/tiled/scrolling/ScrollTapeController.cpp53
-rw-r--r--src/layout/algorithm/tiled/scrolling/ScrollTapeController.hpp1
-rw-r--r--src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp379
-rw-r--r--src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.hpp63
-rw-r--r--src/layout/space/Space.cpp20
-rw-r--r--src/layout/space/Space.hpp2
-rw-r--r--src/layout/target/Target.cpp8
-rw-r--r--src/layout/target/Target.hpp11
-rw-r--r--src/layout/target/WindowGroupTarget.cpp12
-rw-r--r--src/layout/target/WindowGroupTarget.hpp2
-rw-r--r--src/layout/target/WindowTarget.cpp26
-rw-r--r--src/managers/animation/DesktopAnimationManager.cpp2
-rw-r--r--src/managers/input/InputManager.cpp26
-rw-r--r--src/protocols/PointerConstraints.cpp8
29 files changed, 790 insertions, 163 deletions
diff --git a/src/Compositor.cpp b/src/Compositor.cpp
index b5dba342..bf3cf624 100644
--- a/src/Compositor.cpp
+++ b/src/Compositor.cpp
@@ -1462,10 +1462,10 @@ PHLWINDOW CCompositor::getWindowInDirection(PHLWINDOW pWindow, Math::eDirection
if (!PWORKSPACE)
return nullptr; // ??
- return getWindowInDirection(WINDOWIDEALBB, PWORKSPACE, dir, pWindow, pWindow->m_isFloating);
+ return getWindowInDirection(WINDOWIDEALBB, PWORKSPACE, dir, pWindow->m_isFloating, pWindow, pWindow->m_isFloating);
}
-PHLWINDOW CCompositor::getWindowInDirection(const CBox& box, PHLWORKSPACE pWorkspace, Math::eDirection dir, PHLWINDOW ignoreWindow, bool useVectorAngles) {
+PHLWINDOW CCompositor::getWindowInDirection(const CBox& box, PHLWORKSPACE pWorkspace, Math::eDirection dir, bool floatingPreference, PHLWINDOW ignoreWindow, bool useVectorAngles) {
if (dir == Math::DIRECTION_DEFAULT)
return nullptr;
@@ -1509,71 +1509,93 @@ PHLWINDOW CCompositor::getWindowInDirection(const CBox& box, PHLWORKSPACE pWorks
return overlap <= std::min(sizeA, sizeB) * MAX_OVERLAP_RATIO;
};
- for (auto const& w : m_windows) {
- if (w == ignoreWindow || !w->m_workspace || !w->m_isMapped || !w->acceptsInput() || (!w->isFullscreen() && w->m_isFloating) || !w->m_workspace->isVisible())
- continue;
+ auto find = [&]() {
+ for (auto const& w : m_windows) {
+ if (w == ignoreWindow || !w->m_workspace || !w->m_isMapped || (!w->isFullscreen() && w->m_isFloating) || !w->m_workspace->isVisible())
+ continue;
- if (pWorkspace->m_monitor == w->m_monitor && pWorkspace != w->m_workspace)
- continue;
+ if (w->isHidden())
+ continue;
- if (pWorkspace->m_hasFullscreenWindow && !w->isAllowedOverFullscreen())
- continue;
+ // check if the input is blocked by anything except BELOW_FULLSCREEN
+ if (w->isInputBlocked(INPUT_BLOCK_ALL & (~INPUT_BLOCK_BELOW_FULLSCREEN)))
+ continue;
- if (!*PMONITORFALLBACK && pWorkspace->m_monitor != w->m_monitor)
- continue;
+ if (pWorkspace->m_monitor == w->m_monitor && pWorkspace != w->m_workspace)
+ continue;
- const auto BWINDOWIDEALBB = w->getWindowIdealBoundingBoxIgnoreReserved();
+ if (pWorkspace->m_hasFullscreenWindow && !w->isAllowedOverFullscreen())
+ continue;
- const auto POSB = Vector2D(BWINDOWIDEALBB.x, BWINDOWIDEALBB.y);
- const auto SIZEB = Vector2D(BWINDOWIDEALBB.width, BWINDOWIDEALBB.height);
+ if (!*PMONITORFALLBACK && pWorkspace->m_monitor != w->m_monitor)
+ continue;
- double intersectLength = -1;
+ if (w->m_isFloating != floatingPreference)
+ continue;
- 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;
- }
+ const auto BWINDOWIDEALBB = w->getWindowIdealBoundingBoxIgnoreReserved();
- if (*PMETHOD == 0 /* history */) {
- if (intersectLength > 0) {
+ const auto POSB = Vector2D(BWINDOWIDEALBB.x, BWINDOWIDEALBB.y);
+ const auto SIZEB = Vector2D(BWINDOWIDEALBB.width, BWINDOWIDEALBB.height);
- // 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;
+ 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 (*PMETHOD == 0 /* history */) {
+ if (intersectLength > 0) {
+
+ // 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) {
- leaderValue = windowIDX;
+ if (windowIDX > leaderValue) {
+ leaderValue = windowIDX;
+ leaderWindow = w;
+ }
+ }
+ } else /* length */ {
+ if (intersectLength > leaderValue) {
+ leaderValue = intersectLength;
leaderWindow = w;
}
}
- } else /* length */ {
- if (intersectLength > leaderValue) {
- 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}}};
@@ -2248,9 +2270,16 @@ void CCompositor::setWindowFullscreenState(const PHLWINDOW PWINDOW, Desktop::Vie
PWORKSPACE->m_fullscreenMode = NEW_EFFECTIVE_MODE;
PWORKSPACE->m_hasFullscreenWindow = NEW_EFFECTIVE_MODE != FSMODE_NONE;
- g_layoutManager->fullscreenRequestForTarget(PWINDOW->layoutTarget(), OLD_EFFECTIVE_MODE, NEW_EFFECTIVE_MODE);
+ PWORKSPACE->setNoMembersAboveFullscreen();
- PWINDOW->m_fullscreenState.internal = state.internal;
+ const auto FULLSCREEN_REQUEST_RESULT = g_layoutManager->fullscreenRequestForTarget(PWINDOW->layoutTarget(), OLD_EFFECTIVE_MODE, NEW_EFFECTIVE_MODE);
+ const bool LAYOUT_HANDLED_FULLSCREEN = FULLSCREEN_REQUEST_RESULT == Layout::FULLSCREEN_REQUEST_HANDLED_BY_LAYOUT;
+
+ if (LAYOUT_HANDLED_FULLSCREEN) {
+ PWORKSPACE->m_fullscreenMode = FSMODE_NONE;
+ PWORKSPACE->m_hasFullscreenWindow = false;
+ } else
+ PWINDOW->m_fullscreenState.internal = state.internal;
g_pEventManager->postEvent(SHyprIPCEvent{.event = "fullscreen", .data = std::to_string(sc<int>(NEW_EFFECTIVE_MODE) != FSMODE_NONE)});
Event::bus()->m_events.window.fullscreen.emit(PWINDOW);
@@ -2275,8 +2304,9 @@ void CCompositor::setWindowFullscreenState(const PHLWINDOW PWINDOW, Desktop::Vie
ls->m_aboveFullscreen = false;
}
- g_pDesktopAnimationManager->setFullscreenFadeAnimation(
- PWORKSPACE, PWORKSPACE->m_hasFullscreenWindow ? CDesktopAnimationManager::ANIMATION_TYPE_IN : CDesktopAnimationManager::ANIMATION_TYPE_OUT);
+ if (!LAYOUT_HANDLED_FULLSCREEN)
+ g_pDesktopAnimationManager->setFullscreenFadeAnimation(
+ PWORKSPACE, PWORKSPACE->m_hasFullscreenWindow ? CDesktopAnimationManager::ANIMATION_TYPE_IN : CDesktopAnimationManager::ANIMATION_TYPE_OUT);
PWINDOW->sendWindowSize(true);
@@ -2290,7 +2320,7 @@ void CCompositor::setWindowFullscreenState(const PHLWINDOW PWINDOW, Desktop::Vie
// send a scanout tranche if we are entering fullscreen, and send a regular one if we aren't.
// ignore if DS is disabled.
- if (*PDIRECTSCANOUT == 1 || (*PDIRECTSCANOUT == 2 && PWINDOW->getContentType() == CONTENT_TYPE_GAME)) {
+ if (!LAYOUT_HANDLED_FULLSCREEN && (*PDIRECTSCANOUT == 1 || (*PDIRECTSCANOUT == 2 && PWINDOW->getContentType() == CONTENT_TYPE_GAME))) {
auto surf = PWINDOW->getSolitaryResource();
if (surf)
g_pHyprRenderer->setSurfaceScanoutMode(surf, NEW_EFFECTIVE_MODE != FSMODE_NONE ? PMONITOR->m_self.lock() : nullptr);
diff --git a/src/Compositor.hpp b/src/Compositor.hpp
index 7fbc7d52..98473358 100644
--- a/src/Compositor.hpp
+++ b/src/Compositor.hpp
@@ -116,7 +116,8 @@ class CCompositor {
void changeWindowZOrder(PHLWINDOW, bool);
void cleanupFadingOut(const MONITORID& monid);
PHLWINDOW getWindowInDirection(PHLWINDOW, Math::eDirection);
- PHLWINDOW getWindowInDirection(const CBox& box, PHLWORKSPACE pWorkspace, Math::eDirection dir, PHLWINDOW ignoreWindow = nullptr, bool useVectorAngles = false);
+ 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,
diff --git a/src/config/shared/actions/ConfigActions.cpp b/src/config/shared/actions/ConfigActions.cpp
index 56fb88e9..3f586b9f 100644
--- a/src/config/shared/actions/ConfigActions.cpp
+++ b/src/config/shared/actions/ConfigActions.cpp
@@ -409,7 +409,7 @@ ActionResult Actions::moveFocus(Math::eDirection dir) {
}
const auto PWINDOWCANDIDATE = g_pCompositor->getWindowInDirection(box, PMONITOR->m_activeSpecialWorkspace ? PMONITOR->m_activeSpecialWorkspace : PMONITOR->m_activeWorkspace,
- dir, PLASTWINDOW, PLASTWINDOW->m_isFloating);
+ dir, PLASTWINDOW->m_isFloating, PLASTWINDOW, PLASTWINDOW->m_isFloating);
if (PWINDOWCANDIDATE)
switchToWindow(PWINDOWCANDIDATE);
diff --git a/src/desktop/Workspace.cpp b/src/desktop/Workspace.cpp
index 23ea0dd7..6ff6d0b4 100644
--- a/src/desktop/Workspace.cpp
+++ b/src/desktop/Workspace.cpp
@@ -1,5 +1,6 @@
#include "Workspace.hpp"
#include "view/Group.hpp"
+#include "view/LayerSurface.hpp"
#include "../Compositor.hpp"
#include "../config/shared/animation/AnimationTree.hpp"
#include "../config/shared/workspace/WorkspaceRuleManager.hpp"
@@ -8,6 +9,7 @@
#include "../managers/EventManager.hpp"
#include "../helpers/Monitor.hpp"
#include "../layout/space/Space.hpp"
+#include "../layout/target/Target.hpp"
#include "../layout/supplementary/WorkspaceAlgoMatcher.hpp"
#include "../event/EventBus.hpp"
@@ -539,7 +541,10 @@ void CWorkspace::rename(const std::string& name) {
}
void CWorkspace::updateWindows() {
- m_hasFullscreenWindow = std::ranges::any_of(m_space->targets(), [](const auto& t) { return t->fullscreenMode() != FSMODE_NONE; });
+ m_hasFullscreenWindow = std::ranges::any_of(m_space->targets(), [](const auto& t) { return t && t->fullscreenMode() != FSMODE_NONE && !t->layoutManagedFullscreen(); });
+
+ if (!m_hasFullscreenWindow)
+ m_fullscreenMode = FSMODE_NONE;
for (auto const& t : m_space->targets()) {
if (t->window())
@@ -562,3 +567,15 @@ void CWorkspace::setPersistent(bool persistent) {
bool CWorkspace::isPersistent() {
return m_persistent;
}
+
+void CWorkspace::setNoMembersAboveFullscreen() {
+ // make all windows and layers on the same workspace under the fullscreen window
+ for (auto const& w : g_pCompositor->m_windows) {
+ if (w->m_workspace == m_self && !w->isFullscreen() && !w->m_fadingOut && !w->m_pinned)
+ w->m_createdOverFullscreen = false;
+ }
+ for (auto const& ls : g_pCompositor->m_layers) {
+ if (ls->m_monitor == m_monitor)
+ ls->m_aboveFullscreen = false;
+ }
+}
diff --git a/src/desktop/Workspace.hpp b/src/desktop/Workspace.hpp
index 1ccf1454..1abc80c0 100644
--- a/src/desktop/Workspace.hpp
+++ b/src/desktop/Workspace.hpp
@@ -80,6 +80,7 @@ class CWorkspace {
void updateWindows();
void setPersistent(bool persistent);
bool isPersistent();
+ void setNoMembersAboveFullscreen();
struct {
CSignalT<> destroy;
diff --git a/src/desktop/state/FocusState.cpp b/src/desktop/state/FocusState.cpp
index 5dd22f94..48fb6456 100644
--- a/src/desktop/state/FocusState.cpp
+++ b/src/desktop/state/FocusState.cpp
@@ -95,6 +95,9 @@ void CFocusState::rawWindowFocus(PHLWINDOW pWindow, eFocusReason reason, SP<CWLS
static auto PFOLLOWMOUSE = CConfigValue<Config::INTEGER>("input:follow_mouse");
static auto PSPECIALFALLTHROUGH = CConfigValue<Config::INTEGER>("input:special_fallthrough");
+ if (pWindow == m_focusWindow && surface == m_focusSurface)
+ return;
+
if (!pWindow || !pWindow->priorityFocus()) {
if (g_pSessionLockManager->isSessionLocked()) {
Log::logger->log(Log::DEBUG, "Refusing a keyboard focus to a window because of a sessionlock");
diff --git a/src/desktop/view/Window.cpp b/src/desktop/view/Window.cpp
index b2f4821b..19f8f88d 100644
--- a/src/desktop/view/Window.cpp
+++ b/src/desktop/view/Window.cpp
@@ -48,6 +48,7 @@
#include "../../managers/input/InputManager.hpp"
#include "../../managers/PointerManager.hpp"
#include "../../managers/animation/DesktopAnimationManager.hpp"
+#include "../../layout/algorithm/Algorithm.hpp"
#include "../../layout/space/Space.hpp"
#include "../../layout/LayoutManager.hpp"
#include "../../layout/target/WindowTarget.hpp"
@@ -267,7 +268,7 @@ CBox CWindow::getWindowIdealBoundingBoxIgnoreReserved() {
auto POS = m_position;
auto SIZE = m_size;
- if (isFullscreen()) {
+ if (isFullscreen() && (!layoutTarget() || !layoutTarget()->layoutManagedFullscreen())) {
POS = PMONITOR->m_position;
SIZE = PMONITOR->m_size;
@@ -748,12 +749,12 @@ bool CWindow::isInputBlocked() const {
return m_inputBlockReasons != INPUT_BLOCK_NONE;
}
-bool CWindow::isInputBlocked(eWindowInputBlockReason reason) const {
- return (m_inputBlockReasons & sc<uint32_t>(reason)) != 0;
+bool CWindow::isInputBlocked(std::underlying_type_t<eWindowInputBlockReason> reasons) const {
+ return (m_inputBlockReasons & reasons) != 0;
}
bool CWindow::isInputBlockedOnly(eWindowInputBlockReason reason) const {
- return m_inputBlockReasons == sc<uint32_t>(reason);
+ return m_inputBlockReasons == reason;
}
bool CWindow::acceptsInput() const {
@@ -772,7 +773,13 @@ bool CWindow::isAllowedOverFullscreen() const {
}
bool CWindow::isBlockedByFullscreen() const {
- if (!m_workspace || !m_workspace->m_hasFullscreenWindow)
+ if (!m_workspace)
+ return false;
+
+ const auto ALGORITHM = m_workspace->m_space ? m_workspace->m_space->algorithm() : nullptr;
+ const bool HAS_LAYOUT_FULLSCREEN = ALGORITHM && ALGORITHM->layoutFullscreenCoversMonitor();
+
+ if (!m_workspace->m_hasFullscreenWindow && !HAS_LAYOUT_FULLSCREEN)
return false;
return !isAllowedOverFullscreen();
diff --git a/src/desktop/view/Window.hpp b/src/desktop/view/Window.hpp
index 32a7bcfe..6174e694 100644
--- a/src/desktop/view/Window.hpp
+++ b/src/desktop/view/Window.hpp
@@ -89,11 +89,13 @@ namespace Desktop::View {
WINDOW_ALPHA_LAST,
};
- enum eWindowInputBlockReason : uint32_t {
+ enum eWindowInputBlockReason : uint8_t {
INPUT_BLOCK_NONE = 0,
- INPUT_BLOCK_GROUP_INACTIVE = 1 << 0,
- INPUT_BLOCK_MONOCLE_INACTIVE = 1 << 1,
- INPUT_BLOCK_BELOW_FULLSCREEN = 1 << 2,
+ INPUT_BLOCK_GROUP_INACTIVE = (1 << 0),
+ INPUT_BLOCK_MONOCLE_INACTIVE = (1 << 1),
+ INPUT_BLOCK_BELOW_FULLSCREEN = (1 << 2),
+
+ INPUT_BLOCK_ALL = std::numeric_limits<std::underlying_type_t<eWindowInputBlockReason>>::max(),
};
struct SWindowActiveEvent {
@@ -285,6 +287,11 @@ namespace Desktop::View {
// For the noclosefor windowrule
Time::steady_tp m_closeableSince = Time::steadyNow();
+ // layout-settable flags. These are reset when layout changes.
+ struct {
+ bool cantLockCursor = false;
+ } m_layoutFlags;
+
// For the list lookup
bool operator==(const CWindow& rhs) const {
return m_xdgSurface == rhs.m_xdgSurface && m_xwaylandSurface == rhs.m_xwaylandSurface && m_position == rhs.m_position && m_size == rhs.m_size &&
@@ -314,7 +321,7 @@ namespace Desktop::View {
bool isHidden() const;
void setInputBlocked(eWindowInputBlockReason reason, bool blocked);
bool isInputBlocked() const;
- bool isInputBlocked(eWindowInputBlockReason reason) const;
+ bool isInputBlocked(std::underlying_type_t<eWindowInputBlockReason> reasons) const;
bool isInputBlockedOnly(eWindowInputBlockReason reason) const;
bool acceptsInput() const;
bool isAllowedOverFullscreen() const;
diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp
index c7d4168a..958c8480 100644
--- a/src/helpers/Monitor.cpp
+++ b/src/helpers/Monitor.cpp
@@ -30,6 +30,8 @@
#include "../managers/input/InputManager.hpp"
#include "../errorOverlay/Overlay.hpp"
#include "../layout/LayoutManager.hpp"
+#include "../layout/space/Space.hpp"
+#include "../layout/algorithm/Algorithm.hpp"
#include "../i18n/Engine.hpp"
#include "../helpers/cm/ColorManagement.hpp"
#include "time/Time.hpp"
@@ -1672,7 +1674,7 @@ uint32_t CMonitor::isSolitaryBlocked(bool full) {
return reasons;
}
- if (!PWORKSPACE->m_hasFullscreenWindow) {
+ if (!inFullscreenMode()) {
reasons |= SC_WINDOWED;
if (!full)
return reasons;
@@ -1720,7 +1722,7 @@ uint32_t CMonitor::isSolitaryBlocked(bool full) {
return reasons;
}
- const auto PCANDIDATE = PWORKSPACE->getFullscreenWindow();
+ const auto PCANDIDATE = getFullscreenWindow();
if (!PCANDIDATE) {
reasons |= SC_CANDIDATE;
@@ -1791,7 +1793,7 @@ void CMonitor::recheckSolitary() {
if (isSolitaryBlocked())
return;
- m_solitaryClient = PWORKSPACE->getFullscreenWindow();
+ m_solitaryClient = getFullscreenWindow();
}
uint8_t CMonitor::isTearingBlocked(bool full) {
@@ -1871,11 +1873,12 @@ uint16_t CMonitor::isDSBlocked(bool full) {
}
if (*PDIRECTSCANOUT == 2) {
- if (!PWORKSPACE || !PWORKSPACE->m_hasFullscreenWindow || PWORKSPACE->m_fullscreenMode != FSMODE_FULLSCREEN) {
+ const auto FSWINDOW = getFullscreenWindow();
+ if (!PWORKSPACE || !inFullscreenMode() || !FSWINDOW) {
reasons |= DS_BLOCK_WINDOWED;
if (!full)
return reasons;
- } else if (PWORKSPACE->getFullscreenWindow()->getContentType() != CONTENT_TYPE_GAME) {
+ } else if (FSWINDOW->getContentType() != CONTENT_TYPE_GAME) {
reasons |= DS_BLOCK_CONTENT;
if (!full)
return reasons;
@@ -2268,17 +2271,34 @@ bool CMonitor::inHDR() {
bool CMonitor::inFullscreenMode() {
// Check special workspace first since it renders on top of regular workspaces
- if (m_activeSpecialWorkspace && m_activeSpecialWorkspace->m_hasFullscreenWindow && m_activeSpecialWorkspace->m_fullscreenMode == FSMODE_FULLSCREEN)
+ if (m_activeSpecialWorkspace &&
+ ((m_activeSpecialWorkspace->m_hasFullscreenWindow && m_activeSpecialWorkspace->m_fullscreenMode == FSMODE_FULLSCREEN) ||
+ (m_activeSpecialWorkspace->m_space && m_activeSpecialWorkspace->m_space->algorithm() && m_activeSpecialWorkspace->m_space->algorithm()->layoutFullscreenCoversMonitor())))
return true;
- return m_activeWorkspace && m_activeWorkspace->m_hasFullscreenWindow && m_activeWorkspace->m_fullscreenMode == FSMODE_FULLSCREEN;
+ return m_activeWorkspace &&
+ ((m_activeWorkspace->m_hasFullscreenWindow && m_activeWorkspace->m_fullscreenMode == FSMODE_FULLSCREEN) ||
+ (m_activeWorkspace->m_space && m_activeWorkspace->m_space->algorithm() && m_activeWorkspace->m_space->algorithm()->layoutFullscreenCoversMonitor()));
}
PHLWINDOW CMonitor::getFullscreenWindow() {
// Check special workspace first since it renders on top of regular workspaces
if (m_activeSpecialWorkspace && m_activeSpecialWorkspace->m_hasFullscreenWindow && m_activeSpecialWorkspace->m_fullscreenMode == FSMODE_FULLSCREEN)
return m_activeSpecialWorkspace->getFullscreenWindow();
+
+ if (m_activeSpecialWorkspace && m_activeSpecialWorkspace->m_space && m_activeSpecialWorkspace->m_space->algorithm() &&
+ m_activeSpecialWorkspace->m_space->algorithm()->layoutFullscreenCoversMonitor()) {
+ const auto TARGET = m_activeSpecialWorkspace->m_space->algorithm()->layoutFullscreenTarget();
+ return TARGET ? TARGET->window() : nullptr;
+ }
+
if (m_activeWorkspace && m_activeWorkspace->m_hasFullscreenWindow && m_activeWorkspace->m_fullscreenMode == FSMODE_FULLSCREEN)
return m_activeWorkspace->getFullscreenWindow();
+
+ if (m_activeWorkspace && m_activeWorkspace->m_space && m_activeWorkspace->m_space->algorithm() && m_activeWorkspace->m_space->algorithm()->layoutFullscreenCoversMonitor()) {
+ const auto TARGET = m_activeWorkspace->m_space->algorithm()->layoutFullscreenTarget();
+ return TARGET ? TARGET->window() : nullptr;
+ }
+
return nullptr;
}
diff --git a/src/layout/LayoutManager.cpp b/src/layout/LayoutManager.cpp
index f90cedf6..875ae2ae 100644
--- a/src/layout/LayoutManager.cpp
+++ b/src/layout/LayoutManager.cpp
@@ -94,9 +94,11 @@ void CLayoutManager::endDragTarget() {
m_dragStateController->dragEnd();
}
-void CLayoutManager::fullscreenRequestForTarget(SP<ITarget> target, eFullscreenMode currentEffectiveMode, eFullscreenMode effectiveMode) {
- if (target->space())
- target->space()->setFullscreen(target, effectiveMode);
+eFullscreenRequestResult CLayoutManager::fullscreenRequestForTarget(SP<ITarget> target, eFullscreenMode currentEffectiveMode, eFullscreenMode effectiveMode) {
+ if (target && target->space())
+ return target->space()->setFullscreen(target, currentEffectiveMode, effectiveMode);
+
+ return FULLSCREEN_REQUEST_DEFAULT;
}
void CLayoutManager::switchTargets(SP<ITarget> a, SP<ITarget> b, bool preserveFocus) {
diff --git a/src/layout/LayoutManager.hpp b/src/layout/LayoutManager.hpp
index 2660cf28..ef886daa 100644
--- a/src/layout/LayoutManager.hpp
+++ b/src/layout/LayoutManager.hpp
@@ -40,43 +40,48 @@ namespace Layout {
SNAP_RIGHT = (1 << 3),
};
+ enum eFullscreenRequestResult : uint8_t {
+ FULLSCREEN_REQUEST_DEFAULT = 0,
+ FULLSCREEN_REQUEST_HANDLED_BY_LAYOUT,
+ };
+
class CLayoutManager {
public:
CLayoutManager();
~CLayoutManager() = default;
- void newTarget(SP<ITarget> target, SP<CSpace> space);
- void removeTarget(SP<ITarget> target);
+ void newTarget(SP<ITarget> target, SP<CSpace> space);
+ void removeTarget(SP<ITarget> target);
- void changeFloatingMode(SP<ITarget> target);
+ void changeFloatingMode(SP<ITarget> target);
- void beginDragTarget(SP<ITarget> target, eMouseBindMode mode);
- void moveMouse(const Vector2D& mousePos);
- void resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRectCorner corner = CORNER_NONE);
- void moveTarget(const Vector2D& Δ, SP<ITarget> target);
- void setTargetGeom(const CBox& box, SP<ITarget> target); // floats only
- void endDragTarget();
+ void beginDragTarget(SP<ITarget> target, eMouseBindMode mode);
+ void moveMouse(const Vector2D& mousePos);
+ void resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRectCorner corner = CORNER_NONE);
+ void moveTarget(const Vector2D& Δ, SP<ITarget> target);
+ void setTargetGeom(const CBox& box, SP<ITarget> target); // floats only
+ void endDragTarget();
- Config::ErrorResult layoutMsg(const std::string_view& sv);
+ Config::ErrorResult layoutMsg(const std::string_view& sv);
- void fullscreenRequestForTarget(SP<ITarget> target, eFullscreenMode currentEffectiveMode, eFullscreenMode effectiveMode);
+ eFullscreenRequestResult fullscreenRequestForTarget(SP<ITarget> target, eFullscreenMode currentEffectiveMode, eFullscreenMode effectiveMode);
- void switchTargets(SP<ITarget> a, SP<ITarget> b, bool preserveFocus = true);
+ void switchTargets(SP<ITarget> a, SP<ITarget> b, bool preserveFocus = true);
- void moveInDirection(SP<ITarget> target, const std::string& direction, bool silent = false);
+ void moveInDirection(SP<ITarget> target, const std::string& direction, bool silent = false);
- SP<ITarget> getNextCandidate(SP<CSpace> space, SP<ITarget> from);
+ SP<ITarget> getNextCandidate(SP<CSpace> space, SP<ITarget> from);
- bool isReachable(SP<ITarget> target);
+ bool isReachable(SP<ITarget> target);
- void bringTargetToTop(SP<ITarget> target);
+ void bringTargetToTop(SP<ITarget> target);
- std::optional<Vector2D> predictSizeForNewTiledTarget();
+ std::optional<Vector2D> predictSizeForNewTiledTarget();
- void performSnap(Vector2D& sourcePos, Vector2D& sourceSize, SP<ITarget> target, eMouseBindMode mode, int corner, const Vector2D& beginSize);
+ void performSnap(Vector2D& sourcePos, Vector2D& sourceSize, SP<ITarget> target, eMouseBindMode mode, int corner, const Vector2D& beginSize);
- void invalidateMonitorGeometries(PHLMONITOR);
- void recalculateMonitor(PHLMONITOR);
+ void invalidateMonitorGeometries(PHLMONITOR);
+ void recalculateMonitor(PHLMONITOR);
const UP<Supplementary::CDragStateController>& dragController();
diff --git a/src/layout/algorithm/Algorithm.cpp b/src/layout/algorithm/Algorithm.cpp
index b22eb9bf..ae96a7d8 100644
--- a/src/layout/algorithm/Algorithm.cpp
+++ b/src/layout/algorithm/Algorithm.cpp
@@ -154,6 +154,25 @@ void CAlgorithm::moveTarget(const Vector2D& Δ, SP<ITarget> target) {
m_floating->moveTarget(Δ, target);
}
+eFullscreenRequestResult CAlgorithm::requestFullscreen(SP<ITarget> target, eFullscreenMode currentEffectiveMode, eFullscreenMode effectiveMode) {
+ if (!target)
+ return FULLSCREEN_REQUEST_DEFAULT;
+
+ const SFullscreenRequest request = {.target = target, .currentEffectiveMode = currentEffectiveMode, .effectiveMode = effectiveMode};
+ return target->floating() ? m_floating->requestFullscreen(request) : m_tiled->requestFullscreen(request);
+}
+
+SP<ITarget> CAlgorithm::layoutFullscreenTarget() const {
+ if (const auto TARGET = m_tiled->layoutFullscreenTarget(); TARGET)
+ return TARGET;
+
+ return m_floating->layoutFullscreenTarget();
+}
+
+bool CAlgorithm::layoutFullscreenCoversMonitor() const {
+ return m_tiled->layoutFullscreenCoversMonitor() || m_floating->layoutFullscreenCoversMonitor();
+}
+
void CAlgorithm::swapTargets(SP<ITarget> a, SP<ITarget> b) {
auto swapFirst = [&a, &b](std::vector<WP<ITarget>>& targets) -> bool {
auto ia = std::ranges::find(targets, a);
diff --git a/src/layout/algorithm/Algorithm.hpp b/src/layout/algorithm/Algorithm.hpp
index 8b9e471d..61c40285 100644
--- a/src/layout/algorithm/Algorithm.hpp
+++ b/src/layout/algorithm/Algorithm.hpp
@@ -42,6 +42,10 @@ namespace Layout {
void setTargetGeom(const CBox& box, SP<ITarget> target); // only for float
+ eFullscreenRequestResult requestFullscreen(SP<ITarget> target, eFullscreenMode currentEffectiveMode, eFullscreenMode effectiveMode);
+ SP<ITarget> layoutFullscreenTarget() const;
+ bool layoutFullscreenCoversMonitor() const;
+
void updateFloatingAlgo(UP<IFloatingAlgorithm>&& algo);
void updateTiledAlgo(UP<ITiledAlgorithm>&& algo);
diff --git a/src/layout/algorithm/ModeAlgorithm.cpp b/src/layout/algorithm/ModeAlgorithm.cpp
index c029336f..5927e861 100644
--- a/src/layout/algorithm/ModeAlgorithm.cpp
+++ b/src/layout/algorithm/ModeAlgorithm.cpp
@@ -15,6 +15,19 @@ std::optional<Vector2D> IModeAlgorithm::predictSizeForNewTarget() {
return std::nullopt;
}
+eFullscreenRequestResult IModeAlgorithm::requestFullscreen(const SFullscreenRequest& request) {
+ (void)request;
+ return FULLSCREEN_REQUEST_DEFAULT;
+}
+
+SP<ITarget> IModeAlgorithm::layoutFullscreenTarget() const {
+ return nullptr;
+}
+
+bool IModeAlgorithm::layoutFullscreenCoversMonitor() const {
+ return false;
+}
+
std::optional<Vector2D> IModeAlgorithm::focalPointForDir(SP<ITarget> t, Math::eDirection dir) {
Vector2D focalPoint;
diff --git a/src/layout/algorithm/ModeAlgorithm.hpp b/src/layout/algorithm/ModeAlgorithm.hpp
index e53d4391..1348a6f4 100644
--- a/src/layout/algorithm/ModeAlgorithm.hpp
+++ b/src/layout/algorithm/ModeAlgorithm.hpp
@@ -13,6 +13,12 @@ namespace Layout {
class ITarget;
class CAlgorithm;
+ struct SFullscreenRequest {
+ SP<ITarget> target;
+ eFullscreenMode currentEffectiveMode = static_cast<eFullscreenMode>(0);
+ eFullscreenMode effectiveMode = static_cast<eFullscreenMode>(0);
+ };
+
class IModeAlgorithm {
public:
virtual ~IModeAlgorithm() = default;
@@ -44,6 +50,13 @@ namespace Layout {
// optional: predict new window's size
virtual std::optional<Vector2D> predictSizeForNewTarget();
+ // optional: allow algorithms to own fullscreen semantics for a target.
+ virtual eFullscreenRequestResult requestFullscreen(const SFullscreenRequest& request);
+
+ // optional: expose an algorithm-owned fullscreen target and whether it is monitor-exclusive.
+ virtual SP<ITarget> layoutFullscreenTarget() const;
+ virtual bool layoutFullscreenCoversMonitor() const;
+
// Impl'd here: focal point for dir
virtual std::optional<Vector2D> focalPointForDir(SP<ITarget> t, Math::eDirection dir);
diff --git a/src/layout/algorithm/tiled/scrolling/ScrollTapeController.cpp b/src/layout/algorithm/tiled/scrolling/ScrollTapeController.cpp
index 657b6542..722b9bd8 100644
--- a/src/layout/algorithm/tiled/scrolling/ScrollTapeController.cpp
+++ b/src/layout/algorithm/tiled/scrolling/ScrollTapeController.cpp
@@ -147,14 +147,10 @@ double CScrollTapeController::calculateStripSize(size_t stripIndex, const CBox&
return usablePrimary * m_strips[stripIndex].size;
}
-CBox CScrollTapeController::calculateTargetBox(size_t stripIndex, size_t targetIndex, const CBox& usableArea, const Vector2D& workspaceOffset, bool fullscreenOnOne) {
+CBox CScrollTapeController::calculateStripBox(size_t stripIndex, const CBox& usableArea, const Vector2D& workspaceOffset, bool fullscreenOnOne) {
if (stripIndex >= m_strips.size())
return {};
- const auto& strip = m_strips[stripIndex];
- if (targetIndex >= strip.targetSizes.size())
- return {};
-
const double usableSecondary = getSecondary(usableArea.size());
const double usablePrimary = getPrimary(usableArea.size());
const double cameraOffset = calculateCameraOffset(usableArea, fullscreenOnOne);
@@ -163,13 +159,6 @@ CBox CScrollTapeController::calculateTargetBox(size_t stripIndex, size_t targetI
double primaryPos = calculateStripStart(stripIndex, usableArea, fullscreenOnOne);
double primarySize = calculateStripSize(stripIndex, usableArea, fullscreenOnOne);
- // calculate position along secondary axis (within strip)
- double secondaryPos = 0.0;
- for (size_t i = 0; i < targetIndex; ++i) {
- secondaryPos += strip.targetSizes[i] * usableSecondary;
- }
- double secondarySize = strip.targetSizes[targetIndex] * usableSecondary;
-
// apply camera offset based on direction
// for RIGHT/DOWN: scroll offset moves content left/up (subtract)
// for LEFT/UP: scroll offset moves content right/down (different coordinate system)
@@ -185,8 +174,8 @@ CBox CScrollTapeController::calculateTargetBox(size_t stripIndex, size_t targetI
}
// create the box in primary/secondary coordinates
- Vector2D pos = makeVector(primaryPos, secondaryPos);
- Vector2D size = makeVector(primarySize, secondarySize);
+ Vector2D pos = makeVector(primaryPos, 0.0);
+ Vector2D size = makeVector(primarySize, usableSecondary);
// translate to workspace position
pos = pos + workspaceOffset;
@@ -194,6 +183,35 @@ CBox CScrollTapeController::calculateTargetBox(size_t stripIndex, size_t targetI
return CBox{pos, size};
}
+CBox CScrollTapeController::calculateTargetBox(size_t stripIndex, size_t targetIndex, const CBox& usableArea, const Vector2D& workspaceOffset, bool fullscreenOnOne) {
+ if (stripIndex >= m_strips.size())
+ return {};
+
+ const auto& strip = m_strips[stripIndex];
+ if (targetIndex >= strip.targetSizes.size())
+ return {};
+
+ CBox stripBox = calculateStripBox(stripIndex, usableArea, workspaceOffset, fullscreenOnOne);
+ const double usableSecondary = getSecondary(usableArea.size());
+
+ double secondaryPos = 0.0;
+ for (size_t i = 0; i < targetIndex; ++i) {
+ secondaryPos += strip.targetSizes[i] * usableSecondary;
+ }
+
+ const double secondarySize = strip.targetSizes[targetIndex] * usableSecondary;
+
+ if (isPrimaryHorizontal()) {
+ stripBox.y = workspaceOffset.y + secondaryPos;
+ stripBox.h = secondarySize;
+ } else {
+ stripBox.x = workspaceOffset.x + secondaryPos;
+ stripBox.w = secondarySize;
+ }
+
+ return stripBox;
+}
+
double CScrollTapeController::calculateCameraOffset(const CBox& usableArea, bool fullscreenOnOne) {
const double maxExtent = calculateMaxExtent(usableArea, fullscreenOnOne);
const double usablePrimary = getPrimary(usableArea.size());
@@ -275,13 +293,14 @@ size_t CScrollTapeController::getStripAtCenter(const CBox& usableArea, bool full
return 0;
const double usablePrimary = getPrimary(usableArea.size());
- double currentPos = m_offset;
+ const double viewCenter = m_offset + usablePrimary / 2.0;
+ double currentEnd = 0.0;
for (size_t i = 0; i < m_strips.size(); ++i) {
const double stripSize = calculateStripSize(i, usableArea, fullscreenOnOne);
- currentPos += stripSize;
+ currentEnd += stripSize;
- if (currentPos >= usablePrimary / 2.0 - 2.0)
+ if (currentEnd >= viewCenter - 2.0)
return i;
}
diff --git a/src/layout/algorithm/tiled/scrolling/ScrollTapeController.hpp b/src/layout/algorithm/tiled/scrolling/ScrollTapeController.hpp
index da2efbba..8006c4a5 100644
--- a/src/layout/algorithm/tiled/scrolling/ScrollTapeController.hpp
+++ b/src/layout/algorithm/tiled/scrolling/ScrollTapeController.hpp
@@ -55,6 +55,7 @@ namespace Layout::Tiled {
double calculateStripStart(size_t stripIndex, const CBox& usableArea, bool fullscreenOnOne = false) const;
double calculateStripSize(size_t stripIndex, const CBox& usableArea, bool fullscreenOnOne = false) const;
+ CBox calculateStripBox(size_t stripIndex, const CBox& usableArea, const Vector2D& workspaceOffset, bool fullscreenOnOne = false);
CBox calculateTargetBox(size_t stripIndex, size_t targetIndex, const CBox& usableArea, const Vector2D& workspaceOffset, bool fullscreenOnOne = false);
double calculateCameraOffset(const CBox& usableArea, bool fullscreenOnOne = false);
diff --git a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp
index 28941109..2914ae8b 100644
--- a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp
+++ b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp
@@ -11,6 +11,7 @@
#include "../../../../config/shared/workspace/WorkspaceRuleManager.hpp"
#include "../../../../render/Renderer.hpp"
#include "../../../../managers/input/InputManager.hpp"
+#include "../../../../managers/animation/DesktopAnimationManager.hpp"
#include "../../../../event/EventBus.hpp"
#include <hyprutils/string/VarList2.hpp>
@@ -427,23 +428,83 @@ void SScrollingData::recalculate(bool forceInstant) {
algorithm->m_parent->space()->workspace()->m_hasFullscreenWindow)
return;
+ algorithm->syncFullscreenTargets();
+
static const auto PFSONONE = CConfigValue<Config::INTEGER>("scrolling:fullscreen_on_one_column");
const CBox USABLE = algorithm->usableArea();
const auto WORKAREA = algorithm->m_parent->space()->workArea();
+ const CBox MONBOX = algorithm->m_parent->space()->workspace()->m_monitor->logicalBox();
+
+ const auto WORKSPACERULE = Config::workspaceRuleMgr()->getWorkspaceRuleFor(algorithm->m_parent->space()->workspace());
+ static auto PGAPSINDATA = CConfigValue<Config::IComplexConfigValue>("general:gaps_in");
+ auto* const PGAPSIN = sc<Config::CCssGapData*>((PGAPSINDATA.ptr()));
+ const auto GAPSIN = (WORKSPACERULE && WORKSPACERULE->m_gapsIn.has_value()) ? WORKSPACERULE->m_gapsIn.value() : *PGAPSIN;
+
+ bool anyFullscreenCovers = false;
+ for (const auto& COL : columns) {
+ if (algorithm->fullscreenTargetDataForColumn(COL) && algorithm->fullscreenColumnCoversMonitor(COL)) {
+ anyFullscreenCovers = true;
+ break;
+ }
+ }
controller->setDirection(algorithm->getDynamicDirection());
+ algorithm->updateFullscreenFade(anyFullscreenCovers);
+
+ const auto targetBoxWithGaps = [&](const CBox& logical, size_t colIdx, size_t targetIdx, bool fullscreenOrHidden) -> STargetBox {
+ if (fullscreenOrHidden)
+ return {.logicalBox = logical, .visualBox = logical};
+
+ CBox visual = logical;
+ const bool PRIMARY_HORIZ = controller->isPrimaryHorizontal();
+
+ const bool GAP_LEFT = PRIMARY_HORIZ ? colIdx > 0 : targetIdx > 0;
+ const bool GAP_RIGHT = PRIMARY_HORIZ ? colIdx + 1 < columns.size() : targetIdx + 1 < columns[colIdx]->targetDatas.size();
+ const bool GAP_TOP = PRIMARY_HORIZ ? targetIdx > 0 : colIdx > 0;
+ const bool GAP_BOTTOM = PRIMARY_HORIZ ? targetIdx + 1 < columns[colIdx]->targetDatas.size() : colIdx + 1 < columns.size();
+
+ const auto GAPOFFSETTOPLEFT = Vector2D(sc<double>(GAP_LEFT ? GAPSIN.m_left : 0), sc<double>(GAP_TOP ? GAPSIN.m_top : 0));
+ const auto GAPOFFSETBOTTOMRIGHT = Vector2D(sc<double>(GAP_RIGHT ? GAPSIN.m_right : 0), sc<double>(GAP_BOTTOM ? GAPSIN.m_bottom : 0));
+
+ visual.x += GAPOFFSETTOPLEFT.x;
+ visual.y += GAPOFFSETTOPLEFT.y;
+ visual.w = std::max(1.0, visual.w - GAPOFFSETTOPLEFT.x - GAPOFFSETBOTTOMRIGHT.x);
+ visual.h = std::max(1.0, visual.h - GAPOFFSETTOPLEFT.y - GAPOFFSETBOTTOMRIGHT.y);
+
+ return {.logicalBox = logical, .visualBox = visual};
+ };
for (size_t i = 0; i < columns.size(); ++i) {
const auto& COL = columns[i];
+ const auto FS = algorithm->fullscreenTargetDataForColumn(COL);
for (size_t j = 0; j < COL->targetDatas.size(); ++j) {
const auto& TARGET = COL->targetDatas[j];
- TARGET->layoutBox = controller->calculateTargetBox(i, j, USABLE, WORKAREA.pos(), *PFSONONE);
+ if (FS) {
+ if (TARGET == FS) {
+ if (algorithm->fullscreenColumnCoversMonitor(COL))
+ TARGET->layoutBox = MONBOX;
+ else {
+ TARGET->layoutBox = controller->calculateStripBox(i, USABLE, WORKAREA.pos(), *PFSONONE);
+
+ if (controller->isPrimaryHorizontal()) {
+ TARGET->layoutBox.y = MONBOX.y;
+ TARGET->layoutBox.h = MONBOX.h;
+ } else {
+ TARGET->layoutBox.x = MONBOX.x;
+ TARGET->layoutBox.w = MONBOX.w;
+ }
+ }
+ } else
+ TARGET->layoutBox = CBox{WORKAREA.pos() - Vector2D{100000.0, 100000.0}, Vector2D{1.0, 1.0}};
+ } else
+ TARGET->layoutBox = controller->calculateTargetBox(i, j, USABLE, WORKAREA.pos(), *PFSONONE);
if (TARGET->target)
- TARGET->target->setPositionGlobal(TARGET->layoutBox);
+ TARGET->target->setPositionGlobal(targetBoxWithGaps(TARGET->layoutBox, i, j, FS));
+
if (forceInstant && TARGET->target)
TARGET->target->warpPositionSize();
}
@@ -544,6 +605,9 @@ CScrollingAlgorithm::CScrollingAlgorithm() {
}
CScrollingAlgorithm::~CScrollingAlgorithm() {
+ clearFullscreenTarget();
+ updateFullscreenFade(false);
+
m_configCallback.reset();
m_focusCallback.reset();
}
@@ -631,6 +695,8 @@ void CScrollingAlgorithm::removeTarget(SP<ITarget> target) {
if (!DATA)
return;
+ clearFullscreenTarget(target);
+
if (!m_scrollingData->next(DATA->column.lock()) && DATA->column->targetDatas.size() <= 1) {
// move the view if this is the last column
const auto USABLE = usableArea();
@@ -814,6 +880,303 @@ void CScrollingAlgorithm::recalculate() {
m_scrollingData->recalculate();
}
+void CScrollingAlgorithm::syncFullscreenTargets() {
+ for (auto it = m_fullscreenTargets.begin(); it != m_fullscreenTargets.end();) {
+ const auto TARGET = it->target.lock();
+
+ if (!TARGET || !TARGET->layoutManagedFullscreen() || TARGET->fullscreenMode() != FSMODE_FULLSCREEN || TARGET->space() != m_parent->space()) {
+ it = m_fullscreenTargets.erase(it);
+ continue;
+ }
+
+ const auto TDATA = dataFor(TARGET);
+ if (!TDATA) {
+ ++it;
+ continue;
+ }
+
+ if (const auto COL = TDATA->column.lock())
+ COL->setColumnWidth(fullscreenColumnWidth());
+
+ ++it;
+ }
+
+ for (const auto& COL : m_scrollingData->columns) {
+ for (const auto& TDATA : COL->targetDatas) {
+ const auto TARGET = TDATA->target.lock();
+ if (!TARGET || !TARGET->layoutManagedFullscreen() || TARGET->fullscreenMode() != FSMODE_FULLSCREEN || TARGET->space() != m_parent->space())
+ continue;
+
+ if (!fullscreenStateForTarget(TARGET))
+ m_fullscreenTargets.emplace_back(SFullscreenScrollState{.target = TARGET, .restoreColumnWidth = COL ? std::optional<float>{COL->getColumnWidth()} : std::nullopt});
+
+ COL->setColumnWidth(fullscreenColumnWidth());
+ }
+ }
+}
+
+CScrollingAlgorithm::SFullscreenScrollState* CScrollingAlgorithm::fullscreenStateForTarget(SP<ITarget> target) {
+ if (!target)
+ return nullptr;
+
+ for (auto& state : m_fullscreenTargets) {
+ if (state.target.lock() == target)
+ return &state;
+ }
+
+ return nullptr;
+}
+
+CScrollingAlgorithm::SFullscreenScrollState* CScrollingAlgorithm::fullscreenStateForData(SP<SScrollingTargetData> target) {
+ if (!target)
+ return nullptr;
+
+ return fullscreenStateForTarget(target->target.lock());
+}
+
+void CScrollingAlgorithm::expelTarget(SP<SScrollingTargetData> tdata, SP<SColumnData> srcCol, std::optional<int64_t> insertIdx) {
+ auto col = !insertIdx ? m_scrollingData->add() : m_scrollingData->add(*insertIdx);
+ srcCol->remove(tdata->target.lock());
+ col->add(tdata);
+ m_scrollingData->centerOrFitCol(col);
+}
+
+eFullscreenRequestResult CScrollingAlgorithm::requestFullscreen(const SFullscreenRequest& request) {
+ if (!request.target || !m_parent || request.target->space() != m_parent->space())
+ return FULLSCREEN_REQUEST_DEFAULT;
+
+ const auto TDATA = dataFor(request.target);
+ if (!TDATA)
+ return FULLSCREEN_REQUEST_DEFAULT;
+
+ if (request.effectiveMode == FSMODE_FULLSCREEN) {
+ if (!fullscreenStateForTarget(request.target)) {
+ const auto COL = TDATA->column.lock();
+ m_fullscreenTargets.emplace_back(
+ SFullscreenScrollState{.target = request.target, .restoreColumnWidth = COL ? std::optional<float>{COL->getColumnWidth()} : std::nullopt});
+ }
+
+ if (const auto COL = TDATA->column.lock()) {
+ COL->setColumnWidth(fullscreenColumnWidth());
+ m_scrollingData->centerOrFitCol(COL);
+ }
+
+ request.target->setFullscreenMode(FSMODE_FULLSCREEN);
+
+ return FULLSCREEN_REQUEST_HANDLED_BY_LAYOUT;
+ } else if (request.effectiveMode == FSMODE_MAXIMIZED) {
+ // expel, then max width
+ const auto CURRENT_COL = TDATA->column.lock();
+
+ if (CURRENT_COL->targetDatas.size() > 1) {
+ const auto lastTarget = CURRENT_COL->targetDatas.back();
+ const auto currentIdx = m_scrollingData->idx(CURRENT_COL);
+ const auto NEXT_COL = m_scrollingData->next(CURRENT_COL);
+ const auto insertIdx = !NEXT_COL ? std::nullopt : std::optional<int64_t>{currentIdx};
+
+ expelTarget(lastTarget, CURRENT_COL, insertIdx);
+
+ TDATA->column->setColumnWidth(1.F);
+ } else
+ CURRENT_COL->setColumnWidth(1.F);
+
+ request.target->setFullscreenMode(FSMODE_NONE);
+
+ return FULLSCREEN_REQUEST_HANDLED_BY_LAYOUT;
+ }
+
+ if (isFullscreenTarget(TDATA) || request.target->layoutManagedFullscreen()) {
+ clearFullscreenTarget(request.target);
+ request.target->setFullscreenMode(FSMODE_NONE);
+ return request.effectiveMode == FSMODE_NONE ? FULLSCREEN_REQUEST_HANDLED_BY_LAYOUT : FULLSCREEN_REQUEST_DEFAULT;
+ }
+
+ return FULLSCREEN_REQUEST_DEFAULT;
+}
+
+SP<ITarget> CScrollingAlgorithm::layoutFullscreenTarget() const {
+ SP<SScrollingTargetData> fallback;
+
+ for (const auto& COL : m_scrollingData->columns) {
+ for (const auto& TDATA : COL->targetDatas) {
+ if (!isFullscreenTarget(TDATA))
+ continue;
+
+ if (!fallback)
+ fallback = TDATA;
+
+ if (fullscreenColumnCoversMonitor(TDATA->column.lock()))
+ return TDATA->target.lock();
+ }
+ }
+
+ return fallback ? fallback->target.lock() : nullptr;
+}
+
+bool CScrollingAlgorithm::layoutFullscreenCoversMonitor() const {
+ for (const auto& COL : m_scrollingData->columns) {
+ for (const auto& TDATA : COL->targetDatas) {
+ if (!isFullscreenTarget(TDATA))
+ continue;
+
+ if (fullscreenColumnCoversMonitor(TDATA->column.lock()))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+SP<SScrollingTargetData> CScrollingAlgorithm::fullscreenTargetDataForColumn(SP<SColumnData> col) const {
+ if (!col)
+ return nullptr;
+
+ for (const auto& TDATA : col->targetDatas) {
+ if (!isFullscreenTarget(TDATA))
+ continue;
+
+ return TDATA;
+ }
+
+ return nullptr;
+}
+
+bool CScrollingAlgorithm::isFullscreenTarget(SP<SScrollingTargetData> target) const {
+ if (!target)
+ return false;
+
+ const auto TARGET = target->target.lock();
+ if (!TARGET || !TARGET->layoutManagedFullscreen() || TARGET->fullscreenMode() == FSMODE_NONE)
+ return false;
+
+ return dataFor(TARGET) == target;
+}
+
+float CScrollingAlgorithm::fullscreenColumnWidth() const {
+ if (!m_parent || !m_parent->space() || !m_parent->space()->workspace() || !m_parent->space()->workspace()->m_monitor || !m_scrollingData || !m_scrollingData->controller)
+ return 1.F;
+
+ const auto USABLE = usableArea();
+ const auto MONBOX = m_parent->space()->workspace()->m_monitor->logicalBox();
+ const bool PRIMARY_HORIZ = m_scrollingData->controller->isPrimaryHorizontal();
+ const double usablePrimary = PRIMARY_HORIZ ? USABLE.w : USABLE.h;
+ const double monitorPrimary = PRIMARY_HORIZ ? MONBOX.w : MONBOX.h;
+
+ if (usablePrimary <= 0.0)
+ return 1.F;
+
+ return std::max(1.F, sc<float>(monitorPrimary / usablePrimary));
+}
+
+bool CScrollingAlgorithm::fullscreenColumnCoversMonitor(SP<SColumnData> col) const {
+ if (!col || !m_scrollingData || !m_scrollingData->controller || !m_parent || !m_parent->space() || !m_parent->space()->workspace() ||
+ !m_parent->space()->workspace()->m_monitor)
+ return false;
+
+ if (!fullscreenTargetDataForColumn(col))
+ return false;
+
+ const int64_t COL_IDX = m_scrollingData->idx(col);
+ if (COL_IDX < 0)
+ return false;
+
+ static const auto PFSONONE = CConfigValue<Config::INTEGER>("scrolling:fullscreen_on_one_column");
+
+ const auto USABLE = usableArea();
+ const bool PRIMARY_HORIZ = m_scrollingData->controller->isPrimaryHorizontal();
+ const double VIEW_SIZE = PRIMARY_HORIZ ? USABLE.w : USABLE.h;
+ const double VIEW_START = m_scrollingData->controller->getOffset();
+ const double VIEW_END = VIEW_START + VIEW_SIZE;
+ const double COL_START = m_scrollingData->controller->calculateStripStart(COL_IDX, USABLE, *PFSONONE);
+ const double COL_END = COL_START + m_scrollingData->controller->calculateStripSize(COL_IDX, USABLE, *PFSONONE);
+
+ return COL_START <= VIEW_START + 1.0 && COL_END >= VIEW_END - 1.0;
+}
+
+void CScrollingAlgorithm::updateFullscreenFade(bool coversMonitor) {
+ if (m_lastFullscreenCover == coversMonitor)
+ return;
+
+ m_lastFullscreenCover = coversMonitor;
+
+ if (!coversMonitor) {
+ // prevent stuck focus
+ g_pInputManager->unconstrainMouse();
+ for (const auto& fs : m_fullscreenTargets) {
+ if (!fs.target || !fs.target->window())
+ continue;
+
+ auto w = fs.target->window();
+
+ w->m_layoutFlags.cantLockCursor = true;
+ }
+ } else {
+ for (const auto& fs : m_fullscreenTargets) {
+ if (!fs.target || !fs.target->window())
+ continue;
+
+ auto w = fs.target->window();
+
+ w->m_layoutFlags.cantLockCursor = false;
+ }
+ }
+
+ if (!m_parent || !m_parent->space() || !m_parent->space()->workspace())
+ return;
+
+ // properly update things on top / bottom
+ m_parent->space()->workspace()->setNoMembersAboveFullscreen();
+
+ g_pDesktopAnimationManager->setFullscreenFadeAnimation(m_parent->space()->workspace(),
+ coversMonitor ? CDesktopAnimationManager::ANIMATION_TYPE_IN : CDesktopAnimationManager::ANIMATION_TYPE_OUT);
+}
+
+void CScrollingAlgorithm::clearFullscreenTarget(SP<ITarget> target) {
+ bool cleared = false;
+
+ auto clear = [&](SP<ITarget> t) {
+ t->setLayoutManagedFullscreen(false);
+ if (t->window())
+ t->window()->m_layoutFlags.cantLockCursor = false;
+ cleared = true;
+ };
+
+ for (auto it = m_fullscreenTargets.begin(); it != m_fullscreenTargets.end();) {
+ const auto TARGET = it->target.lock();
+
+ if (!TARGET || (target && TARGET != target)) {
+ if (!TARGET)
+ it = m_fullscreenTargets.erase(it);
+ else
+ ++it;
+ continue;
+ }
+
+ const auto TDATA = dataFor(TARGET);
+
+ clear(TARGET);
+
+ if (const auto COL = TDATA ? TDATA->column.lock() : nullptr; COL && it->restoreColumnWidth)
+ COL->setColumnWidth(*it->restoreColumnWidth);
+
+ it = m_fullscreenTargets.erase(it);
+ }
+
+ if (target && target->layoutManagedFullscreen())
+ clear(target);
+ else if (!target) {
+ for (const auto& COL : m_scrollingData->columns) {
+ for (const auto& TDATA : COL->targetDatas) {
+ const auto TARGET = TDATA->target.lock();
+ if (!TARGET || !TARGET->layoutManagedFullscreen())
+ continue;
+
+ clear(TARGET);
+ }
+ }
+ }
+}
+
SP<SScrollingTargetData> CScrollingAlgorithm::closestNode(const Vector2D& posGlobglobgabgalab) {
SP<SScrollingTargetData> res = nullptr;
double distClosest = -1;
@@ -1372,14 +1735,6 @@ Config::ErrorResult CScrollingAlgorithm::layoutMsg(const std::string_view& sv) {
if (!CURRENT_COL)
return stateErr("no current col");
- // expel a target from srcCol into its own new column at insertIdx
- auto expelTarget = [&](SP<SScrollingTargetData> tdata, SP<SColumnData> srcCol, std::optional<int64_t> insertIdx) {
- auto col = !insertIdx ? m_scrollingData->add() : m_scrollingData->add(*insertIdx);
- srcCol->remove(tdata->target.lock());
- col->add(tdata);
- m_scrollingData->centerOrFitCol(col);
- };
-
// consume the first target from adjCol into dstCol
auto consumeTarget = [&](SP<SColumnData> dstCol, SP<SColumnData> adjCol) {
const auto target = adjCol->targetDatas.front();
@@ -1546,7 +1901,7 @@ SP<SScrollingTargetData> CScrollingAlgorithm::findBestNeighbor(SP<SScrollingTarg
return nullptr;
}
-SP<SScrollingTargetData> CScrollingAlgorithm::dataFor(SP<ITarget> t) {
+SP<SScrollingTargetData> CScrollingAlgorithm::dataFor(SP<ITarget> t) const {
if (!t)
return nullptr;
@@ -1584,7 +1939,7 @@ eScrollDirection CScrollingAlgorithm::getDynamicDirection() {
return SCROLL_DIR_RIGHT; // default
}
-CBox CScrollingAlgorithm::usableArea() {
+CBox CScrollingAlgorithm::usableArea() const {
if (!m_parent || !m_parent->space())
return {};
diff --git a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.hpp b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.hpp
index 288d15a6..19eb9ba6 100644
--- a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.hpp
+++ b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.hpp
@@ -5,6 +5,7 @@
#include "ScrollTapeController.hpp"
#include "../../../../helpers/signal/Signal.hpp"
+#include <optional>
#include <vector>
namespace Layout::Tiled {
@@ -94,23 +95,27 @@ namespace Layout::Tiled {
CScrollingAlgorithm();
virtual ~CScrollingAlgorithm();
- virtual void newTarget(SP<ITarget> target);
- virtual void movedTarget(SP<ITarget> target, std::optional<Vector2D> focalPoint = std::nullopt);
- virtual void removeTarget(SP<ITarget> target);
+ virtual void newTarget(SP<ITarget> target);
+ virtual void movedTarget(SP<ITarget> target, std::optional<Vector2D> focalPoint = std::nullopt);
+ virtual void removeTarget(SP<ITarget> target);
- virtual void resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRectCorner corner = CORNER_NONE);
- virtual void recalculate();
+ virtual void resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRectCorner corner = CORNER_NONE);
+ virtual void recalculate();
- virtual SP<ITarget> getNextCandidate(SP<ITarget> old);
+ virtual SP<ITarget> getNextCandidate(SP<ITarget> old);
- virtual Config::ErrorResult layoutMsg(const std::string_view& sv);
- virtual std::optional<Vector2D> predictSizeForNewTarget();
+ virtual Config::ErrorResult layoutMsg(const std::string_view& sv);
+ virtual std::optional<Vector2D> predictSizeForNewTarget();
- virtual void swapTargets(SP<ITarget> a, SP<ITarget> b);
- virtual void moveTargetInDirection(SP<ITarget> t, Math::eDirection dir, bool silent);
+ virtual void swapTargets(SP<ITarget> a, SP<ITarget> b);
+ virtual void moveTargetInDirection(SP<ITarget> t, Math::eDirection dir, bool silent);
- CBox usableArea();
- SP<SScrollingTargetData> dataFor(SP<ITarget> t);
+ virtual eFullscreenRequestResult requestFullscreen(const SFullscreenRequest& request);
+ virtual SP<ITarget> layoutFullscreenTarget() const;
+ virtual bool layoutFullscreenCoversMonitor() const;
+
+ CBox usableArea() const;
+ SP<SScrollingTargetData> dataFor(SP<ITarget> t) const;
enum eInputMode : uint8_t {
INPUT_MODE_SOFT = 0,
@@ -129,16 +134,36 @@ namespace Layout::Tiled {
std::vector<float> configuredWidths;
} m_config;
- eScrollDirection getDynamicDirection();
+ eScrollDirection getDynamicDirection();
+
+ struct SFullscreenScrollState {
+ WP<ITarget> target;
+ std::optional<float> restoreColumnWidth;
+ };
+
+ void syncFullscreenTargets();
+ SFullscreenScrollState* fullscreenStateForTarget(SP<ITarget> target);
+ SFullscreenScrollState* fullscreenStateForData(SP<SScrollingTargetData> target);
+ SP<SScrollingTargetData> fullscreenTargetDataForColumn(SP<SColumnData> col) const;
+ bool isFullscreenTarget(SP<SScrollingTargetData> target) const;
+ float fullscreenColumnWidth() const;
+ bool fullscreenColumnCoversMonitor(SP<SColumnData> col) const;
+ void updateFullscreenFade(bool coversMonitor);
+ void clearFullscreenTarget(SP<ITarget> target = nullptr);
+
+ SP<SScrollingTargetData> findBestNeighbor(SP<SScrollingTargetData> pCurrent, SP<SColumnData> pTargetCol);
+ SP<SScrollingTargetData> closestNode(const Vector2D& posGlobglobgabgalab);
+
+ void focusTargetUpdate(SP<ITarget> target);
+ void moveTargetTo(SP<ITarget> t, Math::eDirection dir, bool silent);
+ void focusOnInput(SP<ITarget> target, eInputMode input);
- SP<SScrollingTargetData> findBestNeighbor(SP<SScrollingTargetData> pCurrent, SP<SColumnData> pTargetCol);
- SP<SScrollingTargetData> closestNode(const Vector2D& posGlobglobgabgalab);
+ void expelTarget(SP<SScrollingTargetData> tdata, SP<SColumnData> srcCol, std::optional<int64_t> insertIdx);
- void focusTargetUpdate(SP<ITarget> target);
- void moveTargetTo(SP<ITarget> t, Math::eDirection dir, bool silent);
- void focusOnInput(SP<ITarget> target, eInputMode input);
+ float defaultColumnWidth();
- float defaultColumnWidth();
+ std::vector<SFullscreenScrollState> m_fullscreenTargets;
+ bool m_lastFullscreenCover = false;
friend struct SScrollingData;
};
diff --git a/src/layout/space/Space.cpp b/src/layout/space/Space.cpp
index 26c02aae..35741a14 100644
--- a/src/layout/space/Space.cpp
+++ b/src/layout/space/Space.cpp
@@ -153,13 +153,29 @@ void CSpace::recalculate() {
m_algorithm->recalculate();
}
-void CSpace::setFullscreen(SP<ITarget> t, eFullscreenMode mode) {
- t->setFullscreenMode(mode);
+eFullscreenRequestResult CSpace::setFullscreen(SP<ITarget> t, eFullscreenMode currentEffectiveMode, eFullscreenMode mode) {
+ if (!t)
+ return FULLSCREEN_REQUEST_DEFAULT;
+
+ const auto REQUEST_RESULT = m_algorithm ? m_algorithm->requestFullscreen(t, currentEffectiveMode, mode) : FULLSCREEN_REQUEST_DEFAULT;
+
+ t->setLayoutManagedFullscreen(REQUEST_RESULT == FULLSCREEN_REQUEST_HANDLED_BY_LAYOUT && mode == FSMODE_FULLSCREEN);
+ if (REQUEST_RESULT != FULLSCREEN_REQUEST_HANDLED_BY_LAYOUT)
+ t->setFullscreenMode(mode);
+
+ if (REQUEST_RESULT == FULLSCREEN_REQUEST_HANDLED_BY_LAYOUT) {
+ if (const auto WORKSPACE = workspace()) {
+ WORKSPACE->m_fullscreenMode = FSMODE_NONE;
+ WORKSPACE->m_hasFullscreenWindow = false;
+ }
+ }
if (mode == FSMODE_NONE && m_algorithm && t->floating())
m_algorithm->recenter(t);
recalculate();
+
+ return REQUEST_RESULT;
}
Config::ErrorResult CSpace::layoutMsg(const std::string_view& sv) {
diff --git a/src/layout/space/Space.hpp b/src/layout/space/Space.hpp
index 5d7f9d01..a42dd55c 100644
--- a/src/layout/space/Space.hpp
+++ b/src/layout/space/Space.hpp
@@ -30,7 +30,7 @@ namespace Layout {
void setAlgorithmProvider(SP<CAlgorithm> algo);
void recheckWorkArea();
- void setFullscreen(SP<ITarget> t, eFullscreenMode mode);
+ eFullscreenRequestResult setFullscreen(SP<ITarget> t, eFullscreenMode currentEffectiveMode, eFullscreenMode mode);
void moveTargetInDirection(SP<ITarget> t, Math::eDirection dir, bool silent);
diff --git a/src/layout/target/Target.cpp b/src/layout/target/Target.cpp
index e8ce48bd..54fff792 100644
--- a/src/layout/target/Target.cpp
+++ b/src/layout/target/Target.cpp
@@ -50,6 +50,14 @@ void ITarget::setSpaceGhost(const SP<CSpace>& space) {
m_ghostSpace = true;
}
+bool ITarget::layoutManagedFullscreen() const {
+ return m_layoutManagedFullscreen;
+}
+
+void ITarget::setLayoutManagedFullscreen(bool enabled) {
+ m_layoutManagedFullscreen = enabled;
+}
+
SP<CSpace> ITarget::space() const {
return m_space;
}
diff --git a/src/layout/target/Target.hpp b/src/layout/target/Target.hpp
index bae5eee5..b0f55f14 100644
--- a/src/layout/target/Target.hpp
+++ b/src/layout/target/Target.hpp
@@ -57,6 +57,8 @@ namespace Layout {
virtual void setPseudoSize(const Vector2D& size);
virtual Vector2D pseudoSize();
virtual void swap(SP<ITarget> b);
+ virtual bool layoutManagedFullscreen() const;
+ virtual void setLayoutManagedFullscreen(bool enabled);
//
virtual bool floating() = 0;
@@ -77,9 +79,10 @@ namespace Layout {
SP<CSpace> m_space;
WP<ITarget> m_self;
Vector2D m_floatingSize;
- bool m_pseudo = false;
- bool m_ghostSpace = false; // ghost space means a target belongs to a space, but isn't sent to the layout
- Vector2D m_pseudoSize = {1280, 720};
- bool m_wasTiling = false;
+ bool m_pseudo = false;
+ bool m_ghostSpace = false; // ghost space means a target belongs to a space, but isn't sent to the layout
+ Vector2D m_pseudoSize = {1280, 720};
+ bool m_wasTiling = false;
+ bool m_layoutManagedFullscreen = false;
};
};
diff --git a/src/layout/target/WindowGroupTarget.cpp b/src/layout/target/WindowGroupTarget.cpp
index 8f9260e2..a738362a 100644
--- a/src/layout/target/WindowGroupTarget.cpp
+++ b/src/layout/target/WindowGroupTarget.cpp
@@ -68,6 +68,18 @@ void CWindowGroupTarget::setFullscreenMode(eFullscreenMode mode) {
m_group->current()->m_fullscreenState.internal = mode;
}
+bool CWindowGroupTarget::layoutManagedFullscreen() const {
+ return m_group->current()->m_target->layoutManagedFullscreen();
+}
+
+void CWindowGroupTarget::setLayoutManagedFullscreen(bool enabled) {
+ ITarget::setLayoutManagedFullscreen(enabled);
+
+ for (const auto& w : m_group->windows()) {
+ w->m_target->setLayoutManagedFullscreen(enabled);
+ }
+}
+
std::optional<Vector2D> CWindowGroupTarget::minSize() {
return m_group->current()->minSize();
}
diff --git a/src/layout/target/WindowGroupTarget.hpp b/src/layout/target/WindowGroupTarget.hpp
index b3e79749..f48bf9e9 100644
--- a/src/layout/target/WindowGroupTarget.hpp
+++ b/src/layout/target/WindowGroupTarget.hpp
@@ -28,6 +28,8 @@ namespace Layout {
virtual void damageEntire();
virtual void warpPositionSize();
virtual void onUpdateSpace();
+ virtual bool layoutManagedFullscreen() const;
+ virtual void setLayoutManagedFullscreen(bool enabled);
private:
CWindowGroupTarget(SP<Desktop::View::CGroup> g);
diff --git a/src/layout/target/WindowTarget.cpp b/src/layout/target/WindowTarget.cpp
index 13984104..9258b4ae 100644
--- a/src/layout/target/WindowTarget.cpp
+++ b/src/layout/target/WindowTarget.cpp
@@ -43,7 +43,7 @@ void CWindowTarget::updatePos() {
if (!m_space)
return;
- if (fullscreenMode() == FSMODE_FULLSCREEN)
+ if (fullscreenMode() == FSMODE_FULLSCREEN && !layoutManagedFullscreen())
return;
if (floating() && fullscreenMode() != FSMODE_MAXIMIZED) {
@@ -83,7 +83,24 @@ void CWindowTarget::updatePos() {
return;
}
- if (fullscreenMode() == FSMODE_FULLSCREEN)
+ if (fullscreenMode() == FSMODE_FULLSCREEN && layoutManagedFullscreen()) {
+ CBox nodeBox = m_box.logicalBox;
+ CBox visualBox = m_box.visualBox.empty() ? nodeBox : m_box.visualBox;
+ nodeBox.round();
+ visualBox.round();
+
+ m_window->m_size = nodeBox.size();
+ m_window->m_position = nodeBox.pos();
+
+ *m_window->m_realSize = visualBox.size();
+ *m_window->m_realPosition = visualBox.pos();
+
+ m_window->updateWindowDecos();
+ m_window->sendWindowSize();
+ return;
+ }
+
+ if (fullscreenMode() == FSMODE_FULLSCREEN && !layoutManagedFullscreen())
return;
g_pHyprRenderer->damageWindow(window());
@@ -206,6 +223,7 @@ void CWindowTarget::updatePos() {
}
m_window->updateWindowDecos();
+ m_window->sendWindowSize();
}
void CWindowTarget::assignToSpace(const SP<CSpace>& space, std::optional<Vector2D> focalPoint) {
@@ -214,6 +232,8 @@ void CWindowTarget::assignToSpace(const SP<CSpace>& space, std::optional<Vector2
return;
}
+ m_window->m_layoutFlags = {};
+
// keep the ref here so that moveToWorkspace doesn't unref the workspace
// and assignToSpace doesn't think this is a new target because space wp is dead
const auto WSREF = space->workspace();
@@ -236,6 +256,8 @@ void CWindowTarget::setFloating(bool x) {
if (x == m_window->m_isFloating)
return;
+ m_window->m_layoutFlags = {};
+
m_window->m_isFloating = x;
m_window->m_pinned = false;
diff --git a/src/managers/animation/DesktopAnimationManager.cpp b/src/managers/animation/DesktopAnimationManager.cpp
index 2b7cb459..a4b994c6 100644
--- a/src/managers/animation/DesktopAnimationManager.cpp
+++ b/src/managers/animation/DesktopAnimationManager.cpp
@@ -487,7 +487,7 @@ void CDesktopAnimationManager::setFullscreenFadeAnimation(PHLWORKSPACE ws, eAnim
if (ws->m_id == PMONITOR->activeWorkspaceID() || ws->m_id == PMONITOR->activeSpecialWorkspaceID()) {
for (auto const& ls : PMONITOR->m_layerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]) {
if (!ls->m_fadingOut && !ls->m_aboveFullscreen)
- *ls->m_alpha = FULLSCREEN && ws->m_fullscreenMode == FSMODE_FULLSCREEN ? 0.f : 1.f;
+ *ls->m_alpha = FULLSCREEN && ws->m_fullscreenMode != FSMODE_MAXIMIZED ? 0.f : 1.f;
}
}
}
diff --git a/src/managers/input/InputManager.cpp b/src/managers/input/InputManager.cpp
index 85699b14..c1ec2978 100644
--- a/src/managers/input/InputManager.cpp
+++ b/src/managers/input/InputManager.cpp
@@ -453,7 +453,9 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus, bool mouse, st
return pWindowIdeal;
};
- if (PWORKSPACE->m_hasFullscreenWindow && PWORKSPACE->m_fullscreenMode == FSMODE_FULLSCREEN) {
+ const bool HAS_EXCLUSIVE_FULLSCREEN = (PWORKSPACE->m_hasFullscreenWindow && PWORKSPACE->m_fullscreenMode == FSMODE_FULLSCREEN) || PMONITOR->inFullscreenMode();
+
+ if (HAS_EXCLUSIVE_FULLSCREEN) {
const auto IS_LS_UNFOCUSABLE = pFoundLayerSurface &&
(pFoundLayerSurface->m_layer < ZWLR_LAYER_SHELL_V1_LAYER_TOP ||
(pFoundLayerSurface->m_layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP && !pFoundLayerSurface->m_aboveFullscreen));
@@ -1741,7 +1743,17 @@ void CInputManager::unconstrainMouse() {
bool CInputManager::isConstrained() {
return std::ranges::any_of(m_constraints, [](auto const& c) {
const auto constraint = c.lock();
- return constraint && constraint->isActive() && constraint->owner()->resource() == Desktop::focusState()->surface();
+
+ if (!constraint || !constraint->isActive() || constraint->owner()->resource() != Desktop::focusState()->surface())
+ return false;
+
+ const auto OWNER = constraint->owner()->view();
+ const auto WINDOW = Desktop::View::CWindow::fromView(OWNER);
+
+ if (!WINDOW)
+ return false;
+
+ return !WINDOW->m_layoutFlags.cantLockCursor;
});
}
@@ -1749,7 +1761,15 @@ bool CInputManager::isLocked() {
if (!isConstrained())
return false;
- const auto SURF = Desktop::View::CWLSurface::fromResource(Desktop::focusState()->surface());
+ const auto SURF = Desktop::View::CWLSurface::fromResource(Desktop::focusState()->surface());
+
+ if (SURF) {
+ const auto WINDOW = Desktop::View::CWindow::fromView(SURF->view());
+
+ if (WINDOW && WINDOW->m_layoutFlags.cantLockCursor)
+ return false;
+ }
+
const auto CONSTRAINT = SURF ? SURF->constraint() : nullptr;
return CONSTRAINT && CONSTRAINT->isLocked();
diff --git a/src/protocols/PointerConstraints.cpp b/src/protocols/PointerConstraints.cpp
index 8bfa8b3a..d958c097 100644
--- a/src/protocols/PointerConstraints.cpp
+++ b/src/protocols/PointerConstraints.cpp
@@ -127,9 +127,11 @@ void CPointerConstraint::activate() {
// TODO: hack, probably not a super duper great idea
if (g_pSeatManager->m_state.pointerFocus != m_hlSurface->resource()) {
- const auto SURFBOX = m_hlSurface->getSurfaceBoxGlobal();
- const auto LOCAL = SURFBOX.has_value() ? logicPositionHint() - SURFBOX->pos() : Vector2D{};
- g_pSeatManager->setPointerFocus(m_hlSurface->resource(), LOCAL);
+ if (const auto W = Desktop::View::CWindow::fromView(m_hlSurface->view()); !W || !W->m_layoutFlags.cantLockCursor) {
+ const auto SURFBOX = m_hlSurface->getSurfaceBoxGlobal();
+ const auto LOCAL = SURFBOX.has_value() ? logicPositionHint() - SURFBOX->pos() : Vector2D{};
+ g_pSeatManager->setPointerFocus(m_hlSurface->resource(), LOCAL);
+ }
}
if (m_locked)