feat(input): Implement stylus vs touch differentiation and palm-rejection #44

Open
opened 2026-10-25 13:49:39 +01:00 by harald · 1 comment
Owner

Description

On Android tablets, input from active pens (S-Pen, USI styluses) must be strictly distinguished from fingers. Drawing gestures should only be triggered by pen input, while finger touches should default to viewport pan/zoom, preventing accidental marks caused by resting the palm on the glass.

Scope of Work

  1. Track input device types in the viewport / canvas layer:
    • Inspect incoming pointer events for device/tool classification (winit pointer types or raw android-activity MotionEvent::tool_type).
  2. Implement Palm-Rejection logic:
    • If an active stylus touch is detected (ToolType::Stylus), lock drawing stroke creation to that pointer ID.
    • If a touch contact is classified as finger (ToolType::Finger), ignore stroke generation and route it to canvas navigation (pan / pinch-to-zoom).
    • If multiple touch contacts occur simultaneously, abort any tentative drawing stroke and engage multitouch zoom/pan.
  3. Pressure sensitivity:
    • Map normalized pressure values from pen events to stroke width interpolation if supported by the vector brush engine.

Acceptance Criteria

  • Resting the palm on the screen while drawing does not produce stray dots or erratic lines.
  • Two-finger gestures reliably pan and zoom the canvas without drawing vector points.
### Description On Android tablets, input from active pens (S-Pen, USI styluses) must be strictly distinguished from fingers. Drawing gestures should only be triggered by pen input, while finger touches should default to viewport pan/zoom, preventing accidental marks caused by resting the palm on the glass. ### Scope of Work 1. Track input device types in the viewport / canvas layer: - Inspect incoming pointer events for device/tool classification (`winit` pointer types or raw `android-activity` `MotionEvent::tool_type`). 2. Implement Palm-Rejection logic: - If an active stylus touch is detected (`ToolType::Stylus`), lock drawing stroke creation to that pointer ID. - If a touch contact is classified as finger (`ToolType::Finger`), ignore stroke generation and route it to canvas navigation (pan / pinch-to-zoom). - If multiple touch contacts occur simultaneously, abort any tentative drawing stroke and engage multitouch zoom/pan. 3. Pressure sensitivity: - Map normalized pressure values from pen events to stroke width interpolation if supported by the vector brush engine. ### Acceptance Criteria - Resting the palm on the screen while drawing does not produce stray dots or erratic lines. - Two-finger gestures reliably pan and zoom the canvas without drawing vector points.
Author
Owner

Corrections after checking the code (planned for the future, not started)

  • The desktop app already has this logic in src/main.rs:

    • Fingers pan and zoom unless finger drawing is enabled.
    • Two fingers cancel a stroke and start pinch.
    • Palm rejection: touches that start while the pen is near, or within 700 ms after it leaves, are ignored until lifted (Issue #39).
    • The pen comes in through octotablet (Wayland tablet protocol), separately from winit touch.
  • On Android, octotablet does not work. winit 0.29 reports pen and finger both as WindowEvent::Touch, without the tool type. So we need either:

    • a small winit patch that passes MotionEvent::tool_type() along (we already vendor winit), or
    • reading the android-activity input events directly.

    The patch is probably simpler, because the rest of the input code stays the same.

  • The best way to share code: turn pen events from both sources (octotablet on desktop, Android stylus) into the same internal pen events (proximity, down, move, up, pressure, tilt, buttons). The existing palm rejection and drawing code then works unchanged.

  • Pressure is already used (pressure curve in the settings, variable stroke width). Android delivers pressure 0..1 per pointer, so it can be passed straight through.

  • To check: S-Pen hover (proximity) and the side button (select and erase like on the desktop).

**Corrections after checking the code** (planned for the future, not started) - The desktop app already has this logic in `src/main.rs`: - Fingers pan and zoom unless finger drawing is enabled. - Two fingers cancel a stroke and start pinch. - Palm rejection: touches that start while the pen is near, or within 700 ms after it leaves, are ignored until lifted (Issue #39). - The pen comes in through **octotablet** (Wayland tablet protocol), separately from winit touch. - On Android, octotablet does not work. winit 0.29 reports pen and finger both as `WindowEvent::Touch`, without the tool type. So we need either: - a small winit patch that passes `MotionEvent::tool_type()` along (we already vendor winit), or - reading the `android-activity` input events directly. The patch is probably simpler, because the rest of the input code stays the same. - The best way to share code: turn pen events from both sources (octotablet on desktop, Android stylus) into the same internal pen events (proximity, down, move, up, pressure, tilt, buttons). The existing palm rejection and drawing code then works unchanged. - Pressure is already used (pressure curve in the settings, variable stroke width). Android delivers pressure 0..1 per pointer, so it can be passed straight through. - To check: S-Pen hover (proximity) and the side button (select and erase like on the desktop).
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: harald/ink-notes#44