Skip to content

Fix mouse motion in VRR#12665

Merged
vaxerski merged 11 commits into
hyprwm:mainfrom
Szwagi:fix-vrr-mouse-motion
Jan 31, 2026
Merged

Fix mouse motion in VRR#12665
vaxerski merged 11 commits into
hyprwm:mainfrom
Szwagi:fix-vrr-mouse-motion

Conversation

@Szwagi
Copy link
Copy Markdown
Contributor

@Szwagi Szwagi commented Dec 16, 2025

I am unfamiliar with the codebase, so this PR most definitely has plenty of issues I haven't found.
If the actual solution is more complicated than this, I'm unlikely to be of any help, but would love to see this fixed and working as well as this is for me.

Fixes #12032 and this sounds related too #12013.

I will take CS2 on a 360Hz display as an example. When I say FPS measurements I mean what the monitor reports.

Before (the issues):

  • no_break_fs_vrr 0: The sensitivity is consistent. Moving the mouse even while the cursor is locked spikes the FPS to 357 (sends duplicate frames, making VRR useless).
  • no_break_fs_vrr 1: The sensitivity of the mouse changes depending on the FPS of the game.
  • The game feels very unsmooth, even when it runs above 300FPS, likely to do with weird delayed mouse motion.

After:

  • no_break_fs_vrr 0: The sensitivity is consistent. Moving the mouse spikes the FPS only when the cursor is unlocked. During gameplay (locked cursor), the FPS stays at the FPS cap I set.
  • no_break_fs_vrr 1: The sensitivity is consistent.
  • The game feels very smooth, basically the same as other compositors.

My idea of what is happening is that

  1. Hyprland is resetting the stored mouse movement before it gets sent to the game.
  2. I do not understand why Hyprland even has to accumulate the movement instead of sending it straight to the game. I guess it could help performance on 8000Hz mice, but in practice it seems like it just adds a bunch of input lag and possibly holds on to it so long it misses frames (hence the unsmoothness).

I have tested in CS2 with native Wayland and XWayland. I have also tested in a Wine game running through Gamescope.

@github-actions
Copy link
Copy Markdown

Hello and thank you for making a PR to Hyprland!

Please check the PR Guidelines and make sure your PR follows them.
It will make the entire review process faster. :)

If your code can be tested, please always add tests. See more here.

beep boop, I'm just a bot. A real human will review your PR soon.

@UjinT34
Copy link
Copy Markdown
Contributor

UjinT34 commented Dec 16, 2025

If this works for all locked/unlocked hw/sw wayland/xwayland combinations with different games then I'm happy that all the pointer skipping and movement accumulation gets nuked. The accumulation was needed because sending the movement with locked cursor to an xwayland client straight away caused and immediate frame event in return which was treated as a valid new frame from the game and broke VRR.
Looks like this also nukes m_pointerEvents.frame.emit() and its handling. Maybe the original issue was cause by using this event to call g_pSeatManager->sendPointerFrame(); instead of calling it right after PROTO::relativePointer->sendRelativeMotion. No idea why the event was used, it was this way before the hack.

@Szwagi Szwagi force-pushed the fix-vrr-mouse-motion branch from aa771e7 to 466690d Compare December 16, 2025 19:18
@Szwagi
Copy link
Copy Markdown
Contributor Author

Szwagi commented Dec 16, 2025

I just checked and this definitely does not work correctly with software cursors.
In CS2, the sensitivity is correct, but VRR is broken in a weird way...
It sends new frames on cursor move when the cursor is locked and does not send frames when the cursor is unlocked.
Could this be as simple as an inverted if statement? I looked for it and found a bunch of weird cases like >0 on a bool, but nothing that fixed it.

@UjinT34
Copy link
Copy Markdown
Contributor

UjinT34 commented Dec 17, 2025

WAYLAND_DEBUG=1 and some HL logging might help. The hack exists because for whatever reason there is an extra unwanted wl surface frame event right after a pointer frame with locked cursor. Nothing says that it should be any different from unlocked cursor or that there should/shouldn't be such surface frame event. The code just accumulates mouse movement to send the result and a pointer frame right after the wanted surface frame and discard the next one surface frame. Without this hack there is no way to distinguish between wanted and unwanted surface frames. The proper fix should somehow avoid getting those extra surface frames but it's probably outside of HL's control.

@Szwagi
Copy link
Copy Markdown
Contributor Author

Szwagi commented Dec 17, 2025

