summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authoralaricljs <me@mooluv.com>2025-04-12 10:43:13 -0400
committerGitHub <noreply@github.com>2025-04-12 16:43:13 +0200
commit65389700878640fa0e90419b9cf34f48e4186fca (patch)
tree55a818c23406da1742599cb2f38ba35d1fc09a6a /src
parent0399e64274ae5c05a7a40ad78e4d4852d9957e99 (diff)
binds: add drag_threshold for click/drag isolation (#9839)
--------- Co-authored-by: Leeman <lstrout@enlj.com>
Diffstat (limited to 'src')
-rw-r--r--src/config/ConfigDescriptions.hpp6
-rw-r--r--src/config/ConfigManager.cpp15
-rw-r--r--src/layout/IHyprLayout.cpp92
-rw-r--r--src/layout/IHyprLayout.hpp7
-rw-r--r--src/managers/KeybindManager.cpp16
-rw-r--r--src/managers/KeybindManager.hpp3
-rw-r--r--src/managers/input/InputManager.hpp5
7 files changed, 103 insertions, 41 deletions
diff --git a/src/config/ConfigDescriptions.hpp b/src/config/ConfigDescriptions.hpp
index 7037f537..78b9f559 100644
--- a/src/config/ConfigDescriptions.hpp
+++ b/src/config/ConfigDescriptions.hpp
@@ -1326,6 +1326,12 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
+ SConfigOptionDescription{
+ .value = "binds:drag_threshold",
+ .description = "Movement threshold in pixels for window dragging and c/g bind flags. 0 to disable and grab on mousedown.",
+ .type = CONFIG_OPTION_INT,
+ .data = SConfigOptionDescription::SRangeData{0, 0, INT_MAX},
+ },
/*
* xwayland:
diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp
index 56ff3f30..70f6701e 100644
--- a/src/config/ConfigManager.cpp
+++ b/src/config/ConfigManager.cpp
@@ -651,6 +651,7 @@ CConfigManager::CConfigManager() {
registerConfigVar("binds:disable_keybind_grabbing", Hyprlang::INT{0});
registerConfigVar("binds:window_direction_monitor_fallback", Hyprlang::INT{1});
registerConfigVar("binds:allow_pin_fullscreen", Hyprlang::INT{0});
+ registerConfigVar("binds:drag_threshold", Hyprlang::INT{0});
registerConfigVar("gestures:workspace_swipe", Hyprlang::INT{0});
registerConfigVar("gestures:workspace_swipe_fingers", Hyprlang::INT{3});
@@ -2255,6 +2256,8 @@ std::optional<std::string> CConfigManager::handleBind(const std::string& command
bool longPress = false;
bool hasDescription = false;
bool dontInhibit = false;
+ bool click = false;
+ bool drag = false;
const auto BINDARGS = command.substr(4);
for (auto const& arg : BINDARGS) {
@@ -2280,6 +2283,12 @@ std::optional<std::string> CConfigManager::handleBind(const std::string& command
hasDescription = true;
} else if (arg == 'p') {
dontInhibit = true;
+ } else if (arg == 'c') {
+ click = true;
+ release = true;
+ } else if (arg == 'g') {
+ drag = true;
+ release = true;
} else {
return "bind: invalid flag";
}
@@ -2291,6 +2300,9 @@ std::optional<std::string> CConfigManager::handleBind(const std::string& command
if (mouse && (repeat || release || locked))
return "flag m is exclusive";
+ if (click && drag)
+ return "flags c and g are mutually exclusive";
+
const int numbArgs = hasDescription ? 5 : 4;
const auto ARGS = CVarList(value, numbArgs);
@@ -2350,7 +2362,8 @@ std::optional<std::string> CConfigManager::handleBind(const std::string& command
g_pKeybindManager->addKeybind(SKeybind{parsedKey.key, KEYSYMS, parsedKey.keycode, parsedKey.catchAll, MOD, MODS, HANDLER,
COMMAND, locked, m_szCurrentSubmap, DESCRIPTION, release, repeat, longPress,
- mouse, nonConsuming, transparent, ignoreMods, multiKey, hasDescription, dontInhibit});
+ mouse, nonConsuming, transparent, ignoreMods, multiKey, hasDescription, dontInhibit,
+ click, drag});
}
return {};
diff --git a/src/layout/IHyprLayout.cpp b/src/layout/IHyprLayout.cpp
index a0f15f5c..0c1c3788 100644
--- a/src/layout/IHyprLayout.cpp
+++ b/src/layout/IHyprLayout.cpp
@@ -228,7 +228,8 @@ bool IHyprLayout::onWindowCreatedAutoGroup(PHLWINDOW pWindow) {
}
void IHyprLayout::onBeginDragWindow() {
- const auto DRAGGINGWINDOW = g_pInputManager->currentlyDraggedWindow.lock();
+ const auto DRAGGINGWINDOW = g_pInputManager->currentlyDraggedWindow.lock();
+ static auto PDRAGTHRESHOLD = CConfigValue<Hyprlang::INT>("binds:drag_threshold");
m_iMouseMoveEventCount = 1;
m_vBeginDragSizeXY = Vector2D();
@@ -240,42 +241,11 @@ void IHyprLayout::onBeginDragWindow() {
return;
}
- const bool WAS_FULLSCREEN = DRAGGINGWINDOW->isFullscreen();
- if (WAS_FULLSCREEN) {
- Debug::log(LOG, "Dragging a fullscreen window");
- g_pCompositor->setWindowFullscreenInternal(DRAGGINGWINDOW, FSMODE_NONE);
- }
-
- const auto PWORKSPACE = DRAGGINGWINDOW->m_pWorkspace;
-
- if (PWORKSPACE->m_bHasFullscreenWindow && (!DRAGGINGWINDOW->m_bCreatedOverFullscreen || !DRAGGINGWINDOW->m_bIsFloating)) {
- Debug::log(LOG, "Rejecting drag on a fullscreen workspace. (window under fullscreen)");
- g_pKeybindManager->changeMouseBindMode(MBIND_INVALID);
+ // Try to pick up dragged window now if drag_threshold is disabled
+ // or at least update dragging related variables for the cursors
+ g_pInputManager->m_bDragThresholdReached = *PDRAGTHRESHOLD <= 0;
+ if (updateDragWindow())
return;
- }
-
- DRAGGINGWINDOW->m_bDraggingTiled = false;
-
- m_vDraggingWindowOriginalFloatSize = DRAGGINGWINDOW->m_vLastFloatingSize;
-
- if (WAS_FULLSCREEN && DRAGGINGWINDOW->m_bIsFloating) {
- const auto MOUSECOORDS = g_pInputManager->getMouseCoordsInternal();
- *DRAGGINGWINDOW->m_vRealPosition = MOUSECOORDS - DRAGGINGWINDOW->m_vRealSize->goal() / 2.f;
- } else if (!DRAGGINGWINDOW->m_bIsFloating) {
- if (g_pInputManager->dragMode == MBIND_MOVE) {
- DRAGGINGWINDOW->m_vLastFloatingSize = (DRAGGINGWINDOW->m_vRealSize->goal() * 0.8489).clamp(Vector2D{5, 5}, Vector2D{}).floor();
- changeWindowFloatingMode(DRAGGINGWINDOW);
- DRAGGINGWINDOW->m_bIsFloating = true;
- DRAGGINGWINDOW->m_bDraggingTiled = true;
-
- *DRAGGINGWINDOW->m_vRealPosition = g_pInputManager->getMouseCoordsInternal() - DRAGGINGWINDOW->m_vRealSize->goal() / 2.f;
- }
- }
-
- m_vBeginDragXY = g_pInputManager->getMouseCoordsInternal();
- m_vBeginDragPositionXY = DRAGGINGWINDOW->m_vRealPosition->goal();
- m_vBeginDragSizeXY = DRAGGINGWINDOW->m_vRealSize->goal();
- m_vLastDragXY = m_vBeginDragXY;
// get the grab corner
static auto RESIZECORNER = CConfigValue<Hyprlang::INT>("general:resize_corner");
@@ -552,7 +522,8 @@ void IHyprLayout::onMouseMove(const Vector2D& mousePos) {
if (g_pInputManager->currentlyDraggedWindow.expired())
return;
- const auto DRAGGINGWINDOW = g_pInputManager->currentlyDraggedWindow.lock();
+ const auto DRAGGINGWINDOW = g_pInputManager->currentlyDraggedWindow.lock();
+ static auto PDRAGTHRESHOLD = CConfigValue<Hyprlang::INT>("binds:drag_threshold");
// Window invalid or drag begin size 0,0 meaning we rejected it.
if ((!validMapped(DRAGGINGWINDOW) || m_vBeginDragSizeXY == Vector2D())) {
@@ -560,6 +531,15 @@ void IHyprLayout::onMouseMove(const Vector2D& mousePos) {
return;
}
+ // Yoink dragged window here instead if using drag_threshold and it has been reached
+ if (*PDRAGTHRESHOLD > 0 && !g_pInputManager->m_bDragThresholdReached) {
+ if ((m_vBeginDragXY.distanceSq(mousePos) <= std::pow(*PDRAGTHRESHOLD, 2) && m_vBeginDragXY == m_vLastDragXY))
+ return;
+ g_pInputManager->m_bDragThresholdReached = true;
+ if (updateDragWindow())
+ return;
+ }
+
static auto TIMER = std::chrono::high_resolution_clock::now(), MSTIMER = TIMER;
const auto SPECIAL = DRAGGINGWINDOW->onSpecialWorkspace();
@@ -962,3 +942,41 @@ Vector2D IHyprLayout::predictSizeForNewWindow(PHLWINDOW pWindow) {
return sizePredicted;
}
+
+bool IHyprLayout::updateDragWindow() {
+ const auto DRAGGINGWINDOW = g_pInputManager->currentlyDraggedWindow.lock();
+
+ if (g_pInputManager->m_bDragThresholdReached) {
+ if (DRAGGINGWINDOW->isFullscreen()) {
+ Debug::log(LOG, "Dragging a fullscreen window");
+ g_pCompositor->setWindowFullscreenInternal(DRAGGINGWINDOW, FSMODE_NONE);
+ }
+
+ const auto PWORKSPACE = DRAGGINGWINDOW->m_pWorkspace;
+
+ if (PWORKSPACE->m_bHasFullscreenWindow && (!DRAGGINGWINDOW->m_bCreatedOverFullscreen || !DRAGGINGWINDOW->m_bIsFloating)) {
+ Debug::log(LOG, "Rejecting drag on a fullscreen workspace. (window under fullscreen)");
+ g_pKeybindManager->changeMouseBindMode(MBIND_INVALID);
+ return true;
+ }
+ }
+
+ DRAGGINGWINDOW->m_bDraggingTiled = false;
+ m_vDraggingWindowOriginalFloatSize = DRAGGINGWINDOW->m_vLastFloatingSize;
+ if (!DRAGGINGWINDOW->m_bIsFloating && g_pInputManager->dragMode == MBIND_MOVE) {
+ DRAGGINGWINDOW->m_vLastFloatingSize = (DRAGGINGWINDOW->m_vRealSize->goal() * 0.8489).clamp(Vector2D{5, 5}, Vector2D{}).floor();
+ *DRAGGINGWINDOW->m_vRealPosition = g_pInputManager->getMouseCoordsInternal() - DRAGGINGWINDOW->m_vRealSize->goal() / 2.f;
+ if (g_pInputManager->m_bDragThresholdReached) {
+ changeWindowFloatingMode(DRAGGINGWINDOW);
+ DRAGGINGWINDOW->m_bIsFloating = true;
+ DRAGGINGWINDOW->m_bDraggingTiled = true;
+ }
+ }
+
+ m_vBeginDragXY = g_pInputManager->getMouseCoordsInternal();
+ m_vBeginDragPositionXY = DRAGGINGWINDOW->m_vRealPosition->goal();
+ m_vBeginDragSizeXY = DRAGGINGWINDOW->m_vRealSize->goal();
+ m_vLastDragXY = m_vBeginDragXY;
+
+ return false;
+}
diff --git a/src/layout/IHyprLayout.hpp b/src/layout/IHyprLayout.hpp
index e31bb63e..f85fc52a 100644
--- a/src/layout/IHyprLayout.hpp
+++ b/src/layout/IHyprLayout.hpp
@@ -204,6 +204,13 @@ class IHyprLayout {
virtual Vector2D predictSizeForNewWindow(PHLWINDOW pWindow);
virtual Vector2D predictSizeForNewWindowFloating(PHLWINDOW pWindow);
+ /*
+ Called to try to pick up window for dragging.
+ Updates drag related variables and floats window if threshold reached.
+ Return true to reject
+ */
+ virtual bool updateDragWindow();
+
private:
int m_iMouseMoveEventCount;
Vector2D m_vBeginDragXY;
diff --git a/src/managers/KeybindManager.cpp b/src/managers/KeybindManager.cpp
index 948ef11e..2644cf53 100644
--- a/src/managers/KeybindManager.cpp
+++ b/src/managers/KeybindManager.cpp
@@ -460,6 +460,7 @@ bool CKeybindManager::onKeyEvent(std::any event, SP<IKeyboard> pKeyboard) {
.modmaskAtPressTime = MODS,
.sent = true,
.submapAtPress = m_szCurrentSelectedSubmap,
+ .mousePosAtPress = g_pInputManager->getMouseCoordsInternal(),
};
m_vActiveKeybinds.clear();
@@ -551,6 +552,7 @@ bool CKeybindManager::onMouseEvent(const IPointer::SButtonEvent& e) {
const auto KEY = SPressedKeyWithMods{
.keyName = KEY_NAME,
.modmaskAtPressTime = MODS,
+ .mousePosAtPress = g_pInputManager->getMouseCoordsInternal(),
};
m_vActiveKeybinds.clear();
@@ -638,7 +640,9 @@ std::string CKeybindManager::getCurrentSubmap() {
SDispatchResult CKeybindManager::handleKeybinds(const uint32_t modmask, const SPressedKeyWithMods& key, bool pressed) {
static auto PDISABLEINHIBIT = CConfigValue<Hyprlang::INT>("binds:disable_keybind_grabbing");
- bool found = false;
+ static auto PDRAGTHRESHOLD = CConfigValue<Hyprlang::INT>("binds:drag_threshold");
+
+ bool found = false;
SDispatchResult res;
if (pressed) {
@@ -736,6 +740,14 @@ SDispatchResult CKeybindManager::handleKeybinds(const uint32_t modmask, const SP
found = true; // suppress the event
continue;
}
+
+ // Require mouse to stay inside drag_threshold for clicks, outside for drags
+ // Check if either a mouse bind has triggered or currently over the threshold (maybe there is no mouse bind on the same key)
+ const auto THRESHOLDREACHED = key.mousePosAtPress.distanceSq(g_pInputManager->getMouseCoordsInternal()) > std::pow(*PDRAGTHRESHOLD, 2);
+ if (k->click && (g_pInputManager->m_bDragThresholdReached || THRESHOLDREACHED))
+ continue;
+ else if (k->drag && !g_pInputManager->m_bDragThresholdReached && !THRESHOLDREACHED)
+ continue;
}
if (k->longPress) {
@@ -788,6 +800,8 @@ SDispatchResult CKeybindManager::handleKeybinds(const uint32_t modmask, const SP
found = true;
}
+ g_pInputManager->m_bDragThresholdReached = false;
+
// if keybind wasn't found (or dispatcher said to) then pass event
res.passEvent |= !found;
diff --git a/src/managers/KeybindManager.hpp b/src/managers/KeybindManager.hpp
index 8b13bac2..0040d761 100644
--- a/src/managers/KeybindManager.hpp
+++ b/src/managers/KeybindManager.hpp
@@ -39,6 +39,8 @@ struct SKeybind {
bool multiKey = false;
bool hasDescription = false;
bool dontInhibit = false;
+ bool click = false;
+ bool drag = false;
// DO NOT INITIALIZE
bool shadowed = false;
@@ -62,6 +64,7 @@ struct SPressedKeyWithMods {
uint32_t modmaskAtPressTime = 0;
bool sent = false;
std::string submapAtPress = "";
+ Vector2D mousePosAtPress = {};
};
struct SParsedKey {
diff --git a/src/managers/input/InputManager.hpp b/src/managers/input/InputManager.hpp
index fa06454f..406e1845 100644
--- a/src/managers/input/InputManager.hpp
+++ b/src/managers/input/InputManager.hpp
@@ -147,8 +147,9 @@ class CInputManager {
// for dragging floating windows
PHLWINDOWREF currentlyDraggedWindow;
- eMouseBindMode dragMode = MBIND_INVALID;
- bool m_bWasDraggingWindow = false;
+ eMouseBindMode dragMode = MBIND_INVALID;
+ bool m_bWasDraggingWindow = false;
+ bool m_bDragThresholdReached = false;
// for refocus to be forced
PHLWINDOWREF m_pForcedFocus;