Skip to content

Add support for cursor confinement and lock settings independent of cursor visibility - #353

Open
Acissathar wants to merge 1 commit into
ProwlEngine:mainfrom
Acissathar:add-more-cursor-states
Open

Add support for cursor confinement and lock settings independent of cursor visibility#353
Acissathar wants to merge 1 commit into
ProwlEngine:mainfrom
Acissathar:add-more-cursor-states

Conversation

@Acissathar

Copy link
Copy Markdown
Contributor

Summary

This adds a Confined cursor mode and splits cursor visibility from lock state, inspired by Unity's CursorLockMode + Cursor.visible split. Previously Input.SetCursorVisible(false) mapped straight to GLFW's CursorMode.Disabled, so the only two reachable states were "normal" and "hidden and locked to center" — CursorMode.Hidden was never reachable at all. Now visibility and constraint are independent, so you can hide the cursor but keep it inside the window and draw your own:

Example

Video.Project.4.mp4

Notes

  • GLFW resolves visibility and constraint from a single input mode, so they can't be set independently. Everything is applied in one place: Locked → Disabled, everything else → Normal/Hidden. Input owns the state and handlers cache nothing — the applied mode is read back from GLFW, which also gives us the transition detection for the delta fix below.

  • There's no hidden-and-confined mode in GLFW. CURSOR_CAPTURED (3.4) is visible-only and can't confine to a sub-rect, so Confined is a per-frame clamp in DefaultInputHandler.LateUpdate. It runs before the position sample so the delta at a wall reads zero instead of a jump back, and it's skipped while unfocused so it doesn't fight you when alt-tabbing.

  • Confine bounds come off the lock context stack. GameViewPanel already pushes a PanelLockContext, so confining in the editor holds the cursor to the Game View image rather than the whole editor window. GetLockCenter() now derives from GetConfineBounds() by default, which let PanelLockContext drop its center override entirely.

  • Fixed a one-frame MouseDelta spike when leaving Locked that I think was pre-existing but is much easier to hit now. GLFW reports unbounded virtual coordinates while Disabled and restores the real position on release, so the cached positions still held the accumulated drift and the next delta was that entire jump. Now resynced on the transition out.

  • Exiting play mode didn't reset cursor state, so a game that set CursorVisible = false with no lock mode left the editor with a permanently invisible cursor, and Escape didn't help because UnlockCursor() does nothing when the mode is already None. Reset on stop and widened the Escape guard to cover hidden-without-a-mode.

  • OnCursorLocked now fires for any constraining mode so a confined cursor gets the same "press Escape" prompt. The toast still says "Cursor Locked" for it. We can use the cursor state to change it, but I wasn't sure if that was really meaningful.

  • Breaking: IInputHandler.SetCursorVisible is replaced by ApplyCursorState(bool, CursorLockMode). Input.SetCursorVisible also loses its miceIndex param since this state is global, not per-device and SetCursorVisible(false) now means hidden-and-free rather than locked. All four samples were relying on the old conflated behaviour to get FPS look and are updated to LockCursor()/UnlockCursor().

Limitations

  • Cursor state is global rather than per-mouse. SetCursorShape still takes a miceIndex and I left it alone, but the visibility/lock path always applies to Mice[0].

@PaperPrototype

Copy link
Copy Markdown
Contributor

Potential relevant, I think the plan was to switch to SDL at some point but we just never got to it?

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.

2 participants