Two reasons for mouse motion scheduling frames when it shouldn't:

  1. Scheduling new frames even though the damage box was empty. This scheduled frames for all mouse motion.
  2. Using monitor->shouldSkipScheduleFrameOnMouseEvent() with g_pHyprRenderer->damageBox(...)... When the cursor is on the edge of the screen, box.overlaps(...) passes, secondary monitor does not want to skip the schedule, then g_pHyprRenderer->damageBox will schedule a frame for the primary monitor.

no_break_fs_vrr=0 with software cursors is still broken, and I'm unsure if there's any hope for it.
In CS2, the cursor is not visible, but the cursor box is still 30x30, I'll investigate if I can check if any cursor is visible at all.

Admittedly that code is in need of some cleanup now, and I'm unsure if g_pCompositor->m_unsafeState is needed there (I think the 'if' checks shouldn't pass?).

Also, maybe the better way is to do this in g_pHyprRenderer->damageBox?

- if (!skipFrameSchedule)
+ if (!skipFrameSchedule && (!isMouseEvent || !monitor->shouldSkipScheduleFrameOnMouseEvent()))

@Szwagi Szwagi force-pushed the fix-vrr-mouse-motion branch from eeda66d to c73e8c1 Compare December 17, 2025 16:55
@Szwagi
Copy link
Copy Markdown
Contributor Author

Szwagi commented Dec 17, 2025

After more testing, the current status is (for things I tested):

  • native, no_hardware_cursors = 0, no_break_fs_vrr = 0 - Works
  • native, no_hardware_cursors = 0, no_break_fs_vrr = 1 - Unlocked cursors schedule extra frames
  • native, no_hardware_cursors = 1, no_break_fs_vrr = 0 - Works
  • native, no_hardware_cursors = 1, no_break_fs_vrr = 1 - Works
  • xwayland, no_hardware_cursors = 0, no_break_fs_vrr = 0 - Works
  • xwayland, no_hardware_cursors = 0, no_break_fs_vrr = 1 - Unlocked cursors schedule extra frames
  • xwayland, no_hardware_cursors = 1, no_break_fs_vrr = 0 - Works when it works, for 50% of game launches the locked cursors get warped back (?)
  • xwayland, no_hardware_cursors = 1, no_break_fs_vrr = 1 - Works when it works, for 50% of game launches the locked cursors get warped back (?)

@Nosamdaman
Copy link
Copy Markdown
Contributor

First of all, thank you @Szwagi for digging deep into this, this is quite literally the only major show-stopper preventing me from gaming on Hyprland. Secondly, I'm not quite able to replicate your above behavior matrix. For me, no matter what, on the latest version of your branch, I'm seeing stuttery camera movement on CS2 with a frame limiter set, though perhaps there's something I'm missing. That being said, It looks like Half-Life 2 works for me with no_break_fs_vrr enabled and an FPS limit below my refresh rate regardless of whether or not hardware cursors are enabled.

This seems like a real nightmare of an issue for you guys lol, if you want me to validate any further findings on my end let me know.

@njdom24
Copy link
Copy Markdown
Contributor

njdom24 commented Dec 18, 2025

Dunno if there's any relation, but last I checked, software cursors disable direct scanout regardless of if they're visible or not. Could direct scanout be related to the difference here?

@Nosamdaman
Copy link
Copy Markdown
Contributor

Dunno if there's any relation, but last I checked, software cursors disable direct scanout regardless of if they're visible or not. Could direct scanout be related to the difference here?

So I just re-tested this. With both direct scanout and hardware cursors enabled, playing Wayland native CS2 with an FPS limit of 140 on 144 VRR display, I'm still getting the occasional spike to 144 when moving the mouse, causing visible hitching.

@Szwagi
Copy link
Copy Markdown
Contributor Author

Szwagi commented Dec 19, 2025

So I just re-tested this. With both direct scanout and hardware cursors enabled, playing Wayland native CS2 with an FPS limit of 140 on 144 VRR display, I'm still getting the occasional spike to 144 when moving the mouse, causing visible hitching.

Can't reproduce on NVIDIA, could you share your config?
For me the only time hardware cursors do this is when the cursor is unlocked (which is fine with no_break_fs_vrr = 1 but broken for no_break_fs_vrr = 0).

Worth noting that CS2's fps_max is unreliable, engine_low_latency_sleep_after_client_tick was meant to fix it but I don't think it works. Try mangohud's fps limiter.

