Skip to content

fix(cursor): a shown pointer is free in a native-cursor session (#378) - #380

Merged
Maxaubert merged 1 commit into
developfrom
fix/378-native-cursor-lockapps
Oct 8, 2026
Merged

Maxaubert merged 1 commit into
developfrom
fix/378-native-cursor-lockapps

Conversation

@Maxaubert

Copy link
Copy Markdown
Owner

Closes #378.

What happened

Field video: with the native cursor, the zoomed view tracked the pointer perfectly on the desktop, but in DOOM: The Dark Ages menus the cursor was laggy and unstable. Old Wind (sprite) and Windows Magnifier did not do this.

Cause

lockApps=DOOMTheDarkAges.exe forces the locked path: the view pans from raw mickeys, DWM centring is off, and the weld re-parks the real pointer once per tick. The sprite hid this because it is drawn at the re-parked point. The native cursor IS the real pointer, and DWM draws it wherever the hand moved it between ticks. The result is a cursor that wanders around the centre and snaps back. Doom's gold arrow is a real Windows cursor shape, so DWM composes it like any other pointer.

Fix

Games show the pointer in menus and hide it for mouselook (the case the locked path exists for). So in a native-cursor session the lock applies only while GetCursorInfo reports the pointer hidden. This is LockApplies in src/native_cursor.h, and every gate reads the tick's result, t.lockEff. A shown pointer gets DWM centring, which is what Windows Magnifier does there. The sprite path keeps the old rule. The new log line is lock pointer shown: free (native cursor).

Verification

  • Unit suite: 593/593 (new LockApplies case). build.bat check is clean.

  • Desktop reproduction (C:\RE harness locked_cursor_test.py): black backdrop, forced lock (lockForce=1), relative sweeps at ~4.7x, pointer tip tracked per captured frame.

    Variant Slow spread Medium spread Worst frame jump
    develop, free 0 px 0 px 0 px
    develop, forced lock 22 px 74 px 118 px
    this branch, forced lock 0 px 0 px 2 px

    2 px is the detector's resolution.

  • In DOOM: The Dark Ages, main menu with your lockApps: the log shows pointer shown: free. Across seven captured frames of a sweep, the gold cursor stays on one screen point while the menu pans underneath.

  • Deployed (signed UIAccess) on the dev PC.

Not tested: Doom gameplay (mouselook). There the pointer is hidden, so the lock applies exactly as before. Please check that the view still pans with mouselook in-game.

🤖 Generated with Claude Code

https://claude.ai/code/session_011zPmivSAQeivGUdBjTsaMu

lockApps (and the lock tells) put DOOM: The Dark Ages menus on the locked path: the view
pans from raw mickeys and the weld re-parks the real pointer once per tick. The sprite
hid that; the native cursor is the real pointer, drawn by DWM wherever the hand moved it
between ticks, so it wandered around the centre and snapped back (4.7x: 22 px spread
slow, 74 px medium, jumps to 118 px; free with DWM centring: 0 px).

Games show the pointer in menus and hide it for mouselook, so in a native-cursor session
the lock applies only while GetCursorInfo reports the pointer hidden (LockApplies). Every
gate reads the tick's result (t.lockEff). The sprite path keeps the old rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011zPmivSAQeivGUdBjTsaMu
@Maxaubert
Maxaubert merged commit 3470a8b into develop Oct 8, 2026
1 check passed
Maxaubert added a commit that referenced this pull request Oct 8, 2026
#380)

lockApps (and the lock tells) put DOOM: The Dark Ages menus on the locked path: the view
pans from raw mickeys and the weld re-parks the real pointer once per tick. The sprite
hid that; the native cursor is the real pointer, drawn by DWM wherever the hand moved it
between ticks, so it wandered around the centre and snapped back (4.7x: 22 px spread
slow, 74 px medium, jumps to 118 px; free with DWM centring: 0 px).

Games show the pointer in menus and hide it for mouselook, so in a native-cursor session
the lock applies only while GetCursorInfo reports the pointer hidden (LockApplies). Every
gate reads the tick's result (t.lockEff). The sprite path keeps the old rule.


Claude-Session: https://claude.ai/code/session_011zPmivSAQeivGUdBjTsaMu

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@Maxaubert
Maxaubert deleted the fix/378-native-cursor-lockapps branch October 8, 2026 10:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant