Skip to content

Fixed stylus handling in TouchpadView#1334

Merged
utkarshdalal merged 3 commits into
utkarshdalal:masterfrom
ppsz:add-stylus-support
May 17, 2026
Merged

Fixed stylus handling in TouchpadView#1334
utkarshdalal merged 3 commits into
utkarshdalal:masterfrom
ppsz:add-stylus-support

Conversation

@ppsz
Copy link
Copy Markdown
Contributor

@ppsz ppsz commented Apr 29, 2026

Description

Using stylus, for example S-Pen, didn't support hovering over the screen to move the mouse, so it was impossible to read tooltips. I also enabled the right click by holding the side button and tapping
It was partially implemented, but one of the methods wasn't even used anywhere

Recording

Screen_Recording_20260429_215641_GameNative.mp4
Screen_Recording_20260429_215518_GameNative.mp4

Type of Change

  • Bug fix
  • Performance / stability improvement
  • Compatibility improvements
  • Other (requires prior approval)

Checklist

  • If I have access to #code-changes, I have discussed this change there and it has been green-lighted. If I do not have access, I have still provided clear context in this PR. If I skip both, I accept that this change may face delays in review, may not be reviewed at all, or may be closed.
  • This change aligns with the current project scope (core functionality, stability, or performance). If not, it has been explicitly approved beforehand.
  • I have attached a recording of the change.
  • I have read and agree to the contribution guidelines in CONTRIBUTING.md.

Summary by cubic

Fixes stylus interactions in TouchpadView. Adds hover cursor movement and reliable right‑click with stylus buttons or eraser, so tooltips and context menus work.

  • Bug Fixes

    • Support stylus/eraser hover with transformed coordinates; skip in relative mouse mode.
    • Reliable right‑click via stylus primary/secondary buttons or eraser tip.
    • Allow stylus and mouse input when touchscreen mode is disabled.
    • Handle ACTION_CANCEL to release presses and prevent stuck clicks.
  • Refactors

    • Added helpers for stylus/eraser detection and button state.
    • Switched logging from android.util.Log to timber.log.Timber.

Written for commit f2b070a. Summary will update on new commits. Review in cubic

Summary by CodeRabbit

  • Bug Fixes
    • Improved detection and routing of stylus and eraser inputs for more reliable touchpad behavior
    • Treated eraser as stylus and mapped eraser/tool mode to secondary (right-click) actions
    • Added hover event support for stylus interactions and proper handling when relative mouse movement is enabled
    • Now handles canceled stylus interactions consistently with lifts
    • Replaced ad-hoc debug logs with structured logging for stylus events

@ppsz ppsz requested a review from utkarshdalal as a code owner April 29, 2026 20:52
Copy link
Copy Markdown
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.

No issues found across 1 file

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 29, 2026

📝 Walkthrough

Walkthrough

Centralizes stylus/eraser detection into a helper, routes stylus hover events via onHoverEvent, changes stylus click detection to use button-flag logic (including eraser-as-right-click), adds handling for ACTION_CANCEL, replaces Log.d with Timber, and short-circuits stylus handling when relative mouse movement is enabled.

Changes

