Skip to content

Commit de2f19f

Browse files
f4mrfauxclaude
andcommitted
💡 Using bat (modern cat with syntax)
docs: Add comprehensive S Pen implementation documentation Create detailed developer documentation explaining the multi-layer protection approach for Samsung S Pen hover→tap phantom click prevention: - Problem analysis and root cause explanation referencing xlabs' previous research - Four-layer defense-in-depth architecture documentation - Input channel separation design with mouse_activated flag logic - ToolType vs source classification strategy and fallback mechanisms - Contact detection using hardware pressure/distance sensors - Time unit consistency patterns across Android NDK APIs - Settings framework integration details - Performance considerations and guard window tuning - Future maintainer guidance with key code locations - Complete test requirements and debug support This provides essential context for future developers working on stylus input handling and helps preserve the architectural knowledge of this complex system. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent d03d1b5 commit de2f19f

1 file changed

Lines changed: 253 additions & 0 deletions

File tree

docs/S-Pen-Implementation.md

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
# Samsung S Pen Hover Phantom Click Prevention
2+
3+
## Overview
4+
5+
This document explains the comprehensive S Pen hover→tap prevention system implemented in RetroArch Android to resolve phantom touchscreen clicks caused by Samsung firmware synthesizing touch events during stylus hover transitions.
6+
7+
## Problem Background
8+
9+
### Original Issue
10+
Samsung S Pen devices generate phantom touchscreen events when the stylus transitions in/out of hover proximity. These synthesized events cause unwanted menu clicks and paint strokes in RetroArch, making stylus hover unusable.
11+
12+
**Symptoms:**
13+
- Hovering over menu items causes automatic selection
14+
- S Pen hover transitions trigger paint strokes in emulated games
15+
- Quick hover gestures produce delayed phantom clicks
16+
- Menu navigation becomes unusable with stylus proximity
17+
18+
### Root Cause Analysis
19+
Based on xlabs' previous research and our investigation, the issue stems from Samsung's firmware architecture:
20+
21+
1. **Dual Event Generation:** S Pen hover events generate both stylus events (`0x5002`) AND phantom touchscreen events (`0x1002`)
22+
2. **Timing Dependency:** Phantom events arrive 50-100ms after hover transitions
23+
3. **Source Ambiguity:** Some stylus events are reported through `TOUCHSCREEN` source, making source-only filtering insufficient
24+
4. **Shared Input Pipeline:** Android's input system processes both event types through the same motion event handlers
25+
26+
## Multi-Layer Protection Architecture
27+
28+
The implementation uses a defense-in-depth approach with four complementary protection layers:
29+
30+
### Layer 1: Hover Guard (Temporal/Spatial Filtering)
31+
**Location:** `input/drivers/android_input.c` - Global state variables
32+
```c
33+
static bool g_hover_guard_active = false;
34+
static int64_t g_hover_guard_until_ms = 0;
35+
static float g_hover_guard_x = 0.0f, g_hover_guard_y = 0.0f;
36+
```
37+
38+
**Function:** Filters phantom touchscreen events immediately following stylus hover transitions.
39+
40+
**Logic:**
41+
- Armed on any stylus `HOVER_ENTER/MOVE/EXIT` event (100ms window)
42+
- Drops finger/touch events within 12px radius during guard period
43+
- Prevents Samsung synthesized touches from promoting to clicks
44+
45+
### Layer 2: Stylus Proximity Tracking
46+
**Location:** `android_input_t` struct fields
47+
```c
48+
bool stylus_proximity_active;
49+
int64_t stylus_proximity_until_ns;
50+
```
51+
52+
**Function:** Tracks stylus hover state with longer temporal window for quick-tap suppression.
53+
54+
**Logic:**
55+
- Activated on stylus hover events (120ms window)
56+
- Disables quick-tap mouse emulation while stylus is nearby
57+
- Uses nanosecond timestamps for precise timing control
58+
59+
### Layer 3: Quick-Tap Defense-in-Depth
60+
**Location:** `android_check_quick_tap()` function
61+
```c
62+
if (g_hover_guard_active) {
63+
android->quick_tap_time = 0;
64+
return 0;
65+
}
66+
```
67+
68+
**Function:** Final safety layer preventing phantom touch promotion to mouse clicks.
69+
70+
**Logic:**
71+
- Direct hover guard check inside quick-tap function
72+
- Cancels pending quick-tap timers when guard is active
73+
- Ensures no hover transition can accidentally trigger clicks
74+
75+
### Layer 4: Menu Gesture Isolation
76+
**Location:** `menu/menu_driver.c` - Gesture detection logic
77+
```c
78+
if (menu_input->pointer.type != MENU_POINTER_TOUCHSCREEN) {
79+
point.gesture = MENU_INPUT_GESTURE_NONE;
80+
}
81+
```
82+
83+
**Function:** Restricts gesture-based menu interactions to touchscreen-only input.
84+
85+
**Logic:**
86+
- Blocks `TAP/SHORT_PRESS/LONG_PRESS/SWIPE` gestures for mouse/stylus input
87+
- Forces stylus to use explicit button presses rather than motion timing
88+
- Prevents hover motion from being interpreted as intentional gestures
89+
90+
## Input Channel Separation
91+
92+
### Design Philosophy
93+
The implementation maintains strict separation between stylus and finger input channels using the `mouse_activated` flag as a channel selector.
94+
95+
### Channel Logic
96+
```c
97+
if (android->mouse_activated) {
98+
// Stylus/mouse mode: Direct button state access
99+
return android->mouse_l;
100+
} else {
101+
// Touch mode: Quick-tap emulation with proximity guards
102+
if (!android->stylus_proximity_active && !g_hover_guard_active)
103+
return android_check_quick_tap(android);
104+
}
105+
```
106+
107+
### State Transitions
108+
1. **Stylus Contact:** `mouse_activated = true` → Switches to mouse mode
109+
2. **Stylus Lift:** `mouse_activated = false` → Returns to touch mode
110+
3. **Finger Touch:** Only processes when `mouse_activated = false`
111+
112+
This design prevents input interference while maintaining responsive behavior.
113+
114+
## ToolType Classification System
115+
116+
### Primary Classification
117+
Uses Android NDK `AMotionEvent_getToolType()` as the primary discriminator:
118+
```c
119+
int32_t tool = AMotionEvent_getToolType(event, 0);
120+
bool is_stylus = (tool == AMOTION_EVENT_TOOL_TYPE_STYLUS);
121+
bool is_finger = (tool == AMOTION_EVENT_TOOL_TYPE_FINGER);
122+
```
123+
124+
### Fallback Classification
125+
When toolType is unavailable or unknown, falls back to input source classification:
126+
```c
127+
if (!is_stylus && !is_finger) {
128+
is_stylus = ((source & AINPUT_SOURCE_STYLUS) == AINPUT_SOURCE_STYLUS);
129+
is_finger = ((source & AINPUT_SOURCE_TOUCHSCREEN) == AINPUT_SOURCE_TOUCHSCREEN);
130+
}
131+
```
132+
133+
**Note:** ToolType classification is more reliable than source-only filtering because xlabs' research showed stylus events sometimes come through `TOUCHSCREEN` source.
134+
135+
## Contact Detection Logic
136+
137+
### Hardware-Based Detection
138+
Uses actual pressure and distance sensors for precise contact detection:
139+
```c
140+
float pressure = AMotionEvent_getPressure(event, motion_ptr);
141+
float distance = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_DISTANCE, motion_ptr);
142+
bool tip_down = (action != AMOTION_EVENT_ACTION_UP) &&
143+
(pressure > 0.01f) &&
144+
(distance <= 0.0f);
145+
```
146+
147+
### Settings Integration
148+
Respects `input_stylus_require_contact_for_click` user preference:
149+
- **ON:** Only tip pressure contact triggers clicks (hover never clicks)
150+
- **OFF:** Tip contact OR side button triggers clicks (hover still never clicks)
151+
152+
## Time Unit Consistency
153+
154+
### Timestamp Handling
155+
The implementation carefully manages different time units across the Android NDK:
156+
157+
- **Event Times:** `AMotionEvent_getEventTime()` returns nanoseconds
158+
- **System Time:** `cpu_features_get_time_usec()` returns microseconds
159+
- **Guard Windows:** Stored in milliseconds for human-readable timeouts
160+
161+
### Conversion Patterns
162+
```c
163+
// Event time (ns) to milliseconds
164+
event_time_ms = AMotionEvent_getEventTime(event) / 1000000;
165+
166+
// Proximity timeout (120ms in nanoseconds)
167+
android->stylus_proximity_until_ns = AMotionEvent_getEventTime(event) + 120000000;
168+
169+
// Time comparison (both converted to milliseconds)
170+
if (now / 1000 > android->stylus_proximity_until_ns / 1000000)
171+
```
172+
173+
## Settings Framework Integration
174+
175+
### Configuration Options
176+
Two user-configurable settings are provided:
177+
178+
1. **`input_stylus_require_contact_for_click`**
179+
- Controls stylus click triggering behavior
180+
- Default: ON (contact required for clicks)
181+
182+
2. **`input_stylus_hover_moves_pointer`**
183+
- Controls cursor movement during hover
184+
- Default: OFF (reduces phantom potential)
185+
186+
### Menu Integration
187+
Full settings menu integration includes:
188+
- Configuration entries in `menu/menu_setting.c`
189+
- Internationalization support in `msg_hash_*.h`
190+
- Context help in `menu_cbs_sublabel.c`
191+
- Display logic in `menu_displaylist.c`
192+
193+
## Performance Considerations
194+
195+
### Minimal Overhead
196+
The protection system adds minimal computational overhead:
197+
- Static guard variables avoid memory allocation
198+
- Simple boolean/timestamp checks in hot paths
199+
- Guard expiration only computed when needed
200+
- No impact on non-stylus input processing
201+
202+
### Guard Window Tuning
203+
Current timeouts are empirically determined:
204+
- **Hover Guard:** 100ms (phantom event suppression)
205+
- **Proximity Tracking:** 120ms (quick-tap suppression)
206+
- **Spatial Tolerance:** 12px (phantom event detection)
207+
208+
These values can be adjusted per device if needed.
209+
210+
## Future Maintainers
211+
212+
### Key Code Locations
213+
- **Core Logic:** `input/drivers/android_input.c`
214+
- Lines ~105-143: Hover guard implementation
215+
- Lines ~698-722: Quick-tap defense function
216+
- Lines ~853-983: Stylus event processing
217+
- Lines ~2040-2055, ~2128-2143: Input state queries
218+
219+
- **Menu Integration:** `menu/menu_driver.c`
220+
- Lines ~6084-6170: Gesture isolation logic
221+
222+
### Debug Support
223+
Enable `DEBUG_ANDROID_INPUT` flag for comprehensive logging:
224+
```c
225+
#ifdef DEBUG_ANDROID_INPUT
226+
RARCH_LOG("[RA Input] act=%d src=0x%x tool=%d dev=%d btn=0x%x dropped=%d\n", ...);
227+
#endif
228+
```
229+
230+
### Testing Requirements
231+
Always test changes on actual Samsung S Pen devices:
232+
- Galaxy Note series
233+
- Galaxy Tab S series
234+
- Galaxy Z Fold series
235+
236+
Virtual devices cannot reproduce the firmware phantom event behavior.
237+
238+
## References
239+
240+
- **xlabs Research:** Previous investigation identified the need for stylus/touch distinction
241+
- **Samsung S Pen Documentation:** Android NDK motion event handling
242+
- **RetroArch Input Architecture:** Existing mouse emulation and quick-tap systems
243+
244+
## Commit History
245+
246+
The implementation was developed across multiple commits:
247+
- `7daf2ae`: Base S Pen implementation with toolType classification
248+
- `050a396`: Comprehensive hover→tap prevention with proximity tracking
249+
- `4c47eac`: Defense-in-depth enhancement to quick-tap function
250+
251+
---
252+
253+
*This documentation should be updated when modifying the S Pen implementation to ensure future maintainers understand the complete protection strategy.*

0 commit comments

Comments
 (0)