summaryrefslogtreecommitdiff
path: root/src/layout/supplementary/DragController.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/layout/supplementary/DragController.cpp')
-rw-r--r--src/layout/supplementary/DragController.cpp396
1 files changed, 396 insertions, 0 deletions
diff --git a/src/layout/supplementary/DragController.cpp b/src/layout/supplementary/DragController.cpp
new file mode 100644
index 00000000..a28aef07
--- /dev/null
+++ b/src/layout/supplementary/DragController.cpp
@@ -0,0 +1,396 @@
+#include "DragController.hpp"
+
+#include "../space/Space.hpp"
+
+#include "../../Compositor.hpp"
+#include "../../managers/cursor/CursorShapeOverrideController.hpp"
+#include "../../desktop/state/FocusState.hpp"
+#include "../../desktop/view/Group.hpp"
+#include "../../render/Renderer.hpp"
+
+using namespace Layout;
+using namespace Layout::Supplementary;
+
+SP<ITarget> CDragStateController::target() const {
+ return m_target.lock();
+}
+
+eMouseBindMode CDragStateController::mode() const {
+ return m_dragMode;
+}
+
+bool CDragStateController::wasDraggingWindow() const {
+ return m_wasDraggingWindow;
+}
+
+bool CDragStateController::dragThresholdReached() const {
+ return m_dragThresholdReached;
+}
+
+void CDragStateController::resetDragThresholdReached() {
+ m_dragThresholdReached = false;
+}
+
+bool CDragStateController::draggingTiled() const {
+ return m_draggingTiled;
+}
+
+bool CDragStateController::updateDragWindow() {
+ const auto DRAGGINGTARGET = m_target.lock();
+ const bool WAS_FULLSCREEN = DRAGGINGTARGET->fullscreenMode() != FSMODE_NONE;
+
+ if (m_dragThresholdReached) {
+ if (WAS_FULLSCREEN) {
+ Log::logger->log(Log::DEBUG, "Dragging a fullscreen window");
+ g_pCompositor->setWindowFullscreenInternal(DRAGGINGTARGET->window(), FSMODE_NONE);
+ }
+
+ const auto PWORKSPACE = DRAGGINGTARGET->workspace();
+ const auto DRAGGINGWINDOW = DRAGGINGTARGET->window();
+
+ if (PWORKSPACE->m_hasFullscreenWindow && (!DRAGGINGTARGET->floating() || (!DRAGGINGWINDOW->m_createdOverFullscreen && !DRAGGINGWINDOW->m_pinned))) {
+ Log::logger->log(Log::DEBUG, "Rejecting drag on a fullscreen workspace. (window under fullscreen)");
+ CKeybindManager::changeMouseBindMode(MBIND_INVALID);
+ return true;
+ }
+ }
+
+ m_draggingTiled = false;
+ m_draggingWindowOriginalFloatSize = DRAGGINGTARGET->lastFloatingSize();
+
+ if (WAS_FULLSCREEN && DRAGGINGTARGET->floating()) {
+ const auto MOUSECOORDS = g_pInputManager->getMouseCoordsInternal();
+ DRAGGINGTARGET->setPositionGlobal(CBox{MOUSECOORDS - DRAGGINGTARGET->position().size() / 2.F, DRAGGINGTARGET->position().size()});
+ } else if (!DRAGGINGTARGET->floating() && m_dragMode == MBIND_MOVE) {
+ Vector2D MINSIZE = DRAGGINGTARGET->minSize().value_or(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE});
+ DRAGGINGTARGET->rememberFloatingSize((DRAGGINGTARGET->position().size() * 0.8489).clamp(MINSIZE, Vector2D{}).floor());
+ DRAGGINGTARGET->setPositionGlobal(CBox{g_pInputManager->getMouseCoordsInternal() - DRAGGINGTARGET->position().size() / 2.F, DRAGGINGTARGET->position().size()});
+
+ if (m_dragThresholdReached) {
+ g_layoutManager->changeFloatingMode(DRAGGINGTARGET);
+ m_draggingTiled = true;
+ }
+ }
+
+ const auto DRAG_ORIGINAL_BOX = DRAGGINGTARGET->position();
+
+ m_beginDragXY = g_pInputManager->getMouseCoordsInternal();
+ m_beginDragPositionXY = DRAG_ORIGINAL_BOX.pos();
+ m_beginDragSizeXY = DRAG_ORIGINAL_BOX.size();
+ m_lastDragXY = m_beginDragXY;
+
+ return false;
+}
+
+void CDragStateController::dragBegin(SP<ITarget> target, eMouseBindMode mode) {
+ m_target = target;
+ m_dragMode = mode;
+
+ const auto DRAGGINGTARGET = m_target.lock();
+ static auto PDRAGTHRESHOLD = CConfigValue<Hyprlang::INT>("binds:drag_threshold");
+
+ m_mouseMoveEventCount = 1;
+ m_beginDragSizeXY = Vector2D();
+
+ // Window will be floating. Let's check if it's valid. It should be, but I don't like crashing.
+ if (!validMapped(DRAGGINGTARGET->window())) {
+ Log::logger->log(Log::ERR, "Dragging attempted on an invalid window (not mapped)");
+ CKeybindManager::changeMouseBindMode(MBIND_INVALID);
+ return;
+ }
+
+ if (!DRAGGINGTARGET->workspace()) {
+ Log::logger->log(Log::ERR, "Dragging attempted on an invalid window (no workspace)");
+ CKeybindManager::changeMouseBindMode(MBIND_INVALID);
+ return;
+ }
+
+ // Try to pick up dragged window now if drag_threshold is disabled
+ // or at least update dragging related variables for the cursors
+ m_dragThresholdReached = *PDRAGTHRESHOLD <= 0;
+ if (updateDragWindow())
+ return;
+
+ // get the grab corner
+ static auto RESIZECORNER = CConfigValue<Hyprlang::INT>("general:resize_corner");
+ if (*RESIZECORNER != 0 && *RESIZECORNER <= 4 && DRAGGINGTARGET->floating()) {
+ switch (*RESIZECORNER) {
+ case 1:
+ m_grabbedCorner = CORNER_TOPLEFT;
+ Cursor::overrideController->setOverride("nw-resize", Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION);
+ break;
+ case 2:
+ m_grabbedCorner = CORNER_TOPRIGHT;
+ Cursor::overrideController->setOverride("ne-resize", Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION);
+ break;
+ case 3:
+ m_grabbedCorner = CORNER_BOTTOMRIGHT;
+ Cursor::overrideController->setOverride("se-resize", Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION);
+ break;
+ case 4:
+ m_grabbedCorner = CORNER_BOTTOMLEFT;
+ Cursor::overrideController->setOverride("sw-resize", Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION);
+ break;
+ }
+ } else if (m_beginDragXY.x < m_beginDragPositionXY.x + m_beginDragSizeXY.x / 2.F) {
+ if (m_beginDragXY.y < m_beginDragPositionXY.y + m_beginDragSizeXY.y / 2.F) {
+ m_grabbedCorner = CORNER_TOPLEFT;
+ Cursor::overrideController->setOverride("nw-resize", Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION);
+ } else {
+ m_grabbedCorner = CORNER_BOTTOMLEFT;
+ Cursor::overrideController->setOverride("sw-resize", Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION);
+ }
+ } else {
+ if (m_beginDragXY.y < m_beginDragPositionXY.y + m_beginDragSizeXY.y / 2.F) {
+ m_grabbedCorner = CORNER_TOPRIGHT;
+ Cursor::overrideController->setOverride("ne-resize", Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION);
+ } else {
+ m_grabbedCorner = CORNER_BOTTOMRIGHT;
+ Cursor::overrideController->setOverride("se-resize", Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION);
+ }
+ }
+
+ if (m_dragMode != MBIND_RESIZE && m_dragMode != MBIND_RESIZE_FORCE_RATIO && m_dragMode != MBIND_RESIZE_BLOCK_RATIO)
+ Cursor::overrideController->setOverride("grabbing", Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION);
+
+ DRAGGINGTARGET->damageEntire();
+
+ g_pKeybindManager->shadowKeybinds();
+
+ if (DRAGGINGTARGET->window()) {
+ Desktop::focusState()->rawWindowFocus(DRAGGINGTARGET->window(), Desktop::FOCUS_REASON_DESKTOP_STATE_CHANGE);
+ g_pCompositor->changeWindowZOrder(DRAGGINGTARGET->window(), true);
+ }
+}
+void CDragStateController::dragEnd() {
+ auto draggingTarget = m_target.lock();
+
+ m_mouseMoveEventCount = 1;
+
+ if (!validMapped(draggingTarget->window())) {
+ if (draggingTarget->window()) {
+ Cursor::overrideController->unsetOverride(Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION);
+ m_target.reset();
+ }
+ return;
+ }
+
+ Cursor::overrideController->unsetOverride(Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION);
+ m_target.reset();
+ m_wasDraggingWindow = true;
+
+ if (m_dragMode == MBIND_MOVE && draggingTarget->window()) {
+ draggingTarget->damageEntire();
+
+ const auto DRAGGING_WINDOW = draggingTarget->window();
+
+ const auto MOUSECOORDS = g_pInputManager->getMouseCoordsInternal();
+ PHLWINDOW pWindow =
+ g_pCompositor->vectorToWindowUnified(MOUSECOORDS, Desktop::View::RESERVED_EXTENTS | Desktop::View::INPUT_EXTENTS | Desktop::View::ALLOW_FLOATING, DRAGGING_WINDOW);
+
+ if (pWindow) {
+ if (pWindow->checkInputOnDecos(INPUT_TYPE_DRAG_END, MOUSECOORDS, DRAGGING_WINDOW))
+ return;
+
+ const bool FLOATEDINTOTILED = !pWindow->m_isFloating && !m_draggingTiled;
+ static auto PDRAGINTOGROUP = CConfigValue<Hyprlang::INT>("group:drag_into_group");
+
+ if (pWindow->m_group && DRAGGING_WINDOW->canBeGroupedInto(pWindow->m_group) && *PDRAGINTOGROUP == 1 && !FLOATEDINTOTILED) {
+ pWindow->m_group->add(DRAGGING_WINDOW);
+ // fix the draggingTarget, now it's DRAGGING_WINDOW
+ draggingTarget = DRAGGING_WINDOW->m_target;
+ }
+ }
+ }
+
+ if (m_draggingTiled) {
+ // static auto PPRECISEMOUSE = CConfigValue<Hyprlang::INT>("dwindle:precise_mouse_move");
+
+ // FIXME: remove or rethink
+ // if (*PPRECISEMOUSE) {
+ // eDirection direction = DIRECTION_DEFAULT;
+
+ // const auto MOUSECOORDS = g_pInputManager->getMouseCoordsInternal();
+ // const PHLWINDOW pReferenceWindow =
+ // g_pCompositor->vectorToWindowUnified(MOUSECOORDS, Desktop::View::RESERVED_EXTENTS | Desktop::View::INPUT_EXTENTS | Desktop::View::ALLOW_FLOATING, DRAGGINGWINDOW);
+
+ // if (pReferenceWindow && pReferenceWindow != DRAGGINGWINDOW) {
+ // const Vector2D draggedCenter = DRAGGINGWINDOW->m_realPosition->goal() + DRAGGINGWINDOW->m_realSize->goal() / 2.f;
+ // const Vector2D referenceCenter = pReferenceWindow->m_realPosition->goal() + pReferenceWindow->m_realSize->goal() / 2.f;
+ // const float xDiff = draggedCenter.x - referenceCenter.x;
+ // const float yDiff = draggedCenter.y - referenceCenter.y;
+
+ // if (fabs(xDiff) > fabs(yDiff))
+ // direction = xDiff < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT;
+ // else
+ // direction = yDiff < 0 ? DIRECTION_UP : DIRECTION_DOWN;
+ // }
+
+ // onWindowRemovedTiling(DRAGGINGWINDOW);
+ // onWindowCreatedTiling(DRAGGINGWINDOW, direction);
+ // } else
+
+ // make sure to check if we are floating because drag into group could make us tiled already
+ if (draggingTarget->floating())
+ g_layoutManager->changeFloatingMode(draggingTarget);
+
+ draggingTarget->rememberFloatingSize(m_draggingWindowOriginalFloatSize);
+ }
+
+ draggingTarget->damageEntire();
+
+ Desktop::focusState()->fullWindowFocus(draggingTarget->window(), Desktop::FOCUS_REASON_DESKTOP_STATE_CHANGE);
+
+ m_wasDraggingWindow = false;
+ m_dragMode = MBIND_INVALID;
+}
+
+void CDragStateController::mouseMove(const Vector2D& mousePos) {
+ if (m_target.expired())
+ return;
+
+ const auto DRAGGINGTARGET = m_target.lock();
+ static auto PDRAGTHRESHOLD = CConfigValue<Hyprlang::INT>("binds:drag_threshold");
+
+ // Window invalid or drag begin size 0,0 meaning we rejected it.
+ if ((!validMapped(DRAGGINGTARGET->window()) || m_beginDragSizeXY == Vector2D())) {
+ CKeybindManager::changeMouseBindMode(MBIND_INVALID);
+ return;
+ }
+
+ // Yoink dragged window here instead if using drag_threshold and it has been reached
+ if (*PDRAGTHRESHOLD > 0 && !m_dragThresholdReached) {
+ if ((m_beginDragXY.distanceSq(mousePos) <= std::pow(*PDRAGTHRESHOLD, 2) && m_beginDragXY == m_lastDragXY))
+ return;
+ m_dragThresholdReached = true;
+ if (updateDragWindow())
+ return;
+ }
+
+ static auto TIMER = std::chrono::high_resolution_clock::now(), MSTIMER = TIMER;
+
+ const auto DELTA = Vector2D(mousePos.x - m_beginDragXY.x, mousePos.y - m_beginDragXY.y);
+ const auto TICKDELTA = Vector2D(mousePos.x - m_lastDragXY.x, mousePos.y - m_lastDragXY.y);
+
+ static auto SNAPENABLED = CConfigValue<Hyprlang::INT>("general:snap:enabled");
+
+ const auto TIMERDELTA = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - TIMER).count();
+ const auto MSDELTA = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - MSTIMER).count();
+ const auto MSMONITOR = 1000.0 / g_pHyprRenderer->m_mostHzMonitor->m_refreshRate;
+ static int totalMs = 0;
+ bool canSkipUpdate = true;
+
+ MSTIMER = std::chrono::high_resolution_clock::now();
+
+ if (m_mouseMoveEventCount == 1)
+ totalMs = 0;
+
+ if (MSMONITOR > 16.0) {
+ totalMs += MSDELTA < MSMONITOR ? MSDELTA : std::round(totalMs * 1.0 / m_mouseMoveEventCount);
+ m_mouseMoveEventCount += 1;
+
+ // check if time-window is enough to skip update on 60hz monitor
+ canSkipUpdate = std::clamp(MSMONITOR - TIMERDELTA, 0.0, MSMONITOR) > totalMs * 1.0 / m_mouseMoveEventCount;
+ }
+
+ if ((abs(TICKDELTA.x) < 1.f && abs(TICKDELTA.y) < 1.f) || (TIMERDELTA < MSMONITOR && canSkipUpdate && (m_dragMode != MBIND_MOVE)))
+ return;
+
+ TIMER = std::chrono::high_resolution_clock::now();
+
+ m_lastDragXY = mousePos;
+
+ DRAGGINGTARGET->damageEntire();
+
+ if (m_dragMode == MBIND_MOVE) {
+
+ Vector2D newPos = m_beginDragPositionXY + DELTA;
+ Vector2D newSize = DRAGGINGTARGET->position().size();
+
+ if (*SNAPENABLED && !m_draggingTiled)
+ g_layoutManager->performSnap(newPos, newSize, DRAGGINGTARGET, MBIND_MOVE, -1, m_beginDragSizeXY);
+
+ newPos = newPos.round();
+
+ DRAGGINGTARGET->setPositionGlobal({newPos, newSize});
+ DRAGGINGTARGET->warpPositionSize();
+ } else if (m_dragMode == MBIND_RESIZE || m_dragMode == MBIND_RESIZE_FORCE_RATIO || m_dragMode == MBIND_RESIZE_BLOCK_RATIO) {
+ if (DRAGGINGTARGET->floating()) {
+
+ Vector2D MINSIZE = DRAGGINGTARGET->minSize().value_or(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE});
+ Vector2D MAXSIZE = DRAGGINGTARGET->maxSize().value_or(Math::VECTOR2D_MAX);
+
+ Vector2D newSize = m_beginDragSizeXY;
+ Vector2D newPos = m_beginDragPositionXY;
+
+ if (m_grabbedCorner == CORNER_BOTTOMRIGHT)
+ newSize = newSize + DELTA;
+ else if (m_grabbedCorner == CORNER_TOPLEFT)
+ newSize = newSize - DELTA;
+ else if (m_grabbedCorner == CORNER_TOPRIGHT)
+ newSize = newSize + Vector2D(DELTA.x, -DELTA.y);
+ else if (m_grabbedCorner == CORNER_BOTTOMLEFT)
+ newSize = newSize + Vector2D(-DELTA.x, DELTA.y);
+
+ eMouseBindMode mode = m_dragMode;
+ if (DRAGGINGTARGET->window() && DRAGGINGTARGET->window()->m_ruleApplicator->keepAspectRatio().valueOrDefault() && mode != MBIND_RESIZE_BLOCK_RATIO)
+ mode = MBIND_RESIZE_FORCE_RATIO;
+
+ if (m_beginDragSizeXY.x >= 1 && m_beginDragSizeXY.y >= 1 && mode == MBIND_RESIZE_FORCE_RATIO) {
+
+ const float RATIO = m_beginDragSizeXY.y / m_beginDragSizeXY.x;
+
+ if (MINSIZE.x * RATIO > MINSIZE.y)
+ MINSIZE = Vector2D(MINSIZE.x, MINSIZE.x * RATIO);
+ else
+ MINSIZE = Vector2D(MINSIZE.y / RATIO, MINSIZE.y);
+
+ if (MAXSIZE.x * RATIO < MAXSIZE.y)
+ MAXSIZE = Vector2D(MAXSIZE.x, MAXSIZE.x * RATIO);
+ else
+ MAXSIZE = Vector2D(MAXSIZE.y / RATIO, MAXSIZE.y);
+
+ if (newSize.x * RATIO > newSize.y)
+ newSize = Vector2D(newSize.x, newSize.x * RATIO);
+ else
+ newSize = Vector2D(newSize.y / RATIO, newSize.y);
+ }
+
+ newSize = newSize.clamp(MINSIZE, MAXSIZE);
+
+ if (m_grabbedCorner == CORNER_TOPLEFT)
+ newPos = newPos - newSize + m_beginDragSizeXY;
+ else if (m_grabbedCorner == CORNER_TOPRIGHT)
+ newPos = newPos + Vector2D(0.0, (m_beginDragSizeXY - newSize).y);
+ else if (m_grabbedCorner == CORNER_BOTTOMLEFT)
+ newPos = newPos + Vector2D((m_beginDragSizeXY - newSize).x, 0.0);
+
+ if (*SNAPENABLED) {
+ g_layoutManager->performSnap(newPos, newSize, DRAGGINGTARGET, mode, m_grabbedCorner, m_beginDragSizeXY);
+ newSize = newSize.clamp(MINSIZE, MAXSIZE);
+ }
+
+ CBox wb = {newPos, newSize};
+ wb.round();
+
+ DRAGGINGTARGET->setPositionGlobal(wb);
+ DRAGGINGTARGET->warpPositionSize();
+ } else {
+ g_layoutManager->resizeTarget(TICKDELTA, DRAGGINGTARGET, m_grabbedCorner);
+ DRAGGINGTARGET->warpPositionSize();
+ }
+ }
+
+ // get middle point
+ Vector2D middle = DRAGGINGTARGET->position().middle();
+
+ // and check its monitor
+ const auto PMONITOR = g_pCompositor->getMonitorFromVector(middle);
+
+ if (PMONITOR && PMONITOR->m_activeWorkspace && DRAGGINGTARGET->floating() /* If we're resaizing a tiled target, don't do this */) {
+ const auto WS = PMONITOR->m_activeSpecialWorkspace ? PMONITOR->m_activeSpecialWorkspace : PMONITOR->m_activeWorkspace;
+ DRAGGINGTARGET->assignToSpace(WS->m_space);
+ }
+
+ DRAGGINGTARGET->damageEntire();
+}