Cohort / File(s) Summary
Stylus & Eraser Input
app/src/main/java/com/winlator/widget/TouchpadView.java
Introduces isEventTriggeredByStylus helper; treats TOOL_TYPE_STYLUS and TOOL_TYPE_ERASER as stylus-sourced; uses helper to gate touchscreen- mouse-disabled early exit; adds ACTION_CANCEL handling via handleStylusUp.
Stylus Button & Click Logic
app/src/main/java/com/winlator/widget/TouchpadView.java
Replaces direct buttonState reads with isStylusButtonPressed logic; derives right vs left click from stylus primary/secondary flags and eraser mode; routes to existing left/right stylus click handlers on ACTION_DOWN.
Hover Handling
app/src/main/java/com/winlator/widget/TouchpadView.java
Overrides onHoverEvent to intercept stylus/eraser hover events and forward them to stylus hover handler; hover handler early-returns when relative mouse movement is enabled.
Logging & Debug
app/src/main/java/com/winlator/widget/TouchpadView.java
Replaced Log.d calls with Timber logging under the StylusEvent tag; minor control-flow tweaks around stylus handling.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 I hopped along the event line,
Stylus and eraser now entwine,
Hover hums and clicks decide,
Timber whispers where logs abide,
A rabbit cheers the tidy input tide.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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
Title check ✅ Passed The title accurately summarizes the main change: fixing stylus handling in TouchpadView, which aligns with the core modifications described in the changeset.
Description check ✅ Passed The description comprehensively covers all required template sections including purpose, recordings, type of change, and completed checklist items.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

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

Copy link
Copy Markdown
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: 1

🧹 Nitpick comments (1)
app/src/main/java/com/winlator/widget/TouchpadView.java (1)

325-325: Rename isEventTriggeredByStulus to isEventTriggeredByStylus for clarity.

This is a small typo cleanup, but it improves readability and discoverability across call sites.

Also applies to: 342-342, 391-394

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/com/winlator/widget/TouchpadView.java` at line 325, Rename
the misspelled method isEventTriggeredByStulus to isEventTriggeredByStylus
throughout TouchpadView: update the method declaration
(isEventTriggeredByStulus) and all call sites (e.g., the boolean assignment
currently using isEventTriggeredByStulus(event) and other usages around the
affected blocks) to use isEventTriggeredByStylus(event); ensure any references
(overrides, imports, or tests) are updated to the new name and rebuild to fix
symbol references.
🤖 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/com/winlator/widget/TouchpadView.java`:
- Around line 396-400: The isStylusButtonPressed method currently checks only
MotionEvent.BUTTON_STYLUS_PRIMARY and MotionEvent.BUTTON_SECONDARY; update the
buttonState mask to also include MotionEvent.BUTTON_STYLUS_SECONDARY so stylus
barrel/secondary buttons reported using the stylus-specific constant are
detected across devices, keeping the existing tool type eraser check
(event.getToolType(0) == MotionEvent.TOOL_TYPE_ERASER) unchanged.

---

Nitpick comments:
In `@app/src/main/java/com/winlator/widget/TouchpadView.java`:
- Line 325: Rename the misspelled method isEventTriggeredByStulus to
isEventTriggeredByStylus throughout TouchpadView: update the method declaration
(isEventTriggeredByStulus) and all call sites (e.g., the boolean assignment
currently using isEventTriggeredByStulus(event) and other usages around the
affected blocks) to use isEventTriggeredByStylus(event); ensure any references
(overrides, imports, or tests) are updated to the new name and rebuild to fix
symbol references.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e6074cf3-1181-4590-9f4d-42a55b8aa608

📥 Commits

Reviewing files that changed from the base of the PR and between b409280 and ab8d6ee.

📒 Files selected for processing (1)
  • app/src/main/java/com/winlator/widget/TouchpadView.java

Comment thread app/src/main/java/com/winlator/widget/TouchpadView.java
Copy link
Copy Markdown
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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/src/main/java/com/winlator/widget/TouchpadView.java (1)

372-389: ⚠️ Potential issue | 🟠 Major

Add ACTION_CANCEL handling to stylus event path.

The stylus handler skips cleanup if the event sequence is canceled after ACTION_DOWN, potentially leaving mouse button state latched. Other input handlers in this file (handleTouchEvent, handleGestureEvent) explicitly handle ACTION_CANCEL; the stylus path must do the same.

