Skip to content

fix: dynamic cursor visibility based on input source#656

Open
jeremybernstein wants to merge 3 commits intoutkarshdalal:masterfrom
jeremybernstein:jb/cursor-visibility
Open

fix: dynamic cursor visibility based on input source#656
jeremybernstein wants to merge 3 commits intoutkarshdalal:masterfrom
jeremybernstein:jb/cursor-visibility

Conversation

@jeremybernstein
Copy link
Contributor

@jeremybernstein jeremybernstein commented Feb 26, 2026

Summary

  • Cursor hides on touch in touchscreen mode, reappears on external mouse or controller-as-mouse movement
  • Removes isTouchscreenMode check that blocked cursor visibility on app window start
  • Controller stick mapped to "Mouse" now shows cursor on first movement

Fixes #655

Test plan

  • Touchscreen mode ON: touch hides cursor, external mouse shows it
  • Touchscreen mode OFF: cursor always visible (touch drags it)
  • Controller stick mapped to Mouse: cursor appears on stick movement
  • disableMouseInput ON: cursor stays hidden regardless

Summary by cubic

Make cursor visibility adapt to the input source. Touch hides the cursor in touchscreen mode; external mouse, controller-as-mouse, and on-screen controls show it. Cursor appears on first app window unless Disable Touchscreen is on.

  • Bug Fixes

    • Removed the touchscreen-mode gate at window start; still respects Disable Touchscreen.
    • Show cursor on external mouse only for real mouse-device events; not blocked by Disable Touchscreen.
    • Controller-as-mouse and on-screen controls set the cursor visible on movement; touch in touchscreen mode hides it.
  • Refactors

    • Renamed “Disable Mouse Input” to “Disable Touchscreen” with a clarifying description, and disabled the Touchscreen Mode toggle when touchscreen is disabled.

Written for commit 1fe1e0f. Summary will update on new commits.

Summary by CodeRabbit

  • Bug Fixes

    • Cursor now auto-shows for external mice, physical controllers, and injected pointer movements.
    • Touch input hides the cursor during direct touchscreen interactions; external mouse events still reveal it.
    • Cursor visibility for physical controllers respects the global mouse-input enabled setting.
  • Settings / UI

    • "Disable Mouse Input" renamed to "Disable Touchscreen" with an added description and dependent setting behavior.

cursor hidden in touchscreen mode on touch, shown on external mouse
or controller-as-mouse movement. removes touchscreen mode check that
was preventing cursor from showing when app window first appears.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 26, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f44523a and 1fe1e0f.

📒 Files selected for processing (2)
  • app/src/main/java/app/gamenative/ui/component/dialog/ControllerTab.kt
  • app/src/main/res/values/strings.xml

📝 Walkthrough

Walkthrough

Show cursor when external mouse/controller input occurs and hide it for touch in touchscreen mode; guard XServer null-safety and ensure renderer cursor is visible before injecting pointer-move deltas; pass mouseEnabled into PhysicalControllerHandler.

Changes

Cohort / File(s) Summary
Touch & input views
app/src/main/java/com/winlator/widget/TouchpadView.java, app/src/main/java/com/winlator/widget/InputControlsView.java
Hide cursor for touch when touchscreen mode is active; show cursor on external mouse events; ensure renderer cursor is visible before injecting pointer-move deltas.
External mouse adapter
app/src/main/java/com/winlator/inputcontrols/TouchMouse.java
On external mouse events, call renderer.setCursorVisible(true) when cursor is hidden before processing button/hover/scroll.
Controller wiring & screen
app/src/main/java/app/gamenative/ui/screen/xserver/PhysicalControllerHandler.kt, app/src/main/java/app/gamenative/ui/screen/xserver/XServerScreen.kt
Wrap pointer-delta injection in an xServer?.let { ... } block and ensure cursor visible before injection; XServerScreen now passes mouseEnabled = !container.isDisableMouseInput and relaxes window-update cursor-show condition.
Settings & strings
app/src/main/java/app/gamenative/ui/component/dialog/ControllerTab.kt, app/src/main/res/values/strings.xml
Add subtitle/description for disable-touchscreen setting and disable touchscreen toggle when mouse input is disabled; update string resources accordingly.

Sequence Diagram(s)

sequenceDiagram
    participant Touchpad as "TouchpadView / InputControlsView"
    participant Controller as "PhysicalControllerHandler"
    participant TouchMouse as "TouchMouse"
    participant XServer as "XServer (renderer)"

    Touchpad->>Touchpad: detect input (touch or external mouse)
    alt External mouse or controller-as-mouse
        Touchpad->>XServer: if cursor hidden -> setCursorVisible(true)
        Touchpad->>Controller: forward pointer delta (if mouseEnabled)
        Controller->>XServer: xServer?.let -> ensure cursor visible -> injectPointerMoveDelta
        TouchMouse->>XServer: onExternalMouseEvent ensures cursor visible then handle buttons/scroll
    else Touch input in touchscreen mode
        Touchpad->>XServer: if cursor visible -> setCursorVisible(false)
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 I nibble code with nimble paws,
When mice arrive I show my claws.
Hide for touch, appear for click,
Pointer hops, then moves so quick.
A tiny hop, a joyful kick!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: dynamic cursor visibility based on input source' accurately and concisely summarizes the main objective: making cursor visibility respond to the active input method.
Linked Issues check ✅ Passed The PR directly addresses all requirements from issue #655: cursor hides on touch in touchscreen mode, shows for external mouse/controller-as-mouse, removes the isTouchscreenMode startup gate, and respects disableMouseInput.
Out of Scope Changes check ✅ Passed All changes are scoped to cursor visibility logic and related UI clarifications; no unrelated modifications to other system components were introduced.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 5 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="app/src/main/java/com/winlator/widget/InputControlsView.java">

<violation number="1" location="app/src/main/java/com/winlator/widget/InputControlsView.java:292">
P2: The new timer tick unconditionally re-shows the cursor, which overrides touchscreen-mode hiding because the timer runs continuously once created.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@app/src/main/java/app/gamenative/ui/screen/xserver/PhysicalControllerHandler.kt`:
- Around line 153-157: The current PhysicalControllerHandler path forcibly calls
it.renderer.setCursorVisible(true) whenever controller simulates mouse; change
this to respect the disabled-mouse policy by checking the screen/container flag
before toggling visibility: retrieve the mouse policy (or a boolean like
isMouseEnabled) from the screen where container flags live and only call
setCursorVisible(true) when mouse input is enabled; update the method in
PhysicalControllerHandler (the block that calls it.injectPointerMoveDelta) to
accept or access that policy and guard the setCursorVisible call accordingly.

In `@app/src/main/java/com/winlator/inputcontrols/TouchMouse.java`:
- Around line 304-307: In TouchMouse, the renderer cursor code
(xServer.getRenderer().isCursorVisible() / setCursorVisible(true)) must be moved
into the branch that confirms the event came from an external mouse-capable
device; locate the branch that checks the input device/type (the mouse-device
handling block in TouchMouse where the code discriminates pointer/mouse devices)
and place the cursor-show logic inside that block so the cursor is only made
visible when the event originates from an external mouse.

In `@app/src/main/java/com/winlator/widget/TouchpadView.java`:
- Around line 609-612: The cursor is being made visible unconditionally; change
the logic in TouchpadView around the xServer.getRenderer().isCursorVisible()
check to only call xServer.getRenderer().setCursorVisible(true) when the
incoming event is an actual external mouse event (verify the input source/type
on the MotionEvent/InputDevice) and when the local "disabled-mouse" mode is not
set; in other words, add a guard that checks the event's input source is a mouse
(external) and that the disabled-mouse flag (use the existing field controlling
local mouse disablement) is false before toggling visibility.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 761d93b and a6e4a11.

📒 Files selected for processing (5)
  • app/src/main/java/app/gamenative/ui/screen/xserver/PhysicalControllerHandler.kt
  • app/src/main/java/app/gamenative/ui/screen/xserver/XServerScreen.kt
  • app/src/main/java/com/winlator/inputcontrols/TouchMouse.java
  • app/src/main/java/com/winlator/widget/InputControlsView.java
  • app/src/main/java/com/winlator/widget/TouchpadView.java

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
app/src/main/java/app/gamenative/ui/screen/xserver/PhysicalControllerHandler.kt (1)

154-157: Guard pointer movement injection when mouse input is disabled.

Line 156 injects pointer movement even when mouseEnabled == false. For consistency with line 155 (which guards cursor visibility) and the intended semantics of disabling mouse input entirely (per PrefManager comment: "prevents external mouse events"), pointer movement injection should also be blocked when mouse is disabled.

🔧 Suggested change
                     xServer?.let {
-                        if (mouseEnabled && !it.renderer.isCursorVisible) it.renderer.setCursorVisible(true)
-                        it.injectPointerMoveDelta(deltaX, deltaY)
+                        if (!mouseEnabled) return@let
+                        if (!it.renderer.isCursorVisible) it.renderer.setCursorVisible(true)
+                        it.injectPointerMoveDelta(deltaX, deltaY)
                     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/app/gamenative/ui/screen/xserver/PhysicalControllerHandler.kt`