Edit: I'm able to reproduce it in MPV when playing back 24FPS footage... it spikes to 28FPS. I'll look into it
Never mind, it was min_refresh_rate... The default is 24FPS and the way it works isn't great so it spikes above that.

@Nosamdaman
Copy link
Copy Markdown
Contributor

Worth noting that CS2's fps_max is unreliable, engine_low_latency_sleep_after_client_tick was meant to fix it but I don't think it works. Try mangohud's fps limiter.

Ah, I was unaware of that, switching to ManguHud's limiter did fix the issue on wayland. I think I'm still seeing the issue with CS2 in xwayland mode, though it is far less frequent. That being said, it feels amazing in Wayland.

@Szwagi
Copy link
Copy Markdown
Contributor Author

Szwagi commented Dec 21, 2025

I am unsure if e4013b8 is entirely correct, but it should function the same or better than the frame schedule removed in ee471d1, and without introducing unnecessary frame schedules on mouse motion. From my testing it's only necessary for Firefox which seems to be broken (based on the amount of Firefox hacks)? It also fixes an issue with frames not being scheduled when the surface x/y is not on any monitor.

I didn't look hard for the remaining hardware cursor issue yet, but I suspect it might be a bug in Aquamarine.

Status to my eye:

  • native, no_hardware_cursors = 0, no_break_fs_vrr = 0 - Works
  • native, no_hardware_cursors = 0, no_break_fs_vrr = 1 - Unlocked cursors schedule extra frames
  • native, no_hardware_cursors = 1, no_break_fs_vrr = 0 - Works
  • native, no_hardware_cursors = 1, no_break_fs_vrr = 1 - Works
  • xwayland, no_hardware_cursors = 0, no_break_fs_vrr = 0 - Works
  • xwayland, no_hardware_cursors = 0, no_break_fs_vrr = 1 - Unlocked cursors schedule extra frames
  • xwayland, no_hardware_cursors = 1, no_break_fs_vrr = 0 - Works
  • xwayland, no_hardware_cursors = 1, no_break_fs_vrr = 1 - Works

@Nosamdaman
Copy link
Copy Markdown
Contributor

Nosamdaman commented Dec 21, 2025

I just pulled and did a test of my own with the 8 combinations when no_break_vs_vrr = 1 and they all seem to be working perfectly to my eyes, as in the games I've tested, the cursor is only unlocked in menus, thus making the extra frame-scheduling not noticeable.

This even fixed the mild VRR flicker I was getting. Good job again on this one, you have no idea how much sanity I was losing testing things before I saw this PR.

@Szwagi
Copy link
Copy Markdown
Contributor Author

Szwagi commented Dec 22, 2025

e60c987 fixes framerate spiking when changing window focus caused by animations scheduling many thousands of frames per second for animations that aren't visible.

I did more research and I don't believe Hyprland is responsible for breaking VRR with hardware cursors and no_break_fs_vrr=1. With an unlocked cursor, Hyprland schedules exactly 125FPS on a 125FPS lock, but my monitor reports 250FPS or an unstable 330FPS coming in depending on the game.

It's possible there's still an extra frame scheduled on cursor visibility change. My reports spikes by 1-2FPS, but I couldn't see any extra frame schedules except for the unrelated 37ae610, perhaps monitor getting confused by a lag spike in the game when opening the menu.

@vaxerski I think this is all for the issues I found. I will now daily this branch to see if something comes up, but would like to know what you think of this and what the glaring issues are :D

Copy link
Copy Markdown
Member

@vaxerski vaxerski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in general looks ok just needs some testing yea

Comment thread src/render/Renderer.cpp Outdated
@gulafaran
Copy link
Copy Markdown
Contributor

i dont think aa533b8 is right, wont this damage the entire logicalbox on every single commit now and basicly ignore if any damage was actually sent?

@gulafaran
Copy link
Copy Markdown
Contributor

i dont think aa533b8 is right, wont this damage the entire logicalbox on every single commit now and basicly ignore if any damage was actually sent?

from the bits ive digged i think there is a frame callback issue in general in HL, ive tried doing something about it but im just not getting it 100% correct and the rabbits nest goes deep see #11454 , some clients like firefox seems to rely on it being sent on monitor pageflip pretty much. both for their vsync handling and for recieving hints on when to send the next buffer. currently its done on commit time and presentation. and eventually hits the renderer here

sendFrameEventsToWorkspace(pMonitor, pMonitor->m_activeWorkspace, NOW);
if no damage because of that scheduled monitor frame you want to remove.

or it hits

because of the scheduled frame.

@Szwagi
Copy link
Copy Markdown
Contributor Author