Proposed fix
 private boolean handleStylusEvent(MotionEvent event) {
     int action = event.getActionMasked();

     switch (action) {
         case MotionEvent.ACTION_DOWN:
             if (isStylusButtonPressed(event)) {
                 handleStylusRightClick(event);
             } else {
                 handleStylusLeftClick(event);
             }
             break;
         case MotionEvent.ACTION_MOVE:
             handleStylusMove(event);
             break;
         case MotionEvent.ACTION_UP:
             handleStylusUp(event);
             break;
+        case MotionEvent.ACTION_CANCEL:
+            handleStylusUp(event);
+            break;
     }

     return true;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/com/winlator/widget/TouchpadView.java` around lines 372 -
389, The stylus event switch in TouchpadView omits MotionEvent.ACTION_CANCEL,
which can leave mouse button state latched after ACTION_DOWN; add an
ACTION_CANCEL case alongside ACTION_UP that calls the same cleanup path (e.g.,
handleStylusUp(event)) so the stylus sequence is properly terminated; update the
switch in the stylus handler to include case MotionEvent.ACTION_CANCEL:
handleStylusUp(event); break; referencing the existing methods
isStylusButtonPressed, handleStylusLeftClick, handleStylusRightClick,
handleStylusMove, and handleStylusUp.
🤖 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/com/winlator/widget/TouchpadView.java`:
- Around line 391-404: isEventTriggeredByStylus and isStylusButtonPressed
currently call event.getToolType(0) which only inspects pointer 0; change both
to use event.getActionIndex() to check the pointer that triggered this
MotionEvent (use event.getToolType(actionIndex)), and if that index is out of
range or doesn't report stylus/eraser, fall back to scanning all pointers via
event.getPointerCount() and event.getToolType(i) to detect any stylus/eraser
pointer; for isStylusButtonPressed keep the existing buttonState bit check
(event.getButtonState() & BUTTON_STYLUS_PRIMARY | BUTTON_STYLUS_SECONDARY |
BUTTON_SECONDARY) but determine toolSetToEraser by checking the action pointer
first and then by scanning all pointers as a fallback so eraser tool detection
works in multi-pointer scenarios.

---

Outside diff comments:
In `@app/src/main/java/com/winlator/widget/TouchpadView.java`:
- Around line 372-389: The stylus event switch in TouchpadView omits
MotionEvent.ACTION_CANCEL, which can leave mouse button state latched after
ACTION_DOWN; add an ACTION_CANCEL case alongside ACTION_UP that calls the same
cleanup path (e.g., handleStylusUp(event)) so the stylus sequence is properly
terminated; update the switch in the stylus handler to include case
MotionEvent.ACTION_CANCEL: handleStylusUp(event); break; referencing the
existing methods isStylusButtonPressed, handleStylusLeftClick,
handleStylusRightClick, handleStylusMove, and handleStylusUp.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 687f7977-8441-421a-a5f1-8f857471a5ad

📥 Commits

Reviewing files that changed from the base of the PR and between ab8d6ee and 9227fe3.

📒 Files selected for processing (1)
  • app/src/main/java/com/winlator/widget/TouchpadView.java

Comment thread app/src/main/java/com/winlator/widget/TouchpadView.java
Copy link
Copy Markdown
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/com/winlator/widget/TouchpadView.java (1)

392-395: ⚠️ Potential issue | 🟠 Major

Avoid hardcoded pointer index for stylus/eraser detection in mixed-pointer events.

At Line [393] and Line [403], using getToolType(0) can misclassify events when the active stylus pointer is not index 0 (e.g., finger down first, stylus second). This can route stylus input to the wrong path and miss eraser/right-click behavior.

Proposed fix
 private static boolean isEventTriggeredByStylus(MotionEvent event) {
-    int toolType = event.getToolType(0);
-    return toolType == MotionEvent.TOOL_TYPE_STYLUS || toolType == MotionEvent.TOOL_TYPE_ERASER;
+    int pointerCount = event.getPointerCount();
+    int actionIndex = event.getActionIndex();
+    if (actionIndex >= 0 && actionIndex < pointerCount) {
+        int toolType = event.getToolType(actionIndex);
+        if (toolType == MotionEvent.TOOL_TYPE_STYLUS || toolType == MotionEvent.TOOL_TYPE_ERASER) {
+            return true;
+        }
+    }
+    for (int i = 0; i < pointerCount; i++) {
+        int toolType = event.getToolType(i);
+        if (toolType == MotionEvent.TOOL_TYPE_STYLUS || toolType == MotionEvent.TOOL_TYPE_ERASER) {
+            return true;
+        }
+    }
+    return false;
 }

 private static boolean isStylusButtonPressed(MotionEvent event) {
     int buttonState = event.getButtonState();
     boolean stylusButtonPressed = (buttonState & (MotionEvent.BUTTON_STYLUS_PRIMARY
             | MotionEvent.BUTTON_STYLUS_SECONDARY
             | MotionEvent.BUTTON_SECONDARY
     )) != 0;
-    boolean toolSetToEraser = event.getToolType(0) == MotionEvent.TOOL_TYPE_ERASER;
+    boolean toolSetToEraser = false;
+    int pointerCount = event.getPointerCount();
+    int actionIndex = event.getActionIndex();
+    if (actionIndex >= 0 && actionIndex < pointerCount) {
+        toolSetToEraser = event.getToolType(actionIndex) == MotionEvent.TOOL_TYPE_ERASER;
+    }
+    if (!toolSetToEraser) {
+        for (int i = 0; i < pointerCount; i++) {
+            if (event.getToolType(i) == MotionEvent.TOOL_TYPE_ERASER) {
+                toolSetToEraser = true;
+                break;
+            }
+        }
+    }
     return stylusButtonPressed || toolSetToEraser;
 }
#!/bin/bash
# Verify stylus helper pointer-index usage and nearby context.
rg -n -C3 'isEventTriggeredByStylus|isStylusButtonPressed|getToolType\(0\)|getActionIndex\(' app/src/main/java/com/winlator/widget/TouchpadView.java

Also applies to: 403-404

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/com/winlator/widget/TouchpadView.java` around lines 392 -
395, The helper currently calls getToolType(0) which misclassifies mixed-pointer
events; change isEventTriggeredByStylus to inspect the correct pointer by
iterating over event.getPointerCount() (or use event.getActionIndex() when you
only care about the pointer that triggered the action) and check
event.getToolType(i) for MotionEvent.TOOL_TYPE_STYLUS or TOOL_TYPE_ERASER, and
apply the same fix to any other places using getToolType(0) (e.g.,
isStylusButtonPressed) so stylus/eraser detection uses the actual pointer index
rather than a hardcoded 0.
🤖 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/com/winlator/widget/TouchpadView.java`:
- Around line 392-395: The helper currently calls getToolType(0) which
misclassifies mixed-pointer events; change isEventTriggeredByStylus to inspect
the correct pointer by iterating over event.getPointerCount() (or use
event.getActionIndex() when you only care about the pointer that triggered the
action) and check event.getToolType(i) for MotionEvent.TOOL_TYPE_STYLUS or
TOOL_TYPE_ERASER, and apply the same fix to any other places using
getToolType(0) (e.g., isStylusButtonPressed) so stylus/eraser detection uses the
actual pointer index rather than a hardcoded 0.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fb05329f-64c6-419f-a2cf-f962cb34742d

📥 Commits

Reviewing files that changed from the base of the PR and between 9227fe3 and f2b070a.

📒 Files selected for processing (1)
  • app/src/main/java/com/winlator/widget/TouchpadView.java

@Unnvaldr
Copy link
Copy Markdown
Contributor

Unnvaldr commented May 4, 2026

@utkarshdalal
Copy link
Copy Markdown
Owner

Thanks, good fix

@utkarshdalal utkarshdalal merged commit 35cdeac into utkarshdalal:master May 17, 2026
3 checks passed
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.

3 participants