@@ -905,9 +905,32 @@ static INLINE void android_input_poll_event_type_motion(
905905 /* Optional: Update cursor position for hover if settings allow */
906906 if (settings -> bools .input_stylus_hover_moves_pointer && action == AMOTION_EVENT_ACTION_HOVER_MOVE )
907907 {
908+ struct video_viewport vp = {0 };
909+
910+ /* Update mouse deltas for mouse-like cursor behavior */
908911 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+
909932#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" );
911934#endif
912935 }
913936
@@ -1058,12 +1081,12 @@ static INLINE void android_input_poll_event_type_motion(
10581081 pressure , distance );
10591082#endif
10601083
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 */
10641087 if (action == AMOTION_EVENT_ACTION_DOWN || action == AMOTION_EVENT_ACTION_MOVE )
10651088 {
1066- if (stylus_pressed ) /* Only when contact detected */
1089+ if (stylus_pressed ) /* ONLY update coordinates on actual contact, like native touchscreen */
10671090 {
10681091 struct video_viewport vp = {0 };
10691092
@@ -1082,33 +1105,71 @@ static INLINE void android_input_poll_event_type_motion(
10821105 & android -> pointer [motion_ptr ].full_x ,
10831106 & android -> pointer [motion_ptr ].full_y );
10841107
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 */
10881141
10891142#ifdef DEBUG_ANDROID_INPUT
10901143 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 );
10931159#endif
10941160 }
10951161 return ; /* Early return - no shared processing */
10961162 }
10971163
10981164 if (action == AMOTION_EVENT_ACTION_UP )
10991165 {
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;
11091170
11101171#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" );
11121173#endif
11131174 return ; /* Early return - no shared processing */
11141175 }
@@ -1152,28 +1213,24 @@ static INLINE void android_input_poll_event_type_motion(
11521213 * and mouse deltas and don't process as touchscreen event.
11531214 * NOTE: AINPUT_SOURCE_* defines have multiple bits set so do full check
11541215 * 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
11561217 || (source & AINPUT_SOURCE_MOUSE_RELATIVE ) == AINPUT_SOURCE_MOUSE_RELATIVE )
1218+ && !is_stylus )
11571219 {
1158- /* Only handle regular mouse if not currently using stylus */
1159- if (!is_stylus )
1220+ if (!android -> mouse_activated )
11601221 {
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 );
11721229
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 );
11771234
11781235 btn = (int )AMotionEvent_getAxisValue (event ,
11791236 AMOTION_EVENT_AXIS_VSCROLL , motion_ptr );
@@ -1182,19 +1239,18 @@ static INLINE void android_input_poll_event_type_motion(
11821239 android -> mouse_wu = btn ;
11831240 else if (btn < 0 )
11841241 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 );
11971242 }
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 );
11981254 /* If stylus is active, don't interfere with its mouse state */
11991255 return ;
12001256 }
0 commit comments