Szwagi commented Dec 27, 2025

from the bits ive digged i think there is a frame callback issue in general in HL, ive tried doing something about it but im just not getting it 100% correct and the rabbits nest goes deep see #11454 , some clients like firefox seems to rely on it being sent on monitor pageflip pretty much. both for their vsync handling and for recieving hints on when to send the next buffer. currently its done on commit time and presentation. and eventually hits the renderer here

sendFrameEventsToWorkspace(pMonitor, pMonitor->m_activeWorkspace, NOW);

if no damage because of that scheduled monitor frame you want to remove.

or it hits


because of the scheduled frame.

Since this is a bigger issue that others are looking into, and the frame schedule hack was already there before my changes, would it be acceptable to keep the hack (adjusted for what this PR is looking to solve) and resolve that at a different time? @vaxerski

i dont think aa533b8 is right, wont this damage the entire logicalbox on every single commit now and basicly ignore if any damage was actually sent?

Right... I didn't quite think that through. My intention was to only schedule a frame, not add damage. Does something like this make more sense? I think this is pretty much what it was 3 years ago (beef23c), just without some issues related to getMonitorFromVector.

// schedule frame events (hack)
if (!WLSURF->resource()->m_current.callbacks.empty()) {
    const auto VIEW = WLSURF->view();
    const auto BOX  = VIEW->logicalBox();
    if (BOX.has_value() && !BOX->empty()) {
        for (auto const& m : g_pCompositor->m_monitors) {
            if (!m->m_output)
                continue;

            if (BOX->overlaps(m->logicalBox()))
                g_pCompositor->scheduleFrameForMonitor(m, Aquamarine::IOutput::AQ_SCHEDULE_NEEDS_FRAME);
        }
    }
}

CRegion damageBox = WLSURF->computeDamage();
if (damageBox.empty())
    return;

@gulafaran
Copy link
Copy Markdown
Contributor

from the bits ive digged i think there is a frame callback issue in general in HL, ive tried doing something about it but im just not getting it 100% correct and the rabbits nest goes deep see #11454 , some clients like firefox seems to rely on it being sent on monitor pageflip pretty much. both for their vsync handling and for recieving hints on when to send the next buffer. currently its done on commit time and presentation. and eventually hits the renderer here

sendFrameEventsToWorkspace(pMonitor, pMonitor->m_activeWorkspace, NOW);

if no damage because of that scheduled monitor frame you want to remove.
or it hits

because of the scheduled frame.

Since this is a bigger issue that others are looking into, and the frame schedule hack was already there before my changes, would it be acceptable to keep the hack (adjusted for what this PR is looking to solve) and resolve that at a different time? @vaxerski

i dont think aa533b8 is right, wont this damage the entire logicalbox on every single commit now and basicly ignore if any damage was actually sent?

Right... I didn't quite think that through. My intention was to only schedule a frame, not add damage. Does something like this make more sense? I think this is pretty much what it was 3 years ago (beef23c), just without some issues related to getMonitorFromVector.

// schedule frame events (hack)
if (!WLSURF->resource()->m_current.callbacks.empty()) {
    const auto VIEW = WLSURF->view();
    const auto BOX  = VIEW->logicalBox();
    if (BOX.has_value() && !BOX->empty()) {
        for (auto const& m : g_pCompositor->m_monitors) {
            if (!m->m_output)
                continue;

            if (BOX->overlaps(m->logicalBox()))
                g_pCompositor->scheduleFrameForMonitor(m, Aquamarine::IOutput::AQ_SCHEDULE_NEEDS_FRAME);
        }
    }
}

CRegion damageBox = WLSURF->computeDamage();
if (damageBox.empty())
    return;

something like that might work, as long as it doesnt show other regressions popping up because it was relying on that scheduled frame unintentionally

@Arisa-Snowbell
Copy link
Copy Markdown
Contributor

Arisa-Snowbell commented Dec 27, 2025

I rebased it on main and it crashes on startup, did I do something wrong?

