From a8bb1dac302ab7da078a41fdd4aefcacb5a4456c Mon Sep 17 00:00:00 2001 From: Szwagi Date: Tue, 16 Dec 2025 16:51:28 +0000 Subject: [PATCH 01/11] Fix mouse motion in VRR --- src/desktop/view/Window.cpp | 15 +------------ src/managers/PointerManager.cpp | 34 +++-------------------------- src/managers/PointerManager.hpp | 9 +------- src/managers/SeatManager.hpp | 3 --- src/managers/input/InputManager.cpp | 10 ++++----- 5 files changed, 9 insertions(+), 62 deletions(-) diff --git a/src/desktop/view/Window.cpp b/src/desktop/view/Window.cpp index 2559e0c4e31..47d2228628c 100644 --- a/src/desktop/view/Window.cpp +++ b/src/desktop/view/Window.cpp @@ -2595,20 +2595,7 @@ void CWindow::commitWindow() { const auto PMONITOR = m_monitor.lock(); - if (PMONITOR) - PMONITOR->debugLastPresentation(g_pSeatManager->m_isPointerFrameCommit ? "listener_commitWindow skip" : "listener_commitWindow"); - - if (g_pSeatManager->m_isPointerFrameCommit) { - g_pSeatManager->m_isPointerFrameSkipped = false; - g_pSeatManager->m_isPointerFrameCommit = false; - } else - g_pHyprRenderer->damageSurface(wlSurface()->resource(), m_realPosition->goal().x, m_realPosition->goal().y, m_isX11 ? 1.0 / m_X11SurfaceScaledBy : 1.0); - - if (g_pSeatManager->m_isPointerFrameSkipped) { - g_pPointerManager->sendStoredMovement(); - g_pSeatManager->sendPointerFrame(); - g_pSeatManager->m_isPointerFrameCommit = true; - } + g_pHyprRenderer->damageSurface(wlSurface()->resource(), m_realPosition->goal().x, m_realPosition->goal().y, m_isX11 ? 1.0 / m_X11SurfaceScaledBy : 1.0); if (!m_isX11) { m_subsurfaceHead->recheckDamageForSubsurfaces(); diff --git a/src/managers/PointerManager.cpp b/src/managers/PointerManager.cpp index d2065d695dd..aa35df306af 100644 --- a/src/managers/PointerManager.cpp +++ b/src/managers/PointerManager.cpp @@ -925,20 +925,6 @@ void CPointerManager::attachPointer(SP pointer) { PROTO::idle->onActivity(); }); - listener->frame = pointer->m_pointerEvents.frame.listen([] { - bool shouldSkip = false; - if (!g_pSeatManager->m_mouse.expired() && g_pInputManager->isLocked()) { - auto PMONITOR = Desktop::focusState()->monitor().get(); - if (PMONITOR && PMONITOR->shouldSkipScheduleFrameOnMouseEvent()) { - auto fsWindow = PMONITOR->m_activeWorkspace->getFullscreenWindow(); - shouldSkip = fsWindow && fsWindow->m_isX11; - } - } - g_pSeatManager->m_isPointerFrameSkipped = shouldSkip; - if (!g_pSeatManager->m_isPointerFrameSkipped) - g_pSeatManager->sendPointerFrame(); - }); - listener->swipeBegin = pointer->m_pointerEvents.swipeBegin.listen([](const IPointer::SSwipeBeginEvent& event) { g_pInputManager->onSwipeBegin(event); @@ -1110,21 +1096,7 @@ Vector2D CPointerManager::cursorSizeLogical() { return m_currentCursorImage.size / m_currentCursorImage.scale; } -void CPointerManager::storeMovement(uint64_t time, const Vector2D& delta, const Vector2D& deltaUnaccel) { - m_storedTime = time; - m_storedDelta += delta; - m_storedUnaccel += deltaUnaccel; -} - -void CPointerManager::setStoredMovement(uint64_t time, const Vector2D& delta, const Vector2D& deltaUnaccel) { - m_storedTime = time; - m_storedDelta = delta; - m_storedUnaccel = deltaUnaccel; -} - -void CPointerManager::sendStoredMovement() { - PROTO::relativePointer->sendRelativeMotion(m_storedTime * 1000, m_storedDelta, m_storedUnaccel); - m_storedTime = 0; - m_storedDelta = Vector2D{}; - m_storedUnaccel = Vector2D{}; +void CPointerManager::sendMovement(uint64_t time, const Vector2D& delta, const Vector2D& deltaUnaccel) { + PROTO::relativePointer->sendRelativeMotion(time * 1000, delta, deltaUnaccel); + g_pSeatManager->sendPointerFrame(); } diff --git a/src/managers/PointerManager.hpp b/src/managers/PointerManager.hpp index e7294fd4082..2bc645107c5 100644 --- a/src/managers/PointerManager.hpp +++ b/src/managers/PointerManager.hpp @@ -60,9 +60,7 @@ class CPointerManager { // Vector2D position(); Vector2D cursorSizeLogical(); - void storeMovement(uint64_t time, const Vector2D& delta, const Vector2D& deltaUnaccel); - void setStoredMovement(uint64_t time, const Vector2D& delta, const Vector2D& deltaUnaccel); - void sendStoredMovement(); + void sendMovement(uint64_t time, const Vector2D& delta, const Vector2D& deltaUnaccel); void recheckEnteredOutputs(); @@ -95,7 +93,6 @@ class CPointerManager { CHyprSignalListener motionAbsolute; CHyprSignalListener button; CHyprSignalListener axis; - CHyprSignalListener frame; CHyprSignalListener swipeBegin; CHyprSignalListener swipeEnd; @@ -154,10 +151,6 @@ class CPointerManager { Vector2D m_pointerPos = {0, 0}; - uint64_t m_storedTime = 0; - Vector2D m_storedDelta = {0, 0}; - Vector2D m_storedUnaccel = {0, 0}; - struct SMonitorPointerState { SMonitorPointerState(const PHLMONITOR& m) : monitor(m) {} ~SMonitorPointerState() = default; diff --git a/src/managers/SeatManager.hpp b/src/managers/SeatManager.hpp index fe11f930838..21736e3ae1d 100644 --- a/src/managers/SeatManager.hpp +++ b/src/managers/SeatManager.hpp @@ -127,9 +127,6 @@ class CSeatManager { void setGrab(SP grab); // nullptr removes SP m_seatGrab; - bool m_isPointerFrameSkipped = false; - bool m_isPointerFrameCommit = false; - private: struct SSeatResourceContainer { SSeatResourceContainer(SP); diff --git a/src/managers/input/InputManager.cpp b/src/managers/input/InputManager.cpp index 7272e1cf66a..8521e2007a9 100644 --- a/src/managers/input/InputManager.cpp +++ b/src/managers/input/InputManager.cpp @@ -130,12 +130,7 @@ void CInputManager::onMouseMoved(IPointer::SMotionEvent e) { const auto DELTA = *PNOACCEL == 1 ? unaccel : delta; - if (g_pSeatManager->m_isPointerFrameSkipped) - g_pPointerManager->storeMovement(e.timeMs, DELTA, unaccel); - else - g_pPointerManager->setStoredMovement(e.timeMs, DELTA, unaccel); - - PROTO::relativePointer->sendRelativeMotion(sc(e.timeMs) * 1000, DELTA, unaccel); + g_pPointerManager->sendMovement(e.timeMs, DELTA, unaccel); if (e.mouse) recheckMouseWarpOnMouseInput(); @@ -676,6 +671,8 @@ void CInputManager::onMouseButton(IPointer::SButtonEvent e) { m_focusHeldByButtons = false; m_refocusHeldByButtons = false; } + + g_pSeatManager->sendPointerFrame(); } void CInputManager::processMouseRequest(const CSeatManager::SSetCursorEvent& event) { @@ -954,6 +951,7 @@ void CInputManager::onMouseWheel(IPointer::SAxisEvent e, SP pointer) { int32_t deltaDiscrete = std::abs(discrete) != 0 && std::abs(discrete) < 1 ? std::copysign(1, discrete) : std::round(discrete); g_pSeatManager->sendPointerAxis(e.timeMs, e.axis, delta, deltaDiscrete, value120, e.source, WL_POINTER_AXIS_RELATIVE_DIRECTION_IDENTICAL); + g_pSeatManager->sendPointerFrame(); } Vector2D CInputManager::getMouseCoordsInternal() { From 026e2eadda59ff863a5e4e1c9f9ebcd2abe8fec5 Mon Sep 17 00:00:00 2001 From: Szwagi Date: Wed, 17 Dec 2025 16:08:43 +0000 Subject: [PATCH 02/11] Fix VRR with software cursors and no_break_fs_vrr=1 --- src/managers/PointerManager.cpp | 13 +++++++++---- src/render/Renderer.cpp | 6 +++--- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/managers/PointerManager.cpp b/src/managers/PointerManager.cpp index aa35df306af..3642ffa09b9 100644 --- a/src/managers/PointerManager.cpp +++ b/src/managers/PointerManager.cpp @@ -738,16 +738,21 @@ Vector2D CPointerManager::closestValid(const Vector2D& pos) { } void CPointerManager::damageIfSoftware() { + if (g_pCompositor->m_unsafeState) + return; + auto b = getCursorBoxGlobal().expand(4); for (auto const& mw : m_monitorStates) { - if (mw->monitor.expired() || !mw->monitor->m_output) + auto monitor = mw->monitor; + if (monitor.expired() || !monitor->m_output || monitor->isMirror()) continue; if ((mw->softwareLocks > 0 || mw->hardwareFailed || g_pConfigManager->shouldUseSoftwareCursors(mw->monitor.lock())) && - b.overlaps({mw->monitor->m_position, mw->monitor->m_size})) { - g_pHyprRenderer->damageBox(b, mw->monitor->shouldSkipScheduleFrameOnMouseEvent()); - break; + b.overlaps({mw->monitor->m_position, mw->monitor->m_size}) && !mw->monitor->shouldSkipScheduleFrameOnMouseEvent()) { + + CBox damageBox = b.copy().translate(-monitor->m_position).scale(monitor->m_scale).round(); + mw->monitor->addDamage(damageBox); } } } diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 9ec59b73478..a7525275555 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -1921,15 +1921,15 @@ void CHyprRenderer::damageSurface(SP pSurface, double x, dou return; } + if (damageBox.empty()) + return; + if (scale != 1.0) damageBox.scale(scale); // schedule frame events g_pCompositor->scheduleFrameForMonitor(g_pCompositor->getMonitorFromVector(Vector2D(x, y)), Aquamarine::IOutput::AQ_SCHEDULE_DAMAGE); - if (damageBox.empty()) - return; - damageBox.translate({x, y}); CRegion damageBoxForEach; From def48dd2d329b714047ccf6d90c6203c0b9e8c64 Mon Sep 17 00:00:00 2001 From: Szwagi Date: Wed, 17 Dec 2025 19:58:42 +0000 Subject: [PATCH 03/11] Fix VRR with software cursors and no_break_fs_vrr=0 --- src/helpers/Monitor.cpp | 6 ++++-- src/render/Renderer.cpp | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp index c042fe77f67..707ee83ef91 100644 --- a/src/helpers/Monitor.cpp +++ b/src/helpers/Monitor.cpp @@ -1043,8 +1043,10 @@ bool CMonitor::shouldSkipScheduleFrameOnMouseEvent() { static auto PMINRR = CConfigValue("cursor:min_refresh_rate"); // skip scheduling extra frames for fullsreen apps with vrr - const auto FS_WINDOW = getFullscreenWindow(); - const bool shouldSkip = FS_WINDOW && (*PNOBREAK == 1 || (*PNOBREAK == 2 && FS_WINDOW->getContentType() == CONTENT_TYPE_GAME)) && m_output->state->state().adaptiveSync; + const auto FS_WINDOW = getFullscreenWindow(); + const bool shouldRenderCursor = g_pHyprRenderer->shouldRenderCursor(); + const bool noBreak = FS_WINDOW && (*PNOBREAK == 1 || (*PNOBREAK == 2 && FS_WINDOW->getContentType() == CONTENT_TYPE_GAME)); + const bool shouldSkip = (!shouldRenderCursor || noBreak) && m_output->state->state().adaptiveSync; // keep requested minimum refresh rate if (shouldSkip && *PMINRR && m_lastPresentationTimer.getMillis() > 1000.0f / *PMINRR) { diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index a7525275555..e600197b5a7 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -2049,7 +2049,7 @@ void CHyprRenderer::renderDragIcon(PHLMONITOR pMonitor, const Time::steady_tp& t } void CHyprRenderer::setCursorSurface(SP surf, int hotspotX, int hotspotY, bool force) { - m_cursorHasSurface = surf; + m_cursorHasSurface = surf && surf->resource(); m_lastCursorData.name = ""; m_lastCursorData.surf = surf; From bfcdcf0a1b2b1f2beecb851402d28ae3c0c73ef5 Mon Sep 17 00:00:00 2001 From: Szwagi Date: Fri, 19 Dec 2025 03:13:38 +0000 Subject: [PATCH 04/11] Fix relative mouse motion on Xwayland --- src/managers/PointerManager.cpp | 5 ----- src/managers/PointerManager.hpp | 1 - src/managers/input/InputManager.cpp | 5 +++-- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/managers/PointerManager.cpp b/src/managers/PointerManager.cpp index 3642ffa09b9..3ad9f1e9326 100644 --- a/src/managers/PointerManager.cpp +++ b/src/managers/PointerManager.cpp @@ -1100,8 +1100,3 @@ void CPointerManager::damageCursor(PHLMONITOR pMonitor) { Vector2D CPointerManager::cursorSizeLogical() { return m_currentCursorImage.size / m_currentCursorImage.scale; } - -void CPointerManager::sendMovement(uint64_t time, const Vector2D& delta, const Vector2D& deltaUnaccel) { - PROTO::relativePointer->sendRelativeMotion(time * 1000, delta, deltaUnaccel); - g_pSeatManager->sendPointerFrame(); -} diff --git a/src/managers/PointerManager.hpp b/src/managers/PointerManager.hpp index 2bc645107c5..e80e1eaaa8d 100644 --- a/src/managers/PointerManager.hpp +++ b/src/managers/PointerManager.hpp @@ -60,7 +60,6 @@ class CPointerManager { // Vector2D position(); Vector2D cursorSizeLogical(); - void sendMovement(uint64_t time, const Vector2D& delta, const Vector2D& deltaUnaccel); void recheckEnteredOutputs(); diff --git a/src/managers/input/InputManager.cpp b/src/managers/input/InputManager.cpp index 8521e2007a9..054677fa722 100644 --- a/src/managers/input/InputManager.cpp +++ b/src/managers/input/InputManager.cpp @@ -130,11 +130,10 @@ void CInputManager::onMouseMoved(IPointer::SMotionEvent e) { const auto DELTA = *PNOACCEL == 1 ? unaccel : delta; - g_pPointerManager->sendMovement(e.timeMs, DELTA, unaccel); - if (e.mouse) recheckMouseWarpOnMouseInput(); + PROTO::relativePointer->sendRelativeMotion(sc(e.timeMs) * 1000, delta, unaccel); g_pPointerManager->move(DELTA); mouseMoveUnified(e.timeMs, false, e.mouse); @@ -146,6 +145,8 @@ void CInputManager::onMouseMoved(IPointer::SMotionEvent e) { if (e.mouse) m_lastMousePos = getMouseCoordsInternal(); + + g_pSeatManager->sendPointerFrame(); } void CInputManager::onMouseWarp(IPointer::SMotionAbsoluteEvent e) { From 833b13d4c7ab94744f11ab86e47257bb9127829e Mon Sep 17 00:00:00 2001 From: Szwagi Date: Fri, 19 Dec 2025 03:55:58 +0000 Subject: [PATCH 05/11] Remove unnecessary frame schedule --- src/render/Renderer.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index e600197b5a7..0a41448e05f 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -1927,9 +1927,6 @@ void CHyprRenderer::damageSurface(SP pSurface, double x, dou if (scale != 1.0) damageBox.scale(scale); - // schedule frame events - g_pCompositor->scheduleFrameForMonitor(g_pCompositor->getMonitorFromVector(Vector2D(x, y)), Aquamarine::IOutput::AQ_SCHEDULE_DAMAGE); - damageBox.translate({x, y}); CRegion damageBoxForEach; From 4ccd85b2a9e1b9342ec72191a603e66f968d1f8d Mon Sep 17 00:00:00 2001 From: Szwagi Date: Fri, 19 Dec 2025 10:26:33 +0000 Subject: [PATCH 06/11] Fix locks and clean up CPointerManager::damageIfSoftware --- src/managers/PointerManager.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/managers/PointerManager.cpp b/src/managers/PointerManager.cpp index 3ad9f1e9326..65f16555a0c 100644 --- a/src/managers/PointerManager.cpp +++ b/src/managers/PointerManager.cpp @@ -744,16 +744,20 @@ void CPointerManager::damageIfSoftware() { auto b = getCursorBoxGlobal().expand(4); for (auto const& mw : m_monitorStates) { - auto monitor = mw->monitor; - if (monitor.expired() || !monitor->m_output || monitor->isMirror()) + auto monitor = mw->monitor.lock(); + if (!monitor || !monitor->m_output || monitor->isMirror()) continue; - if ((mw->softwareLocks > 0 || mw->hardwareFailed || g_pConfigManager->shouldUseSoftwareCursors(mw->monitor.lock())) && - b.overlaps({mw->monitor->m_position, mw->monitor->m_size}) && !mw->monitor->shouldSkipScheduleFrameOnMouseEvent()) { + auto usesSoftwareCursor = (mw->softwareLocks > 0 || mw->hardwareFailed || g_pConfigManager->shouldUseSoftwareCursors(monitor)); + if (!usesSoftwareCursor) + continue; - CBox damageBox = b.copy().translate(-monitor->m_position).scale(monitor->m_scale).round(); - mw->monitor->addDamage(damageBox); - } + auto shouldAddDamage = !monitor->shouldSkipScheduleFrameOnMouseEvent() && b.overlaps({monitor->m_position, monitor->m_size}); + if (!shouldAddDamage) + continue; + + CBox damageBox = b.copy().translate(-monitor->m_position).scale(monitor->m_scale).round(); + monitor->addDamage(damageBox); } } From 2c81cc711140ca27ed94c61f98e4beca8327ab4e Mon Sep 17 00:00:00 2001 From: Szwagi Date: Sun, 21 Dec 2025 14:22:47 +0000 Subject: [PATCH 07/11] Fix some windows not scheduling frames --- src/render/Renderer.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 0a41448e05f..1a04d5aa252 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -1921,13 +1921,22 @@ void CHyprRenderer::damageSurface(SP pSurface, double x, dou return; } - if (damageBox.empty()) - return; + if (damageBox.empty()) { + const auto VIEW = WLSURF->view(); + if (VIEW->type() == Desktop::View::eViewType::VIEW_TYPE_WINDOW) + return; + + const auto BOX = VIEW->logicalBox(); + if (!BOX || BOX->empty()) + return; - if (scale != 1.0) - damageBox.scale(scale); + damageBox = *BOX; + } else { + if (scale != 1.0) + damageBox.scale(scale); - damageBox.translate({x, y}); + damageBox.translate({x, y}); + } CRegion damageBoxForEach; From fae1a7532523bbcfccbf9715e10dfe8c0189e7c2 Mon Sep 17 00:00:00 2001 From: Szwagi Date: Mon, 22 Dec 2025 12:03:27 +0000 Subject: [PATCH 08/11] Don't schedule frames for animating fullscreen windows --- src/managers/animation/AnimationManager.cpp | 2 +- src/render/decorations/CHyprBorderDecoration.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/managers/animation/AnimationManager.cpp b/src/managers/animation/AnimationManager.cpp index 05ce693907d..67b068e2745 100644 --- a/src/managers/animation/AnimationManager.cpp +++ b/src/managers/animation/AnimationManager.cpp @@ -198,7 +198,7 @@ static void handleUpdate(CAnimatedVariable& av, bool warp) { } // manually schedule a frame - if (PMONITOR) + if (PMONITOR && !PMONITOR->inFullscreenMode()) g_pCompositor->scheduleFrameForMonitor(PMONITOR, Aquamarine::IOutput::AQ_SCHEDULE_ANIMATION); } diff --git a/src/render/decorations/CHyprBorderDecoration.cpp b/src/render/decorations/CHyprBorderDecoration.cpp index a082f0738fe..eba4d76e0cd 100644 --- a/src/render/decorations/CHyprBorderDecoration.cpp +++ b/src/render/decorations/CHyprBorderDecoration.cpp @@ -115,7 +115,7 @@ void CHyprBorderDecoration::updateWindow(PHLWINDOW) { } void CHyprBorderDecoration::damageEntire() { - if (!validMapped(m_window)) + if (!validMapped(m_window) || m_window->isFullscreen()) return; auto surfaceBox = m_window->getWindowMainSurfaceBox(); From d651a25575d55fa2459d7ed00a3a88bb288aa990 Mon Sep 17 00:00:00 2001 From: Szwagi Date: Mon, 22 Dec 2025 13:14:13 +0000 Subject: [PATCH 09/11] Respect cursor:no_break_vrr on cursor visibility change --- src/managers/PointerManager.cpp | 4 ++-- src/managers/PointerManager.hpp | 2 +- src/render/Renderer.cpp | 27 ++++++++------------------- 3 files changed, 11 insertions(+), 22 deletions(-) diff --git a/src/managers/PointerManager.cpp b/src/managers/PointerManager.cpp index 65f16555a0c..c97c5daf4af 100644 --- a/src/managers/PointerManager.cpp +++ b/src/managers/PointerManager.cpp @@ -1085,7 +1085,7 @@ void CPointerManager::detachTablet(SP tablet) { std::erase_if(m_tabletListeners, [tablet](const auto& e) { return e->tablet.expired() || e->tablet == tablet; }); } -void CPointerManager::damageCursor(PHLMONITOR pMonitor) { +void CPointerManager::damageCursor(PHLMONITOR pMonitor, bool skipFrameSchedule) { for (auto const& mw : m_monitorStates) { if (mw->monitor != pMonitor) continue; @@ -1095,7 +1095,7 @@ void CPointerManager::damageCursor(PHLMONITOR pMonitor) { if (b.empty()) return; - g_pHyprRenderer->damageBox(b); + g_pHyprRenderer->damageBox(b, skipFrameSchedule); return; } diff --git a/src/managers/PointerManager.hpp b/src/managers/PointerManager.hpp index e80e1eaaa8d..d60903a625c 100644 --- a/src/managers/PointerManager.hpp +++ b/src/managers/PointerManager.hpp @@ -55,7 +55,7 @@ class CPointerManager { // this is needed e.g. during screensharing where // the software cursors aren't locked during the cursor move, but they // are rendered later. - void damageCursor(PHLMONITOR pMonitor); + void damageCursor(PHLMONITOR pMonitor, bool skipFrameSchedule = false); // Vector2D position(); diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 1a04d5aa252..60409b43d37 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -2146,30 +2146,19 @@ void CHyprRenderer::ensureCursorRenderingMode() { if (HIDE == m_cursorHidden) return; - if (HIDE) { + if (HIDE) Log::logger->log(Log::DEBUG, "Hiding the cursor (hl-mandated)"); - - for (auto const& m : g_pCompositor->m_monitors) { - if (!g_pPointerManager->softwareLockedFor(m)) - continue; - - damageMonitor(m); // TODO: maybe just damage the cursor area? - } - - setCursorHidden(true); - - } else { + else Log::logger->log(Log::DEBUG, "Showing the cursor (hl-mandated)"); - for (auto const& m : g_pCompositor->m_monitors) { - if (!g_pPointerManager->softwareLockedFor(m)) - continue; - - damageMonitor(m); // TODO: maybe just damage the cursor area? - } + for (auto const& m : g_pCompositor->m_monitors) { + if (!g_pPointerManager->softwareLockedFor(m)) + continue; - setCursorHidden(false); + g_pPointerManager->damageCursor(m, m->shouldSkipScheduleFrameOnMouseEvent()); } + + setCursorHidden(HIDE); } void CHyprRenderer::setCursorHidden(bool hide) { From a448a5f50f7b0cd109c6c8ff804011e2885e8926 Mon Sep 17 00:00:00 2001 From: Szwagi Date: Sat, 27 Dec 2025 15:00:54 +0000 Subject: [PATCH 10/11] Bring back old frame event scheduling hack --- src/render/Renderer.cpp | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 60409b43d37..a51acc764b8 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -1914,29 +1914,34 @@ void CHyprRenderer::damageSurface(SP pSurface, double x, dou if (g_pCompositor->m_unsafeState) return; - const auto WLSURF = Desktop::View::CWLSurface::fromResource(pSurface); - CRegion damageBox = WLSURF ? WLSURF->computeDamage() : CRegion{}; + const auto WLSURF = Desktop::View::CWLSurface::fromResource(pSurface); if (!WLSURF) { Log::logger->log(Log::ERR, "BUG THIS: No CWLSurface for surface in damageSurface!!!"); return; } - if (damageBox.empty()) { - const auto VIEW = WLSURF->view(); - if (VIEW->type() == Desktop::View::eViewType::VIEW_TYPE_WINDOW) - return; + // hack: schedule frame events + if (!WLSURF->resource()->m_current.callbacks.empty() && pSurface->m_hlSurface) { + const auto BOX = pSurface->m_hlSurface->getSurfaceBoxGlobal(); + if (BOX && !BOX->empty()) { + for (auto const& m : g_pCompositor->m_monitors) { + if (!m->m_output) + continue; - const auto BOX = VIEW->logicalBox(); - if (!BOX || BOX->empty()) - return; + if (BOX->overlaps(m->logicalBox())) + g_pCompositor->scheduleFrameForMonitor(m, Aquamarine::IOutput::AQ_SCHEDULE_NEEDS_FRAME); + } + } + } - damageBox = *BOX; - } else { - if (scale != 1.0) - damageBox.scale(scale); + CRegion damageBox = WLSURF->computeDamage(); + if (damageBox.empty()) + return; - damageBox.translate({x, y}); - } + if (scale != 1.0) + damageBox.scale(scale); + + damageBox.translate({x, y}); CRegion damageBoxForEach; From d31f3966b68d6fc92ea41be6b9efb47242e57a87 Mon Sep 17 00:00:00 2001 From: Szwagi Date: Thu, 15 Jan 2026 08:48:09 +0000 Subject: [PATCH 11/11] Check for VRR on any monitor when NVIDIA and cursor:no_hardware_cursors=2 --- src/Compositor.cpp | 4 ++++ src/Compositor.hpp | 1 + src/config/ConfigManager.cpp | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Compositor.cpp b/src/Compositor.cpp index 3299113c805..0864d240dfe 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -3099,3 +3099,7 @@ std::optional CCompositor::getVTNr() { return ttynum; } + +bool CCompositor::isVRRActiveOnAnyMonitor() const { + return std::ranges::any_of(m_monitors, [](const PHLMONITOR& m) { return m->m_vrrActive; }); +} diff --git a/src/Compositor.hpp b/src/Compositor.hpp index abcf7ec6874..afcda222caa 100644 --- a/src/Compositor.hpp +++ b/src/Compositor.hpp @@ -161,6 +161,7 @@ class CCompositor { void onNewMonitor(SP output); void ensurePersistentWorkspacesPresent(const std::vector& rules, PHLWORKSPACE pWorkspace = nullptr); std::optional getVTNr(); + bool isVRRActiveOnAnyMonitor() const; NColorManagement::PImageDescription getPreferredImageDescription(); NColorManagement::PImageDescription getHDRImageDescription(); diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index d5ffe8f2f8f..0208c71e4f0 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -3017,7 +3017,7 @@ bool CConfigManager::shouldUseSoftwareCursors(PHLMONITOR pMonitor) { switch (*PNOHW) { case 0: return false; case 1: return true; - case 2: return g_pHyprRenderer->isNvidia() && g_pHyprRenderer->isMgpu(); + case 2: return g_pHyprRenderer->isNvidia() && (g_pHyprRenderer->isMgpu() || g_pCompositor->isVRRActiveOnAnyMonitor()); default: break; }