diff --git a/src/layout/IHyprLayout.cpp b/src/layout/IHyprLayout.cpp index 4ae41d57..38868424 100644 --- a/src/layout/IHyprLayout.cpp +++ b/src/layout/IHyprLayout.cpp @@ -12,6 +12,7 @@ #include "../managers/LayoutManager.hpp" #include "../managers/EventManager.hpp" #include "../managers/HookSystemManager.hpp" +#include "../managers/cursor/CursorShapeOverrideController.hpp" void IHyprLayout::onWindowCreated(PHLWINDOW pWindow, eDirection direction) { CBox desiredGeometry = g_pXWaylandManager->getGeometryForWindow(pWindow); @@ -243,7 +244,7 @@ void IHyprLayout::onBeginDragWindow() { // Window will be floating. Let's check if it's valid. It should be, but I don't like crashing. if (!validMapped(DRAGGINGWINDOW)) { Debug::log(ERR, "Dragging attempted on an invalid window!"); - g_pKeybindManager->changeMouseBindMode(MBIND_INVALID); + CKeybindManager::changeMouseBindMode(MBIND_INVALID); return; } @@ -259,41 +260,41 @@ void IHyprLayout::onBeginDragWindow() { switch (*RESIZECORNER) { case 1: m_grabbedCorner = CORNER_TOPLEFT; - g_pInputManager->setCursorImageUntilUnset("nw-resize"); + Cursor::overrideController->setOverride("nw-resize", Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION); break; case 2: m_grabbedCorner = CORNER_TOPRIGHT; - g_pInputManager->setCursorImageUntilUnset("ne-resize"); + Cursor::overrideController->setOverride("ne-resize", Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION); break; case 3: m_grabbedCorner = CORNER_BOTTOMRIGHT; - g_pInputManager->setCursorImageUntilUnset("se-resize"); + Cursor::overrideController->setOverride("se-resize", Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION); break; case 4: m_grabbedCorner = CORNER_BOTTOMLEFT; - g_pInputManager->setCursorImageUntilUnset("sw-resize"); + Cursor::overrideController->setOverride("sw-resize", Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION); break; } } else if (m_beginDragXY.x < m_beginDragPositionXY.x + m_beginDragSizeXY.x / 2.0) { if (m_beginDragXY.y < m_beginDragPositionXY.y + m_beginDragSizeXY.y / 2.0) { m_grabbedCorner = CORNER_TOPLEFT; - g_pInputManager->setCursorImageUntilUnset("nw-resize"); + Cursor::overrideController->setOverride("nw-resize", Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION); } else { m_grabbedCorner = CORNER_BOTTOMLEFT; - g_pInputManager->setCursorImageUntilUnset("sw-resize"); + Cursor::overrideController->setOverride("sw-resize", Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION); } } else { if (m_beginDragXY.y < m_beginDragPositionXY.y + m_beginDragSizeXY.y / 2.0) { m_grabbedCorner = CORNER_TOPRIGHT; - g_pInputManager->setCursorImageUntilUnset("ne-resize"); + Cursor::overrideController->setOverride("ne-resize", Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION); } else { m_grabbedCorner = CORNER_BOTTOMRIGHT; - g_pInputManager->setCursorImageUntilUnset("se-resize"); + Cursor::overrideController->setOverride("se-resize", Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION); } } if (g_pInputManager->m_dragMode != MBIND_RESIZE && g_pInputManager->m_dragMode != MBIND_RESIZE_FORCE_RATIO && g_pInputManager->m_dragMode != MBIND_RESIZE_BLOCK_RATIO) - g_pInputManager->setCursorImageUntilUnset("grabbing"); + Cursor::overrideController->setOverride("grabbing", Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION); g_pHyprRenderer->damageWindow(DRAGGINGWINDOW); @@ -310,13 +311,13 @@ void IHyprLayout::onEndDragWindow() { if (!validMapped(DRAGGINGWINDOW)) { if (DRAGGINGWINDOW) { - g_pInputManager->unsetCursorImage(); + Cursor::overrideController->unsetOverride(Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION); g_pInputManager->m_currentlyDraggedWindow.reset(); } return; } - g_pInputManager->unsetCursorImage(); + Cursor::overrideController->unsetOverride(Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION); g_pInputManager->m_currentlyDraggedWindow.reset(); g_pInputManager->m_wasDraggingWindow = true; diff --git a/src/managers/cursor/CursorShapeOverrideController.cpp b/src/managers/cursor/CursorShapeOverrideController.cpp new file mode 100644 index 00000000..45064277 --- /dev/null +++ b/src/managers/cursor/CursorShapeOverrideController.cpp @@ -0,0 +1,43 @@ +#include "CursorShapeOverrideController.hpp" + +#include + +using namespace Cursor; + +void CShapeOverrideController::setOverride(const std::string& name, eCursorShapeOverrideGroup group) { + if (m_overrides[group] == name) + return; + + m_overrides[group] = name; + + recheckOverridesResendIfChanged(); +} + +void CShapeOverrideController::unsetOverride(eCursorShapeOverrideGroup group) { + if (m_overrides[group].empty()) + return; + + m_overrides[group] = ""; + + recheckOverridesResendIfChanged(); +} + +void CShapeOverrideController::recheckOverridesResendIfChanged() { + for (const auto& s : m_overrides | std::views::reverse) { + if (s.empty()) + continue; + + if (s == m_overrideShape) + return; + + m_overrideShape = s; + m_events.overrideChanged.emit(s); + return; + } + + if (m_overrideShape.empty()) + return; + + m_overrideShape = ""; + m_events.overrideChanged.emit(""); +} diff --git a/src/managers/cursor/CursorShapeOverrideController.hpp b/src/managers/cursor/CursorShapeOverrideController.hpp new file mode 100644 index 00000000..eca2ccf7 --- /dev/null +++ b/src/managers/cursor/CursorShapeOverrideController.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include "../../helpers/memory/Memory.hpp" +#include "../../helpers/signal/Signal.hpp" + +#include +#include + +namespace Cursor { + enum eCursorShapeOverrideGroup : uint8_t { + // unknown group - lowest priority + CURSOR_OVERRIDE_UNKNOWN = 0, + // window edges for resizing from edge + CURSOR_OVERRIDE_WINDOW_EDGE, + // Drag and drop + CURSOR_OVERRIDE_DND, + // special action: Interactive::CDrag, kill, etc. + CURSOR_OVERRIDE_SPECIAL_ACTION, + + // + CURSOR_OVERRIDE_END, + }; + + class CShapeOverrideController { + public: + CShapeOverrideController() = default; + ~CShapeOverrideController() = default; + + CShapeOverrideController(const CShapeOverrideController&) = delete; + CShapeOverrideController(CShapeOverrideController&) = delete; + CShapeOverrideController(CShapeOverrideController&&) = delete; + + void setOverride(const std::string& name, eCursorShapeOverrideGroup group); + void unsetOverride(eCursorShapeOverrideGroup group); + + struct { + // if string is empty, override was cleared + CSignalT overrideChanged; + } m_events; + + private: + void recheckOverridesResendIfChanged(); + + std::array m_overrides; + std::string m_overrideShape; + }; + + inline UP overrideController = makeUnique(); +}; \ No newline at end of file diff --git a/src/managers/input/InputManager.cpp b/src/managers/input/InputManager.cpp index 80a5fabd..fe93a83b 100644 --- a/src/managers/input/InputManager.cpp +++ b/src/managers/input/InputManager.cpp @@ -44,6 +44,7 @@ #include "../../helpers/MiscFunctions.hpp" #include "trackpad/TrackpadGestures.hpp" +#include "../cursor/CursorShapeOverrideController.hpp" #include @@ -65,20 +66,33 @@ CInputManager::CInputManager() { m_cursorSurfaceInfo.name = event.shapeName; m_cursorSurfaceInfo.hidden = false; - m_cursorSurfaceInfo.inUse = true; g_pHyprRenderer->setCursorFromName(m_cursorSurfaceInfo.name); }); - m_listeners.newIdleInhibitor = PROTO::idleInhibit->m_events.newIdleInhibitor.listen([this](const auto& data) { this->newIdleInhibitor(data); }); + m_listeners.newIdleInhibitor = PROTO::idleInhibit->m_events.newIdleInhibitor.listen([this](const auto& data) { newIdleInhibitor(data); }); + m_listeners.newVirtualKeyboard = PROTO::virtualKeyboard->m_events.newKeyboard.listen([this](const auto& keyboard) { - this->newVirtualKeyboard(keyboard); + newVirtualKeyboard(keyboard); updateCapabilities(); }); - m_listeners.newVirtualMouse = PROTO::virtualPointer->m_events.newPointer.listen([this](const auto& mouse) { - this->newVirtualMouse(mouse); + + m_listeners.newVirtualMouse = PROTO::virtualPointer->m_events.newPointer.listen([this](const auto& mouse) { + newVirtualMouse(mouse); updateCapabilities(); }); - m_listeners.setCursor = g_pSeatManager->m_events.setCursor.listen([this](const auto& event) { this->processMouseRequest(event); }); + + m_listeners.setCursor = g_pSeatManager->m_events.setCursor.listen([this](const auto& event) { processMouseRequest(event); }); + + m_listeners.overrideChanged = Cursor::overrideController->m_events.overrideChanged.listen([this](const std::string& shape) { + if (shape.empty()) { + m_cursorImageOverridden = false; + restoreCursorIconToApp(); + return; + } + + m_cursorImageOverridden = true; + g_pHyprRenderer->setCursorFromName(shape); + }); m_cursorSurfaceInfo.wlSurface = CWLSurface::create(); } @@ -472,17 +486,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus, bool mouse, st if (!foundSurface) { if (!m_emptyFocusCursorSet) { - if (*PRESIZEONBORDER && *PRESIZECURSORICON && m_borderIconDirection != BORDERICON_NONE) { - m_borderIconDirection = BORDERICON_NONE; - unsetCursorImage(); - } - - // TODO: maybe wrap? - if (m_clickBehavior == CLICKMODE_KILL) - setCursorImageOverride("crosshair"); - else - setCursorImageOverride("left_ptr"); - + g_pHyprRenderer->setCursorFromName("left_ptr"); m_emptyFocusCursorSet = true; } @@ -532,7 +536,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus, bool mouse, st if (pFoundWindow && foundSurface == pFoundWindow->m_wlSurface->resource() && !m_cursorImageOverridden) { const auto BOX = pFoundWindow->getWindowMainSurfaceBox(); if (VECNOTINRECT(mouseCoords, BOX.x, BOX.y, BOX.x + BOX.width, BOX.y + BOX.height)) - setCursorImageOverride("left_ptr"); + g_pHyprRenderer->setCursorFromName("left_ptr"); else restoreCursorIconToApp(); } @@ -540,11 +544,8 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus, bool mouse, st if (pFoundWindow) { // change cursor icon if hovering over border if (*PRESIZEONBORDER && *PRESIZECURSORICON) { - if (!pFoundWindow->isFullscreen() && !pFoundWindow->hasPopupAt(mouseCoords)) { + if (!pFoundWindow->isFullscreen() && !pFoundWindow->hasPopupAt(mouseCoords)) setCursorIconOnBorder(pFoundWindow); - } else if (m_borderIconDirection != BORDERICON_NONE) { - unsetCursorImage(); - } } if (FOLLOWMOUSE != 1 && !refocus) { @@ -595,7 +596,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus, bool mouse, st } else { if (*PRESIZEONBORDER && *PRESIZECURSORICON && m_borderIconDirection != BORDERICON_NONE) { m_borderIconDirection = BORDERICON_NONE; - unsetCursorImage(); + Cursor::overrideController->unsetOverride(Cursor::CURSOR_OVERRIDE_WINDOW_EDGE); } if (pFoundLayerSurface && (pFoundLayerSurface->m_layerSurface->m_current.interactivity != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE) && FOLLOWMOUSE != 3 && @@ -667,14 +668,10 @@ void CInputManager::processMouseRequest(const CSeatManager::SSetCursorEvent& eve m_cursorSurfaceInfo.name = ""; - m_cursorSurfaceInfo.inUse = true; g_pHyprRenderer->setCursorSurface(m_cursorSurfaceInfo.wlSurface, event.hotspot.x, event.hotspot.y); } void CInputManager::restoreCursorIconToApp() { - if (m_cursorSurfaceInfo.inUse) - return; - if (m_cursorSurfaceInfo.hidden) { g_pHyprRenderer->setCursorSurface(nullptr, 0, 0); return; @@ -683,29 +680,12 @@ void CInputManager::restoreCursorIconToApp() { if (m_cursorSurfaceInfo.name.empty()) { if (m_cursorSurfaceInfo.wlSurface->exists()) g_pHyprRenderer->setCursorSurface(m_cursorSurfaceInfo.wlSurface, m_cursorSurfaceInfo.vHotspot.x, m_cursorSurfaceInfo.vHotspot.y); - } else { + } else g_pHyprRenderer->setCursorFromName(m_cursorSurfaceInfo.name); - } - - m_cursorSurfaceInfo.inUse = true; -} - -void CInputManager::setCursorImageOverride(const std::string& name) { - if (m_cursorImageOverridden) - return; - - m_cursorSurfaceInfo.inUse = false; - g_pHyprRenderer->setCursorFromName(name); } bool CInputManager::cursorImageUnlocked() { - if (m_clickBehavior == CLICKMODE_KILL) - return false; - - if (m_cursorImageOverridden) - return false; - - return true; + return !m_cursorImageOverridden; } eClickBehaviorMode CInputManager::getClickMode() { @@ -729,7 +709,7 @@ void CInputManager::setClickMode(eClickBehaviorMode mode) { refocus(); // set cursor - g_pHyprRenderer->setCursorFromName("crosshair", true); + Cursor::overrideController->setOverride("crosshair", Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION); break; default: break; } @@ -834,6 +814,7 @@ void CInputManager::processMouseDownKill(const IPointer::SButtonEvent& e) { // reset click behavior mode m_clickBehavior = CLICKMODE_DEFAULT; + Cursor::overrideController->unsetOverride(Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION); } void CInputManager::onMouseWheel(IPointer::SAxisEvent e, SP pointer) { @@ -1867,20 +1848,6 @@ void CInputManager::destroySwitch(SSwitchDevice* pDevice) { m_switches.remove(*pDevice); } -void CInputManager::setCursorImageUntilUnset(std::string name) { - g_pHyprRenderer->setCursorFromName(name); - m_cursorImageOverridden = true; - m_cursorSurfaceInfo.inUse = false; -} - -void CInputManager::unsetCursorImage() { - if (!m_cursorImageOverridden) - return; - - m_cursorImageOverridden = false; - restoreCursorIconToApp(); -} - std::string CInputManager::getNameForNewDevice(std::string internalName) { auto proposedNewName = deviceNameToInternalString(internalName); @@ -1908,16 +1875,12 @@ void CInputManager::releaseAllMouseButtons() { } void CInputManager::setCursorIconOnBorder(PHLWINDOW w) { - // do not override cursor icons set by mouse binds - if (g_pInputManager->m_currentlyDraggedWindow.expired()) { - m_borderIconDirection = BORDERICON_NONE; + // ignore X11 OR windows, they shouldn't be touched + if (w->m_isX11 && w->isX11OverrideRedirect()) { + Cursor::overrideController->unsetOverride(Cursor::CURSOR_OVERRIDE_WINDOW_EDGE); return; } - // ignore X11 OR windows, they shouldn't be touched - if (w->m_isX11 && w->isX11OverrideRedirect()) - return; - static auto PEXTENDBORDERGRAB = CConfigValue("general:extend_border_grab_area"); const int BORDERSIZE = w->getRealBorderSize(); const int ROUNDING = w->rounding(); @@ -1998,15 +1961,15 @@ void CInputManager::setCursorIconOnBorder(PHLWINDOW w) { m_borderIconDirection = direction; switch (direction) { - case BORDERICON_NONE: unsetCursorImage(); break; - case BORDERICON_UP: setCursorImageUntilUnset("top_side"); break; - case BORDERICON_DOWN: setCursorImageUntilUnset("bottom_side"); break; - case BORDERICON_LEFT: setCursorImageUntilUnset("left_side"); break; - case BORDERICON_RIGHT: setCursorImageUntilUnset("right_side"); break; - case BORDERICON_UP_LEFT: setCursorImageUntilUnset("top_left_corner"); break; - case BORDERICON_DOWN_LEFT: setCursorImageUntilUnset("bottom_left_corner"); break; - case BORDERICON_UP_RIGHT: setCursorImageUntilUnset("top_right_corner"); break; - case BORDERICON_DOWN_RIGHT: setCursorImageUntilUnset("bottom_right_corner"); break; + case BORDERICON_NONE: Cursor::overrideController->unsetOverride(Cursor::CURSOR_OVERRIDE_WINDOW_EDGE); break; + case BORDERICON_UP: Cursor::overrideController->setOverride("top_side", Cursor::CURSOR_OVERRIDE_WINDOW_EDGE); break; + case BORDERICON_DOWN: Cursor::overrideController->setOverride("bottom_side", Cursor::CURSOR_OVERRIDE_WINDOW_EDGE); break; + case BORDERICON_LEFT: Cursor::overrideController->setOverride("left_side", Cursor::CURSOR_OVERRIDE_WINDOW_EDGE); break; + case BORDERICON_RIGHT: Cursor::overrideController->setOverride("right_side", Cursor::CURSOR_OVERRIDE_WINDOW_EDGE); break; + case BORDERICON_UP_LEFT: Cursor::overrideController->setOverride("top_left_corner", Cursor::CURSOR_OVERRIDE_WINDOW_EDGE); break; + case BORDERICON_DOWN_LEFT: Cursor::overrideController->setOverride("bottom_left_corner", Cursor::CURSOR_OVERRIDE_WINDOW_EDGE); break; + case BORDERICON_UP_RIGHT: Cursor::overrideController->setOverride("top_right_corner", Cursor::CURSOR_OVERRIDE_WINDOW_EDGE); break; + case BORDERICON_DOWN_RIGHT: Cursor::overrideController->setOverride("bottom_right_corner", Cursor::CURSOR_OVERRIDE_WINDOW_EDGE); break; } } diff --git a/src/managers/input/InputManager.hpp b/src/managers/input/InputManager.hpp index 60ff49ef..9fbf68b4 100644 --- a/src/managers/input/InputManager.hpp +++ b/src/managers/input/InputManager.hpp @@ -193,11 +193,7 @@ class CInputManager { uint32_t getModsFromAllKBs(); // for virtual keyboards: whether we should respect them as normal ones - bool shouldIgnoreVirtualKeyboard(SP); - - // for special cursors that we choose - void setCursorImageUntilUnset(std::string); - void unsetCursorImage(); + bool shouldIgnoreVirtualKeyboard(SP); std::string getNameForNewDevice(std::string); @@ -226,6 +222,7 @@ class CInputManager { CHyprSignalListener newVirtualKeyboard; CHyprSignalListener newVirtualMouse; CHyprSignalListener setCursor; + CHyprSignalListener overrideChanged; } m_listeners; bool m_cursorImageOverridden = false; @@ -282,16 +279,12 @@ class CInputManager { void setBorderCursorIcon(eBorderIconDirection); void setCursorIconOnBorder(PHLWINDOW w); - // temporary. Obeys setUntilUnset. - void setCursorImageOverride(const std::string& name); - // cursor surface struct { bool hidden = false; // null surface = hidden SP wlSurface; Vector2D vHotspot; std::string name; // if not empty, means set by name. - bool inUse = false; } m_cursorSurfaceInfo; void restoreCursorIconToApp(); // no-op if restored diff --git a/src/protocols/core/DataDevice.cpp b/src/protocols/core/DataDevice.cpp index bf17086e..b3239cf1 100644 --- a/src/protocols/core/DataDevice.cpp +++ b/src/protocols/core/DataDevice.cpp @@ -11,6 +11,7 @@ #include "../../xwayland/Server.hpp" #include "../../managers/input/InputManager.hpp" #include "../../managers/HookSystemManager.hpp" +#include "../../managers/cursor/CursorShapeOverrideController.hpp" #include "../../helpers/Monitor.hpp" #include "../../render/Renderer.hpp" #include "../../xwayland/Dnd.hpp" @@ -553,7 +554,7 @@ void CWLDataDeviceProtocol::initiateDrag(WP currentSource abortDrag(); } - g_pInputManager->setCursorImageUntilUnset("grabbing"); + Cursor::overrideController->setOverride("grabbing", Cursor::CURSOR_OVERRIDE_DND); m_dnd.overriddenCursor = true; LOGM(LOG, "initiateDrag: source {:x}, surface: {:x}, origin: {:x}", (uintptr_t)currentSource.get(), (uintptr_t)dragSurface, (uintptr_t)origin); @@ -734,7 +735,7 @@ void CWLDataDeviceProtocol::dropDrag() { if (m_dnd.focusedDevice->getX11()) { m_dnd.focusedDevice->sendLeave(); if (m_dnd.overriddenCursor) - g_pInputManager->unsetCursorImage(); + Cursor::overrideController->unsetOverride(Cursor::CURSOR_OVERRIDE_DND); m_dnd.overriddenCursor = false; cleanupDndState(true, true, true); return; @@ -743,7 +744,7 @@ void CWLDataDeviceProtocol::dropDrag() { m_dnd.focusedDevice->sendLeave(); if (m_dnd.overriddenCursor) - g_pInputManager->unsetCursorImage(); + Cursor::overrideController->unsetOverride(Cursor::CURSOR_OVERRIDE_DND); m_dnd.overriddenCursor = false; cleanupDndState(false, false, false); } @@ -784,7 +785,7 @@ void CWLDataDeviceProtocol::abortDrag() { cleanupDndState(false, false, false); if (m_dnd.overriddenCursor) - g_pInputManager->unsetCursorImage(); + Cursor::overrideController->unsetOverride(Cursor::CURSOR_OVERRIDE_DND); m_dnd.overriddenCursor = false; if (!m_dnd.focusedDevice && !m_dnd.currentSource)