around lines 154 - 157, The pointer move is being injected regardless of
mouseEnabled; update the guard around xServer usage in PhysicalControllerHandler
(the block handling xServer?.let { ... }) so that injectPointerMoveDelta(deltaX,
deltaY) is only called when mouseEnabled is true (same condition used for
renderer.setCursorVisible), i.e. check mouseEnabled before calling
injectPointerMoveDelta on the xServer instance to prevent external pointer
movement when mouse input is disabled.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@app/src/main/java/app/gamenative/ui/screen/xserver/PhysicalControllerHandler.kt`:
- Around line 154-157: The pointer move is being injected regardless of
mouseEnabled; update the guard around xServer usage in PhysicalControllerHandler
(the block handling xServer?.let { ... }) so that injectPointerMoveDelta(deltaX,
deltaY) is only called when mouseEnabled is true (same condition used for
renderer.setCursorVisible), i.e. check mouseEnabled before calling
injectPointerMoveDelta on the xServer instance to prevent external pointer
movement when mouse input is disabled.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a6e4a11 and e53d6df.

📒 Files selected for processing (4)
  • app/src/main/java/app/gamenative/ui/screen/xserver/PhysicalControllerHandler.kt
  • app/src/main/java/app/gamenative/ui/screen/xserver/XServerScreen.kt
  • app/src/main/java/com/winlator/inputcontrols/TouchMouse.java
  • app/src/main/java/com/winlator/widget/TouchpadView.java
🚧 Files skipped from review as they are similar to previous changes (3)
  • app/src/main/java/com/winlator/widget/TouchpadView.java
  • app/src/main/java/app/gamenative/ui/screen/xserver/XServerScreen.kt
  • app/src/main/java/com/winlator/inputcontrols/TouchMouse.java

Review suggested guarding setCursorVisible behind disableMouseInput
checks, but that flag means "don't let touch generate mouse events"
not "disable all pointing devices." External mouse and controller-
as-mouse should show cursor regardless. Reverted touchscreenMouseDisabled
guard in TouchpadView, removed mouseEnabled param from
PhysicalControllerHandler.

Kept: SOURCE_MOUSE check in TouchpadView and isMouseDevice check in
TouchMouse — cursor only shows for actual mouse device events.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
app/src/main/java/app/gamenative/ui/screen/xserver/PhysicalControllerHandler.kt (1)

153-156: ⚠️ Potential issue | 🟠 Major

Controller-mouse movement still bypasses disabled-mouse visibility policy.

This block always re-shows the cursor during controller-as-mouse movement. It should be gated by a mouse-enabled policy so disabled-mouse mode keeps the cursor hidden.

🔧 Suggested fix
 class PhysicalControllerHandler(
     private var profile: ControlsProfile?,
     private val xServer: XServer?,
+    private val isMouseInputEnabled: () -> Boolean = { true },
     private val onOpenNavigationMenu: (() -> Unit)? = null
 ) {
@@
                     val deltaX = (mouseMoveOffset.x * 10 * cursorSpeed).toInt()
                     val deltaY = (mouseMoveOffset.y * 10 * cursorSpeed).toInt()
                     xServer?.let {
-                        if (!it.renderer.isCursorVisible) it.renderer.setCursorVisible(true)
+                        if (isMouseInputEnabled() && !it.renderer.isCursorVisible) {
+                            it.renderer.setCursorVisible(true)
+                        }
                         it.injectPointerMoveDelta(deltaX, deltaY)
                     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/app/gamenative/ui/screen/xserver/PhysicalControllerHandler.kt`
around lines 153 - 156, The code inside xServer?.let always calls
it.renderer.setCursorVisible(true) which re-enables the cursor during
controller-as-mouse motion; wrap the visibility change in a check against the
app's mouse-enabled policy (e.g., an isMouseEnabled()/mouseEnabled flag) so that
you only call it.renderer.setCursorVisible(true) when mouse mode is allowed,
while still calling it.injectPointerMoveDelta(deltaX, deltaY) unconditionally;
update the block around xServer, renderer.isCursorVisible, setCursorVisible and
injectPointerMoveDelta to gate visibility changes by the policy check.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In
`@app/src/main/java/app/gamenative/ui/screen/xserver/PhysicalControllerHandler.kt`:
- Around line 153-156: The code inside xServer?.let always calls
it.renderer.setCursorVisible(true) which re-enables the cursor during
controller-as-mouse motion; wrap the visibility change in a check against the
app's mouse-enabled policy (e.g., an isMouseEnabled()/mouseEnabled flag) so that
you only call it.renderer.setCursorVisible(true) when mouse mode is allowed,
while still calling it.injectPointerMoveDelta(deltaX, deltaY) unconditionally;
update the block around xServer, renderer.isCursorVisible, setCursorVisible and
injectPointerMoveDelta to gate visibility changes by the policy check.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e53d6df and f44523a.