#0  0x000055ffbc6bbfe7 n/a (/usr/bin/Hyprland (deleted) + 0x758fe7)
#1  0x00007fc369bc5150 n/a (libEGL_mesa.so.0 + 0x13150)
#2  0x00007fc369bc54bd n/a (libEGL_mesa.so.0 + 0x134bd)
#3  0x00007fc369bbce66 n/a (libEGL_mesa.so.0 + 0xae66)
#4  0x00007fc371e57376 _ZN10Aquamarine12CDRMRendererD2Ev (libaquamarine.so.9 + 0xcb376)
#5  0x00007fc371e6a16b _ZN9Hyprutils6Memory14CSharedPointerIN10Aquamarine12CDRMRendererEE7_deleteEPv (libaquamarine.so.9 + 0xde16b)
#6  0x00007fc371e331b6 _ZN10Aquamarine11CDRMBackendD1Ev (libaquamarine.so.9 + 0xa71b6)
#7  0x00007fc371e4d64e _ZN9Hyprutils6Memory14CSharedPointerIN10Aquamarine11CDRMBackendEE7_deleteEPv (libaquamarine.so.9 + 0xc164e)
#8  0x00007fc371e06862 _ZN10Aquamarine8CBackendD1Ev (libaquamarine.so.9 + 0x7a862)
#9  0x00007fc371e071bb _ZN9Hyprutils6Memory14CSharedPointerIN10Aquamarine8CBackendEE7_deleteEPv (libaquamarine.so.9 + 0x7b1bb)
#10 0x00007fc371e22575 n/a (libaquamarine.so.9 + 0x96575)
#11 0x00007fc370e406bc __cxa_finalize (libc.so.6 + 0x406bc)
#12 0x00007fc371dea248 n/a (libaquamarine.so.9 + 0x5e248)
#13 0x00007fc371f74012 n/a (ld-linux-x86-64.so.2 + 0x2012)
#14 0x00007fc371f7816e n/a (ld-linux-x86-64.so.2 + 0x616e)
#15 0x00007fc370e40c71 n/a (libc.so.6 + 0x40c71)
#16 0x00007fc370e40d4e exit (libc.so.6 + 0x40d4e)
#17 0x00007fc370e2763c n/a (libc.so.6 + 0x2763c)
#18 0x00007fc370e276e9 __libc_start_main (libc.so.6 + 0x276e9)
#19 0x000055ffbc1ba6e5 n/a (/usr/bin/Hyprland (deleted) + 0x2576e5)

Edit: Wrong stack trace

@Szwagi Szwagi force-pushed the fix-vrr-mouse-motion branch 3 times, most recently from 2826e26 to 4131c67 Compare December 28, 2025 21:50
@Szwagi Szwagi force-pushed the fix-vrr-mouse-motion branch from 567cd8f to 9ccb4ae Compare January 15, 2026 08:48
@Szwagi
Copy link
Copy Markdown
Contributor Author

Szwagi commented Jan 15, 2026

Rebased it on current main.

I initially wanted to implement 9ccb4ae by checking if any other monitor has VRR active, so it'd still use the hardware cursor in a lot of common cases. That ended up breaking VRR when the cursor was on the boundary between two monitors. I'll look into why that happens.

I am on 5080. Maybe it's a hw/gsp firmware thing? While I was on 3080 with some older drivers enabling/disabling gsp didn't change anything.

Do you have cursor:use_cpu_buffer disabled or anything else that could make Hyprland switch back to software cursors automatically? I use hyprpicker -r -z to see what Hyprland is actually using (software cursors get captured by the screenshot).

@Szwagi
Copy link
Copy Markdown
Contributor Author

Szwagi commented Jan 26, 2026

@vaxerski Opinion?

Have been using this branch daily for desktop use and gaming (with and without VRR). I didn't encounter any more issues on NVIDIA.

Things to note:

  • Frame scheduling hack remains in the code, but was slightly rewritten to not break VRR. I think renderer: store used buffers per monitor #11454 is a better place to remove the hack.
  • Hardware cursors are disabled on NVIDIA whenever VRR is active on any monitor (cursor:no_hardware_cursors = 2). Changing cursor plane props on any monitor breaks VRR. I see the same thing happening on COSMIC. Comments in here suggest this is not an issue on AMD. A better solution could be some more sophisticated hack in Aquamarine, I tried some things but nothing really worked.

Not perfect, but significantly more usable for gaming than before. Potentially ready to merge?

Copy link
Copy Markdown
Member

@vaxerski vaxerski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure fuck it just format it

@Szwagi Szwagi force-pushed the fix-vrr-mouse-motion branch from 9ccb4ae to d31f396 Compare January 27, 2026 21:57
@vaxerski vaxerski merged commit cbeb698 into hyprwm:main Jan 31, 2026
10 checks passed
@vaxerski
Copy link
Copy Markdown
Member

vaxerski commented Feb 5, 2026

