Hyprland/src/managers/input/UnifiedWorkspaceSwipeGesture.cpp
Nikolai Nechaev 222dbe99d0
keybinds: fix previous workspace remembering (#12399)
* swipe: Fix previous workspace remembering in workspace gesture

Fixes a bug that previous workspace does not exist after swiping to a workspace

* tests: Test that `workspace previous` works after workspace gesture

* moveActiveToWorkspace: remember previous workspace unconditionally
2025-12-05 20:43:30 +00:00

314 lines
13 KiB
C++

#include "UnifiedWorkspaceSwipeGesture.hpp"
#include "../../Compositor.hpp"
#include "../../desktop/state/FocusState.hpp"
#include "../../render/Renderer.hpp"
#include "InputManager.hpp"
bool CUnifiedWorkspaceSwipeGesture::isGestureInProgress() {
return m_workspaceBegin;
}
void CUnifiedWorkspaceSwipeGesture::begin() {
if (isGestureInProgress())
return;
const auto PWORKSPACE = Desktop::focusState()->monitor()->m_activeWorkspace;
Debug::log(LOG, "CUnifiedWorkspaceSwipeGesture::begin: Starting a swipe from {}", PWORKSPACE->m_name);
m_workspaceBegin = PWORKSPACE;
m_delta = 0;
m_monitor = Desktop::focusState()->monitor();
m_avgSpeed = 0;
m_speedPoints = 0;
if (PWORKSPACE->m_hasFullscreenWindow) {
for (auto const& ls : Desktop::focusState()->monitor()->m_layerSurfaceLayers[2]) {
*ls->m_alpha = 1.f;
}
}
}
void CUnifiedWorkspaceSwipeGesture::update(double delta) {
if (!isGestureInProgress())
return;
static auto PSWIPEDIST = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_distance");
static auto PSWIPENEW = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_create_new");
static auto PSWIPEDIRLOCK = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_direction_lock");
static auto PSWIPEDIRLOCKTHRESHOLD = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_direction_lock_threshold");
static auto PSWIPEFOREVER = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_forever");
static auto PSWIPEUSER = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_use_r");
static auto PWORKSPACEGAP = CConfigValue<Hyprlang::INT>("general:gaps_workspaces");
const auto SWIPEDISTANCE = std::clamp(*PSWIPEDIST, sc<int64_t>(1LL), sc<int64_t>(UINT32_MAX));
const auto XDISTANCE = m_monitor->m_size.x + *PWORKSPACEGAP;
const auto YDISTANCE = m_monitor->m_size.y + *PWORKSPACEGAP;
const auto ANIMSTYLE = m_workspaceBegin->m_renderOffset->getStyle();
const bool VERTANIMS = ANIMSTYLE == "slidevert" || ANIMSTYLE.starts_with("slidefadevert");
const double d = m_delta - delta;
m_delta = delta;
m_avgSpeed = (m_avgSpeed * m_speedPoints + abs(d)) / (m_speedPoints + 1);
m_speedPoints++;
auto workspaceIDLeft = getWorkspaceIDNameFromString((*PSWIPEUSER ? "r-1" : "m-1")).id;
auto workspaceIDRight = getWorkspaceIDNameFromString((*PSWIPEUSER ? "r+1" : "m+1")).id;
if ((workspaceIDLeft == WORKSPACE_INVALID || workspaceIDRight == WORKSPACE_INVALID || workspaceIDLeft == m_workspaceBegin->m_id) && !*PSWIPENEW) {
m_workspaceBegin = nullptr; // invalidate the swipe
return;
}
m_workspaceBegin->m_forceRendering = true;
m_delta = std::clamp(m_delta, sc<double>(-SWIPEDISTANCE), sc<double>(SWIPEDISTANCE));
if ((m_workspaceBegin->m_id == workspaceIDLeft && *PSWIPENEW && (m_delta < 0)) ||
(m_delta > 0 && m_workspaceBegin->getWindows() == 0 && workspaceIDRight <= m_workspaceBegin->m_id) || (m_delta < 0 && m_workspaceBegin->m_id <= workspaceIDLeft)) {
m_delta = 0;
g_pHyprRenderer->damageMonitor(m_monitor.lock());
m_workspaceBegin->m_renderOffset->setValueAndWarp(Vector2D(0.0, 0.0));
return;
}
if (*PSWIPEDIRLOCK) {
if (m_initialDirection != 0 && m_initialDirection != (m_delta < 0 ? -1 : 1))
m_delta = 0;
else if (m_initialDirection == 0 && abs(m_delta) > *PSWIPEDIRLOCKTHRESHOLD)
m_initialDirection = m_delta < 0 ? -1 : 1;
}
if (m_delta < 0) {
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(workspaceIDLeft);
if (workspaceIDLeft > m_workspaceBegin->m_id || !PWORKSPACE) {
if (*PSWIPENEW) {
g_pHyprRenderer->damageMonitor(m_monitor.lock());
if (VERTANIMS)
m_workspaceBegin->m_renderOffset->setValueAndWarp(Vector2D(0.0, ((-m_delta) / SWIPEDISTANCE) * YDISTANCE));
else
m_workspaceBegin->m_renderOffset->setValueAndWarp(Vector2D(((-m_delta) / SWIPEDISTANCE) * XDISTANCE, 0.0));
m_workspaceBegin->updateWindowDecos();
return;
}
m_delta = 0;
return;
}
PWORKSPACE->m_forceRendering = true;
PWORKSPACE->m_alpha->setValueAndWarp(1.f);
if (workspaceIDLeft != workspaceIDRight && workspaceIDRight != m_workspaceBegin->m_id) {
const auto PWORKSPACER = g_pCompositor->getWorkspaceByID(workspaceIDRight);
if (PWORKSPACER) {
PWORKSPACER->m_forceRendering = false;
PWORKSPACER->m_alpha->setValueAndWarp(0.f);
}
}
if (VERTANIMS) {
PWORKSPACE->m_renderOffset->setValueAndWarp(Vector2D(0.0, ((-m_delta) / SWIPEDISTANCE) * YDISTANCE - YDISTANCE));
m_workspaceBegin->m_renderOffset->setValueAndWarp(Vector2D(0.0, ((-m_delta) / SWIPEDISTANCE) * YDISTANCE));
} else {
PWORKSPACE->m_renderOffset->setValueAndWarp(Vector2D(((-m_delta) / SWIPEDISTANCE) * XDISTANCE - XDISTANCE, 0.0));
m_workspaceBegin->m_renderOffset->setValueAndWarp(Vector2D(((-m_delta) / SWIPEDISTANCE) * XDISTANCE, 0.0));
}
PWORKSPACE->updateWindowDecos();
} else {
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(workspaceIDRight);
if (workspaceIDRight < m_workspaceBegin->m_id || !PWORKSPACE) {
if (*PSWIPENEW) {
g_pHyprRenderer->damageMonitor(m_monitor.lock());
if (VERTANIMS)
m_workspaceBegin->m_renderOffset->setValueAndWarp(Vector2D(0.0, ((-m_delta) / SWIPEDISTANCE) * YDISTANCE));
else
m_workspaceBegin->m_renderOffset->setValueAndWarp(Vector2D(((-m_delta) / SWIPEDISTANCE) * XDISTANCE, 0.0));
m_workspaceBegin->updateWindowDecos();
return;
}
m_delta = 0;
return;
}
PWORKSPACE->m_forceRendering = true;
PWORKSPACE->m_alpha->setValueAndWarp(1.f);
if (workspaceIDLeft != workspaceIDRight && workspaceIDLeft != m_workspaceBegin->m_id) {
const auto PWORKSPACEL = g_pCompositor->getWorkspaceByID(workspaceIDLeft);
if (PWORKSPACEL) {
PWORKSPACEL->m_forceRendering = false;
PWORKSPACEL->m_alpha->setValueAndWarp(0.f);
}
}
if (VERTANIMS) {
PWORKSPACE->m_renderOffset->setValueAndWarp(Vector2D(0.0, ((-m_delta) / SWIPEDISTANCE) * YDISTANCE + YDISTANCE));
m_workspaceBegin->m_renderOffset->setValueAndWarp(Vector2D(0.0, ((-m_delta) / SWIPEDISTANCE) * YDISTANCE));
} else {
PWORKSPACE->m_renderOffset->setValueAndWarp(Vector2D(((-m_delta) / SWIPEDISTANCE) * XDISTANCE + XDISTANCE, 0.0));
m_workspaceBegin->m_renderOffset->setValueAndWarp(Vector2D(((-m_delta) / SWIPEDISTANCE) * XDISTANCE, 0.0));
}
PWORKSPACE->updateWindowDecos();
}
g_pHyprRenderer->damageMonitor(m_monitor.lock());
m_workspaceBegin->updateWindowDecos();
if (*PSWIPEFOREVER) {
if (abs(m_delta) >= SWIPEDISTANCE) {
end();
begin();
}
}
}
void CUnifiedWorkspaceSwipeGesture::end() {
if (!isGestureInProgress())
return;
static auto PSWIPEPERC = CConfigValue<Hyprlang::FLOAT>("gestures:workspace_swipe_cancel_ratio");
static auto PSWIPEDIST = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_distance");
static auto PSWIPEFORC = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_min_speed_to_force");
static auto PSWIPENEW = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_create_new");
static auto PSWIPEUSER = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_use_r");
static auto PWORKSPACEGAP = CConfigValue<Hyprlang::INT>("general:gaps_workspaces");
const auto ANIMSTYLE = m_workspaceBegin->m_renderOffset->getStyle();
const bool VERTANIMS = ANIMSTYLE == "slidevert" || ANIMSTYLE.starts_with("slidefadevert");
// commit
auto workspaceIDLeft = getWorkspaceIDNameFromString((*PSWIPEUSER ? "r-1" : "m-1")).id;
auto workspaceIDRight = getWorkspaceIDNameFromString((*PSWIPEUSER ? "r+1" : "m+1")).id;
const auto SWIPEDISTANCE = std::clamp(*PSWIPEDIST, sc<int64_t>(1LL), sc<int64_t>(UINT32_MAX));
// If we've been swiping off the right end with PSWIPENEW enabled, there is
// no workspace there yet, and we need to choose an ID for a new one now.
if (workspaceIDRight <= m_workspaceBegin->m_id && *PSWIPENEW)
workspaceIDRight = getWorkspaceIDNameFromString("r+1").id;
auto PWORKSPACER = g_pCompositor->getWorkspaceByID(workspaceIDRight); // not guaranteed if PSWIPENEW || PSWIPENUMBER
auto PWORKSPACEL = g_pCompositor->getWorkspaceByID(workspaceIDLeft); // not guaranteed if PSWIPENUMBER
const auto RENDEROFFSETMIDDLE = m_workspaceBegin->m_renderOffset->value();
const auto XDISTANCE = m_monitor->m_size.x + *PWORKSPACEGAP;
const auto YDISTANCE = m_monitor->m_size.y + *PWORKSPACEGAP;
PHLWORKSPACE pSwitchedTo = nullptr;
if ((abs(m_delta) < SWIPEDISTANCE * *PSWIPEPERC && (*PSWIPEFORC == 0 || (*PSWIPEFORC != 0 && m_avgSpeed < *PSWIPEFORC))) || abs(m_delta) < 2) {
// revert
if (abs(m_delta) < 2) {
if (PWORKSPACEL)
PWORKSPACEL->m_renderOffset->setValueAndWarp(Vector2D(0, 0));
if (PWORKSPACER)
PWORKSPACER->m_renderOffset->setValueAndWarp(Vector2D(0, 0));
m_workspaceBegin->m_renderOffset->setValueAndWarp(Vector2D(0, 0));
} else {
if (m_delta < 0) {
// to left
if (PWORKSPACEL) {
if (VERTANIMS)
*PWORKSPACEL->m_renderOffset = Vector2D{0.0, -YDISTANCE};
else
*PWORKSPACEL->m_renderOffset = Vector2D{-XDISTANCE, 0.0};
}
} else if (PWORKSPACER) {
// to right
if (VERTANIMS)
*PWORKSPACER->m_renderOffset = Vector2D{0.0, YDISTANCE};
else
*PWORKSPACER->m_renderOffset = Vector2D{XDISTANCE, 0.0};
}
*m_workspaceBegin->m_renderOffset = Vector2D();
}
pSwitchedTo = m_workspaceBegin;
} else if (m_delta < 0) {
// switch to left
const auto RENDEROFFSET = PWORKSPACEL ? PWORKSPACEL->m_renderOffset->value() : Vector2D();
if (PWORKSPACEL)
m_monitor->changeWorkspace(workspaceIDLeft);
else {
m_monitor->changeWorkspace(g_pCompositor->createNewWorkspace(workspaceIDLeft, m_monitor->m_id));
PWORKSPACEL = g_pCompositor->getWorkspaceByID(workspaceIDLeft);
PWORKSPACEL->rememberPrevWorkspace(m_workspaceBegin);
}
PWORKSPACEL->m_renderOffset->setValue(RENDEROFFSET);
PWORKSPACEL->m_alpha->setValueAndWarp(1.f);
m_workspaceBegin->m_renderOffset->setValue(RENDEROFFSETMIDDLE);
if (VERTANIMS)
*m_workspaceBegin->m_renderOffset = Vector2D(0.0, YDISTANCE);
else
*m_workspaceBegin->m_renderOffset = Vector2D(XDISTANCE, 0.0);
m_workspaceBegin->m_alpha->setValueAndWarp(1.f);
g_pInputManager->unconstrainMouse();
Debug::log(LOG, "Ended swipe to the left");
pSwitchedTo = PWORKSPACEL;
} else {
// switch to right
const auto RENDEROFFSET = PWORKSPACER ? PWORKSPACER->m_renderOffset->value() : Vector2D();
if (PWORKSPACER)
m_monitor->changeWorkspace(workspaceIDRight);
else {
m_monitor->changeWorkspace(g_pCompositor->createNewWorkspace(workspaceIDRight, m_monitor->m_id));
PWORKSPACER = g_pCompositor->getWorkspaceByID(workspaceIDRight);
PWORKSPACER->rememberPrevWorkspace(m_workspaceBegin);
}
PWORKSPACER->m_renderOffset->setValue(RENDEROFFSET);
PWORKSPACER->m_alpha->setValueAndWarp(1.f);
m_workspaceBegin->m_renderOffset->setValue(RENDEROFFSETMIDDLE);
if (VERTANIMS)
*m_workspaceBegin->m_renderOffset = Vector2D(0.0, -YDISTANCE);
else
*m_workspaceBegin->m_renderOffset = Vector2D(-XDISTANCE, 0.0);
m_workspaceBegin->m_alpha->setValueAndWarp(1.f);
g_pInputManager->unconstrainMouse();
Debug::log(LOG, "Ended swipe to the right");
pSwitchedTo = PWORKSPACER;
}
pSwitchedTo->rememberPrevWorkspace(m_workspaceBegin);
g_pHyprRenderer->damageMonitor(m_monitor.lock());
if (PWORKSPACEL)
PWORKSPACEL->m_forceRendering = false;
if (PWORKSPACER)
PWORKSPACER->m_forceRendering = false;
m_workspaceBegin->m_forceRendering = false;
m_workspaceBegin = nullptr;
m_initialDirection = 0;
g_pInputManager->refocus();
// apply alpha
for (auto const& ls : Desktop::focusState()->monitor()->m_layerSurfaceLayers[2]) {
*ls->m_alpha = pSwitchedTo->m_hasFullscreenWindow && pSwitchedTo->m_fullscreenMode == FSMODE_FULLSCREEN ? 0.f : 1.f;
}
}