📒 Files selected for processing (3)
  • app/src/main/java/app/gamenative/ui/screen/xserver/PhysicalControllerHandler.kt
  • app/src/main/java/com/winlator/inputcontrols/TouchMouse.java
  • app/src/main/java/com/winlator/widget/TouchpadView.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/src/main/java/com/winlator/widget/TouchpadView.java

@jeremybernstein
Copy link
Contributor Author

♻️ Duplicate comments (1)

app/src/main/java/app/gamenative/ui/screen/xserver/PhysicalControllerHandler.kt (1)> 153-156: ⚠️ Potential issue | 🟠 Major

Controller-mouse movement still bypasses disabled-mouse visibility policy.
This block always re-shows the cursor during controller-as-mouse movement. It should be gated by a mouse-enabled policy so disabled-mouse mode keeps the cursor hidden.

🔧 Suggested fix

 class PhysicalControllerHandler(
     private var profile: ControlsProfile?,
     private val xServer: XServer?,
+    private val isMouseInputEnabled: () -> Boolean = { true },
     private val onOpenNavigationMenu: (() -> Unit)? = null
 ) {
@@
                     val deltaX = (mouseMoveOffset.x * 10 * cursorSpeed).toInt()
                     val deltaY = (mouseMoveOffset.y * 10 * cursorSpeed).toInt()
                     xServer?.let {
-                        if (!it.renderer.isCursorVisible) it.renderer.setCursorVisible(true)
+                        if (isMouseInputEnabled() && !it.renderer.isCursorVisible) {
+                            it.renderer.setCursorVisible(true)
+                        }
                         it.injectPointerMoveDelta(deltaX, deltaY)
                     }

🤖 Prompt for AI Agents

Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/app/gamenative/ui/screen/xserver/PhysicalControllerHandler.kt`
around lines 153 - 156, The code inside xServer?.let always calls
it.renderer.setCursorVisible(true) which re-enables the cursor during
controller-as-mouse motion; wrap the visibility change in a check against the
app's mouse-enabled policy (e.g., an isMouseEnabled()/mouseEnabled flag) so that
you only call it.renderer.setCursorVisible(true) when mouse mode is allowed,
while still calling it.injectPointerMoveDelta(deltaX, deltaY) unconditionally;
update the block around xServer, renderer.isCursorVisible, setCursorVisible and
injectPointerMoveDelta to gate visibility changes by the policy check.

🤖 Prompt for all review comments with AI agents

Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In
`@app/src/main/java/app/gamenative/ui/screen/xserver/PhysicalControllerHandler.kt`:
- Around line 153-156: The code inside xServer?.let always calls
it.renderer.setCursorVisible(true) which re-enables the cursor during
controller-as-mouse motion; wrap the visibility change in a check against the
app's mouse-enabled policy (e.g., an isMouseEnabled()/mouseEnabled flag) so that
you only call it.renderer.setCursorVisible(true) when mouse mode is allowed,
while still calling it.injectPointerMoveDelta(deltaX, deltaY) unconditionally;
update the block around xServer, renderer.isCursorVisible, setCursorVisible and
injectPointerMoveDelta to gate visibility changes by the policy check.

ℹ️ Review info

@coderabbitai enable mouse mode isn't supposed to prevent all mouse activity, just touchscreen mouse events. The code is correct.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 26, 2026

Tip

For best results, initiate chat on the files or code changes.

@jeremybernstein, thank you for the clarification! I understand now that disableMouseInput is specifically scoped to touchscreen mouse events only, and controller-as-mouse and external mouse inputs should continue to show the cursor regardless of that flag. The current implementation is correct for the intended behavior.


✏️ Learnings added
Learnt from: jeremybernstein
URL: https://github.com/utkarshdalal/GameNative/pull/656

Timestamp: 2026-02-26T21:20:07.832Z
Learning: In the GameNative project, the `disableMouseInput` flag only controls touchscreen mouse events, not all mouse activity. Controller-as-mouse and external mouse inputs should show the cursor even when `disableMouseInput` is ON.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

rename to "Disable Touchscreen" with description clarifying it only
prevents touch-generated mouse events. external mouse and controller
still work. touchscreen mode preference disabled when touchscreen is
disabled since it has no effect.
@jeremybernstein
Copy link
Contributor Author

Added: renamed "Disable Mouse Input" to "Disable Touchscreen" with description clarifying it only prevents touch-generated mouse events. Touchscreen Mode preference is now disabled (greyed out) when touchscreen is disabled.

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.

Cursor not visible in touchscreen mode when using external mouse or controller

1 participant