cbeb6984e748029ff481d5581d7e4f5279fd3d1a is the first bad commit
commit cbeb6984e748029ff481d5581d7e4f5279fd3d1a
Author: Szwagi <12988954+Szwagi@users.noreply.github.com>
Date:   Sat Jan 31 13:37:01 2026 +0000

    renderer: fix mouse motion in VRR (#12665)

maximized windows' border animation causes repaint artifacts

@njdom24
Copy link
Copy Markdown
Contributor

njdom24 commented Feb 9, 2026

Seems I was wrong about my report on multi-monitor working fine on AMD.
Playing Final Fantasy XIV, moving the camera locks the cursor, and that keeps my refresh rate maxed.

It works fine if I disable my secondary monitor. Software cursors don't fix it.

EDIT: This is using native Wayland. Gamescope cursor capture doesn't have this issue, nor does native Wayland under Sway.

@Arisa-Snowbell
Copy link
Copy Markdown
Contributor

When I get out of bed I’m gonna test it on multi-monitor AMD setup.

@Arisa-Snowbell
Copy link
Copy Markdown
Contributor

Arisa-Snowbell commented Feb 9, 2026

I have two monitors, one 165hz, main one the game is on 240hz.
Used direct scanout.
Used hardware cursor cause software one will cause direct scanout to be blocked when on screen.
Doesn't matter if no_break_fs_vrr is 0 or 1, when cursor unlocked and moved it sends the refresh rate to 240, when locked VRR, or cursor not moving then it is on point. Seems correct.
Both XWayland and Wayland.
Mouse movement seems consistent across different settings.

Edit: I don't know accurate my monitor OSD is and such, so take with bit of salt, seems finicky.

@njdom24
Copy link
Copy Markdown
Contributor

njdom24 commented Feb 10, 2026

Hm, it does look direct scanout related. At least with cm=hdr, I'm seeing this issue with Proton-wayland when direct scanout is disabled/blocked. Cursors are otherwise fine. Not having this problem in Gamescope, though.

Sorry, false alarm. I might be seeing a form of #12423

@bea4dev bea4dev mentioned this pull request Feb 10, 2026
@UjinT34
Copy link
Copy Markdown
Contributor

UjinT34 commented Feb 10, 2026

DS and tearing shouldn't affect this directly. They just have different sw/hw cursors requirements.
With sw cursors no_break_fs_vrr=1 shouldn't break VRR. If it does then it's a HL issue that can be solved (ugly hacks might be required). Invisible cursors should work the same.
With visible hw it might break no matter what we do in HL/AQ because some driver&videocard combos have a bug with atomic cursor commit and vrr.

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());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this check seems wrong to me.

if you have vrr=1 at all, then this will just force sw cursor all the time, it should only do it if vrr is active + fullscreen afaik

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this check seems wrong to me.

if you have vrr=1 at all, then this will just force sw cursor all the time, it should only do it if vrr is active + fullscreen afaik

On my setup moving a hardware cursor on the second monitor makes the VRR monitor go black (framerate spike switches mode to non-vrr for a second). That is not usable.

The condition could definitely be better (especially in case of 1 monitor).
I have tried switching to software cursor for the secondary monitor, but it's very pretty wonky on the edges of the monitors.

Ultimately I want to look into making hardware cursors work correctly on NVIDIA.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry to hear that your monitor modesets when going in and out of VRR, but that is definitely not the norm. This PR was also specific to fixing the variable no_break_fs_vrr which is specific to FS.

Anyway I've just set no hw cursor to 0 for now

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry to hear that your monitor modesets when going in and out of VRR, but that is definitely not the norm. This PR was also specific to fixing the variable no_break_fs_vrr which is specific to FS.

Anyway I've just set no hw cursor to 0 for now

It was a thing on 2/2 ASUS monitors I owned, so I think it's pretty common. Not a very reputable brand so I'd believe if you said it's not a thing on others.

The PR ended up being about removing most instances of VRR breaking (not just the ones caused by removal of mouse input hack), that's why the hardware cursor check was added too... hopefully temporarily.

Copy link
Copy Markdown
Contributor

@njdom24 njdom24 Feb 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK the AMDGPU driver behaves such that, when a display supports VRR, it's always sending a VRR signal. Even with VRR disabled, it just keeps the FPS locked at max, so it doesn't need to modeset.

NVIDIA probably doesn't do that. In which case it might be necessary to emulate that behavior within Hyprland.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NVIDIA probably doesn't do that.

nvidia also does that unless you use a specific kernel param to outright disable vrr

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants