@@ -905,9 +905,32 @@ static INLINE void android_input_poll_event_type_motion(
905
905
/* Optional: Update cursor position for hover if settings allow */
906
906
if (settings -> bools .input_stylus_hover_moves_pointer && action == AMOTION_EVENT_ACTION_HOVER_MOVE )
907
907
{
908
+ struct video_viewport vp = {0 };
909
+
910
+ /* Update mouse deltas for mouse-like cursor behavior */
908
911
android_mouse_calculate_deltas (android , event , motion_ptr , source );
912
+
913
+ /* Also update absolute pointer coordinates for RETRO_DEVICE_POINTER */
914
+ video_driver_translate_coord_viewport_confined_wrap (
915
+ & vp , x , y ,
916
+ & android -> pointer [motion_ptr ].confined_x ,
917
+ & android -> pointer [motion_ptr ].confined_y ,
918
+ & android -> pointer [motion_ptr ].full_x ,
919
+ & android -> pointer [motion_ptr ].full_y );
920
+
921
+ video_driver_translate_coord_viewport_wrap (
922
+ & vp , x , y ,
923
+ & android -> pointer [motion_ptr ].x ,
924
+ & android -> pointer [motion_ptr ].y ,
925
+ & android -> pointer [motion_ptr ].full_x ,
926
+ & android -> pointer [motion_ptr ].full_y );
927
+
928
+ /* Ensure pointer_count covers this motion_ptr */
929
+ if (android -> pointer_count < (int )motion_ptr + 1 )
930
+ android -> pointer_count = (int )motion_ptr + 1 ;
931
+
909
932
#ifdef DEBUG_ANDROID_INPUT
910
- RARCH_LOG ("[RA Input] Stylus hover cursor update (user enabled)\n" );
933
+ RARCH_LOG ("[RA Input] Stylus hover cursor update (user enabled) - mouse + pointer coords \n" );
911
934
#endif
912
935
}
913
936
@@ -1058,12 +1081,12 @@ static INLINE void android_input_poll_event_type_motion(
1058
1081
pressure , distance );
1059
1082
#endif
1060
1083
1061
- /* STYLUS AS POINTER: Route to RETRO_DEVICE_POINTER for menu interaction
1062
- * Handle DOWN/MOVE => active pointer; UP => release pointer
1063
- * Only activate pointer when stylus_pressed (respects contact setting) */
1084
+ /* STYLUS POINTER: Mimic native touchscreen behavior for menu consistency
1085
+ * Native touchscreen ONLY updates coordinates on contact, NOT during hover
1086
+ * This prevents menu jumping between hover and tap states */
1064
1087
if (action == AMOTION_EVENT_ACTION_DOWN || action == AMOTION_EVENT_ACTION_MOVE )
1065
1088
{
1066
- if (stylus_pressed ) /* Only when contact detected */
1089
+ if (stylus_pressed ) /* ONLY update coordinates on actual contact, like native touchscreen */
1067
1090
{
1068
1091
struct video_viewport vp = {0 };
1069
1092
@@ -1082,33 +1105,71 @@ static INLINE void android_input_poll_event_type_motion(
1082
1105
& android -> pointer [motion_ptr ].full_x ,
1083
1106
& android -> pointer [motion_ptr ].full_y );
1084
1107
1085
- /* Ensure pointer_count covers this motion_ptr */
1086
- if (android -> pointer_count < (int )motion_ptr + 1 )
1087
- android -> pointer_count = (int )motion_ptr + 1 ;
1108
+ /* S-Pen Menu Coordination: Activate mouse mode for menu consistency */
1109
+ if (!android -> mouse_activated )
1110
+ {
1111
+ RARCH_LOG ("[Android Input] S-Pen activated menu mouse mode.\n" );
1112
+ android -> mouse_activated = true;
1113
+ }
1114
+
1115
+ /* Update mouse coordinates only on contact - matches native touchscreen behavior */
1116
+ android -> mouse_x_viewport = android -> pointer [motion_ptr ].x ;
1117
+ android -> mouse_y_viewport = android -> pointer [motion_ptr ].y ;
1118
+
1119
+ /* S-Pen Virtual Pointer System for libretro cores:
1120
+ * Index 0 = tip pointer (when tip pressed)
1121
+ * Index 1 = virtual barrel pointer (when barrel pressed, mirrors tip XY)
1122
+ * This allows cores to detect barrel via pointer_count > 1 or checking index 1 */
1123
+
1124
+ android -> pointer_count = 0 ; /* Reset count, will increment based on active states */
1125
+
1126
+ if (tip_down ) {
1127
+ /* Set up tip pointer at index 0 */
1128
+ android -> pointer [0 ] = android -> pointer [motion_ptr ]; /* Copy translated coordinates */
1129
+ android -> pointer_count = 1 ;
1130
+ }
1131
+
1132
+ if (side_primary ) {
1133
+ /* Set up virtual barrel pointer at index 1, mirroring tip coordinates */
1134
+ android -> pointer [1 ] = android -> pointer [motion_ptr ]; /* Same coordinates as tip */
1135
+ android -> pointer_count = tip_down ? 2 : 1 ; /* Increment if tip also active */
1136
+ }
1137
+
1138
+ /* Update mouse button states for menu interaction */
1139
+ android -> mouse_l = tip_down ; /* Left click when tip touches */
1140
+ android -> mouse_r = side_primary ; /* Right click on barrel button */
1088
1141
1089
1142
#ifdef DEBUG_ANDROID_INPUT
1090
1143
if (action == AMOTION_EVENT_ACTION_DOWN )
1091
- RARCH_LOG ("[Stylus] POINTER DOWN @ (%.1f, %.1f) idx=%zu cnt=%d\n" ,
1092
- x , y , motion_ptr , android -> pointer_count );
1144
+ RARCH_LOG ("[Stylus] POINTER DOWN @ (%.1f, %.1f) tip=%d barrel=%d cnt=%d\n" ,
1145
+ x , y , tip_down , side_primary , android -> pointer_count );
1146
+ #endif
1147
+ }
1148
+ else
1149
+ {
1150
+ /* Hovering: Like native touchscreen, DON'T update coordinates during hover
1151
+ * This prevents menu jumping and matches user expectations */
1152
+ android -> pointer_count = 0 ;
1153
+ android -> mouse_l = false;
1154
+ android -> mouse_r = false;
1155
+
1156
+ #ifdef DEBUG_ANDROID_INPUT
1157
+ RARCH_LOG ("[Stylus] HOVER (no coord update) @ (%.1f, %.1f) p=%.3f d=%.3f\n" ,
1158
+ x , y , pressure , distance );
1093
1159
#endif
1094
1160
}
1095
1161
return ; /* Early return - no shared processing */
1096
1162
}
1097
1163
1098
1164
if (action == AMOTION_EVENT_ACTION_UP )
1099
1165
{
1100
- /* Compact/release pointer array like the touch path */
1101
- if (motion_ptr < MAX_TOUCH - 1 )
1102
- {
1103
- memmove (android -> pointer + motion_ptr ,
1104
- android -> pointer + motion_ptr + 1 ,
1105
- (MAX_TOUCH - motion_ptr - 1 ) * sizeof (android -> pointer [0 ]));
1106
- }
1107
- if (android -> pointer_count > 0 )
1108
- android -> pointer_count -- ;
1166
+ /* S-Pen UP: Clear all virtual pointers and button states */
1167
+ android -> pointer_count = 0 ;
1168
+ android -> mouse_l = false;
1169
+ android -> mouse_r = false;
1109
1170
1110
1171
#ifdef DEBUG_ANDROID_INPUT
1111
- RARCH_LOG ("[Stylus] POINTER UP idx=%zu cnt=%d \n" , motion_ptr , android -> pointer_count );
1172
+ RARCH_LOG ("[Stylus] POINTER UP - cleared all virtual pointers \n" );
1112
1173
#endif
1113
1174
return ; /* Early return - no shared processing */
1114
1175
}
@@ -1152,28 +1213,24 @@ static INLINE void android_input_poll_event_type_motion(
1152
1213
* and mouse deltas and don't process as touchscreen event.
1153
1214
* NOTE: AINPUT_SOURCE_* defines have multiple bits set so do full check
1154
1215
* Stylus events are now handled above in the toolType-first section */
1155
- if ( (source & AINPUT_SOURCE_MOUSE ) == AINPUT_SOURCE_MOUSE
1216
+ if (( (source & AINPUT_SOURCE_MOUSE ) == AINPUT_SOURCE_MOUSE
1156
1217
|| (source & AINPUT_SOURCE_MOUSE_RELATIVE ) == AINPUT_SOURCE_MOUSE_RELATIVE )
1218
+ && !is_stylus )
1157
1219
{
1158
- /* Only handle regular mouse if not currently using stylus */
1159
- if (!is_stylus )
1220
+ if (!android -> mouse_activated )
1160
1221
{
1161
- if (!android -> mouse_activated )
1162
- {
1163
- #ifdef DEBUG_ANDROID_INPUT
1164
- RARCH_LOG ("[Android Input] Mouse activated.\n" );
1165
- #endif
1166
- android -> mouse_activated = true;
1167
- }
1168
- /* getButtonState requires API level 14 */
1169
- if (p_AMotionEvent_getButtonState )
1170
- {
1171
- int btn = (int )AMotionEvent_getButtonState (event );
1222
+ RARCH_LOG ("[Android Input] Mouse activated.\n" );
1223
+ android -> mouse_activated = true;
1224
+ }
1225
+ /* getButtonState requires API level 14 */
1226
+ if (p_AMotionEvent_getButtonState )
1227
+ {
1228
+ int btn = (int )AMotionEvent_getButtonState (event );
1172
1229
1173
- /* Regular mouse button mapping (stylus events handled above) */
1174
- android -> mouse_l = (btn & AMOTION_EVENT_BUTTON_PRIMARY );
1175
- android -> mouse_r = (btn & AMOTION_EVENT_BUTTON_SECONDARY );
1176
- android -> mouse_m = (btn & AMOTION_EVENT_BUTTON_TERTIARY );
1230
+ /* Regular mouse button mapping (stylus events handled above) */
1231
+ android -> mouse_l = (btn & AMOTION_EVENT_BUTTON_PRIMARY );
1232
+ android -> mouse_r = (btn & AMOTION_EVENT_BUTTON_SECONDARY );
1233
+ android -> mouse_m = (btn & AMOTION_EVENT_BUTTON_TERTIARY );
1177
1234
1178
1235
btn = (int )AMotionEvent_getAxisValue (event ,
1179
1236
AMOTION_EVENT_AXIS_VSCROLL , motion_ptr );
@@ -1182,19 +1239,18 @@ static INLINE void android_input_poll_event_type_motion(
1182
1239
android -> mouse_wu = btn ;
1183
1240
else if (btn < 0 )
1184
1241
android -> mouse_wd = btn ;
1185
- }
1186
- else
1187
- {
1188
- /* If getButtonState is not available
1189
- * then treat all MotionEvent.ACTION_DOWN as left button presses */
1190
- if (action == AMOTION_EVENT_ACTION_DOWN )
1191
- android -> mouse_l = 1 ;
1192
- if (action == AMOTION_EVENT_ACTION_UP )
1193
- android -> mouse_l = 0 ;
1194
- }
1195
-
1196
- android_mouse_calculate_deltas (android ,event ,motion_ptr ,source );
1197
1242
}
1243
+ else
1244
+ {
1245
+ /* If getButtonState is not available
1246
+ * then treat all MotionEvent.ACTION_DOWN as left button presses */
1247
+ if (action == AMOTION_EVENT_ACTION_DOWN )
1248
+ android -> mouse_l = 1 ;
1249
+ if (action == AMOTION_EVENT_ACTION_UP )
1250
+ android -> mouse_l = 0 ;
1251
+ }
1252
+
1253
+ android_mouse_calculate_deltas (android ,event ,motion_ptr ,source );
1198
1254
/* If stylus is active, don't interfere with its mouse state */
1199
1255
return ;
1200
1256
}
0 commit comments