@@ -187,6 +187,8 @@ typedef struct android_input
187187 int64_t quick_tap_time ;
188188 bool stylus_proximity_active ;
189189 int64_t stylus_proximity_until_ns ;
190+ bool stylus_side_button_active ; /* Track side button state */
191+ size_t stylus_side_button_ptr ; /* Track which pointer index */
190192 state_device_t pad_states [MAX_USERS ]; /* int alignment */
191193 int mouse_x , mouse_y ;
192194 int16_t mouse_x_viewport_screen , mouse_y_viewport_screen ;
@@ -666,6 +668,8 @@ static void *android_input_init(const char *joypad_driver)
666668 android -> quick_tap_time = 0 ;
667669 android -> stylus_proximity_active = false;
668670 android -> stylus_proximity_until_ns = 0 ;
671+ android -> stylus_side_button_active = false;
672+ android -> stylus_side_button_ptr = 0 ;
669673
670674 input_keymaps_init_keyboard_lut (rarch_key_map_android );
671675
@@ -869,6 +873,18 @@ static INLINE void android_input_poll_event_type_motion(
869873 settings = config_get_ptr ();
870874 if (!settings )
871875 return ;
876+
877+ /* Check if stylus support is globally disabled */
878+ if (!settings -> bools .input_stylus_enable )
879+ {
880+ #ifdef DEBUG_ANDROID_INPUT
881+ RARCH_LOG ("[RA Input] Stylus support disabled - ignoring stylus event\n" );
882+ #endif
883+ return ; /* Treat stylus events as if they never happened */
884+ }
885+
886+ /* Get stylus settings once for all cases */
887+ require_contact = settings -> bools .input_stylus_require_contact_for_click ;
872888
873889 switch (action )
874890 {
@@ -894,7 +910,104 @@ static INLINE void android_input_poll_event_type_motion(
894910 RARCH_LOG ("[RA Input] Stylus hover cursor update (user enabled)\n" );
895911#endif
896912 }
897- return ; /* NEVER allow hover to trigger clicks */
913+
914+ /* Handle side button during hover for DRAG, regardless of click/contact policy */
915+ buttons = p_AMotionEvent_getButtonState ?
916+ AMotionEvent_getButtonState (event ) : 0 ;
917+ side_primary = (buttons & AMOTION_EVENT_BUTTON_STYLUS_PRIMARY ) != 0 ;
918+ side_secondary = (buttons & AMOTION_EVENT_BUTTON_STYLUS_SECONDARY ) != 0 ;
919+ bool side_pressed = side_primary || side_secondary ;
920+
921+ #ifdef DEBUG_ANDROID_INPUT
922+ if (buttons != 0 )
923+ RARCH_LOG ("[Stylus Btn] state=0x%x primary=%d secondary=%d\n" ,
924+ buttons , !!side_primary , !!side_secondary );
925+ #endif
926+
927+ /* Side button pressed - start drag operation */
928+ if (side_pressed && !android -> stylus_side_button_active )
929+ {
930+ /* Start new pointer down */
931+ struct video_viewport vp = {0 };
932+
933+ video_driver_translate_coord_viewport_confined_wrap (
934+ & vp , x , y ,
935+ & android -> pointer [motion_ptr ].confined_x ,
936+ & android -> pointer [motion_ptr ].confined_y ,
937+ & android -> pointer [motion_ptr ].full_x ,
938+ & android -> pointer [motion_ptr ].full_y );
939+
940+ video_driver_translate_coord_viewport_wrap (
941+ & vp , x , y ,
942+ & android -> pointer [motion_ptr ].x ,
943+ & android -> pointer [motion_ptr ].y ,
944+ & android -> pointer [motion_ptr ].full_x ,
945+ & android -> pointer [motion_ptr ].full_y );
946+
947+ if (android -> pointer_count < (int )motion_ptr + 1 )
948+ android -> pointer_count = (int )motion_ptr + 1 ;
949+
950+ android -> stylus_side_button_active = true;
951+ android -> stylus_side_button_ptr = motion_ptr ;
952+
953+ #ifdef DEBUG_ANDROID_INPUT
954+ RARCH_LOG ("[Stylus] POINTER DOWN @ (%.1f, %.1f) idx=%zu cnt=%d (side button start)\n" ,
955+ x , y , motion_ptr , android -> pointer_count );
956+ #endif
957+ return ;
958+ }
959+ /* Side button held - continue drag (update coordinates) */
960+ else if (side_pressed && android -> stylus_side_button_active )
961+ {
962+ /* Update existing pointer coordinates */
963+ struct video_viewport vp = {0 };
964+ size_t ptr_idx = android -> stylus_side_button_ptr ;
965+
966+ video_driver_translate_coord_viewport_confined_wrap (
967+ & vp , x , y ,
968+ & android -> pointer [ptr_idx ].confined_x ,
969+ & android -> pointer [ptr_idx ].confined_y ,
970+ & android -> pointer [ptr_idx ].full_x ,
971+ & android -> pointer [ptr_idx ].full_y );
972+
973+ video_driver_translate_coord_viewport_wrap (
974+ & vp , x , y ,
975+ & android -> pointer [ptr_idx ].x ,
976+ & android -> pointer [ptr_idx ].y ,
977+ & android -> pointer [ptr_idx ].full_x ,
978+ & android -> pointer [ptr_idx ].full_y );
979+
980+ #ifdef DEBUG_ANDROID_INPUT
981+ RARCH_LOG ("[Stylus] POINTER MOVE @ (%.1f, %.1f) idx=%zu (side button drag)\n" ,
982+ x , y , ptr_idx );
983+ #endif
984+ return ;
985+ }
986+ /* Side button released - end drag operation */
987+ else if (!side_pressed && android -> stylus_side_button_active )
988+ {
989+ /* End pointer operation */
990+ size_t ptr_idx = android -> stylus_side_button_ptr ;
991+
992+ if (ptr_idx < MAX_TOUCH - 1 )
993+ {
994+ memmove (android -> pointer + ptr_idx ,
995+ android -> pointer + ptr_idx + 1 ,
996+ (MAX_TOUCH - ptr_idx - 1 ) * sizeof (android -> pointer [0 ]));
997+ }
998+ if (android -> pointer_count > 0 )
999+ android -> pointer_count -- ;
1000+
1001+ android -> stylus_side_button_active = false;
1002+
1003+ #ifdef DEBUG_ANDROID_INPUT
1004+ RARCH_LOG ("[Stylus] POINTER UP idx=%zu cnt=%d (side button end)\n" ,
1005+ ptr_idx , android -> pointer_count );
1006+ #endif
1007+ return ;
1008+ }
1009+
1010+ return ; /* Standard hover - no clicks allowed */
8981011
8991012 case AMOTION_EVENT_ACTION_DOWN :
9001013 case AMOTION_EVENT_ACTION_MOVE :
@@ -932,8 +1045,7 @@ static INLINE void android_input_poll_event_type_motion(
9321045 /* Apply contact requirement setting */
9331046 stylus_pressed = require_contact
9341047 ? tip_down /* Only tip contact counts when setting enabled */
935- : (tip_down || side_primary ||
936- action == AMOTION_EVENT_ACTION_DOWN ); /* Tip or side */
1048+ : (tip_down || side_primary ); /* Tip contact OR side button when disabled */
9371049
9381050#ifdef DEBUG_ANDROID_INPUT
9391051 RARCH_LOG ("[RA Input] S Pen contact - req_contact:%s tip_down:%s "
0 commit comments